UCOSII 移植文件详解——笔记

1.滴答定时器 SysTick

  • 滴答定时器是一个24位的倒计数定时器,当计数到0时,将从RELOAD寄存器中自动重装载定时器初值,只要不关闭SysTick使能位,就将永久不息。
  • SysTick的最大使命:定期的产生异常请求作为系统的时基。
//初始化延迟函数
//当使用OS的时候,此函数会初始化OS的时钟节拍
//SYSTICK的时钟固定为AHB时钟的1/8
//SYSCLK:系统时钟频率
void delay_init(u8 SYSCLK)
{
#if SYSTEM_SUPPORT_OS 						//如果需要支持OS.
	u32 reload;
#endif
 	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); 
	fac_us=SYSCLK/8;						//不论是否使用OS,fac_us都需要使用
#if SYSTEM_SUPPORT_OS 						//如果需要支持OS.
	reload=SYSCLK/8;						//每秒钟的计数次数 单位为M	   
	reload*=1000000/delay_ostickspersec;	//根据delay_ostickspersec设定溢出时间,溢出所需的计数次数
											//reload为24位寄存器,最大值:16777216,在168M下,约合0.7989s左右	
	fac_ms=1000/delay_ostickspersec;		//代表OS可以延时的最少单位	   
	SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;   	//开启SYSTICK中断
	SysTick->LOAD=reload; 					//每1/delay_ostickspersec秒中断一次	
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; 	//开启SYSTICK    
#else
	fac_ms=(u16)fac_us*1000;				//非OS下,代表每个ms需要的systick时钟数   
#endif
}								

PS:
reload = SYSCLK/8 = 168MHz/8 = 21MHz,每us 可计数21次
delay_ostickspersec :OS_TICKS_PER_SEC (200)
溢出所需的计数次数:reload = 1/200
100000021 = 105000

//systick中断服务函数,使用OS时用到
void SysTick_Handler(void)
{	
	if(delay_osrunning==1)					//OS开始跑了,才执行正常的调度处理
	{
		OSIntEnter();						//进入中断
		OSTimeTick();       				//调用ucos的时钟服务程序               
		OSIntExit();       	 				//触发任务切换软中断
	}
}

PS: OSTimeTick在时钟节拍到来时,检查每个任务的任务控制块中 OSTCBDly-1 后是否为0,如果为0 则刚才此任务为挂起状态,现在应该为就绪状态。
OSTCBDly :标志任务的延时时间

2.os_cpu_a.asm 文件详解

		IMPORT  OSRunning             
        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                                    
    	EXPORT  OS_CPU_SR_Restore       
        EXPORT  PendSV_Handler

PS:
IMPORT 定义表示 是一个外部变量标号,不是在本程序定义的;
EXPORT 定义表示这些函数是在本文中定义的,供其他文件调用;

NVIC_INT_CTRL   	EQU     0xE000ED04  ; 中断控制寄存器
NVIC_SYSPRI2    	EQU     0xE000ED22  ; 系统优先级寄存器(2)
NVIC_PENDSV_PRI 	EQU         0xFFFF  ; PendSV中断和系统节拍中断
                                        ; (都为最低,0xff).
NVIC_PENDSVSET  	EQU     0x10000000  ; 触发软件中断的值.

PS:
① EQU 和 C中的define 相同,定义一个宏;
② NVIC_PENDSV_PRI 为PendSV和Systick的中断优先级,0xFF都为最低;
③ NVIC_PENDSVSET 可以触发软件中断,通过中断控制寄存器(NVIC_INT_CTRL)的bit28 写1来触发,因此值为0x10000000 ;

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				;返回

PS:
OS_CPU_SR_Save 和 OS_CPU_SR_Restore 开关中断的汇编代码

;/**************************************************************************************
;* 函数名称: OSStartHighRdy
;*
;* 功能描述: 使用调度器运行第一个任务
;* 
;* 参    数: None
;*
;* 返 回 值: None
;**************************************************************************************/  

OSStartHighRdy
        LDR     R4, =NVIC_SYSPRI2      ; set the PendSV exception priority
        LDR     R5, =NVIC_PENDSV_PRI
        STR     R5, [R4]

        MOV     R4, #0                 ; set the PSP to 0 for initial context switch call
        MSR     PSP, R4

        LDR     R4, =OSRunning         ; OSRunning = TRUE
        MOV     R5, #1
        STRB    R5, [R4]

                                       ;切换到最高优先级的任务
        LDR     R4, =NVIC_INT_CTRL     ;rigger the PendSV exception (causes context switch)
        LDR     R5, =NVIC_PENDSVSET
        STR     R5, [R4]

        CPSIE   I                      ;enable interrupts at processor level
OSStartHang
        B       OSStartHang            ;should never get here

PS: OSStartHighRdy 是由OSStart() 调用,用来开启多任务的。

;/**************************************************************************************
;* 函数名称: OSCtxSw
;*
;* 功能描述: 任务级上下文切换         
;*
;* 参    数: None
;*
;* 返 回 值: None
;***************************************************************************************/
  
OSCtxSw
		PUSH    {R4, R5}
        LDR     R4, =NVIC_INT_CTRL  	;触发PendSV异常 (causes context switch)
        LDR     R5, =NVIC_PENDSVSET
        STR     R5, [R4]
		POP     {R4, R5}
        BX      LR

;/**************************************************************************************
;* 函数名称: OSIntCtxSw
;*
;* 功能描述: 中断级任务切换
;*
;* 参    数: None
;*
;* 返 回 值: None
;***************************************************************************************/

OSIntCtxSw
		PUSH    {R4, R5}
        LDR     R4, =NVIC_INT_CTRL      ;触发PendSV异常 (causes context switch)
        LDR     R5, =NVIC_PENDSVSET
        STR     R5, [R4]
		POP     {R4, R5}
        BX      LR
        NOP

PS:
①OSCtxSw 和 OSIntCtxSw 用来做任务切换,它们都只是触发1个PendSV中断,具体切换过程在PendSV中断服务函数中进行。
②OSCtxSw 和 OSIntCtxSw 意义不同:
OSCtxSw :任务级切换,如从任务A切换到任务B
OSIntCtxSw :中断级切换,从中断退出时切换到一个任务中

;/**************************************************************************************
;* 函数名称: OSPendSV
;*
;* 功能描述: OSPendSV is used to cause a context switch.
;*
;* 参    数: None
;*
;* 返 回 值: None
;***************************************************************************************/

PendSV_Handler
    CPSID   I                           ;关中断,任务切换期间要关中断
    MRS     R0, PSP           	     	;R0 = PSP                            
    CBZ     R0, PendSV_Handler_Nosave   ; 如果PSP 为0 就转移到  PendSV_Handler_Nosave   (1)               
	
	;任务如果使用FPU 就保存S16~S31寄存器
	TST 	R14, #0x10					;(2)
	IT 		EQ
	VSTMDBEQ R0!, {S16-S31} 
	
    SUBS    R0, R0, #0x20               ;R0 -= 0x20
    STM     R0, {R4-R11}				;保存剩余R4~R11寄存器

    LDR     R1, =OSTCBCur               ; R1= &OSTCBCur               
    LDR     R1, [R1]                    ;R1 = *R1 即 R1 = OSTCBCur               
    STR     R0, [R1]                    ; *R1 = R0 即 OSTCBCur = SP               

                                                                ; At this point, entire context of process has been saved
PendSV_Handler_Nosave
    PUSH    {R14}                       ; 保存R14的值,后面要调用函数
    LDR     R0, =OSTaskSwHook           ;R0 = &OSTaskSwHook           
    BLX     R0							;调用OSTaskSwHook()函数
    POP     {R14} 				        ;恢复R14

    LDR     R0, =OSPrioCur              ; R0 = &OSPrioCur
    LDR     R1, =OSPrioHighRdy			;R1 = &OSPrioHighRdy			
    LDRB    R2, [R1]				    ; R2 = *R1即 R2 = OSPrioHighRdy			
    STRB    R2, [R0]				    ; *R0 = R2 即OSPrioCur = OSPrioHighRdy			

    LDR     R0, =OSTCBCur               ; R0 = &OSPrioCur
    LDR     R1, =OSTCBHighRdy           ; R1 = &OSPrioHighRdy			
    LDR     R2, [R1]					;R2 = *R1即 R2 = OSPrioHighRdy	
    STR     R2, [R0] 					; *R0 = R2 即OSPrioCur = OSPrioHighRdy			

    LDR     R0, [R2]                	 ; R0 = *R2即 R0 = OSPrioHighRdy			R0是新任务的SP(3)
    LDM     R0, {R4-R11} 			    ; 从堆栈中恢复R4~R11
    ADDS    R0, R0, #0x20				; R0 += 0x20

	;任务如果使用FPU 将S16~S31寄存器恢复出来
	TST 	R14, #0x10					;(4)
	IT 		EQ
	VLDMIAEQ R0!, {S16-S31} 
		
	MSR     PSP, R0                     ; PSP = R0 用新任务的SP加载PSP
    ORR     LR, LR, #0x04               ; 确保LR的位2为1,返回后使用进程堆栈(5)
    CPSIE   I						    ;开中断
    BX      LR                          ; 中断返回
	NOP
    end  

PS:
(1) 如果PSP 为0,说明是第一次做任务切换,而任务创建的时候会调用堆栈初始化函数OSTaskStkInit() 来初始化堆栈,在初始化过程中已经做了入栈处理,所以这里就不需要再做入栈处理,直接跳转到PendSV_Handler_Nosave 。
(2) EXC_RETURN的bit4 用来表示是否使用FPU,故通过判断R14的bit4 来决定是否将S16~S31寄存器做入栈处理。
(3) SP指向的是要运行的最高优先级任务
(4) 同(2)
(5) 因为进入中断使用的是MSP,而退出中断的时候使用的是PSP,此处须将LR的位2置1.

3.os_cpu.h 文件详解

定义了一些数据类型

typedef unsigned char  BOOLEAN;
typedef unsigned char  INT8U;			/* 无符号8位数       */
typedef signed   char  INT8S;			/* 有符号8位数      */
typedef unsigned short INT16U;			/* 无符号16位数      */
typedef signed   short INT16S;			/* 有符号16位数       */
typedef unsigned int   INT32U;			/* 无符号32位数       */
typedef signed   int   INT32S;			/* 有符号32位数        */
typedef float          FP32;			/* 单精度浮点数*/
typedef double         FP64;			/* 双精度浮点数*/

//STM32是32位位宽的,这里OS_STK和OS_CPU_SR都应该为32位数据类型
typedef unsigned int   OS_STK;			/* OS_STK 32位数据,即4字节*/
typedef unsigned int   OS_CPU_SR;		/* 默认CPU状态寄存器 32位*/
//定义栈的增长方向.
//CM3中,栈是由高地址向低地址增长的,所以OS_STK_GROWTH设置为1
#define  OS_STK_GROWTH        1      /* 堆栈增长方向    */
//任务切换宏,由汇编实现.
#define  OS_TASK_SW()         OSCtxSw()

//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

PS:
进入临界段的方法为3 则进出临界段的宏定义分别为OS_ENTER_CRITICAL() 、OS_EXIT_CRITICAL() (由汇编编写的)。

4.os_cpu_c.c 文件详解

OSTaskStkInit() 函数,堆栈初始化函数

OS_STK *OSTaskStkInit (void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT16U opt)
{
    OS_STK *stk;


    (void)opt;                                   /* 'opt' is not used, prevent warning                 */
    stk       = ptos;                            /* Load stack pointer                                 */

#if (__FPU_PRESENT==1)&&(__FPU_USED==1)	
	*(--stk) = (INT32U)0x00000000L; //No Name Register  
	*(--stk) = (INT32U)0x00001000L; //FPSCR
	*(--stk) = (INT32U)0x00000015L; //s15
	*(--stk) = (INT32U)0x00000014L; //s14
	*(--stk) = (INT32U)0x00000013L; //s13
	*(--stk) = (INT32U)0x00000012L; //s12
	*(--stk) = (INT32U)0x00000011L; //s11
	*(--stk) = (INT32U)0x00000010L; //s10
	*(--stk) = (INT32U)0x00000009L; //s9
	*(--stk) = (INT32U)0x00000008L; //s8
	*(--stk) = (INT32U)0x00000007L; //s7
	*(--stk) = (INT32U)0x00000006L; //s6
	*(--stk) = (INT32U)0x00000005L; //s5
	*(--stk) = (INT32U)0x00000004L; //s4
	*(--stk) = (INT32U)0x00000003L; //s3
	*(--stk) = (INT32U)0x00000002L; //s2
	*(--stk) = (INT32U)0x00000001L; //s1
	*(--stk) = (INT32U)0x00000000L; //s0
#endif
                                                 /* Registers stacked as if auto-saved on exception    */
    *(stk)    = (INT32U)0x01000000L;             /* xPSR                                               */
    *(--stk)  = (INT32U)task;                    /* Entry Point                                        */
    *(--stk)  = (INT32U)OS_TaskReturn;           /* R14 (LR) (init value will cause fault if ever used)*/
    *(--stk)  = (INT32U)0x12121212L;             /* R12                                                */
    *(--stk)  = (INT32U)0x03030303L;             /* R3                                                 */
    *(--stk)  = (INT32U)0x02020202L;             /* R2                                                 */
    *(--stk)  = (INT32U)0x01010101L;             /* R1                                                 */
    *(--stk)  = (INT32U)p_arg;                   /* R0 : argument                                      */

#if (__FPU_PRESENT==1)&&(__FPU_USED==1)	
	*(--stk) = (INT32U)0x00000031L; //s31
	*(--stk) = (INT32U)0x00000030L; //s30
	*(--stk) = (INT32U)0x00000029L; //s29
	*(--stk) = (INT32U)0x00000028L; //s28
	*(--stk) = (INT32U)0x00000027L; //s27
	*(--stk) = (INT32U)0x00000026L; //s26	
	*(--stk) = (INT32U)0x00000025L; //s25
	*(--stk) = (INT32U)0x00000024L; //s24
	*(--stk) = (INT32U)0x00000023L; //s23
	*(--stk) = (INT32U)0x00000022L; //s22
	*(--stk) = (INT32U)0x00000021L; //s21
	*(--stk) = (INT32U)0x00000020L; //s20
	*(--stk) = (INT32U)0x00000019L; //s19
	*(--stk) = (INT32U)0x00000018L; //s18
	*(--stk) = (INT32U)0x00000017L; //s17
	*(--stk) = (INT32U)0x00000016L; //s16
#endif
		
                                                /* Remaining registers saved on process stack         */
    *(--stk)  = (INT32U)0x11111111L;             /* R11                                                */
    *(--stk)  = (INT32U)0x10101010L;             /* R10                                                */
    *(--stk)  = (INT32U)0x09090909L;             /* R9                                                 */
    *(--stk)  = (INT32U)0x08080808L;             /* R8                                                 */
    *(--stk)  = (INT32U)0x07070707L;             /* R7                                                 */
    *(--stk)  = (INT32U)0x06060606L;             /* R6                                                 */
    *(--stk)  = (INT32U)0x05050505L;             /* R5                                                 */
    *(--stk)  = (INT32U)0x04040404L;             /* R4                                                 */

    return (stk);
}

PS:
堆栈初始化函数OSTaskStkInit() ,由任务创建函数OSTaskCreate()和OSTaskCreateExt() 调用,用于创建任务时初始堆栈,即在任务堆栈中保存寄存器的值。如果使用FPU ,就要保存FPU寄存器的值,否则只保存通用寄存器。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

madao1024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值