Cortex-M3和Cortex-M4 Fault異常應(yīng)用之二 ----- Fault處理函數(shù)的實(shí)現(xiàn)
掃描二維碼
隨時(shí)隨地手機(jī)看文章
在項(xiàng)目處于調(diào)試期間,F(xiàn)ault處理程序可能只是一個(gè)斷點(diǎn)指令,調(diào)試器遇到這個(gè)指令后停止程序的運(yùn)行。默認(rèn)情況下,由于非硬Fault被禁能,所有發(fā)生的非Fault都會(huì)上訪成硬Fault,因此只要在硬Fault處理程序中設(shè)置一個(gè)斷點(diǎn),就可以觀察所有Fault信息。當(dāng)使用MDK-ARM的RealView編譯器時(shí),你可以用下面的C代碼替代默認(rèn)硬Fault處理程序,這段代碼檢測(cè)產(chǎn)品是否連接到一個(gè)調(diào)試器,只有在連接到一個(gè)調(diào)試器的情況下,才會(huì)執(zhí)行斷點(diǎn)指令。
voidHardFault_Handler(void){if(CoreDebug->DHCSR&1){//checkC_DEBUGEN==1->DebuggerConnected__breakpoint(0);//haltprogramexecutionhere}while(1);//enterendlessloopotherwise}
說明一下,在這段代碼中,關(guān)于這個(gè)CoreDebug->DHCSR也可以在core_cm3.h中找到;__breakpoint()函數(shù)是ARM編譯器所支持的內(nèi)部指令,這個(gè)函數(shù)的作用是在指令流中插入一個(gè)斷點(diǎn)指令(BKTP)。詳細(xì)可以查看編譯器手冊(cè)Compiler Reference Guide – Compiler-specific Features - __breakpoint。
為了使能除數(shù)為零以及未對(duì)齊內(nèi)存訪問產(chǎn)生Fault,應(yīng)用程序初始化代碼要設(shè)置SCB->CCR寄存器,下面的C代碼清單用于使能除數(shù)為零以及未對(duì)齊內(nèi)存訪問產(chǎn)生Fault。
SCB->CCR|=0x18;/*enablediv-by-0andunalignedfault*/
對(duì)于最終的應(yīng)用程序,F(xiàn)ault處理程序或許會(huì)按照下面所說的實(shí)現(xiàn):
系統(tǒng)復(fù)位:通過置一SCB->AIRCR(應(yīng)用程序中斷和復(fù)位控制寄存器)的位2(SYSRESETREQ)。這樣將會(huì)強(qiáng)制對(duì)調(diào)試設(shè)備之外的所有主要設(shè)備進(jìn)行一次大的系統(tǒng)復(fù)位。如果你不想復(fù)位整個(gè)系統(tǒng),你可以只置一SCB->AIRCR寄存器的位0.(注:LPC1778/88不支持這個(gè)位)
恢復(fù):在某些情況下,還是有希望解決產(chǎn)生Fault的問題的。例如,如果程序嘗試訪問了協(xié)處理器,可以通過一個(gè)協(xié)處理器的軟件模擬器來解決。
終止任務(wù):如果系統(tǒng)運(yùn)行了一個(gè)RTOS,則相關(guān)的任務(wù)可以被終結(jié)或者重新開始。
注:下面的C代碼清單可以用來使能用法、存儲(chǔ)器管理和總線Fault:
SCB->SHCSR|=0x00007000;//enableUsageFault,BusFault,andMMUFault
利用串口上報(bào)Fault異常信息
在調(diào)試期間,最主要的是要弄清楚觸發(fā)了哪類Fault,什么原因觸發(fā)了Fault以及定位到觸發(fā)Fault的代碼,可以利用一份空閑串口當(dāng)作調(diào)試用,將以上信息發(fā)給PC,通過串口調(diào)試助手接收這些Fault信息。
主要步驟如下:
1.如有必要,使能非硬Fault(用法、存儲(chǔ)器管理和總線Fault)
2.如果有必要使能捕獲除法為零和未對(duì)齊內(nèi)存訪問
3.編寫Fault處理程序
4.將啟動(dòng)代碼中默認(rèn)的Fault處理程序更換成自己需要的Fault處理程序
補(bǔ)充一些基礎(chǔ)知識(shí),有利于理解下面的代碼:
堆棧:
Cortex-M3的堆棧是使用“向下生長(zhǎng)的滿?!蹦P?,SP指針指向最后一個(gè)被壓入堆棧的32位數(shù)值。在下一次壓棧時(shí),SP先自減4,再存入新的數(shù)值。POP操作正好相反,先彈出當(dāng)前SP指針處的32位數(shù)值,再將SP的值增4.
Cortex-M3的異常/中斷過程:
1. 入棧:硬件自動(dòng)把8個(gè)寄存器的值壓入堆棧(8個(gè)寄存器依次為:xPSR、PC、LR、R12以及R3~R0)。如果異常發(fā)生時(shí),當(dāng)前的代碼正在使用PSP(進(jìn)程堆棧),則上面8個(gè)寄存器壓入PSP;否則就壓入MSP(主堆棧)。一旦進(jìn)入服務(wù)例程,就將一直使用MSP。Cortex-M3內(nèi)核響應(yīng)中斷/異常的延時(shí)固定為12個(gè)時(shí)鐘周期。以上操作使用Cortex-M3的數(shù)據(jù)總線。
2.取向量:與入棧同時(shí),Cortex-M3內(nèi)核從向量表中找出正確的異常向量,然后在服務(wù)程序的入口地址欲取指。以上操作使用Cortex-M3的指令總線
3.更新寄存器:入棧和取向量操作完成后,在執(zhí)行服務(wù)例程之前,還要更新一些列寄存器:
SP:在入棧操作后,會(huì)把堆棧指針(PSP或MSP)更新到新的位置。在執(zhí)行中斷/異常服務(wù)例程時(shí),一定是使用MSP。
PSR:更新IPSR位段(PSR最低部分)的值為新響應(yīng)的異常編號(hào)。
PC:在取向量完成后,PC將指向服務(wù)例程的入口地址。
LR:在出入ISR的時(shí)候,LR保存一些在異常返回時(shí)用到的特殊位。
寄存器:
1.通用寄存器:R0~R3、R12,C函數(shù)調(diào)用標(biāo)準(zhǔn)會(huì)使用R0~R3用來傳遞參數(shù),R12(用于子程序保存SP指針,不太清楚)
2.LR:連接寄存器。LR用于在調(diào)用子程序時(shí)存儲(chǔ)返回地址。
3.PC:程序計(jì)數(shù)器,CM3內(nèi)部使用了指令流水線,讀PC時(shí)返回的值是當(dāng)前指令的地址+4.
4.xPSR程序狀態(tài)寄存器
下面以硬Fault處理為例,介紹一下如何將Fault信息上報(bào)到PC的調(diào)試助手上。
1. 在程序初始化代碼中,使能非硬Fault(使用Keil MDK編譯器,必須包含core_cm3.h頭文件)
static__INLINEvoidEnableFault(void){SCB->SHCSR|=(SCB_SHCSR_USGFAULTENA_Msk|SCB_SHCSR_BUSFAULTENA_Msk|SCB_SHCSR_MEMFAULTENA_Msk);}
2. 編寫硬Fault處理程序
/**截獲硬Fault異常*arg:堆棧指針*/voidHardFaultHandle(unsignedint*arg){unsignedintstacked_r0,stacked_r1,stacked_r2,stacked_r3,stacked_r12,stacked_lr,stacked_pc,stacked_psr;stacked_r0=((unsignedlong)arg[0]);stacked_r1=((unsignedlong)arg[1]);stacked_r2=((unsignedlong)arg[2]);stacked_r3=((unsignedlong)arg[3]);stacked_r12=((unsignedlong)arg[4]);stacked_lr=((unsignedlong)arg[5]);stacked_pc=((unsignedlong)arg[6]);stacked_psr=((unsignedlong)arg[7]);PLC_DEBUGF(TEST_DEBUG,("致命錯(cuò)誤:系統(tǒng)發(fā)生硬Fault!!n"));PLC_DEBUGF(TEST_DEBUG,("捕獲錯(cuò)誤發(fā)生時(shí)的環(huán)境,上報(bào)Fault狀態(tài)寄存器:n"));PLC_DEBUGF(TEST_DEBUG,("R0=0x%xn",stacked_r0));PLC_DEBUGF(TEST_DEBUG,("R1=0x%xn",stacked_r1));PLC_DEBUGF(TEST_DEBUG,("R2=0x%xn",stacked_r2));PLC_DEBUGF(TEST_DEBUG,("R3=0x%xn",stacked_r3));PLC_DEBUGF(TEST_DEBUG,("R12=0x%xn",stacked_r12));PLC_DEBUGF(TEST_DEBUG,("LR=0x%xn",stacked_lr));PLC_DEBUGF(TEST_DEBUG,("PC=0x%xn",stacked_pc));PLC_DEBUGF(TEST_DEBUG,("PSR=0x%xn",stacked_psr));PLC_DEBUGF(TEST_DEBUG,("HFSR=0x%xn",HFSR));PLC_DEBUGF(TEST_DEBUG,("BFSR=0x%xn",BFSR));PLC_DEBUGF(TEST_DEBUG,("BFAR=0x%xn",BFAR));PLC_DEBUGF(TEST_DEBUG,("MMSR=0x%xn",MMSR));PLC_DEBUGF(TEST_DEBUG,("MMAR=0x%xn",MMAR));PLC_DEBUGF(TEST_DEBUG,("UFSR=0x%xn",UFSR));HFSR=0xFFFFFFFF;BFSR=0xFF;MMSR=0xFF;UFSR=0xFFFF;while(1);}
3. 在啟動(dòng)代碼中,將默認(rèn)硬Fault處理程序更換為自己需要的Fault處理程序
HardFault_HandlerPROCIMPORTHardFaultHandleTSTLR,#4ITEEQMRSEQR0,MSPMRSNER0,PSPBHardFaultHandleENDP