首先,在您動手做這個實驗之前,先要弄清除咱倆的軟硬件有什么不同:
1. 我的CPU是STM32F103ZET6,里面有512K的Flash,您的CPU如果是其它類型,也不要緊,只是在程序里面,地址上限可能不一樣。但是,個人覺得,最好能用256K以下的FLASH。
2. 我的外部存儲介質是U盤,如果您的外部存儲介質是SD卡,那也應該一樣用,只是它們必須是FAT16,FAT32文件系統。如果您的板上沒有外部存儲介質,那也能做跳轉實驗,只是不能做加載APP實驗。
3. 我的仿真器是JLINK7,如果您的仿真器是其它的,估計也沒多大問題,只要您會用它就行了。什么?沒有仿真器,那還是別做這個實驗吧,出錯了沒法調試。
4. 我的開發環境是RVMDK 3。7,STM庫是V2。03。使用其它開發環境的話,您要是能找到MDK中的設置對應到您那里怎么設置,估計也沒問題。至于庫嘛,您現在是用哪個就哪個吧,全部包在您的工程里,沒問題的。
好了,開始啦。
先找個你以前調好的工程,當然,最好是非常可靠的,內容很精彩的,帶液晶顯示的,這樣比較容易知道你后面有沒有調好。這個工程還最好是在FLASH里面運行的,如果不是,要將它改回來。
至于什么開發文檔,太麻煩了,不用看。我之前看了STM的IAP應用筆記AN2557,就覺得一個字“亂”,特別是心里還沒譜的時候,更是越看越糊涂,這么大個工程,到最后對我有幫助的,就是一小段,就是如何擦除,如何編程那小段。當然,STM32的庫還是非常有用的,如果不用庫的話,學習、工作進度會慢很多。
說多啦,找好工程沒有?找好工程咱就開工了。將這個工程復制兩份,一份命名為IAP,一份命名為APP。
第一步:規劃好你兩個程序的存放位置。
IAP程序肯定是從0X08000000開始的,因為它是引導程序。將IAP程序放在0X08000000-0X0800FFFF的位置,給它64K空間,足夠了。
APP程序從0X08010000-0X0807FFFF,給它448K空間。
如果您的CPU不同,那APP程序的空間小一點,也沒問題。
第二步:制作你的APP程序。
1. 將程序定位在0X08010000開始的位置。
點魔術棒,打開目標選項設置。
選Target選項卡,IROM1改成從0X08010000開始,尺寸0X00070000;
Debug選項卡,Load application at Startup打上勾,Run to main()打上勾;
Utilitiles選項卡,點settings按紐,彈出Flash download卡,Erase sectors打上勾,點你的編程算法,將底下的的起始地址改成0X08010000,尺寸0X00070000。
2. 制作一個RunInFlashOffset.ini文件。文件內容為:
SP = _RDWord(0x08010000); // Setup Stack Pointer
PC = _RDWORD(0x08010004); // Setup PRogram Counter
目的是在用JLINK調試的時候,引導程序運行。
點魔術棒,打開目標選項設置。
選Debug選項卡,Initialization File:項,選擇上面的RunInFlashOffset.ini。
3. 為了從IAP程序跳來運行APP的時候正常開始,初始化時要恢復RCC為復位狀態,恢復NVIC為復位狀態。
在你的RCC初始化部分,第一句加上:
RCC_DeInit();
在你的NVIC初始化部分,第一句加上:
NVIC_DeInit ();
4. 重定位中斷表到0X08010000位置。
在上面NVIC_DeInit ();后面加上:
NVIC_SetVectorTable (NVIC_VectTab_FLASH, 0x00010000);
如果原來有其它的定位語句,將它刪掉。
5. 編寫跳轉IAP函數:
/**************************************************************************************************
函數: 運行IAP程序.
輸入: 無
返回: 無.不再返回.
說明: 由于APP是在IAP的基礎上運行的,因此,IAP一定是有效的,這里不再作IAP有效性檢查.
**************************************************************************************************/
#define IAP_ADDR 0X08000000
void IapProgramRun(void)
{
INT32U IapSpInitVal; //IAP程序的SP初值.
INT32U IapJumpAddr; //IAP程序的跳轉地址.即,IAP程序的入口.
void (*pIapFun)(void); //定義一個函數指針.用于指向APP程序入口.
NVIC_DeInit (); //恢復NVIC為復位狀態.使中斷不再發生.
IapSpInitVal = *(INT32U *)IAP_ADDR; //取APP的SP初值.
IapJumpAddr = *(INT32U *)(IAP_ADDR + 4); //取程序入口.
__MSR_MSP (IapSpInitVal); //設置SP.
pIapFun = (void (*)(void))IapJumpAddr; //生成跳轉函數.
(*pIapFun) (); //跳轉.不再返回.
}
6. 編寫在一定條件下跳轉IAP的部分。比如按下某個鍵,就跳到IAP去。
完成上述幾步后,編譯調試,用JLINK調試,可以直接運行的,跟你原來的工程應該沒區別。有問題的話,將它解決。
第三步:制作您的IAP程序。
1. 將程序定位在0X08000000開始的位置。如果您的程序本來就是在這個位置的,不用改了。
點魔術棒,打開目標選項設置。
選Target選項卡,IROM1改成從0X08000000開始,尺寸0X00010000;
Debug選項卡,Load Application at Startup打上勾,Run to main()打上勾;
Utilitiles選項卡,點settings按紐,彈出Flash download卡,Erase sectors打上勾,點你的編程算法,將底下的的起始地址改成0X08000000,尺寸0X00010000。
2. 為了從APP程序跳回來運行IAP的時候正常開始,初始化時要恢復RCC為復位狀態,恢復NVIC為復位狀態。
在你的RCC初始化部分,第一句加上:
RCC_DeInit();
在你的NVIC初始化部分,第一句加上:
NVIC_DeInit ();
3. 重定位中斷表到0X08000000位置。
在上面NVIC_DeInit ();后面加上:
NVIC_SetVectorTable (NVIC_VectTab_FLASH, 0x0);
如果原來有其它的定位語句,將它刪掉。
4. 編寫在一定條件下跳轉APP的部分。比如按下某個鍵,就跳到APP去。
5. 編寫跳轉APP函數:
#define APP_ADDR 0X08010000
OP_RESULT AppProgramRun(void)
{
INT32U AppSpInitVal; //App程序的SP初值.
INT32U AppJumpAddr; //APP程序的跳轉地址.即,APP程序的入口.
void (*pAppFun)(void); //定義一個函數指針.用于指向APP程序入口.
AppSpInitVal = *(INT32U *)APP_ADDR; //取APP的SP初值.
if (AppSpInitVal & 0XFFFF 0000 != 0X20 00 00 00) //APP未寫入.不能跳.
{
FaceEnterDialog (&OpFailDialog);
return OP_FAIL;
}
AppJumpAddr = *(INT32U *)(APP_ADDR + 4); //取程序入口.
if ((AppJumpAddr & 0X FF F8 00 00) != 0X 08 00 00 00) //APP無效.不能跳.
{
FaceEnterDialog (&OpFailDialog);
return OP_FAIL;
}
NVIC_DeInit (); //恢復NVIC為復位狀態.使中斷不再發生.
__MSR_MSP (AppSpInitVal); //設置SP.
pAppFun = (void (*)(void))AppJumpAddr; //生成跳轉函數.
(*pAppFun) (); //跳轉.不再返回.
return OP_SUCCESS;
}
完成上述幾步后,編譯調試,OK。
第四步:雙程序調試:
1. 用仿真器運行IAP程序,然后按下按鍵,轉到APP去。如果你正常轉到APP,說明成功。不能的話,用仿真器跟一下,把問題解決。
2. 用仿真器運行APP程序,然后按下按鍵,轉到IAP去。如果你正常轉到IAP,說明成功。不能的話,用仿真器跟一下,把問題解決。
3. 用仿真器運行IAP程序,然后按下按鍵,轉到APP。
在APP中又按下按鍵,轉回IAP。如此反復。
可以在IAP第一句設個斷點,每次轉回來的時候,都應該會停在那里的。
注意:在跳到另一個程序中運行的時候,要停止不能直接點“停止調試按紐”,就是那個放大鏡一樣的按紐,否則MDK立馬出錯退出。要停止的話,要先打開反匯編觀察窗口,然后按下“停止”按紐,就是左上角紅圓圈里一把叉那個,
再按下“停止調試按紐”。
第五步:在IAP中加載APP。
如果你的板子上沒有USB,或都SD卡,這后面的就做不了了。
1. 改IAP程序,加上加載APP程序功能。就是按下某個鍵時,從U盤讀取APP程序,并把它寫到FLASH中。這個參考附件。
2. 在APP程序中,選擇輸出HEX文件。目前來說,HEX文件是比較方便處理的文件。
點魔術棒,打開目標選項設置。
選Output選項卡,Create HEX File打上勾。
編譯,生成HEX文件。
3. 把APP。HEX拷到U盤中,然后用IAP程序加載。
第六步:讓IAP區分是復位運行,還是從APP轉過來運行的。
打開你的啟動文件(我這里是stm32f10x_vector.s),看一下它里面棧空間是多大,堆空間是多大。在IAP程序主函數第一句設個斷點,記下此時的SP值,一般這個值比 棧+堆+全局變量還要大一些。在這個值+8之上的內部RAM空間,是程序用不上的。所以可以讓APP程序在RAM空間的頂端設置一個標志,然I后讓AP程序去根據這個標志來區別復位運行、從APP轉過來的運行。
區分IAP的運行方式有一個特殊的用途,那就是從APP程序中,跳轉IAP程序,來更新APP程序。這是真正的在線升級。
最后,我總結一下,要做IAP和APP間的互相跳轉,要注間以下幾點:
1. APP程序是放在FLASH的中間位置運行的,所以在編譯、下載、調試時,都要指定它的入口(本例是0X08010000)。具體實現就是在魔術棒中的設置。
2. 程序可以是從另外一個程序轉來的,而另外一個程序的RCC,NVIC設置不可知,所以必須在初始化時,恢復RCC,NVIC為復位狀態,并且設置正確的NVIC向量表。
3. 在要跳到別的程序之前,要恢復NVIC為復位狀態,防止在跳轉過程中出現中斷。
4. 如果IAP要判斷是復位開始運行的,還是從APP跳轉過來的,應該用程序啟動部分不會被改變的內存、外存存儲一個標志,用它來判定從哪跳來的。
新聞熱點
疑難解答