?
一、環境介紹
二、 IAP介紹
IAP,全稱是“In-Application Programming”,中文解釋為“在程序中編程”。IAP是一種對通過微控制器的對外接口(如USART,IIC,CAN,USB,以太網接口甚至是無線射頻通道)對正在運行程序的微控制器進行內部程序的更新的技術(注意這完全有別于ICP或者ISP技術)。
ICP(In-Circuit Programming)技術即通過在線仿真器對單片機進行程序燒寫,而ISP技術則是通過單片機內置的bootloader程序引導的燒寫技術。無論是ICP技術還是ISP技術,都需要有機械性的操作如連接下載線,設置跳線帽等。若產品的電路板已經層層密封在外殼中,要對其進行程序更新無疑困難重重,若產品安裝于狹窄空間等難以觸及的地方,更是一場災難。但若進引入了IAP技術,則完全可以避免上述尷尬情況,而且若使用遠距離或無線的數據傳輸方案,甚至可以實現遠程編程和無線編程。這絕對是ICP或ISP技術無法做到的。某種微控制器支持IAP技術的首要前提是其必須是基于可重復編程閃存的微控制器。STM32微控制器帶有可編程的內置閃存,同時STM32擁有在數量上和種類上都非常豐富的外設通信接口,因此在STM32上實現IAP技術是完全可行的。
實現IAP技術的核心是一段預先燒寫在單片機內部的IAP程序。這段程序主要負責與外部的上位機軟件進行握手同步,然后將通過外設通信接口將來自于上位機軟件的程序數據接收后寫入單片機內部指定的閃存區域,然后再跳轉執行新寫入的程序,最終就達到了程序更新的目的。
在STM32微控制器上實現IAP程序之前首先要回顧一下STM32的內部閃存組織架構和其啟動過程。STM32的內部閃存地址起始于0x8000000,一般情況下,程序文件就從此地址開始寫入。此外STM32是基于Cortex-M3內核的微控制器,其內部通過一張“中斷向量表”來響應中斷,程序啟動后,將首先從“中斷向量表”取出復位中斷向量執行復位中斷程序完成啟動。而這張“中斷向量表”的起始地址是0x8000004,當中斷來臨,STM32的內部硬件機制亦會自動將PC指針定位到“中斷向量表”處,并根據中斷源取出對應的中斷向量執行中斷服務程序。最后還需要知道關鍵的一點,通過修改STM32工程的鏈接腳本可以修改程序文件寫入閃存的起始地址。
在STM32微控制器上實現IAP方案,除了常規的串口接收數據以及閃存數據寫入等常規操作外,還需注意STM32的啟動過程和中斷響應方式。
下圖顯示了STM32常規的運行流程:
?
?
圖解讀如下:
1、 STM32復位后,會從地址為0x8000004處取出復位中斷向量的地址,并跳轉執行復位中斷服務程序。
2、 復位中斷服務程序執行的最終結果是跳轉至C程序的main函數,而main函數應該是一個死循環,是一個永不返回的函數。
3、 在main函數執行的過程中,發生了一個中斷請求,此時STM32的硬件機制會將PC指針強制指回中斷向量表處。
4、 根據中斷源進入相應的中斷服務程序。
5、 中斷服務程序執行完畢后,程序再度返回至main函數中執行。
若在STM32中加入了IAP程序:
?
1、 STM32復位后,從地址為0x8000004處取出復位中斷向量的地址,并跳轉執行復位中斷服務程序,隨后跳轉至IAP程序的main函數。
2、 執行完IAP過程后(STM32內部多出了新寫入的程序,地址始于0x8000004+N+M)跳轉至新寫入程序的復位向量表,取出新程序的復位中斷向量的地址,并跳轉執行新程序的復位中斷服務程序,隨后跳轉至新程序的main函數。新程序的main函數應該也具有永不返回的特性。同時應該注意在STM32的內部存儲空間在不同的位置上出現了2個中斷向量表。
3、 在新程序main函數執行的過程中,一個中斷請求來臨,PC指針仍會回轉至地址為0x8000004中斷向量表處,而并不是新程序的中斷向量表,注意到這是由STM32的硬件機制決定的。
4、 根據中斷源跳轉至對應的中斷服務,注意此時是跳轉至了新程序的中斷服務程序中。
5、 中斷服務執行完畢后,返回main函數。
二、hex文件與bin文件區別
Intel HEX文件是記錄文本行的ASCII文本文件,在Intel HEX文件中,每一行是一個HEX記錄,由十六進制數組成的機器碼或者數據常量。Intel HEX文件經常被用于將程序或數據傳輸存儲到ROM、EPROM,大多數編程器和模擬器使用Intel HEX文件。
很多編譯器的支持生成HEX格式的燒錄文件,尤其是Keil c。但是編程器能夠下載的往往是BIN格式,因此HEX轉BIN是每個編程器都必須支持的功能。HEX格式文件以行為單位,每行由“:”(0x3a)開始,以回車鍵結束(0x0d,0x0a)。行內的數據都是由兩個字符表示一個16進制字節,比如”01”就表示數0x01;”0a”,就表示0x0a。對于16位的地址,則高位在前低位在后,比如地址0x010a,在HEX格式文件中就表示為字符串”010a”。hex和bin文件格式
Hex文件,這里指的是Intel標準的十六進制文件,也就是機器代碼的十六進制形式,并且是用一定文件格式的ASCII碼來表示。具體格式介紹如下: Intel hex 文件常用來保存單片機或其他處理器的目標程序代碼。它保存物理程序存儲區中的目標代碼映象。一般的編程器都支持這種格式。 hex和bin文件格式Hex文件,這里指的是Intel標準的十六進制文件,也就是機器代碼的十六進制形式,并且是用一定文件格式的ASCII碼來表示。具體格式介紹如下: Intel hex 文件常用來保存單片機或其他處理器的目標程序代碼。它保存物理程序存儲區中的目標代碼映象。一般的編程器都支持這種格式。
三、使用Keil軟件完成hex文件轉bin文件
?
?
選項框里的代碼:
C:\app_setup\for_KEIL\ARM\ARMCC\bin\fromelf.exe --bin -o ./OBJECT/STM32_MD.bin ./OBJECT/STM32_MD.axf
解析如下:
C:\app_setup\for_KEIL\ARM\ARMCC\bin\fromelf.exe:是keil軟件安裝目錄下的一個工具,用于生成bin
--bin -o ./OBJECT/STM32_MD.bin :指定生成bin文件的目錄和名稱
./OBJECT/STM32_MD.axf :指定輸入的文件. 生成hex文件需要axf文件
新工程的編譯指令:
C:\Keil_v5\ARM\ARMCC\bin\fromelf.exe --bin -o ./obj/STM32HD.bin ./obj/STM32HD.axf
?
?
將該文件下載到STM32內置FLASH,復位開發板,即可啟動程序。
四、 使用win hex軟件將bin文件搞成數組
?
?
生成數組之后,可以直接將數組編譯到程序里,然后使用STM32內置FLASH編程代碼,將該程序燒寫到內置FLASH里,再復位開發板即可運行新的程序。
五、 Keil編譯程序大小計算
?
Program Size: Code=x RO-data=x RW-data=x ZI-data=x 的含義
- Code(代碼): 程序所占用的FLASH大小,存儲在FLASH.
2. RO-data(只讀的數據): Read-only-data,程序定義的常量,如const型,存儲在FLASH中。
3. RW-data(有初始值要求的、可讀可寫的數據):
4. Read-write-data,已經被初始化的變量,存儲在FLASH中。初始化時RW-data從flash拷貝到SRAM。
5. ZI-data:Zero-Init-data,未被初始化的可讀寫變量,存儲在SRAM中。ZI-data不會被算做代碼里因為不會被初始化。
ROM(Flash) size = Code + RO-data + RW-data;
RAM size = RW-data + ZI-data
簡單的說就是在燒寫的時候是FLASH中的被占用的空間為:Code+RO Data+RW Data
程序運行的時候,芯片內部RAM使用的空間為: RW Data + ZI Data
六、工程編譯信息與堆棧信息查看
?
對于沒有OS的程序,堆棧大小是在 startup.s 里設置的: Stack_Size EQU 0x00000800
對于使用用 uCos 的系統,OS自帶任務的堆棧,在 os_cfg.h 里定義:
/* ——————— TASK STACK SIZE ———————- */
#define OS_TASK_TMR_STK_SIZE 128 /* Timer task stack size (# of OS_STK wide entries) */
#define OS_TASK_STAT_STK_SIZE 128 /* Statistics task stack size (# of OS_STK wide entries) */
#define OS_TASK_IDLE_STK_SIZE 128 /* Idle task stack size (# of OS_STK wide entries) */
用戶程序的任務堆棧,在 app_cfg.h 里定義:
#define APP_TASK_MANAGER_STK_SIZE 512
#define APP_TASK_GSM_STK_SIZE 512
#define APP_TASK_OBD_STK_SIZE 512
#define OS_PROBE_TASK_STK_SIZE 128
總結:
1, 合理設置堆棧很重要
2, 多種方法結合,相互核對、校驗
3, 盡量避免大數組,如果一定要用,盡量定義為 全局變量,使其不占用堆棧空間, 如果函數有重入可能性,則要注意保護。
七、實現STM32在線升級程序
7.1 升級的思路與步驟
1. 首先得完成STM32內置FLASH編程操作
2. 將(升級的程序)新的程序編譯生成bin文件(編譯之前需要在Keil軟件里設置FLASH的起始位置)
3. 創建一個專門用于升級的boot程序(IAP Bootloader)
4. 使用網絡、串口、SD卡等方式接收到bin文件,再將bin文件燒寫到STM32內置FLASH里
5. 設置主堆棧指針
6. 將用戶代碼區第二個字(第4個字節)為程序開始地址(強制轉為函數指針)
7. 執行函數,進行程序跳轉
7.2 待升級的程序FLASH起始設置
Bootloader的程序大小先固定為: 20KB,最好是越小越好,可以預留更加多的空間給APP程序使用。
20KB----->20480Byte-----> 0x5000 |
STM32內置FLASH閃存的起始地址是: 0x08000000 ,大小是512KB。
現在將內置FLASH閃存前20KB的空間留給Bootloader程序使用,后面剩下的空間就給APP程序使用。
APP程序的起始位置就可以設置為: 0x08000000+ 0x5000=0x08005000 剩余的大小就是: 512KB-20KB=492KB------>503808Byte-------->0x7B000 |
設置FLASH的起始位置(APP主程序):
?
中斷向量表偏移量設置
?
設置編譯bin文件
?
?
7.3 Bootloader的程序設置
//設置寫入的地址,必須偶數,因為數據讀寫都是按照2個字節進行
#define FLASH_APP_ADDR 0x08005000 //應用程序存放到FLASH中的起始地址
int main()
{
printf("UART1 OK.....\n");
printf("進入IAP Bootloader程序!\n");
while(1)
{
key=KEY_Scanf();
if(key==1) //KEY1按下,寫入STM32 FLASH
{
printf("正在更新IAP程序...............\n");
iap_write_appbin(FLASH_APP_ADDR,(u8*)app_bin_data,sizeof(app_bin_data));//燒寫新的程序到內置FLASH
printf("程序更新成功....\n");
iap_load_app(FLASH_APP_ADDR);//執行FLASH APP代碼
}
}
}
/*
函數功能:跳轉到應用程序段
appxaddr:用戶代碼起始地址.
*/
typedef void (*iap_function)(void); //定義一個函數類型的參數.
void IAP_LoadApp(u32 app_addr)
{
//給函數指針賦值合法地址
jump2app=(iap_function)*(vu32*)(app_addr+4);//用戶代碼區第二個字為程序開始地址(復位地址)
__set_MSP(*(vu32*)app_addr); //設置主堆棧指針
jump2app(); //跳轉到APP.
}
?
?
-
微控制器
+關注
關注
48文章
7570瀏覽量
151628 -
STM32
+關注
關注
2270文章
10910瀏覽量
356590 -
ISP
+關注
關注
6文章
477瀏覽量
51882 -
IIC
+關注
關注
11文章
302瀏覽量
38368 -
IAP
+關注
關注
2文章
164瀏覽量
24317
發布評論請先 登錄
相關推薦
評論