移植uCOS-II到Cortex-M3平台 (补遗)

去年写过一篇介绍 uCOS-II Cortex-M3平台移植的文章:

http://blog.csdn.net/liyuanbhu/article/details/9082767

 最近闲下来,研究了一下 FreeRTOS 官方的Cortex-M3平台的移植代码,很有收获,发现了几处比 uCOS-II 移植代码写的好的地方。这里简单总结一下,算是给自己做个备忘。

对临界区的处理

uCOS-II 移植代码中对临界区的处理是这样的。

#define OS_ENTER_CRITICAL() {cpu_sr = OS_CPU_SR_Save();}  
#define OS_EXIT_CRITICAL() {OS_CPU_SR_Restore(cpu_sr);} 
 
__asm OS_CPU_SR OS_CPU_SR_Save(void)  
{  
	MRS     R0, PRIMASK ; Set prio int mask to mask all (except faults)  
	CPSID   I  
	BX      LR  
}  
__asm void OS_CPU_SR_Restore(OS_CPU_SR cpu_sr)  
{  
	MSR     PRIMASK, R0  
	BX      LR  
}  

这种处理没有什么错,但是处理的有点简单。实际上 Cortex M3 内核支持对优先级分级,最多可以分255级。像STM32系列的Cortex M3 内核单片机支持16个优先级。uCOS-II 移植代码中这样已进入临界区就将所有的中断都给屏蔽掉会导致有些很紧急的事情无法及时处理。FreeRTOS移植代码处理的就聪明的多。FreeRTOS移植代码要求受操作系统管理的中断使用较低优先级,不受操作系统管理的中断可以使用任意优先级。临界区中只屏蔽掉可以产生任务切换的中断,也就是较低优先级的中断,而高优先级不受影响。这样可以提高对重要中断的响应速度。

FreeRTOS 中定义了一个宏 configMAX_SYSCALL_INTERRUPT_PRIORITY

所有用到操作系统功能的中断优先级数字都要大于这个宏定义的值。(也就是优先级比它低,Cortex M3中低优先级对应的数字大)

进入临界区只是屏蔽比 configMAX_SYSCALL_INTERRUPT_PRIORITY优先级低的中断。对应的代码如下。

#define portENTER_CRITICAL()					vPortEnterCritical()
#define portEXIT_CRITICAL()						vPortExitCritical()

#define portDISABLE_INTERRUPTS()				ulPortSetInterruptMask()
#define portENABLE_INTERRUPTS()				vPortClearInterruptMask( 0 )

void vPortEnterCritical( void )
{
	portDISABLE_INTERRUPTS();
	uxCriticalNesting++;
	__dsb( portSY_FULL_READ_WRITE );
	__isb( portSY_FULL_READ_WRITE );
}
void vPortExitCritical( void )
{
	uxCriticalNesting--;
	if( uxCriticalNesting == 0 )
	{
		portENABLE_INTERRUPTS();
	}
}
__asm unsigned long ulPortSetInterruptMask( void )
{
	PRESERVE8

	mrs r0, basepri
	mov r1, #configMAX_SYSCALL_INTERRUPT_PRIORITY
	msr basepri, r1
	bx r14
}
__asm void vPortClearInterruptMask( unsigned long ulNewMask )
{
	PRESERVE8

	msr basepri, r0
	bx r14
}

这里可能有些人会觉得代码有问题。退出临界区时应该恢复进入临界区前的状态。如果进入临界区之前就屏蔽了一小部分优先级很低的中断,那么退出临界区后这一小部分中断还是应该被屏蔽的。FreeRTOS 网站对此的解释如下:“Many bug reports are received that claim BASEPRI should be returned to its original value on exit, and not just set to zero, but the Cortex-M NVIC will never accept an interrupt that has a priority below that of the currently executing interrupt - no matter what BASEPRI is set to. An implementation that always sets BASEPRI to zero will result in faster code execution than an implementation that stores, then restores, the BASEPRI value (when the compiler's optimiser is turned on). 

说的有些道理,其实将PRIMASK的值保存下来也增加不了多少负担。因此,我觉得可以这么写:

__asm OS_CPU_SR OS_CPU_SR_Save(void)  
{  
	MRS     R0, PRIMASK ; 
    MOV R1, #configMAX_SYSCALL_INTERRUPT_PRIORITY 
	MSR BASEPRI, R1  
	BX      LR  
}  
__asm void OS_CPU_SR_Restore(OS_CPU_SR cpu_sr)  
{  
	MSR     PRIMASK, R0  
	BX      LR  
}  

再多说一句,如果使用的是STM32系列单片机,在启动内核之前,应该如下设置:

NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );

保证所有的优先级都是抢占式优先级。

任务切换

uCOS-II 移植代码中任务切换的代码(伪代码)如下。

OS_CPU_PendSVHandler  
{     
	if (PSP != NULL)   
	{  
		Save R4-R11 onto task stack;   
		OSTCBCur->OSTCBStkPtr = SP;   
	}  
	OSTaskSwHook();   
	OSPrioCur = OSPrioHighRdy;   
	OSTCBCur = OSTCBHighRdy;   
	PSP = OSTCBHighRdy->OSTCBStkPtr;  
	Restore R4-R11 from new task stack;   
	Return from exception;   
}  

代码中需要根据PSP 的值来判断是否是从 OSStartHighRdy() 函数过来的。因为OSStartHighRdy()的最后无法模拟一次中断返回,所以用了这么个法子。FreeRTOS 移植代码就聪明的多。反正 Cortex M3 系列支持的中断很多,根本用不完,浪费点也无所谓。OSStartHighRdy() 的最后不触发 PendSV 中断,换个其他不用的中断用不就可以了。FreeRTOS 移植代码用的是 SVC 中断。

下面是采用这种方法重写的OSStartHighRdy() 函数,其中注释掉的几行是原始的uCOS-II 移植代码采用的方法。

OSStartHighRdy
    LDR     R0, =NVIC_SYSPRI14    ; Set the PendSV exception priority
    LDR     R1, =NVIC_PENDSV_PRI
    STRB    R1, [R0]
;    MOVS    R0, #0                ; Set the PSP to 0 for initial context switch call
;	MSR     PSP, R0
    LDR     R0, =OSRunning        ; OSRunning = TRUE
    MOVS    R1, #1
    STRB    R1, [R0]

	SVC     0x00
;    LDR     R0, =NVIC_INT_CTRL    ; Trigger the PendSV exception (causes context switch)
;    LDR     R1, =NVIC_PENDSVSET
;    STR     R1, [R0]
    CPSIE   I                     ; Enable interrupts at processor level

OSStartHang
  B       OSStartHang           ; Should never get here

这样的话,任务切换就分配到了两个中断中,好像有点浪费,不够SVC 中断不用更是浪费。

PendSV_Handler
    CPSID   I                         ; Prevent interruption during context switch
    MRS     R0, PSP                   ; PSP is process stack pointer
;    CBZ     R0, PendSVHandler_nosave  ; Skip register save the first time

    SUBS    R0, R0, #0x20             ; Save remaining regs r4-11 on process stack
    STM     R0, {R4-R11}

    LDR     R1, =OSTCBCur             ; OSTCBCur->OSTCBStkPtr = SP;
    LDR     R1, [R1]
    STR     R0, [R1]                  ; R0 is SP of process being switched out

                                      ; At this point, entire context of process has been saved
PendSVHandler_nosave
    PUSH    {R14}                     ; Save LR exc_return value
    LDR     R0, =OSTaskSwHook         ; OSTaskSwHook();
    BLX     R0
    POP     {R14}

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

    LDR     R0, =OSTCBCur             ; OSTCBCur  = OSTCBHighRdy;
    LDR     R1, =OSTCBHighRdy
    LDR     R2, [R1]
    STR     R2, [R0]

    LDR     R0, [R2]                  ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;
    LDM     R0, {R4-R11}              ; Restore r4-11 from new process stack
    ADDS    R0, R0, #0x20
    MSR     PSP, R0                   ; Load PSP with new process SP
    ORR     LR, LR, #0x04             ; Ensure exception return uses process stack
    CPSIE   I
    BX      LR                        ; Exception return will restore remaining context


SVC_Handler
	CPSID   I                         ; Prevent interruption during context switch

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

    LDR     R0, =OSTCBCur             ; OSTCBCur  = OSTCBHighRdy;
    LDR     R1, =OSTCBHighRdy
    LDR     R2, [R1]
    STR     R2, [R0]

    LDR     R0, [R2]                  ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;
    LDM     R0, {R4-R11}              ; Restore r4-11 from new process stack
    ADDS    R0, R0, #0x20
    MSR     PSP, R0                   ; Load PSP with new process SP
    ORR     LR, LR, #0x04             ; Ensure exception return uses process stack
    CPSIE   I
    BX      LR                        ; Exception return will restore remaining context

这样的话,任务切换中断中少了一个条件跳转语句,可以节省几个时钟周期,代码也稍微简单一些,这样做还是值得的。

加上这些改进,这个移植代码应该就比较完美了。





  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值