在systick中断中,调用ucos的时钟服务程序,核心函数~
具体真正的任务切换,其实写在汇编语言os_cpu_a.asm里面,如果不感兴趣,可以直接跳过:
- ;/*********************** (C) COPYRIGHT 2010 Libraworks *************************
- ;* File Name : os_cpu_a.asm
- ;* Author : Librae
- ;* Version : V1.0
- ;* Date : 06/10/2010
- ;* Description : μCOS-II asm port for STM32
- ;*******************************************************************************/
- IMPORT OSRunning ; External references
- IMPORT OSPrioCur
- IMPORT OSPrioHighRdy
- IMPORT OSTCBCur
- IMPORT OSTCBHighRdy
- IMPORT OSIntNesting
- IMPORT OSIntExit
- IMPORT OSTaskSwHook
-
- EXPORT OSStartHighRdy
- EXPORT OSCtxSw
- EXPORT OSIntCtxSw
- EXPORT OS_CPU_SR_Save ; Functions declared in this file
- EXPORT OS_CPU_SR_Restore
- EXPORT PendSV_Handler
-
-
- NVIC_INT_CTRL EQU 0xE000ED04 ; 中断控制寄存器
- NVIC_SYSPRI2 EQU 0xE000ED22 ; 系统优先级寄存器(2)
- NVIC_PENDSV_PRI EQU 0xFFFF ; PendSV中断和系统节拍中断
- ; (都为最低,0xff).
- NVIC_PENDSVSET EQU 0x10000000 ; 触发软件中断的值.
- PRESERVE8
-
- AREA |.text|, CODE, READONLY
- THUMB
-
-
- ;********************************************************************************************************
- ; CRITICAL SECTION METHOD 3 FUNCTIONS
- ;
- ; Description: Disable/Enable interrupts by preserving the state of interrupts. Generally speaking you
- ; would store the state of the interrupt disable flag in the local variable 'cpu_sr' and then
- ; disable interrupts. 'cpu_sr' is allocated in all of uC/OS-II's functions that need to
- ; disable interrupts. You would restore the interrupt disable state by copying back 'cpu_sr'
- ; into the CPU's status register.
- ;
- ; Prototypes : OS_CPU_SR OS_CPU_SR_Save(void);
- ; void OS_CPU_SR_Restore(OS_CPU_SR cpu_sr);
- ;
- ;
- ; Note(s) : 1) These functions are used in general like this:
- ;
- ; void Task (void *p_arg)
- ; {
- ; #if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
- ; OS_CPU_SR cpu_sr;
- ; #endif
- ;
- ; :
- ; :
- ; OS_ENTER_CRITICAL(); /* cpu_sr = OS_CPU_SaveSR(); */
- ; :
- ; :
- ; OS_EXIT_CRITICAL(); /* OS_CPU_RestoreSR(cpu_sr); */
- ; :
- ; :
- ; }
- ;********************************************************************************************************
- OS_CPU_SR_Save
- MRS R0, PRIMASK ;读取PRIMASK到R0,R0为返回值
- CPSID I ;PRIMASK=1,关中断(NMI和硬件FAULT可以响应)
- BX LR ;返回
- OS_CPU_SR_Restore
- MSR PRIMASK, R0 ;读取R0到PRIMASK中,R0为参数
- BX LR ;返回
- ;/**************************************************************************************
- ;* 函数名称: OSStartHighRdy
- ;*
- ;* 功能描述: 使用调度器运行第一个任务
- ;*
- ;* 参 数: None
- ;*
- ;* 返 回 值: None
- ;**************************************************************************************/
- OSStartHighRdy
- LDR R4, =NVIC_SYSPRI2 ; 设置PendSV的优先级
- LDR R5, =NVIC_PENDSV_PRI
- STR R5, [R4]
- MOV R4, #0 ; 设置PSP为0用于初始化上下文切换
- MSR PSP, R4
- LDR R4, =OSRunning ; 设置OSRunning为1
- MOV R5, #1
- STRB R5, [R4]
- ;切换到最高优先级的任务
- LDR R4, =NVIC_INT_CTRL
- LDR R5, =NVIC_PENDSVSET
- STR R5, [R4] ;触发上下文切换
- CPSIE I ;开中断
- OSStartHang
- B OSStartHang ;死循环,程序不会执行到这
- ;/**************************************************************************************
- ;* 函数名称: OSCtxSw
- ;*
- ;* 功能描述: 任务级上下文切换
- ;*
- ;* 参 数: None
- ;*
- ;* 返 回 值: None
- ;***************************************************************************************/
-
- OSCtxSw
- PUSH {R4, R5}
- LDR R4, =NVIC_INT_CTRL ;触发PendSV异常
- LDR R5, =NVIC_PENDSVSET
- STR R5, [R4] ;向NVIC_INT_CTRL写入NVIC_PENDSVSET触发PendSV中断
- POP {R4, R5}
- BX LR
- ;/**************************************************************************************
- ;* 函数名称: OSIntCtxSw
- ;*
- ;* 功能描述: 中断级任务切换
- ;*
- ;* 参 数: None
- ;*
- ;* 返 回 值: None
- ;***************************************************************************************/
- OSIntCtxSw
- PUSH {R4, R5}
- LDR R4, =NVIC_INT_CTRL ;触发PendSV异常
- LDR R5, =NVIC_PENDSVSET
- STR R5, [R4]
- POP {R4, R5} ;向NVIC_INT_CTRL写入NVIC_PENDSVSET触发PendSV中断
- BX LR
- NOP
- ;/**************************************************************************************
- ;* 函数名称: OSPendSV
- ;*
- ;* 功能描述: OSPendSV is used to cause a context switch.
- ;*
- ;* 参 数: None
- ;*
- ;* 返 回 值: None
- ;***************************************************************************************/
- PendSV_Handler
- CPSID I ; 关中断,任务切换期间要关中断
- MRS R0, PSP ; 如果在用PSP堆栈,则可以忽略保存寄存器,参考CM3权威中的双堆栈
- CBZ R0, PendSV_Handler_Nosave ; 如果PSP为0就跳转到PendSV_Handler_Nosave
-
- TST R14, #0x10 ; 如果当前任务使用到FPU的话就保存S16-S31寄存器
- IT EQ
- VSTMDBEQ R0!, {S16-S31}
-
- SUBS R0, R0, #0x20 ; R0-=0x20
- STM R0, {R4-R11} ; 保存剩下的R4-R11寄存器
- LDR R1, =OSTCBCur ; OSTCBCur->OSTCBStkPtr = SP;
- LDR R1, [R1]
- STR R0, [R1]
-
- PendSV_Handler_Nosave
- ;OSTaskSwHook用于扩展任务切换代码的功能
- PUSH {R14} ; 保存R14的值,因为接着需要调用函数
- LDR R0, =OSTaskSwHook ; 获取OSTaskSwHook函数地址保存到R0
- BLX R0 ; 调用OSTaskSwHook
- POP {R14} ; 恢复R14
- ;指向当前最高优先级的任务
- LDR R0, =OSPrioCur ; 将OSPrioCur变量地址保存到R0
- LDR R1, =OSPrioHighRdy ; 将OSPrioHighRdy变量地址保存到R1
- LDRB R2, [R1] ; 获取OSPrioHighRdy的值保存到R2
- STRB R2, [R0] ; 将R2的值写入OSPrioCur变量中
-
- ;指向就绪的最高优先级的任务控制块
- LDR R0, =OSTCBCur ; 将OSTCBCur变量地址保存到R0
- LDR R1, =OSTCBHighRdy ; 将OSTCBHighRdy变量地址保存到R1
- LDR R2, [R1] ; 获取OSTCBHighRdy的值保存到R2
- STR R2, [R0] ; 将R2的值写入OSTCBCur变量中
- LDR R0, [R2] ; R0=*R2,即R0=OSTCBHighRdy,R0是新任务的SP(堆栈)
- LDM R0, {R4-R11} ; 从堆栈中恢复R4-R11
- ADDS R0, R0, #0x20 ; R0+=20
- ;任务如果使用FPU的话就将S16-S31从堆栈中恢复出来
- TST R14, #0x10
- TST R14, #0x10
- IT EQ
- VLDMIAEQ R0!, {S16-S31}
-
- MSR PSP, R0 ; PSP=R0,用新任务的SP加载PSP
- ORR LR, LR, #0x04 ; 确保LR的位2为1,返回后使用进程堆栈
- CPSIE I ; 开中断
- BX LR ; 中断返回
- NOP
- end
-
移植后,我们创建3个任务:
定义堆栈及任务名
- /* START 任务 */
- /* 设置任务优先级 */
- #define START_TASK_PRIO 10
- /* 设置任务堆栈大小 */
- #define START_STK_SIZE 64
- /* 创建任务堆栈空间 */
- OS_STK START_TASK_STK[START_STK_SIZE];
- /* 任务函数接口 */
- void StartTask(void *pdata);
- /* LED1 任务 */
- /* 设置任务优先级 */
- #define LED1_TASK_PRIO 7
- /* 设置任务堆栈大小 */
- #define LED1_STK_SIZE 64
- /* 创建任务堆栈空间 */
- OS_STK LED1_TASK_STK[LED1_STK_SIZE];
- /* 任务函数接口 */
- void Led1Task(void *pdata);
- /* printf 任务 */
- /* 设置任务优先级 */
- #define PRINTF_TASK_PRIO 6
- /* 设置任务堆栈大小 */
- #define PRINTF_STK_SIZE 64
- /* 创建任务堆栈空间 */
- OS_STK PRINTF_TASK_STK[PRINTF_STK_SIZE];
- /* 任务函数接口 */
- void PrintfTask(void *pdata);
具体任务函数:
- /****************************************
- *函数名称:StartTask
- *输 入:pdata -传入的参数
- *输 出:无
- *功 能:起始任务
- ******************************************/
- void StartTask(void *pdata)
- {
- OS_CPU_SR cpu_sr=0;
- pdata = pdata;
-
- /* 初始化统计任务.这里会延时1秒钟左右 */
- OSStatInit();
-
- /* 进入临界区(无法被中断打断) */
- OS_ENTER_CRITICAL();
-
- /* 创建led1任务 */
- OSTaskCreate(Led1Task,(void *)0,(OS_STK*)&LED1_TASK_STK[LED1_STK_SIZE-1],LED1_TASK_PRIO);
- /* 创建printf任务 */
- OSTaskCreate(PrintfTask,(void *)0,(OS_STK*)&PRINTF_TASK_STK[PRINTF_STK_SIZE-1],PRINTF_TASK_PRIO);
-
- /* 挂起起始任务 */
- OSTaskSuspend(START_TASK_PRIO);
-
- /* 退出临界区(可以被中断打断) */
- OS_EXIT_CRITICAL();
- }
- /****************************************
- *函数名称:Led1Task
- *输 入:pdata -传入的参数
- *输 出:无
- *功 能:Led1任务
- ******************************************/
- void Led1Task(void *pdata)
- {
- printf("This is Led1 Task\r\n");
-
- while(1)
- {
- LED0=1;
- delay_ms(100);
-
- LED0=0;
- delay_ms(100);
- }
- }
- /****************************************
- *函数名称:PrintfTask
- *输 入:pdata -传入的参数
- *输 出:无
- *功 能:printf任务
- ******************************************/
- void PrintfTask(void *pdata)
- {
- u32 count=0;
- printf("This is Printf Task\r\n");
-
- while(1)
- {
- printf("count=%d\r\n",count++);
- delay_ms(500);
- }
- }
在main函数里面,初始化SYSTICK,并调用:
- /* UCOS延时初始化*/
- UCOS_DelayInit();
-
- /* OS初始化 */
- OSInit();
-
- /* 创建起始任务 */
- OSTaskCreate(StartTask,(void *)0,(OS_STK *)&START_TASK_STK[START_STK_SIZE-1],START_TASK_PRIO );
-
- /* 执行任务调度 */
- OSStart();
下载,并执行:
同时LED闪烁:
好了UCOSII的移植就到了,M471的测试目前先就这几个,下面可能会忙一阵子。
好了,谢谢观看~