翻了半天的《ARM Cortex-M3与Cortex-M4权威指南》,想出这么个能凑合着用的办法,如有雷同纯属巧合。以下都是在Explorer开发板附带的UCOS例程上修改,IDE是Keil MDK 5.11
原始的例程在切换任务时,对浮点计算单元的处理是把全部寄存器都压栈,实际上发现即使任务没有浮点计算,也会有把S0-S31,FPSCR都压栈。另外,在初始化任务栈时,得给FPU寄存器留空间,否则弹栈时就会出错。感觉这样有点浪费内存和时间。以下是我想的一个办法,貌似运行还算正常
先看下和FPU有关的异常标志:
没找到中文版的PDF,凑合着看吧
这个是原始的PendSV_Handler
处理和FPU相关时,是先检查R14的bit4,也就是看EXC_RETURN指示是否执行了浮点指令,如果有就压S15-S31,然后载入下一个任务的TCB,再检查LR的bit4,若为0就弹出S15-S31。
不过这样会出两个问题,其一是这样实际是不知道下一个任务是否是要用FPU,因为设置Control的FPCA位和EXC_RETURN的是上一个任务。
假如上一个任务用了FPU,那么LR的bit4就是0,但如果下一个任务不用FPU,由于此时的LR的bit4还是0,那么就会按长帧弹栈,假若下一个任务的栈里没给FPU留位置,那么就会弹栈顺序出错,然后就是Hardfault了。另一个问题就是,在Reset_Handler里,进入主函数main()前,还有个初始化的函数__main(),这个函数会初始化FPU,其中会执行VMRS指令来设置FPU的工作模式,这样会把Control的FPCA置1。所以进入main()后就会看到说执行浮点指令了,这个标志会一直保持到有中断产生,并且会让进入中断的压栈是压长帧。
而且,由于启动UCOSII会调用OSStartHighRdy()函数,触发PendSV中断,而PendSV_Handler会根据LR的bit4确定是否压FPU——显然,由于一开始Control的FPCA就是1,所以LR的bit4是0,这样就会执行VSTMDBEQ和VLDMIAEQ指令,原本被中断清零的Control的FPCA又被置1了。以上的结果就是,在OS运行期间,Control的FPCA位一直为1,不管有没有执行浮点指令,对此的解决办法通常就是把所有的任务都当成用FPU的,比如初始化任务栈时,所有的任务都是按带FPU初始化
这样是可以正常运行,但是感觉浪费了不少的内存空间,因为不是所有的任务都有浮点计算,而且这样也拖慢了任务切换速度。
以下是本人想出的解决办法,可以运行,只是不知道是否会在未来某个时候出些BUG……因为得改写部分内核代码
首先,由于在切换任务时,是不知道下一个任务是否有浮点计算的,但编程序的人肯定知道,所以可以人为的加个flag,比如就加在任务的TCB里面,设个是否使用FPU的标志
对应的要修改OSTaskCreate()函数和OSTaskStkInit(),OS_TCBInit(),为这三个增加一个输入参数FPU_flag,用于指示任务是否有浮点计算
OSTaskCreate()里的修改如下,只是给OSTaskStkInit(),OS_TCBInit()传参数,OSTaskCreateExt()什么的暂时不管
OS_TCBInit()的里面要增加给TCB里新加的那个FPU标志赋值