?? 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 + -