一数据类型
/******************************************************************************
* 定义数据类型
******************************************************************************/
typedef unsigned char BOOLEAN;
typedef unsigned char INT8U; /*Unsigned 8 bit quantity */
typedef signed char INT8S; /*Signed 8 bit quantity */
typedef unsigned short INT16U; /* Unsigned 16 bitquantity */
typedef signed short INT16S; /* Signed 16bit quantity */
typedef unsigned int INT32U; /*Unsigned 32 bit quantity */
typedef signed int INT32S; /*Signed 32 bit quantity */
typedef float FP32; /*Single precision floating point*/
typedef double FP64; /*Double precision floating point*/
typedef unsigned int OS_STK; /*Each stack entry is 32-bit wide*/
typedef unsigned int OS_CPU_SR; /*Define size of CPU status register*/
解析:因为CM3是32位宽的,所有OS_STK被类型重定义成32位的unsigned int,CM3的状态寄存器(xPSR)也是32位宽的,OS_CPU_SR被类型重定义为unsigned int类型重定义是为了方便移植,不同处理器编译不一样,比如ARM7 处理器字长为32位,半字长为16位,字节为8位,因此在移植时涉及到变量类型时只需要修改类型重定义即可。
二临界区
/*******************************************************************************OS_CRITICAL_METHOD=1直接使用处理器的开关中断指令来实线宏
*OS_CRITICAL_METHOD=2 利用堆栈保护和恢复CPU状态
*OS_CRITICAL_METHOD=3 利用编译器扩展功能获得的程序状态字,保存在局部变量cpu_sr中
******************************************************************************/
#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
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR OS_CPU_SR_Save(void);
void OS_CPU_SR_Restore(OS_CPU_SR cpu_sr);
解析:一般喜欢使用第三种方法来实现临界区。
第一种直接开关中断的实现临界区的方法很少采用,因为这种方法可能将原本关闭了的中断意外的打开。
(简单的如51单片机的开关中断指令EA=1与EA=0,即实现直接使用处理器的开关中断,但问题是调用了OS_EXIT_CRITICAL后,处理器的中断一定为打开,而无论在进入临界代码之前的处理器的中断是打开或者关闭。但最理想的状态应该是,在用了OS_EXIT_CRITICAL后,处理器的中断打开与否状态,应该跟进入临界代码段之前处理器的中断状态一致)
第二种方法是最高效的实现方法,但是这种方法调整了堆栈指针,对于需要利用堆栈指针间接寻址局部变量的系统并不适用。(x86通常采用第二种方法,因为它有单独的寄存器来做局部变量的寻址)
(即在关闭中断之前,通过堆栈缓存CPU的中断寄存器状态,即记住进入临界代码之前CPU的中断打开与否状态,然后在推出临界代码的时候,只需将堆栈中的CPU中断状态寄存器弹出堆栈,这样即能实现用了宏OS_EXIT_CRITICAL后,处理器的中断打开与否状态,跟进入临界代码段之前处理器的中断状态一致)
第三种方法最保险,虽然效率比第二种方法略低一点。
(一些处理器可以读取PSW寄存器的值,将包含中断开关状态的PSW寄存器值保持在C语言变量中没在退出临界代码段时,再将局部变量中的值回写到PSW寄存器中。STM32可以通过MRS与MSR指令读写特殊寄存器,因此选用了第三种方式)
OS_CPU_SR_Save()和OS_CPU_SR_Restore(cpu_sr)在os_cpu_a.asm中实现
其实也可以通过C代码(OS_CPU_C.C)中插入汇编的方式来实现:
1. __asm OS_CPU_SR OS_CPU_SR_Save(void)
2. {
3. MRS R0, PRIMASK ; Set prio int mask to mask all (except faults)
4. CPSID I
5. BX LR
6. }
7. __asm void OS_CPU_SR_Restore(OS_CPU_SR cpu_sr)
8. {
9. MSR PRIMASK, R0
10. BX LR
11. }
三堆栈
#define OS_STK_GROWTH 1
CM3中,栈是由高地址向低地址生长的,因此OS_STK_GROWTH设为1
四任务切换
#define OS_TASK_SW() OSCtxSw()
任务切换宏,由汇编实现,在os_cpu_a.asm中
五函数原型声明
未作修改应该是这样
void OSCtxSw(void);
void OSIntCtxSw(void);
void OSStartHighRdy(void);
void OS_CPU_PendSVHandler(void);
void OS_CPU_SysTickHandler(void);
void OS_CPU_SysTickInit(INT32U cnts);
修改:
A:将OS_CPU_PendSVHandler(void)改为PendSV_Handler(void)
B:注释掉
OS_CPU_SysTickHandler(void)和OS_CPU_SysTickInit(INT32U cnts)
详细分析:
A:修改为PendSV_Handler
UCOSII在stm32中的上下文切换是通过触发一个PendSV的异常(优先级较低),然后再异常服务程序里实现的。os_cpu_a.asm中标号为OS_CPU_PendSVHandler和OS_CPU_PendSVHandler_nosave的内容即是。
但是Micrium官方没有ST公司官方的启动代码,用的是自己的启动代码,在startup_stm32f10x_cl.s中看到在启动设置中断向量表时,PendSV的异常标号为PendSV_Handler,因此两者有矛盾,可选择不改变ST官方的文件,即不改变启动代码,修改ucosII的接口文件中的代码
修改PendSV
(1)在os_cpu.a.asm中
(2)在os_cpu.h中
(3)在stm32f10x_it.h中
B:注释掉OS_CPU_SysTickHandler(void)
OS_CPU_SysTickHandler(void)定义在os_cpu_c.c中,是systick的中断处理函数,而在stm32f10x_it.c中已经有该函数的定义
C:注释掉OS_CPU_SysTickInit(INT32U cnts)
OS_CPU_SysTickInit(INT32U cnts)定义在os_cpu_c.c中,用以初始化systick定时器,此函数我们自己实现,故注释掉