logo资料库

ucosii在STM32上的移植详解.pdf

第1页 / 共24页
第2页 / 共24页
第3页 / 共24页
第4页 / 共24页
第5页 / 共24页
第6页 / 共24页
第7页 / 共24页
第8页 / 共24页
资料共24页,剩余部分请下载后查看
ucosii在stm32 上的移植详解 虽然目前网上已经有不少关于 ucosii 在 stm32 上的移植版本,包括 micrium 也 有官方移植版本。但这些版本具体是怎么移植出来的,又该怎么基于移植好的 ucosii 开发应用软件,网上介绍的并不多。这里介绍一下我的移植经历,希望对大家有所 帮助。 我的移植基本上是从零开始的。首先想要做好移植,有两方面的内容是必须要 了解。1.目标芯片;2.ucosii 内核原理。 虽然我们移植的目标芯片是 stm32,但操作系统的移植基本是针对 Cortex-M3 内核(以下简称 CM3)而言的,所以我们只需了解 CM3 内核就好了。stm32 芯片 就是 CM3 内核加上各种各样的外设。 怎么才能了解 CM3 呢?看一本书<>(宋岩译,网 上多的很)就好了,很多同学可能想,看完这本书移植的新鲜劲都没了,因此我把 该书和移植有关的章节都列了出来,并对其中的重点内容进行介绍,我数了数相关 章节还不到 100 页,就这点内容,总要看了吧。 相关章节如下: chapter2 Cortex-M3 概览 2.1 - 2.9 主要了解 Cortex-M3 的概貌。刚开始看时不用追求全部理解,后面会有详细介 绍,很多内容多看几遍就明白。其中 2.8 指令集,只要了解,CM3 只使用 thumb2 就 ok 了。 chapter3 Cortex-M3 基础 3.1 寄存器组 R0-R12: 通用寄存器 R13: 堆栈寄存器 有两个,MSP 和 PSP,同时只能看见一个 引用 R13 时,引用的是正在使用的那个 MSP:可用于异常服务和应用程序 PSP:只能用于应用程序 系统复位后,用的堆栈指针是 MSP。 R14: 连接寄存器,又名 LR,存储返回地址 R15: 程序计数寄存器,又名 PC 3.2 特殊功能寄存器 程序状态字寄存器组(PSRs) 中断屏蔽寄存器组(PRIMASK, FAULTMASK, BASEPRI) 控制寄存器(CONTROL) 程序状态字寄存器组(PSRs)分为 应用程序 PSR(APSR) 中断号 PSR(IPSR) 执行 PSR(EPSR)
每个都是 32 位,由于这 3 个寄存器有效位是错开的,因此可以组合访问。 中断屏蔽寄存器组(PRIMASK, FAULTMASK, BASEPRI),这三个寄存器用于 控制异常的使能和除能。 控制寄存器(CONTROL)它有两个作用: 1.定义特权级别 2.选择当前使用哪个堆栈指针 3.3 操作模式和特权极别 操作模式: 处理者模式和线程模式 异常处理:处理者模式 主程序:线程模式 ucosii 不区分特权级和用户级,程序始终工作在特权级 这两个堆栈指针的切换是全自动的,就在出入异常服务例程时由硬件处理。 3.4 - 3.7 没什么好讲的,需要看。 3.8 复位序列 0x00000000 MSP 初值 0x00000004 PC 初值 复位向量 chapter7 异常 7.1 异常类型 分为系统异常(编号 1-15)和外部中断(大于 15) 7.2 优先级 CM3 支持 3 个固定的高优先级和多达 256 级的可编程优先级。 在 NVIC 中,每个中断都有一个优先级配置寄存器(1 个 byte),用来配置该 中断的优先级。但该寄存器并不是每个位都被使用,不同制造商生产的芯片不相同, 譬如 stm32 使用 4 位,也就是说 stm32 支持 16 个可编程优先级(参考:chapter9)。 注意该寄存器是以 MSB 对齐的,因此 stm32 每个中断的优先级配置寄存器 7:4 位有效,3:0 位无效。 对于优先级,CM3 又分为抢占优先级和亚优先级, NVIC 中的应用程序中断及复位控制寄存器(AIRCR)的优先级分组(10:8)描述了 如何划分抢占优先级和亚优先级。 什么意思?以 stm32 为例,优先级配置寄存器不是 7:4 位有效吗,如果 AIRCR 中的优先级分组值为 4,则优先级配置寄存器的 7:5 位确定抢占优先级,位 4 确定 亚优先级。此时所有中断有 8 个抢占优先级,每个抢占优先级有 2 个亚优先级。 抢占优先级高的中断可以抢占抢占优先级低的中断,即抢占优先级决定了中断是 否可以嵌套。 相同抢占优先级的中断不能嵌套,但当抢占优先级相同的异常有不止一个到来 时,就优先响应亚优先级最高的异常。 参考附录 D 表 D.9 中断优先级寄存器阵列 0xE000_E400 - 0xE000_E4EF 共 240 个。 表 D.16 系统异常优先级寄存器 0xE000_ED18 - 0xE000_ED23 共 12 个。 优先级相同,看中断号,中断号小的优先。
7.3 向量表 初始在 0x00000000 处,可以通过向量表偏移量寄存器(VTOR)(地址: 0xE000_ED08)更改,一般无需更改。 7.4 中断输入及挂起行为 需要看。 7.5 Fault 异常 可不看。 7.6 SVC 和 PendSV SVC SVC 主要用在分特权级和用户级的操作系统,ucosii 不区分特权级和用户级, 可以不管这个东西。 这里说点题外话,一开始我很奇怪为什么会提供这种中断,因为这种中断一般 都是用在大型的操作系统上,如 linux 系统上,可 CM3 又不提供 MMU,应该是无 法移植 linux 系统。后来我才知道 uclinux 是针对没有 MMU 的嵌入式系统而设计的, 不过还是很怀疑有人会在像 stm32 这种芯片上用 uclinux。 PendSV PendSV 中断主要做上下文切换,也就是任务切换,是 ucosii 移植过程中最重要 的中断。 主要有两点: 1.PendSV 中断是手工往 NVIC 的 PendSV 悬起寄存器中写 1 产生的(由 OS 写)。 2.PendSV 中断优先级必须设为最低。 在讲移植代码时会介绍具体是如何做的。 对于 7.6 的 PendSV 部分应认真研读一下。 chapter8 NVIC 与中断控制 NVIC 负责芯片的中断管理,它和 CM3 内核紧密相关。 如果对于 CM3 中断配置不是很了解,可以看看 8.1, 8.2, 8.3, 8.4 节。 8.7 节讲述了 SysTick 定时器,需要看。 chapter9 中断的具体行为 9.1 中断/异常的响应序列 当 CM3 开始响应一个中断时 1.xPSR, PC, LR, R12 以及 R3‐R0 入栈 2.取向量 3.选择堆栈指针 MSP/PSP,更新堆栈指针 SP,更新连接寄存器 LR,更新程序 计数器 PC 对移植 ucosii 来说,需要注意 1,3 9.2 异常返回 在 CM3 中,进入中断时,LR 寄存器的值会被自动更新。9.6 节对更新后的值进 行说明。这里统称 EXC_RETURN。返回时通过把 EXC_RETURN 往 PC 里写来识 别返回动作的。因为 EXC_RETURN 是一个特殊值,所以对于 CM3,汇编语言就 不需要类似 reti 这种指令,而用 C 语言开发时,不需要特殊编译器命令指示一个函
数为中断服务程序。实际上,中断服务程序如果是 c 代码编写,汇编成汇编代码, 函数结尾一般是 reti。 9.3 嵌套的中断 只要注意:中断嵌套不能过深即可。 9.4 和 9.5 这两节说明 CM3 对中断的响应能力大大提高了,主要是硬件机制的改进。但对 移植来说,并不需要关注。 9.6 异常返回值 对不同状态进入中断时,LR 寄存器的值进行说明,需要看。这里有一点需要注 意,该点在讲移植代码时再介绍。 9.7 和 9.8 对移植来说,并不需要关注。 chapter10 Cortex-M3 的低层编程 这一章仅需关注 10.2 节,因为对移植来说汇编与 C 的接口是必须面对的。 10.2 汇编与 C 的接口 有两点需要知道: 1.当主调函数需要传递参数(实参)时,它们使用 R0‐R3。其中 R0 传递第一 个,R1 传递第 2 个……在返回时,把返回值写到 R0 中。 2.在函数中,用汇编写代码时,R0-R3, R12 可以随便使用,而使用 R4‐R11, 则必须先 PUSH,后 POP。 以上内容和移植多少都有些关系,刚开始看,可能不太明白,多看几遍就好了。
ucosii在stm32 上的移植详解 2 在详解 1 中主要讲了移植需要用到的 CM3 内核知识,本文讲一讲 ucosii 的原理 和代码组成。ucosii 最经典的学习资料莫过于邵贝贝老师的<<嵌入式实时操作系统 uc/os-ii(第 2 版)>>,我想这本书对学 ucosii 已经足够了,因为他把 ucosii V2.55 代 码都讲了一遍。移植前应该好好看看此书。 下面说说我对 ucosii 的理解。应该说 ucosii 这个内核还是比较简单的,基本可 以分为任务调度,任务同步和内存管理三个部分。 任务调度 ucosii 为保证实时性,给每个任务分配一个不同的优先级。当发生任务切换时, 总是切换到就绪的最高优先级任务。有 2 种情况会发生任务切换。 1.任务等待资源就绪或自我延时; 2.退出中断; 情况 1 可以理解为任务主动放弃 cpu 的使用权。 情况 2 可以理解为中断后,某种资源可能就绪了,需要任务切换。 需要注意的是 SysTick 中断,这个中断是 os 的“心跳”,必须得有。这样就使得 cpu 会发生周期性地做任务切换。由于 ucosii 不支持时间片轮转调度,因此在该中 断中必须做的工作仅有 os 的时间管理。也就是调用 OSTimeTick()。 任务同步 任务同步和大多数操作系统的做法差不多,如果学过操作系统或是有多线程编 程经验的话,应该很好理解。无非是任务 A 因为某个资源未就绪,就放弃 cpu 使用 权,等任务 B 或是中断使该资源就绪,当再次任务进行切换时如果任务 A 优先级最 高,则任务 A 继续执行。具体怎么实现就看邵老师的书吧。 内存管理 ucosii 的内存管理比较简单,就不说了。 下面看看 ucosii 代码组成: os_core.c 是 ucosii 的核心,它包含了内核初始化,任务切换,事件块管理等, 其中事件块是各个同步量(这里我把互斥量,信号量,邮箱,队列统称为同步量, 不是很科学,图个方便。事件标志组不是以事件块为基础的,不过原理也差不多) 的基础。
os_task.c 任务管理代码。 os_flag.c os_mbox.c os_mutex.c os_q.c os_sem.c 各个同步量管理代码。 os_mem.c 内存管理代码。 os_time.c 时间管理代码,主要做各种延时。 os_tmr.c 定时器管理代码,这部分代码时从 V2.81 版才开始有的,邵老师的书讲的是 V2.55 版的代码,是没有这部分内容的。如果前面的代码都理解的话,这部分代码 也是不难理解的。一个定时器大体由 3 部分组成:定时时间,回调函数和属性。当 定时时间到了的话,就进行一次回调函数的处理,定时器属性说明定时器是周期性 的定时还是只做一次定时。如果用户使能了 OS_TMR_EN,ucosii 会在内部创建一 个定时器任务,负责处理各个定时器。这个任务一般应该由硬件定时器的中断函数 中调用 OSTmrSignal()去激活。所以从本质上说 os_tmr.c 中的定时器是由一个硬件 定时器分化出来的。 默认情况下是由 SysTick 中断里通过 OSTimeTickHook()去激活定时器任务的。 移植相关文件 os_cpu.h: 进行数据类型定义,处理器相关代码和几个函数原型。 os_cpu_c.c: 定义一些用户 hook 函数。 os_cpu_a.asm: 移植需要用汇编代码完成的函数,主要就是任务切换函数。 os_dbg.c: 内核调试相关数据和函数,可以不改。 ucosii 内核就介绍到这里。
ucosii在stm32 上的移植详解 3 移植详解 1 和 2 中主要讲了移植需要用到的基础知识,本文则对具体的移植过 程进行介绍。 首先从 micrium 网站上下载官方移植版本(编译器使用 ARM/Keil 的,V2.86 版 本,V2.85 有问题)。 下载地址:http://micrium.com/page/downloads/ports/st/stm32 解压缩后得到如下文件夹和文件: Micrium\ AppNotes Licensing Software ReadMe.pdf AppNotes 包含 ucosii 移植说明文件。这两个文件中我们仅需关心 Micrium\AppNotes\AN1xxx-RTOS\AN1018-uCOS-II-Cortex-M3\AN-1018.pdf。因 为这个文件对 ucosii 在 CM3 内核移植过程中需要修改的代码进行了说明。 Licensing 包含 ucosii 使用许可证。 Software 下有好几个文件夹,在本文的移植中仅需关心 uCOS-II 即可。 CPU: stm32 标准外设库 EvalBoards: micrium 官方评估板相关代码 uc-CPU: 基于 micrium 官方评估板的 ucosii 移植代码 uC-LCD:micrium 官方评估板 LCD 驱动代码 uc-LIB: micrium 官方的一个库代码 uCOS-II: ucosii 源代码 uC-Probe: 和 uC-Probe 相关代码 ReadMe.pdf 就不说了。 好了,官方的东西介绍完了,该我们自己建立工程着手移植了。关于建立工程, 并使用stm32 标准外设库在我之前的文章《stm32 标准外设库使用详解》已有介绍, 这里请大家下载其中模板代码(http://download.csdn.net/source/3448543),本文 的移植是基于这个工程的。 建立文件夹 template\src\ucosii template\src\ucosii\src template\src\ucosii\port;
把 Micrium\Software\uCOS-II\Source 下的文件拷贝至 template\src\ucosii\src; 把 Micrium\Software\uCOS-II\Ports\ARM-Cortex-M3\Generic\RealView 下的文 件拷贝至 template\src\ucosii\port; ucosii\src 下的代码是 ucosii 中无需修改部分 ucosii\port 下的代码是移植时需要修改的。为防止对源码的误改动造成移植失 败,可以把 ucosii\src 下的代码文件设为只读。 这里根据 AN-1018.pdf 和移植详解 1、2 中介绍的移植基础知识,对 ucosii\port 下的代码解释一下。 os_cpu.h #ifdef OS_CPU_GLOBALS #define OS_CPU_EXT #else #define OS_CPU_EXT extern #endif typedef unsigned char BOOLEAN; typedef unsigned char INT8U; typedef signed char INT8S; typedef unsigned short INT16U; typedef signed short INT16S; typedef unsigned int INT32U; typedef signed int INT32S; typedef float FP32; typedef double FP64; 就不解释了。 typedef unsigned int OS_STK; typedef unsigned int OS_CPU_SR; 因为 CM3 是 32 位宽的,所以 OS_STK(堆栈的数据类型)被类型重定义为 unsigned int。 因为 CM3 的状态寄存器(xPSR)是 32 位宽的,因此 OS_CPU_SR 被类型重 定义为 unsigned int。OS_CPU_SR 是在 OS_CRITICAL_METHOD 方法 3 中保存 cpu 状态寄存器用的。在 CM3 中,移植 OS_ENTER_CRITICAL(), OS_EXIT_CRITICAL()选方法 3 是最合适的。 #define OS_CRITICAL_METHOD 3
分享到:
收藏