关于STM32的IAP与APP互相跳转
關(guān)于STM32的IAP與APP互相跳轉(zhuǎn)
之前做了一個(gè)不帶系統(tǒng)的IAP與APP互相跳轉(zhuǎn),在網(wǎng)上找了資料后,很順暢就完成了,后來(lái)在IAR集成開(kāi)發(fā)環(huán)境下,IAP無(wú)系統(tǒng),APP用UCOS系統(tǒng)做互相跳轉(zhuǎn)出現(xiàn)了很多問(wèn)題。現(xiàn)將IAP學(xué)習(xí)過(guò)程和實(shí)際遇到問(wèn)題總結(jié)一下。
首先說(shuō)一下什么是IAP。IAP(In Application Programming)即在應(yīng)用編程,IAP是用戶(hù)自己的程序在運(yùn)行過(guò)程中對(duì)User Flash的部分區(qū)域進(jìn)行燒寫(xiě),目的是為了在產(chǎn)品發(fā)布后可以方便地通過(guò)預(yù)留的通信口對(duì)產(chǎn)品中的固件程序進(jìn)行更新升級(jí)。通常實(shí)現(xiàn)IAP功能時(shí),即用戶(hù)程序運(yùn)行中作自身的更新操作,需要在設(shè)計(jì)固件程序時(shí)編寫(xiě)兩個(gè)項(xiàng)目代碼,第一個(gè)項(xiàng)目程序不執(zhí)行正常的功能操作,而只是通過(guò)某種通信方式(如USB、USART)接收程序或數(shù)據(jù),執(zhí)行對(duì)第二部分代碼的更新;第二個(gè)項(xiàng)目代碼才是真正的功能代碼。
以上內(nèi)容摘自原子的開(kāi)發(fā)指南。說(shuō)的通俗一點(diǎn),要做IAP功能(也可以說(shuō)成是遠(yuǎn)程升級(jí)功能),需要有兩段程序一個(gè)是IAP程序(也可以稱(chēng)為BootLoader),另一個(gè)是APP程序(主應(yīng)用程序)。通過(guò)USB、串口、CAN等通訊方式向STM32發(fā)送要升級(jí)的程序文件數(shù)據(jù)(按自定的協(xié)議),IAP程序中將接收到的數(shù)據(jù)寫(xiě)到APP程序的地址實(shí)現(xiàn)將APP程序的升級(jí)。這是大致的流程。
此文檔只做互相跳轉(zhuǎn)的總結(jié)不包含接收數(shù)據(jù)、FLASH寫(xiě)入等操作。
說(shuō)到IAP升級(jí)不得不說(shuō)兩個(gè)圖(圖片引自原子的開(kāi)發(fā)指南)
第一個(gè)是正常運(yùn)行時(shí)的流程圖
? STM32的FLASH地址起始于0x08000000,程序文件就從此地址開(kāi)始寫(xiě)入。此外STM32內(nèi)部通過(guò)“中斷向量表”來(lái)響應(yīng)中斷,程序啟動(dòng)后,將首先從“中斷向量表”取出復(fù)位中斷向量執(zhí)行復(fù)位中斷程序完成啟動(dòng),而“中斷向量表”的起始地址是0x08000004,當(dāng)中斷來(lái)臨,STM32的內(nèi)部硬件機(jī)制亦會(huì)自動(dòng)將PC指針定位到“中斷向量表”處,并根據(jù)中斷源取出對(duì)應(yīng)的中斷向量執(zhí)行中斷服務(wù)程序。
根據(jù)上圖分析啟動(dòng)和運(yùn)行過(guò)程,
① STM32在復(fù)位后,先從0X08000004地址取出復(fù)位中斷向量的地址,并跳轉(zhuǎn)到復(fù)位中斷服務(wù)程序,
② 在復(fù)位中斷服務(wù)程序執(zhí)行完之后,會(huì)跳轉(zhuǎn)到的main函數(shù)(如使用KEIL MDK調(diào)試時(shí)一下載進(jìn)程序,會(huì)發(fā)現(xiàn)需要運(yùn)行幾次下一步才會(huì)跳轉(zhuǎn)到main函數(shù)的位置)
③ main函數(shù)一般都是超循環(huán)體(while(1)死循環(huán)),在main函數(shù)執(zhí)行過(guò)程中,如果收到中斷請(qǐng)求(發(fā)生重中斷),此時(shí)STM32強(qiáng)制將PC指針指回中斷向量表處
④ 根據(jù)中斷源進(jìn)入相應(yīng)的中斷服務(wù)程序,
⑤ 在執(zhí)行完中斷服務(wù)程序以后,程序再次返回main函數(shù)執(zhí)行。
第二個(gè)是加入IAP功能后的流程圖
根據(jù)上圖分析加入IAP后的起動(dòng)和運(yùn)行過(guò)程
① STM32復(fù)位后,還是從0X08000004地址取出復(fù)位中斷向量的地址,并跳轉(zhuǎn)到復(fù)位中斷服務(wù)程序,在運(yùn)行完復(fù)位中斷服務(wù)程序之后跳轉(zhuǎn)到IAP的main函數(shù),如將IAP看作是一個(gè)APP的話,那么此部分和正常起動(dòng)是一樣的,此步相當(dāng)于正常運(yùn)行的①和②。
② 在執(zhí)行完IAP以后(如需要將新的APP代碼寫(xiě)入STM32的FLASH或沒(méi)有更新直接跳轉(zhuǎn)。APP的復(fù)位中斷向量起始地址為0X08000004+N+M),跳轉(zhuǎn)至APP的復(fù)位向量表
③ 取出APP的復(fù)位中斷向量的地址,并跳轉(zhuǎn)執(zhí)行新程序的復(fù)位中斷服務(wù)程序,隨后跳轉(zhuǎn)至APP的main函數(shù)
④ 同樣main函數(shù)為一個(gè)超循環(huán),并且注意到此時(shí)STM32的FLASH,在不同位置上,共有兩個(gè)中斷向量表。在main函數(shù)執(zhí)行過(guò)程中,如果CPU得到一個(gè)中斷請(qǐng)求,PC指針仍強(qiáng)制跳轉(zhuǎn)到地址0X08000004中斷向量表處,而不是APP程序的中斷向量表。
⑤ 程序再根據(jù)我們?cè)O(shè)置的中斷向量表偏移量,跳轉(zhuǎn)到對(duì)應(yīng)中斷源的APP中斷服務(wù)程序中,
⑥ 在執(zhí)行完中斷服務(wù)程序后,程序返回main函數(shù)繼續(xù)運(yùn)行。
注意:IAP和APP跳轉(zhuǎn)過(guò)程中,是通過(guò)PC指針定位進(jìn)行跳轉(zhuǎn),所有的寄存器都保持原有狀態(tài),跳轉(zhuǎn)過(guò)程中并不是做了復(fù)位。
下面是IAP和APP互相跳轉(zhuǎn)程序
/******************************************************************************
IAP 跳轉(zhuǎn)到 APP 之間跳轉(zhuǎn)函數(shù)
******************************************************************************/
void IAP_APP_Jump (void)
{
INT32U SpInitVal; //要跳轉(zhuǎn)到程序的SP初值.
INT32U JumpAddr; //要跳轉(zhuǎn)到程序的地址.即,要跳轉(zhuǎn)到程序的入口
void (*pFun)(void); //定義一個(gè)函數(shù)指針.用于指向APP程序入口
RCC_DeInit(); //關(guān)閉外設(shè)
NVIC_DeInit ();?????????????????????????? //恢復(fù)NVIC為復(fù)位狀態(tài).使中斷不再發(fā)生.
SpInitVal = *(INT32U *)IAP_ADDR;//IAP_ADDR IAP的棧頂?shù)刂?#xff08;0x08000000)
//跳轉(zhuǎn)到APP時(shí) APP_ADDR AAP的棧頂?shù)刂?#xff08;如:0x08003800)
JumpAddr = *(INT32U *)( IAP_ADDR + 4); //設(shè)置復(fù)位中斷向量(如上面流程分析)
__set_MSP(SpInitVal); //設(shè)置SP.,堆棧棧頂?shù)刂?/p>
pFun = (void (*)(void))JumpAddr; //生成跳轉(zhuǎn)函數(shù).將復(fù)位中斷向量地址做為函數(shù)指針
(*pFun) (); //執(zhí)行函數(shù),實(shí)現(xiàn)跳轉(zhuǎn).不再返回.
}
在IAP和APP中都需要進(jìn)行中斷向量表的設(shè)置,如正常程序中設(shè)置一樣。
再介紹一下在集成開(kāi)發(fā)環(huán)境下APP程序起始地址設(shè)置
Keil MDK環(huán)境下點(diǎn)擊Options for TargetàTarget選項(xiàng)卡
在IAP和APP均為無(wú)系統(tǒng)時(shí),上面程序能夠很好實(shí)現(xiàn)互相跳轉(zhuǎn)。但在IAP無(wú)系統(tǒng),而APP使用UCOS系統(tǒng)時(shí)出現(xiàn)了較多問(wèn)題(IAR環(huán)境)。如下一一記錄各種問(wèn)題的解決過(guò)程。
1、在程序正常設(shè)置APP的程序和向量表時(shí)后,在IAP直接調(diào)用跳轉(zhuǎn)APP時(shí),程序沒(méi)有正常跳轉(zhuǎn)到APP中,跳入未區(qū)域,具體情況現(xiàn)已回憶不清。但分析是因?yàn)樘D(zhuǎn)后沒(méi)有找到中斷向量表,從而沒(méi)有正常進(jìn)入APP的main()函數(shù)。
但在BSP_Init()函數(shù)中確實(shí)有對(duì)復(fù)位中斷向量進(jìn)行了設(shè)置。
NVIC_SetVectorTable(NVIC_VectTab_FLASH,(APP1_ADDR+4));
說(shuō)明一下,這里將中斷向量表設(shè)置為 (APP1_ADDR+4)(偏移4個(gè)字節(jié)即為復(fù)位中斷向量),而有的程序中設(shè)置為APP1_ADDR。實(shí)際上兩種設(shè)置是一樣的,在
NVIC_SetVectorTable()函數(shù)中執(zhí)行下句
SCB->VTOR = NVIC_VectTab | (Offset & (uint32_t)0x1FFFFF80); 所以可以看出是否偏移4字節(jié)是一樣的。
經(jīng)測(cè)試發(fā)現(xiàn)需要在調(diào)用OSStart()的前一句重新設(shè)置復(fù)位中斷向量才能正確設(shè)置。
2、 經(jīng)上一步修改后,IAP可以跳轉(zhuǎn)到APP中,APP也能跳轉(zhuǎn)到IAP中,但再重IAP就不能再跳回APP了。修改跳轉(zhuǎn)函數(shù)
/***********************************************************************
函數(shù)功能:跳轉(zhuǎn)到IAP函數(shù)
***********************************************************************/
void JumpToIAP(void)
{
INT32U IapSpInitVal;
INT32U IapJumpAddr;
void (*pIapFun)(void);
RCC_DeInit();
NVIC_DeInit();
__disable_irq(); //關(guān)中斷()
// APP如跳轉(zhuǎn)前關(guān)中斷,跳轉(zhuǎn)到IAP后,IAP初始化后要打開(kāi)中斷
IapSpInitVal = *(INT32U *)IAP_ADDR;
IapJumpAddr = *(INT32U *)(IAP_ADDR + 4);
__set_CONTROL(0);
//進(jìn)入用戶(hù)級(jí)線程模式 進(jìn)入軟中斷后才可以回到特權(quán)級(jí)線程模式
//APP如使用系統(tǒng)如ucos必須要有此過(guò)程否則跳到IAP后,無(wú)法再次跳到APP
__set_MSP (IapSpInitVal);
pIapFun = (void (*)(void))IapJumpAddr;
(*pIapFun) ();
}
/***********************************************************************
IAP 跳轉(zhuǎn)到 APP 函數(shù)
***********************************************************************/
void Jumpto_APP(void)
{
INT32U IapSpInitVal;
INT32U IapJumpAddr;
void (*pIapFun)(void);
RCC_DeInit();//關(guān)閉外設(shè)
NVIC_DeInit();
__disable_irq(); //關(guān)中斷()如IAP關(guān)中斷 APP如果沒(méi)用UCOS系統(tǒng),APP
//初始化后要開(kāi)中斷,用UCOS后,在起動(dòng)任務(wù)后會(huì)開(kāi)中斷
IapSpInitVal = *(INT32U *)APP1_ADDR;
IapJumpAddr = *(INT32U *)(APP1_ADDR + 4);
if((IapSpInitVal & 0x2FFE0000)==0x20000000)//檢查棧頂?shù)刂肥欠窈戏?
{
__set_MSP (IapSpInitVal);
pIapFun = (void (*)(void))IapJumpAddr;
(*pIapFun) ();
}
}
如此調(diào)整后可以正常互相跳轉(zhuǎn)。
3、 將IAP增加3秒延時(shí)后再執(zhí)行跳轉(zhuǎn)APP函數(shù)(使用定時(shí)器update中斷(更新中斷)定時(shí)),但一執(zhí)行跳轉(zhuǎn)就進(jìn)入APP的HardFault_Handler()。
因?yàn)樵贗AP有初始化定時(shí)器并始能了中斷,而在APP中沒(méi)有初始化定時(shí)器和初始化定時(shí)器的中斷向量表,所以在APP中定時(shí)器仍然工作,產(chǎn)生中斷后沒(méi)有相應(yīng)中斷向量表就起出現(xiàn)錯(cuò)誤。(自己理解,總之不管什么原因這樣操作是錯(cuò)誤的。)在執(zhí)行跳轉(zhuǎn)APP函數(shù)前將定時(shí)器更新中斷失能,并且將定時(shí)器失能可解決!
/***************************************************************************
更能:關(guān)閉計(jì)時(shí)器
輸入?yún)?shù):num 定時(shí)器號(hào)
***************************************************************************/
void CloseTim(u8 num)
{
u32 tim = 0;
tim = (APB1PERIPH_BASE + (num-2)*0x0400); //計(jì)算定時(shí)器地址
TIM_ITConfig((TIM_TypeDef*)tim,TIM_IT_Update,DISABLE);
TIM_Cmd((TIM_TypeDef*)tim, DISABLE);
}
在用到STM32定時(shí)器的更新中斷時(shí),發(fā)現(xiàn)有些情形下只要開(kāi)啟定時(shí)器就立即進(jìn)入一次中斷。準(zhǔn)確說(shuō),只要使能更新中斷允許位就立即響應(yīng)一次更新中斷【當(dāng)然前提是相關(guān)NVIC也已經(jīng)配置好】。換言之,只要使能了相關(guān)定時(shí)器更新中斷,不管你定時(shí)間隔多長(zhǎng)甚至不在乎你是否啟動(dòng)了相關(guān)定時(shí)器,它都會(huì)立即進(jìn)入一次定時(shí)器更新中斷服務(wù)程序。這個(gè)問(wèn)題比較容易忽視,有些情況下也無(wú)關(guān)緊要,但有些情況可能會(huì)給應(yīng)用帶來(lái)困擾。
解決辦法是在使能定時(shí)器更新中斷前先清除更新中斷標(biāo)志位。
在初始化定時(shí)器時(shí)使用如下代碼:
TIM_ClearITPendingBit(TIM4,TIM_IT_Update);
TIM_ITConfig(TIM4,TIM_IT_Update, ENABLE);
TIM_Cmd(TIM4, ENABLE); //開(kāi)啟時(shí)鐘
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專(zhuān)家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的关于STM32的IAP与APP互相跳转的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 访问备份数据寄存器时,需要打开BKP时钟
- 下一篇: stm32 IAP APP 相互跳转实验