一步步移植uCOS-II and LwIP (四)
来源:互联网 发布:win10图片查看软件 编辑:程序博客网 时间:2024/05/21 06:43
二、uCOS-II移植
嵌入式实时操作系统uCOS-II移植的核心在于任务切换时上下文环境的保存及恢复,针对Cortex-M3内核的单片机,其采用了PendSVHandler中断处理的方式解决这一核心问题。我们要做的就是在任务切换及中断任务切换过程中开启触发PendSV中断,并在PendSVHandler中实现上下文环境的切换,即保存CPU内部寄存器值和恢复切换任务的相关信息。
为什么要使用PendSV中断实现上下文切换呢?参阅Cortex-M3的一段话。
另一个相关的异常是PendSV(可悬起的系统调用),它和SVC协同使用。一方面,SVC异常是必须在执行SVC指令后立即得到响应的(对于SVC异常来说,若因优先级不比当前正处理的高,或是其它原因使之无法立即响应,将上访成硬fault——译者注),应用程序执行SVC时都是希望所需的请求立即得到响应。另一方面,PendSV则不同,它是可以像普通的中断一样被悬起的(不像SVC那样会上访)。OS可以利用它“缓期执行”一个异常——直到其它重要的任务完成后才执行动作。悬起PendSV 的方法是:手工往NVIC的PendSV悬起寄存器中写1。悬起后,如果优先级不够高,则将缓期等待执行。 PendSV的典型使用场合是在上下文切换时(在不同任务之间切换)。例如,一个系统中有两个就绪的任务,上下文切换被触发的场合可以是: - 执行一个系统调用 -系统滴答定时器(SYSTICK)中断,(轮转调度中需要) 让我们举个简单的例子来辅助理解。假设有这么一个系统,里面有两个就绪的任务,并且通过SysTick异常启动上下文切换。如图所示。
上图是两个任务轮转调度的示意图。但若在产生SysTick 异常时正在响应一个中断,则SysTick异常会抢占其ISR。在这种情况下,OS是不能执行上下文切换的,否则将使中断请求被延迟,而且在真实系统中延迟时间还往往不可预知——任何有一丁点实时要求的系统都决不能容忍这种事。因此,在CM3中也是严禁没商量——如果OS在某中断活跃时尝试切入线程模式,将触犯用法fault异常。
为解决此问题,早期的OS大多会检测当前是否有中断在活跃中,只有在无任何中断需要响应时,才执行上下文切换(切换期间无法响应中断)。然而,这种方法的弊端在于,它可以把任务切换动作拖延很久(因为如果抢占了IRQ,则本次SysTick在执行后不得作上下文切换,只能等待下一次SysTick异常),尤其是当某中断源的频率和SysTick异常的频率比较接近时,会发生“共振”,使上下文切换迟迟不能进行。 现在好了,PendSV来完美解决这个问题了。PendSV异常会自动延迟上下文切换的请求,直到其它的ISR都完成了处理后才放行。为实现这个机制,需要把PendSV编程为最低优先级的异常。如果OS检测到某IRQ正在活动并且被SysTick抢占,它将悬起一个PendSV异常,以便缓期执行上下文切换。如图所示
个中事件的流水账记录如下:
1. 任务A呼叫SVC来请求任务切换(例如,等待某些工作完成)
2. OS接收到请求,做好上下文切换的准备,并且悬起一个PendSV异常。
3. 当CPU退出SVC后,它立即进入PendSV,从而执行上下文切换。
4. 当PendSV执行完毕后,将返回到任务B,同时进入线程模式。
5. 发生了一个中断,并且中断服务程序开始执行
6. 在ISR执行过程中,发生SysTick异常,并且抢占了该ISR。
7. OS执行必要的操作,然后悬起PendSV异常以作好上下文切换的准备。
8. 当SysTick退出后,回到先前被抢占的ISR中,ISR继续执行
9. ISR执行完毕并退出后,PendSV服务例程开始执行,并且在里面执行上下文切换
10. 当PendSV执行完毕后,回到任务A,同时系统再次进入线程模式。
1、任务堆栈初始化
创建一个新任务时需要对该任务的堆栈进行初始化,其程序如下
/*********************************************************************************************************** INITIALIZE A TASK'S STACK** Description: This function is called by either OSTaskCreate() or OSTaskCreateExt() to initialize the* stack frame of the task being created. This function is highly processor specific.** Arguments : task is a pointer to the task code** p_arg is a pointer to a user supplied data area that will be passed to the task when the task first executes.** ptos is a pointer to the top of stack. It is assumed that 'ptos' points to a 'free' entry on the task stack. If OS_STK_GROWTH is set to 1 then* 'ptos' will contain the HIGHEST valid address of the stack. Similarly, if OS_STK_GROWTH is set to 0, the 'ptos' will contains the LOWEST valid address of the stack.** opt specifies options that can be used to alter the behavior of OSTaskStkInit(). (see uCOS_II.H for OS_TASK_OPT_xxx).** Returns : Always returns the location of the new top-of-stack once the processor registers have been placed on the stack in the proper order.** Note(s) : 1) Interrupts are enabled when your task starts executing.* 2) All tasks run in Thread mode, using process stack.**********************************************************************************************************/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 */ /* Registers stacked as if auto-saved on exception */ *(stk) = (INT32U)0x01000000L; /* xPSR */ *(--stk) = (INT32U)task; /* Entry Point */ *(--stk) = (INT32U)0xFFFFFFFEL; /* 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 */ /* 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);}
任务堆栈的初始化主要是对CPU内部寄存器进行入栈操作,由于M3内核堆栈方向是高地址向低地址生长的,故堆栈地址是递减的,且须按照程序中的顺序操作。具体原因,请研读Cortex-M3权威指南。
2、上下文环境切换
上文中已经介绍过,一般发生系统调用时,例如使用系统函数OSTimeDly 、OSTimeDlyHMSM 需要调用OS_Sched的OSCtxSw进行任务级别的上下文切换。systick轮询中断产生时,需调用OSIntExit 中的OSIntCtxSw() 进行中断级的任务切换,在这两个函数中我们只需开启PendSV中断,在PendSV函数中实现上下文环境的的保存及恢复。
OSCtxSw LDR R0, =NVIC_INT_CTRL LDR R1, =NVIC_PENDSVSET STR R1, [R0] BX LROSIntCtxSw LDR R0, =NVIC_INT_CTRL LDR R1, =NVIC_PENDSVSET STR R1, [R0] BX LRPendSV_Handler CPSID I MRS R0, PSP CBZ R0, OS_CPU_PendSVHandler_nosave SUBS R0, R0, #0x20 STM R0, {R4-R11} LDR R1, =OSTCBCur LDR R1, [R1] STR R0, [R1] OS_CPU_PendSVHandler_nosave PUSH {R14} LDR R0, =OSTaskSwHook BLX R0 POP {R14} LDR R0, =OSPrioCur LDR R1, =OSPrioHighRdy LDRB R2, [R1] STRB R2, [R0] LDR R0, =OSTCBCur LDR R1, =OSTCBHighRdy LDR R2, [R1] STR R2, [R0] LDR R0, [R2] LDM R0, {R4-R11} ADDS R0, R0, #0x20 MSR PSP, R0 ORR LR, LR, #0x04 CPSIE I BX LR
在这之前我们需要设置PendSV中断的优先级,以及在系统初始化的时候,启动最高优先级别的任务,这主要是由OSStartHighRdy 函数来实现的。
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] 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 levelOSStartHang B OSStartHang
SYSTICK中断服务函数:
void SysTick_Handler(void){ OSIntEnter(); OSTimeTick(); OSIntExit(); }
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);}#endifOS_CPU_SR_Save MRS R0, PRIMASK CPSID I BX LROS_CPU_SR_Restore MSR PRIMASK, R0 BX LR
在移植过程中,一定要注意修改uncomment掉stmf10x_it.c文件中的pendsv handler和systick handler,并修改os_cpu_asm.asm文件中的中断处理函数名,并使用 EXPORT PendSV_Handler,否则移植不能通过。
4、测试
测试代码如下:
OS_STK Task1Stack[TASK1STACKSIZE];OS_STK Task2Stack[TASK2STACKSIZE];void TaskCreate(void){ OSInit(); OSTaskCreate(Task1,(void *)0,(OS_STK*)&Task1Stack[TASK1STACKSIZE-1],TASK1PRIO); OSTaskCreate(Task2,(void *)0,(OS_STK*)&Task2Stack[TASK2STACKSIZE-1],TASK2PRIO); OSStart();}void Task1(void *pdata){ pdata = pdata; for(;;) { printf("Task1 is running!\n"); OSTimeDly(10); }}void Task2(void *pdata){ pdata = pdata; for(;;) { printf("Task2 is running!\n"); OSTimeDly(10); }}
测试输出:
- 一步步移植uCOS-II and LwIP (四)
- 一步步移植uCOS-II and LwIP (一)
- 一步步移植uCOS-II and LwIP (二)
- 一步步移植uCOS-II and LwIP (三)
- LPC2388+UCOS/II+LWIP移植
- UCOS移植LWIP
- s3c2440上ucos-ii下lwip移植成功
- ucos-II(四)
- LWIP UCOS 移植步骤记录
- LWIP-1.3.0在S3C2440平台UCOS-II-2.8.6系统上的移植过程DEBUG
- uCOS-II移植(Freescale MPC5644A)
- STM32移植uCOS-II系统(1)
- STM32移植uCOS-II系统(2)
- ucos-ii移植
- 移植ucos II
- STM32移植uCOS-II
- UCOS II移植内容
- uCOS-II移植
- bzoj1150 [CTSC2007]数据备份Backup
- 大家都在用的dialog MaterialDialog
- Solr Fields字段Copying Fields/Dynamic Fields
- intellij配置EJB项目入门
- PAT甲级1078
- 一步步移植uCOS-II and LwIP (四)
- 安装包方法
- 242. Valid Anagram
- 2017年1月21日
- 设计模式之适配器模式
- A+B Problem (sdut oj)
- ESP-TOUCH编码规则及解码
- 浣花溪公园(SPFA模板)
- 2017年1月22日