📄 arm_00_os_taskswitch.c
字号:
}
TimeOfTaskStart=T0VAL; //保存当前T0VAL的值
OSCurrentPcb=OSReadyList; //当前进程控制块设置为就绪态任务列表中的第一个。它是优先级最高的就绪任务。
OSResumeSP(); //恢复出堆栈指针{}
__asm LDMIA R0!,{R4-R12} //将R4-R12从用户堆栈中恢复
__asm MOV R2,R8 //将R8保存到R2中{}
__asm MOV R3,R12 //将R12保存到R3中{}
__asm LDMIA R0!,{R14}^ //恢复用户模式下的R14
__asm LDMIA R0!,{R1} //将用户堆栈中的SPSR弹出到R1中
__asm ADD SP,SP,#24 //调整特权模式下的SP,使SP指向堆栈中返回地址的前一个位置{}
__asm ADD R0,R0,#16 //调整R0,使R0指向用户栈中的返回地址{}
__asm LDMIA R0!,{R12} //将用户栈中的返回地址弹出至R12{}
__asm STMDB SP!,{R2,R3,R12} //R2中保存的是R8,R3中保存的是R12,R12中保存的是返回地址
//将它们压入到特权模式下的堆栈中
__asm SUB R8,R0,#20 //调整用户模式下的堆栈并将其保存至R8{}
__asm SUB SP,SP,#4 //{}调整SP,使其指向堆栈中的SPSR前一个位置
__asm STMDB SP!,{R1} //{}R1中保存的是SPSR,将其压入堆栈中
__asm ADD SP,SP,#8 //{}调整回SP
__asm LDMIA R8!,{R0-R3} //将R0-R3弹出
__asm STMDB SP!,{R3} //R3的值压入特权模式下的堆栈中
__asm SUB SP,SP,#8 //调整回SP,使其指向正确的位置
__asm ADD R8,R8,#4 //调整R8{}
__asm STMDB SP,{R8} //将R8入栈,R8中保存的是堆栈指针,要将其恢复到用户模式下的SP中。但并未更新SP的值。
__asm SUB R8,SP,#4 //{}将特权模式下的SP-4的值放入到R3中,因为不能直接使用SP,要借用R8作为堆栈指针。
//-4是因为刚刚未更新SP的值。
__asm LDMIA R8,{SP}^ //将刚压入的用户模式下的堆栈指针弹出至用户模式下的SP中。
__asm NOP //{}加入一个空操作,在特权模式下访问用户模式下的SP后,后面不能紧跟访问备份寄存器的操作
}
//////////////////////////////////End of function//////////////////////////////////////////////
/**********************************************************************************************
功能:FIQ中断服务程序。
入口参数:无。
返回:无。
备注:时钟节拍在此产生,修改这里的代码是要注意,可能会导致程序跑飞。
**********************************************************************************************/
void FIQ_Handler(void) __fiq //FIQ中断服务程序
{
//函数进来时,编译器会自动将R0-R3,R14压入堆栈
if(FIQSTA & TIMER0) //读取FIQ中断状态,判断FIQ中断源。这里为定时器0中断。
{
OSpcb * TempDelayPcb; //临时用的进程控制块,查找延迟列表时使用。
OSpcb * TempReadyPcb; //临时用的进程控制块,查找就绪列表时使用。
uint32 DeleteFirstFlag; //是否删除表头的标志
T0CLRI=0; //清定时器0中断
__asm MOV R0,SP //将FIQ模式下的SP移入R0{}
__asm STMDB R0!,{SP}^ //将用户模式下的堆栈指针SP压入FIQ的堆栈
__asm NOP //插入一个NOP指令。在访问用户模式下的寄存器后,后面不能紧跟访问备份寄存器的指令,所以插入一个NOP指令{}
__asm MOV SP,R0 //将R0放回SP中{}
__asm LDMIA SP!,{R0} //将用户模式下的SP弹出至R0
__asm ADD SP,SP,#16 //{}调整堆栈指针SP,使其指向堆栈中的返回地址
__asm LDMDA SP!,{R8} //{}将返回地址弹出到R8中
__asm SUB R8,R8,#4 //{}因为FIQ发生时,PC已经更新,所以要减去4,使其指向中断发生时的下一条指令。
__asm STMDB R0!,{R8} //将返回地址压入用户堆栈中
__asm LDMDA SP!,{R8} //将R3弹出至R8中
__asm STMDB R0!,{R8} //将被弹出的R3压入到用户堆栈中
__asm LDMDA SP!,{R8} //将R2弹出至R8中
__asm STMDB R0!,{R8} //将被弹出的R2压入到用户堆栈中
__asm LDMDA SP!,{R8} //将R1弹出至R8中
__asm STMDB R0!,{R8} //将被弹出的R1压入到用户堆栈中
__asm LDMDA SP,{R8} //将R0弹出至R8中
__asm STMDB R0!,{R8} //将被弹出的R0压入到用户堆栈中
__asm ADD SP,SP,#20 //调整堆栈指针,使其指向进入中断前的位置{}
__asm MRS R8,SPSR //将SPSR放入R8中。SPSR用来恢复CPSR{}
__asm STMDB R0!,{R8} //将SPSR压入到用户堆栈中
__asm STMDB R0!,{R4-R12,R14}^ //将用户模式下的R4-R12,R14压入到用户堆栈中
__asm BL OSSaveSP //保存堆栈指针
if(TimeOfTaskStart<=T0VAL) //T0VAL是计数器0的值。T0VAL的值是减小的。如果T0VAL大于或者等于上一次的值,则说明已经溢出
{
OSCurrentPcb->RunTimeInThisRefreshPeriod+=TimeOfTaskStart+MaxOfTimer0+1-T0VAL; //计算时间差
}
else
{
OSCurrentPcb->RunTimeInThisRefreshPeriod+=TimeOfTaskStart-T0VAL; //计算时间差
}
TimeOfTaskStart=T0VAL; //保存当前T0VAL的值
TempDelayPcb=OSDelayList; //从延时列表的表头开始查找
while(TempDelayPcb->Next) //直到查到表尾
{
TempDelayPcb->Delay--; //延迟列表中的任务延迟数,减1
DeleteFirstFlag=0; //删除表头的标志清0
if(TempDelayPcb->Delay==0) //如果延时的任务时间到,则
{
TempDelayPcb->Status=OSInReadyStatus;
TempReadyPcb=OSReadyList; //将就绪表表头放入TempReadyPcb中,从表头开始查找
while(1)
{
if((TempDelayPcb->Priority)<=(TempReadyPcb->Priority)) //如果被唤醒的任务的优先级比被查找的就绪表中的任务的优先级高
{
if(TempDelayPcb==OSDelayList) //如果被唤醒的任务处于延时列表的表头
{
OSDelayList=TempDelayPcb->Next; //延时列表的表头指向被唤醒的任务的下一个
OSDelayList->Prior=OSDelayList; //延时列表的前趋指向自己
DeleteFirstFlag=1; //设置表头被删除的标志
}
else //如果被唤醒的任务不处于延时列表的表头
{
TempDelayPcb->Next->Prior=TempDelayPcb->Prior; //被唤醒任务的下一个任务的前一个任务指向被唤醒任务的前一个任务
TempDelayPcb->Prior->Next=TempDelayPcb->Next; //被唤醒任务的前一个任务的下一个任务指向被唤醒任务的下一个任务
}
if(TempReadyPcb==OSReadyList) //如果被唤醒的任务要插入到就绪任务列表的表头
{
OSReadyList=TempDelayPcb; //就绪列表的表头设置为被唤醒的任务
OSReadyList->Prior=OSReadyList; //就绪列表的表头的前趋指向自己
}
else //如果被唤醒的任务不是被插入到表头
{
//则将被唤醒的任务,插入到就绪表中
//被唤醒的任务被插在TempReadyPcb之前
TempReadyPcb->Prior->Next=TempDelayPcb; //TempReadyPcb的前趋的后趋设置为被唤醒的任务
TempDelayPcb->Prior=TempReadyPcb->Prior; //被唤醒的任务的前趋设置为TempReadyPcb的前趋
}
TempReadyPcb->Prior=TempDelayPcb; //TempReadyPcb的前趋设置为被唤醒的任务
TempDelayPcb=TempDelayPcb->Next->Prior; //将TempDelayPcb被设置为被唤醒任务的前一个任务
TempReadyPcb->Prior->Next=TempReadyPcb; //因为TempReadyPcb->Prior等于TempDelayPcb,所以相当于
//TempDelayPcb=TempReadyPcb。即被唤醒任务的后趋被置为TempReadyPcb。
break;
}
if(TempReadyPcb->Next==(OSpcb *)0)break; //如果已经查找到就绪表的尾部,则退出
TempReadyPcb=TempReadyPcb->Next; //TempReadyPcb移到下一个
}
}
if(DeleteFirstFlag) //如果刚刚被唤醒的任务处于延时列表的表头,则
{
TempDelayPcb=OSDelayList; //TempDelayPcb指向延时列表的表头
}
else
{
TempDelayPcb=TempDelayPcb->Next; //TempDelayPcb设置为下一个
}
}
if((OSCurrentPcb->Status)==OSInReadyStatus) //如果当前任务处于就绪态
{
if((OSCurrentPcb->Next)!=0) //如果当前任务不是空闲任务
{
if((OSCurrentPcb->Priority)==(OSCurrentPcb->Next->Priority)) //如果当前任务的优先级跟它的下一个就绪态任务的优先级一样
{
OSCurrentPcb=OSCurrentPcb->Next; //则切换到下一个任务,实现时间片轮循调度
}
else //如果不相等
{
OSCurrentPcb=OSReadyList; //则将就绪表的表头的任务做为运行任务
}
}
else //如果当前任务是空闲任务
{
OSCurrentPcb=OSReadyList; //则将就绪表的表头的任务做为运行任务
}
}
else //如果当前任务被挂起或者延时了,
{
OSCurrentPcb=OSReadyList; //则将就绪表的表头的任务做为运行任务
}
OSResumeSP(); //恢复出任务堆栈指针
__asm LDMIA R0!,{R4-R12,R14}^ //恢复出R4-R12,R14
__asm LDMIA R0!,{R8} //将SPSR出栈至R8
__asm MSR SPSR_fsxc,R8 //将R8写入SPSR。在函数返回时,以恢复CPSR{}
__asm ADD R0,R0,#16 //调整堆栈指针,使其指向堆栈中的返回地址{}
__asm LDMDA R0!,{R8} //返回地址弹出至R8
__asm ADD R8,R8,#4 //由于函数返回时,编译器会自动将返回地址减4后才返回,所以事先加上4{}
__asm STMDB SP!,{R8} //将返回地址压入到FIQ的堆栈中
__asm LDMDA R0!,{R8} //将R3弹出至R8
__asm STMDB SP!,{R8} //将刚弹出的R3,压入FIQ的堆栈中
__asm LDMDA R0!,{R8} //将R2弹出至R8
__asm STMDB SP!,{R8} //将刚弹出的R2,压入FIQ的堆栈中
__asm LDMDA R0!,{R8} //将R1弹出至R8
__asm STMDB SP!,{R8} //将刚弹出的R1,压入FIQ的堆栈中
__asm LDMDA R0!,{R8} //将R0弹出至R8
__asm STMDB SP!,{R8} //将刚弹出的R0,压入FIQ的堆栈中
__asm ADD R0,R0,#24 //{}将R0调整到出栈后的值。R0作为堆栈地址用。
__asm STMDB SP,{R0} //将堆栈地址暂时压入FIQ的堆栈中。不更新SP,所以下面需要-4操作。
__asm SUB R0,SP,#4 //将SP-4放入R0中,借用R0来压栈,因为访问用户模式下的寄存器时,不能访问备份寄存器{}
__asm LDMIA R0,{SP}^ //将堆栈指针弹出至用户模式下的堆栈指针中
__asm NOP //{}加入一个空操作,在特权模式下访问用户模式下的SP后,后面不能紧跟访问备份寄存器的操作
return;
}
}
//////////////////////////////////End of function//////////////////////////////////////////////
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -