關於STM32的IAP與APP互相跳轉


關於STM32IAPAPP互相跳轉

之前做了一個不帶系統的IAP與APP互相跳轉,在網上找了資料后,很順暢就完成了,后來在IAR集成開發環境下,IAP無系統,APP用UCOS系統做互相跳轉出現了很多問題。現將IAP學習過程和實際遇到問題總結一下。

首先說一下什么是IAP。IAP(In Application Programming)即在應用編程,IAP是用戶自己的程序在運行過程中對User Flash的部分區域進行燒寫,目的是為了在產品發布后可以方便地通過預留的通信口對產品中的固件程序進行更新升級。通常實現IAP功能時,即用戶程序運行中作自身的更新操作,需要在設計固件程序時編寫兩個項目代碼,第一個項目程序不執行正常的功能操作,而只是通過某種通信方式(如USB、USART)接收程序或數據,執行對第二部分代碼的更新;第二個項目代碼才是真正的功能代碼。

以上內容摘自原子的開發指南。說的通俗一點,要做IAP功能(也可以說成是遠程升級功能),需要有兩段程序一個是IAP程序(也可以稱為BootLoader),另一個是APP程序(主應用程序)。通過USB、串口、CAN等通訊方式向STM32發送要升級的程序文件數據(按自定的協議),IAP程序中將接收到的數據寫到APP程序的地址實現將APP程序的升級。這是大致的流程。

此文檔只做互相跳轉的總結不包含接收數據、FLASH寫入等操作。

說到IAP升級不得不說兩個圖(圖片引自原子的開發指南)

第一個是正常運行時的流程圖

clip_image002

  STM32的FLASH地址起始於0x08000000,程序文件就從此地址開始寫入。此外STM32內部通過“中斷向量表”來響應中斷,程序啟動后,將首先從“中斷向量表”取出復位中斷向量執行復位中斷程序完成啟動,而“中斷向量表”的起始地址是0x08000004,當中斷來臨,STM32的內部硬件機制亦會自動將PC指針定位到“中斷向量表”處,並根據中斷源取出對應的中斷向量執行中斷服務程序。

根據上圖分析啟動和運行過程,

① STM32在復位后,先從0X08000004地址取出復位中斷向量的地址,並跳轉到復位中斷服務程序,

② 在復位中斷服務程序執行完之后,會跳轉到的main函數(如使用KEIL MDK調試時一下載進程序,會發現需要運行幾次下一步才會跳轉到main函數的位置)

③ main函數一般都是超循環體(while(1)死循環),在main函數執行過程中,如果收到中斷請求(發生重中斷),此時STM32強制將PC指針指回中斷向量表處

④ 根據中斷源進入相應的中斷服務程序,

⑤ 在執行完中斷服務程序以后,程序再次返回main函數執行。

第二個是加入IAP功能后的流程圖

clip_image004

根據上圖分析加入IAP后的起動和運行過程

① STM32復位后,還是從0X08000004地址取出復位中斷向量的地址,並跳轉到復位中斷服務程序,在運行完復位中斷服務程序之后跳轉到IAP的main函數,如將IAP看作是一個APP的話,那么此部分和正常起動是一樣的,此步相當於正常運行的①和②。

② 在執行完IAP以后(如需要將新的APP代碼寫入STM32的FLASH或沒有更新直接跳轉。APP的復位中斷向量起始地址為0X08000004+N+M),跳轉至APP的復位向量表

③ 取出APP的復位中斷向量的地址,並跳轉執行新程序的復位中斷服務程序,隨后跳轉至APP的main函數

④ 同樣main函數為一個超循環,並且注意到此時STM32的FLASH,在不同位置上,共有兩個中斷向量表。在main函數執行過程中,如果CPU得到一個中斷請求,PC指針仍強制跳轉到地址0X08000004中斷向量表處,而不是APP程序的中斷向量表。

⑤ 程序再根據我們設置的中斷向量表偏移量,跳轉到對應中斷源的APP中斷服務程序中,

⑥ 在執行完中斷服務程序后,程序返回main函數繼續運行。

注意:IAP和APP跳轉過程中,是通過PC指針定位進行跳轉,所有的寄存器都保持原有狀態,跳轉過程中並不是做了復位。

下面是IAP和APP互相跳轉程序

/******************************************************************************

IAP 跳轉到 APP 之間跳轉函數

******************************************************************************/

void IAP_APP_Jump (void)

{

INT32U SpInitVal; //要跳轉到程序的SP初值.

INT32U JumpAddr; //要跳轉到程序的地址.即,要跳轉到程序的入口

void (*pFun)(void); //定義一個函數指針.用於指向APP程序入口

RCC_DeInit(); //關閉外設

NVIC_DeInit ();                           //恢復NVIC為復位狀態.使中斷不再發生.

SpInitVal = *(INT32U *)IAP_ADDR;//IAP_ADDR IAP的棧頂地址(0x08000000)

//跳轉到APP時 APP_ADDR AAP的棧頂地址(如:0x08003800)

JumpAddr = *(INT32U *)( IAP_ADDR + 4); //設置復位中斷向量(如上面流程分析)

__set_MSP(SpInitVal); //設置SP.,堆棧棧頂地址

pFun = (void (*)(void))JumpAddr; //生成跳轉函數.將復位中斷向量地址做為函數指針

(*pFun) (); //執行函數,實現跳轉.不再返回.

}

在IAP和APP中都需要進行中斷向量表的設置,如正常程序中設置一樣。

再介紹一下在集成開發環境下APP程序起始地址設置

Keil MDK環境下點擊Options for TargetàTarget選項卡

clip_image006

clip_image008

clip_image010

在IAP和APP均為無系統時,上面程序能夠很好實現互相跳轉。但在IAP無系統,而APP使用UCOS系統時出現了較多問題(IAR環境)。如下一一記錄各種問題的解決過程。

1、在程序正常設置APP的程序和向量表時后,在IAP直接調用跳轉APP時,程序沒有正常跳轉到APP中,跳入未區域,具體情況現已回憶不清。但分析是因為跳轉后沒有找到中斷向量表,從而沒有正常進入APP的main()函數。

clip_image012

但在BSP_Init()函數中確實有對復位中斷向量進行了設置。

NVIC_SetVectorTable(NVIC_VectTab_FLASH,(APP1_ADDR+4));

說明一下,這里將中斷向量表設置為 (APP1_ADDR+4)(偏移4個字節即為復位中斷向量),而有的程序中設置為APP1_ADDR。實際上兩種設置是一樣的,在

NVIC_SetVectorTable()函數中執行下句

SCB->VTOR = NVIC_VectTab | (Offset & (uint32_t)0x1FFFFF80); 所以可以看出是否偏移4字節是一樣的。

經測試發現需要在調用OSStart()的前一句重新設置復位中斷向量才能正確設置。

2、 經上一步修改后,IAP可以跳轉到APP中,APP也能跳轉到IAP中,但再重IAP就不能再跳回APP了。修改跳轉函數

/***********************************************************************

函數功能:跳轉到IAP函數

***********************************************************************/

void JumpToIAP(void)

{

INT32U IapSpInitVal;

INT32U IapJumpAddr;

void (*pIapFun)(void);

RCC_DeInit();

NVIC_DeInit();

__disable_irq(); //關中斷()

// APP如跳轉前關中斷,跳轉到IAP后,IAP初始化后要打開中斷

IapSpInitVal = *(INT32U *)IAP_ADDR;

IapJumpAddr = *(INT32U *)(IAP_ADDR + 4);

__set_CONTROL(0);

//進入用戶級線程模式 進入軟中斷后才可以回到特權級線程模式

//APP如使用系統如ucos必須要有此過程否則跳到IAP后,無法再次跳到APP

__set_MSP (IapSpInitVal);

pIapFun = (void (*)(void))IapJumpAddr;

(*pIapFun) ();

}

/***********************************************************************

IAP 跳轉到 APP 函數

***********************************************************************/

void Jumpto_APP(void)

{

INT32U IapSpInitVal;

INT32U IapJumpAddr;

void (*pIapFun)(void);

RCC_DeInit();//關閉外設

NVIC_DeInit();

__disable_irq(); //關中斷()如IAP關中斷 APP如果沒用UCOS系統,APP

//初始化后要開中斷,用UCOS后,在起動任務后會開中斷

IapSpInitVal = *(INT32U *)APP1_ADDR;

IapJumpAddr = *(INT32U *)(APP1_ADDR + 4);

if((IapSpInitVal & 0x2FFE0000)==0x20000000)//檢查棧頂地址是否合法.

{

__set_MSP (IapSpInitVal);

pIapFun = (void (*)(void))IapJumpAddr;

(*pIapFun) ();

}

}

如此調整后可以正常互相跳轉。

3、 將IAP增加3秒延時后再執行跳轉APP函數(使用定時器update中斷(更新中斷)定時),但一執行跳轉就進入APP的HardFault_Handler()。

因為在IAP有初始化定時器並始能了中斷,而在APP中沒有初始化定時器和初始化定時器的中斷向量表,所以在APP中定時器仍然工作,產生中斷后沒有相應中斷向量表就起出現錯誤。(自己理解,總之不管什么原因這樣操作是錯誤的。)在執行跳轉APP函數前將定時器更新中斷失能,並且將定時器失能可解決!

/***************************************************************************

更能:關閉計時器

輸入參數:num 定時器號

***************************************************************************/

void CloseTim(u8 num)

{

u32 tim = 0;

tim = (APB1PERIPH_BASE + (num-2)*0x0400); //計算定時器地址

TIM_ITConfig((TIM_TypeDef*)tim,TIM_IT_Update,DISABLE);

TIM_Cmd((TIM_TypeDef*)tim, DISABLE);

}

在用到STM32定時器的更新中斷時,發現有些情形下只要開啟定時器就立即進入一次中斷。准確說,只要使能更新中斷允許位就立即響應一次更新中斷【當然前提是相關NVIC也已經配置好】。換言之,只要使能了相關定時器更新中斷,不管你定時間隔多長甚至不在乎你是否啟動了相關定時器,它都會立即進入一次定時器更新中斷服務程序。這個問題比較容易忽視,有些情況下也無關緊要,但有些情況可能會給應用帶來困擾。

解決辦法是在使能定時器更新中斷前先清除更新中斷標志位。

在初始化定時器時使用如下代碼:

TIM_ClearITPendingBit(TIM4,TIM_IT_Update);

TIM_ITConfig(TIM4,TIM_IT_Update, ENABLE);

TIM_Cmd(TIM4, ENABLE); //開啟時鍾

記錄自己的工作中遇到的問題,一是方便自己查找學習,二是方便有初學者遇到相似問題,提供解決思路。以前工作或學習中遇到問題沒有及時記錄,后來再總結時,對當時的現象回憶不清。以后要及時截圖記錄,及時總結!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM