os_cpu_a.asm
这个文件包含着有必要用汇编写的代码。
EXTERN OSRunning ; External references
EXTERN OSPrioCur
EXTERN OSPrioHighRdy
EXTERN OSTCBCur
EXTERN OSTCBHighRdy
EXTERN OSIntNesting
EXTERN OSIntExit
EXTERN OSTaskSwHook
声明这些变量是在其他文件界说的,本文件只做引证(有几个如同并未引证,不过没有关系)。
EXPORT OS_CPU_SR_Save ; Functions declared in this file
EXPORT OS_CPU_SR_Restore
EXPORT OSStartHighRdy
EXPORT OSCtxSw
EXPORT OSIntCtxSw
EXPORT OS_CPU_PendSVHandler ; #0
#0这儿 OS_CPU_PendSVHandler 要替换为PendSV_Handler;如下图:
替换后的 PPendSV中止
声明这些函数是在本文件中界说的。EXPORT这个关键字是跟编译器有关的,能被keil辨认,但不能被IAR 辨认。
NVIC_INT_CTTRL EQU 0xE000ED04 ;中止操控及状况寄存器 ICSR 的地址
NVIC_SYSPRI14 EQU 0xE000ED22 ;PendSV优先级寄存器的地址
NVIC_PENDSV_PRI EQU 00xFF ; PendSV 中止的优先级为 255(最低)
NVIC_PENDSVSET EQU 0x10000000 ; 位 28 为 1
;界说几个常量,相似 C 语言中的#define预处理指令。
S_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()由 OSStart()调用,用来发动最高优先级使命,当然使命有必要在OSStart()前已被创立。
OSStartHighRdy
;设置 PendSV 中止的优先级 #1
LDR R0, =NVIC_SYSPRI14 ;R0 = NVIC_SYSPRI14
LDR R1, =NVIC_PENDSV_PRI ;R1 = NVIC_PENDSV_PRI
STRB R1, [R0] ; *(uint8_t *)NVIC_SYSPRI14 = NVIC_PENDSV_PRI
;设置 PSP 为0 #2
MOVS R0, #0 ;R0 = 0
MSR PSP, R0 ;PSP = R0
;设置 OSRunning 为TRUE
LDR R0, =OSRunning ;R0 = OSRunning
MOVS R1, #1 ;R1 = 1
STRB R1, [R0] ;OSRunning = 1
;触发 PendSV 中止 #3
LDR R0, =NVIC_INT_CTRL ;R0 = NVIC_INT_CTRL
LDR R1, =NVIC_PENDSVSET ;R1 = NVIC_PENDSVSET
STR R1, [R0] ; *(uint32_t *)NVIC_INT_CTRL = NVIC_PENDSVSET
CPSIE I ;开中止
OSStartHang
;死循环,应该不会到这儿
B OSStartHang
#1.PendSV 中止的优先级应该为最低优先级,原因在<>的 7.6 节已有阐明。
#2.PSP 设置为 0,是告知详细的使命切换程序(OS_CPU_PendSVHandler()),这是第一次使命切换。做过切换后 PSP 就不会为0了,后边会看到。
#3.往中止操控及状况寄存器 ICSR(0xE000ED04)第 28 位写 1 即可发生 PendSV 中止。这个<>8.4.5 其它反常的装备寄存器有阐明。
当一个使命抛弃 cpu 的使用权,就会调用OS_TASK_SW()宏,而 OS_TASK_SW()便是 OSCtxSw()。OSCtxSw()应该做使命切换。但是在 CM3 中,一切使命切换都被放到PendSV 的中止处理函数中去做了,因而 OSCtxSw() 只需简略的触发 PendSV中止即可。OS_TASK_SW()是由OS_Sched()调用。
void OS_Sched (void)
{
# if OS_CRITICAL_METHOD==3
OS_CPU_SR cpu_sr = 0;
#endif
OS_ENTER_CRITICAL();
if (OSIntNesting ==0) {
if (OSLockNesting == 0) {
OS_SchedNew();
if (OSPrioHighRdy != OSPrioCur)
{
OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
#if OS_TASK_PROFILE_EN > 0
OSTCBHighRdy->OSTCBCtxSwCtr++;
#endif
OSCtxSwCtr++;
OS_TASK_SW();
}
}
}
OS_EXIT_CRITICAL();
}
OSCtxSw ;触发PendSV 中止
LDR R0, =NVIC_INT_CTRL ;R0 = NVIC_INT_CTRL
LDR R1, =NVIC_PENDSVSET ;R1 = NVIC_PENDSVSET
STR R1, [R0] ;*(uint32_t *)NVIC_INT_CTRL = NVIC_PENDSVSET
BX LR ;回来
当一个中止处理函数退出时,OSIntExit()会被调用来决议是否有优先级更高的使命需求履行。如果有
OSIntExit()对调用 OSIntCtxSw()做使命切换。
OSIntCtxSw ;触发 PendSV 中止
LDR R0, =NVIC_INT_CTRL
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
BX LR
看到这儿有人或许古怪怎样 OSCtxSw()和OSIntCtxSw()彻底相同,事实上,这两个函数的含义是不相同的,OSCtxSw()做的是使命之间的切换,如使命 A 因为等候某个资源或是做延时切换到使命 B,而
OSIntCtxSw()则是中止退出时,由中止状况切换到另一个使命。由中止切换到使命时,CPU 寄存器入栈的作业现已做完了,所以无需做第2次了(参阅邵教师书的 3.10 节)。这儿只不过因为 CM3 的特别机制导致了在这两个函数中只要做触发 PendSV中止即可,详细切换由 PendSV 中止来处理。
前面现已说过真实的使命切换是在 PendSV 中止处理函数里做的,因为 CM3 在中止时会有一半的寄存器主动保存到使命仓库里,所以在 PendSV 中止处理函数中只需保存 R4-R11并调理仓库指针即可。
声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/qianrushi/xitong/259588.html