微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > 移植ucosII到STM32F103ZE(四)

移植ucosII到STM32F103ZE(四)

时间:11-25 来源:互联网 点击:
a) 根据stm32f103芯片对系统文件进行修改
根据AN-1018.pdf 和移植详解介绍的移植基础知识,对OS-uCOSIIport 下的代码解释下。
并进行相关特性修改。
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

#if OS_CRITICAL_METHOD 3
#define OS_ENTER_CRITICAL() {cpu_sr = OS_CPU_SR_Save();}
#define OS_EXIT_CRITICAL() {OS_CPU_SR_Restore(cpu_sr);}
#endif
具体定义宏OS_ENTER_CRITICAL() 和OS_EXIT_CRITICAL()其中OS_CPU_SR_Save() 和OS_CPU_SR_Restore()是用汇编代码写的,代码在 os_cpu_a.asm 中,到时再解释。
#define OS_STK_GROWTH 1
CM3 中,栈是由高地址向低地址增长的,因此 OS_STK_GROWTH定义为 1。
#define OS_TASK_SW() OSCtxSw()
定义任务切换宏,OSCtxSw()是用汇编代码写的,代码在 os_cpu_a.asm 中,到时再解释。
#if OS_CRITICAL_METHOD 3
OS_CPU_SR OS_CPU_SR_Save(void);
void OS_CPU_SR_Restore(OS_CPU_SR cpu_sr);
#endif

void OSCtxSw(void);
void OSIntCtxSw(void);
void OSStartHighRdy(void);
void OS_CPU_PendSVHandler(void); //PendSV 中断服务程序

void OS_CPU_SysTickHandler(void); //SysTick 中断服务程序
void OS_CPU_SysTickInit(void);
INT32U OS_CPU_SysTickClkFreq(void);

声明几个函数,OS_CPU_PendSVHandler(void)要替换为 PendSV__Handler(void)。另外这里最后三个函数需要注释掉,为什么呢?



答案就在启动文件上,一般我们自己开发基于 stm32 芯片的软件,都会使用标准外设库 CMSIS 中提供的启动文件,比如 startup_stm32f10x_hd.s,而 Micrium官方没有用 ST 的标准启动文件,自写了启动文件,而且分开写成了两个.s 文件,即 init.s,vectors.s
(MicriumSoftwareEvalBoardsSTSTM3210B-EVALRVMDK)。init.s 负责进入 main(),vectors.s设置中断向量。OS_CPU_SysTickHandler和OS_CPU_PendSVHandler 这两个中断向量就是在 vectors.s 中被设置的。
使用标准的 startup_stm32f10x_hd.s 作为启动文件的,那该怎么来匹配呢?事实上在 startup_stm32f10x_hd.s 文件中 PendSV 中断向量名为 PendSV_Handler,所以只需用PendSV_Handler 替换掉OS_CPU_C.h和 OS_CPU_A.ASM 中的OS_CPU_PendSVHandler。
这样,替换后的void PendSV_Handler(void)函数在OS_CPU_C.h 中有声明,在OS_CPU_A.ASM 中有具体的中断服务函数代码,在startup_stm32f10x_hd.s中有向量地址,匹配完毕,Ok。
接下来就该处理 SysTick 中断和启动任务了。SysTick是 OS 的“心跳”,可称为滴答时钟,本质上来说就是一个定时器。
同样,在 startup_stm32f10x_hd.s 文件中 SysTick 的中断向量名为 SysTick_Handler,我们本可按照上法继续炮制,但因为 ST 标准库已经有相关库函数,所以我们按照如下法子修改:
把 OS_CPU_C.C 文件中的 void OS_CPU_SysTickHandler (void)的内容代码复制到 stm32f10x_it.c
文件中的 SysTick_Handler (void)函数内;


并且在文件头部添加:#include



这样子,替换后的 SysTick_Handler (void)函数在 stm32f10x_it.h 文件中声明,在stm32f10x_it.c
中有具体代码,在startup_stm32f100x_hd.s中有向量地址;同时需要在App文件中有对 SysTick的初始化函数(后面编写App.c时需要初始化)。
整个过程中 ST 官方标准启动文件啥也没动,防止了误修改。
OS_CPU_SysTickHandler()定义在 os_cpu_c.c 中,是 SysTick中断的中断处理函数,而 stm32f10x_it.c 中已经有该中断函数的定义 SysTick_Handler()),这里也就不需要了。
OS_CPU_SysTickInit() 定义在os_cpu_c.c中,用于初始化SysTick定时器,它依赖于 OS_CPU_SysTickClkFreq(),而此函数我们自己会实现,所以注释掉。
OS_CPU_SysTickClkFreq()定义在 BSP.C (MicriumSoftwareEvalBoards)中,而本文移植中并未用到BSP.C,后面我们会自己实现,因此也将它注释掉,解除我们和 Micrium 官方bsp 的依赖关系。

os_cpu_c.c
ucosii 移植时需要我们写 10个相当简单的C 函数。
OSInitHookBegin()
OSInitHookEnd()
OSTaskCreateHook()
OSTaskDelHook()
OSTaskIdleHook()
OSTaskStatHook()
OSTaskStkInit()
OSTaskSwHook()
OSTCBInitHook()
OSTimeTickHook()
这些函数除了 OSTaskStkInit(),都是一些 hook 函数。这些 hook 函数如果不使能的话,都不会用上,也都比较简单,看看就应该明白了,所以就不介绍。
下面就说一说 OSTaskStkInit()。说之前还是得先说一下任务切换,因为初始化任务堆栈,是为任务切换服务的。代码在正常运行时,一行一行往下执行,怎么才能跑到另一个任务(即函数)执行呢?首先大家可以回想一下中断过程,当中断发生时,原来函数执行的地方(程序计数器PC、处理器状态寄存器及所有通用寄存器,即当前代码的现场)被保存到栈里面去了,然后开始取中断向量,跑到中断函数里面执行。执行完了呢,想回到原来函数执行的地方,该怎么办呢,只要把栈中保存的原来函数执行的信息恢复即可(把栈中保存的代码现场重新赋给 cpu 的各个寄存器),一切就都回去了,好像什么事都没发生一样。这个过程大家应该都比较熟悉,任务切换和这有什么关系,试想一下,如果有 3 个函数 foo1(), foo2(), foo3() 都像是刚被中断,现场保存到栈里面去了,而中断返回时做点手脚(调度程序的作用),想回哪个回哪个,是不是就做了函数(任务)切换了。看到这里应该有点明白 OSTaskStkInit()的作用了吧,它被任务创建函数调用,所以要在开始时,在栈中作出该任务好像刚被中断一样的假象。(关于任务切换的原理邵老师书中的 3.06 节有介绍)。
那么中断后栈中是个什么情形呢,<>中 9.1.1 有介绍,xPSR,PC,LR,R12,R3-R0 被自动保存到栈中的,R11-R4如果需要保存,只能手工保存。因此 OSTaskStkInit()的工作就是在任务自己的栈中保存 cpu 的所有寄存器。这些值里 R1-R12 都没什么意义,这里用相应的数字代号(如 R1 用0x01010101)主要是方便调试。
OS_STK *OSTaskStkInit (void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT16U opt)
{
OS_STK *stk;
(void)opt;
stk = ptos;


*(stk) = (INT32U)0x01000000L;
*(--stk) = (INT32U)task;
*(--stk) = (INT32U)0xFFFFFFFEL;
*(--stk) = (INT32U)0x12121212L;
*(--stk) = (INT32U)0x03030303L;
*(--stk) = (INT32U)0x02020202L;
*(--stk) = (INT32U)0x01010101L;
*(--stk) = (INT32U)p_arg;


*(--stk) = (INT32U)0x11111111L;
*(--stk) = (INT32U)0x10101010L;
*(--stk) = (INT32U)0x09090909L;
*(--stk) = (INT32U)0x08080808L;
*(--stk) = (INT32U)0x07070707L;
*(--stk) = (INT32U)0x06060606L;
*(--stk) = (INT32U)0x05050505L;
*(--stk) = (INT32U)0x04040404L;

return (stk);
}
xPSR = 0x01000000L,xPSR T 位(第24 位)置 1,否则第一次执行任务时 Fault,
PC 肯定得指向任务入口,
R14 = 0xFFFFFFFEL,最低4位为E,是一个非法值,主要目的是不让使用 R14,即任务是不能返回的。R0 用于传递任务函数的参数,因此等于 p_arg。
把 OS_CPU_SysTickHandler(), OS_CPU_SysTickInit()这两个函数的内容代码注释掉。
os_cpu_c.c文件中的





#define OS_CPU_CM3_NVIC_ST_CTRL (*((volatile INT32U *)0xE000E010))
#define OS_CPU_CM3_NVIC_ST_RELOAD (*((volatile INT32U *)0xE000E014))
#define OS_CPU_CM3_NVIC_ST_CURRENT (*((volatile INT32U *)0xE000E018))
#define OS_CPU_CM3_NVIC_ST_CALL (*((volatile INT32U *)0xE000E01C))

#define OS_CPU_CM3_NVIC_ST_CTRL_COUNT 0x00010000
#define OS_CPU_CM3_NVIC_ST_CTRL_CLK_SRC 0x00000004
#define OS_CPU_CM3_NVIC_ST_CTRL_INTEN 0x00000002
#define OS_CPU_CM3_NVIC_ST_CTRL_ENABLE 0x00000001
把上面这些宏定义也注释掉,因为它们都用于 OS_CPU_SysTickHandler(), OS_CPU_SysTickInit()。


Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top