uCOS上下文切换,PendSV中断函数

摘自:http://www.stmcu.org/module/forum/thread-384142-1-1.html

介绍一:
移植详解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被类型重定义为unsignedint。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);
 
void            OS_CPU_SysTickHandler(void);
void            OS_CPU_SysTickInit(void);
INT32U        OS_CPU_SysTickClkFreq(void);
      申明几个函数,这里要注意最后三个函数需要注释掉,为什么呢?
      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(Micrium\Software\EvalBoards)中,而本文移植中并未用到BSP.C,后面我们会自己实现,因此可以把它注释掉。
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)主要是方便调试。
      其他几个:
      xPSR =0x01000000L,xPSR T位(第24位)置1,否则第一次执行任务时Fault,
      PC肯定得指向任务入口,
      R14 =0xFFFFFFFEL,最低4位为E,是一个非法值,主要目的是不让使用R14,即任务是不能返回的。
      R0用于传递任务函数的参数,因此等于p_arg。     
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);
}
把OS_CPU_SysTickHandler(), OS_CPU_SysTickInit()注释掉。
#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 (*((volatileINT32U *)0xE000E018)) 
#define  OS_CPU_CM3_NVIC_ST_CAL        (*((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()。
os_cpu_a.asm
这个文件包含着必须用汇编写的代码。
      EXTERN  OSRunning      ;External references
      EXTERN  OSPrioCur
      EXTERN  OSPrioHighRdy
      EXTERN  OSTCBCur
      EXTERN  OSTCBHighRdy
      EXTERN  OSIntNesting
      EXTERN  OSIntExit
      EXTERN  OSTaskSwHook
      申明这些变量是在其他文件定义的,本文件只做引用(有几个好像并未引用,不过没有关系)。
      EXPORT  OS_CPU_SR_Save    ; Functionsdeclared in this file
      EXPORT  OS_CPU_SR_Restore
      EXPORT  OSStartHighRdy
      EXPORT  OSCtxSw
      EXPORT  OSIntCtxSw
      EXPORT  OS_CPU_PendSVHandler
      申明这些函数是在本文件中定义的。
NVIC_INT_CTRL    EQU        0xE000ED04    ;中断控制及状态寄存器ICSR的地址
NVIC_SYSPRI14    EQU        0xE000ED22   ;endSV优先级寄存器的地址
NVIC_PENDSV_PRIEQU                    0xFF   ;endSV中断的优先级为255(最低)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值