公司產品要求,需要做一個能遠程升級程序的功能,找了很多例程,大多都是需要按鍵來完成操作的,而我需要的是通過串口發(fā)送指令來完成,于是東拼西湊最后還是用了四天的時間勉強做出來
整個功能需要的程序是兩個部分。一個是IAP程序,一個是APP程序。對于IAP程序和APP原理方面的內容就不再過多贅述。直接從操作開始吧。
IAP程序
寫IAP程序之前首先得配置程序的起始地址和大小。這里根據(jù)個人情況而定,我這里單片機flash大小是512k,所以IAP程序選擇分配的大小是64k。
點擊魔術棒,然后在選擇target,設置起始地址(start)和大小(size),IAP程序起始地址都是從0x8000000開始的,大小就是0x10000也就是64k啦。
在這里我參考了原子哥的源碼和一位大神的分享,原子的資料一搜一大堆,這里就僅貼出大佬的鏈接
stm32 IAP 程序編寫心得
有了前車之鑒,做起來也稍顯得心應手,這里的flash操作的函數(shù)和IAP功能函數(shù)都是拿來主義了,原子IAP例程里拿來就可以用。主要工作還是針對main函數(shù),這里直接貼出源碼:
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "stmflash.h"
#include "iap.h"
#define ADDR_CodeWriteFlag 0X08070000 //設置跳轉標志位保存地址
#define ADDR_JumpToIAPFlag 0X08070001
int main(void)
{
u16 IAPFlagBuf[2];
u16 RxDataCount=0; //串口接收到的數(shù)據(jù)計數(shù)
u16 RxDataLength=0; //串口接收到的數(shù)據(jù)長度
u16 AppCodeLength = 0; //接收到的app代碼長度
u8 RxCmdFlag = 0;
u8 AppRunFlag = 0; //應用程序運行標志
u16 JumpToAPPFlag; //跳轉至APP程序標志位
u16 JumpToIAPFlag; //跳轉回IAP標志位
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //設置NVIC中斷分組2:2位搶占優(yōu)先級,2位響應優(yōu)先級
JumpToAPPFlag = STMFLASH_ReadHalfWord(ADDR_CodeWriteFlag); //讀取APP寫入標志位,判斷是否已有程序
JumpToIAPFlag = STMFLASH_ReadHalfWord(ADDR_JumpToIAPFlag);
uart_init(9600); //串口初始化為9600
delay_init(); //延時初始化
IAPFlagBuf[0] = 0x11;
IAPFlagBuf[1] = 0x00;
printf("提示:輸入send發(fā)送bin文件!\r\n");
while(1)
{
if(JumpToAPPFlag != 0x11) //判斷是否已有APP程序,如果已有,跳轉至APP程序運行
{
if(JumpToIAPFlag == 0x11) //判斷是否從APP程序跳轉回來,如果是擦除已有APP程序
{
JumpToIAPFlag = 0x00;
}
if(USART_RX_CNT) //如果有數(shù)據(jù)進來
{
if(RxDataCount == USART_RX_CNT) //串口沒有再收到新數(shù)據(jù)
{
RxDataLength = USART_RX_CNT;
if(RxCmdFlag == 0 && RxDataLength == 4) //接收到IAP指令
{
if(USART_RX_BUF[0] == 's' && USART_RX_BUF[1] == 'e' && USART_RX_BUF[2] == 'n' && USART_RX_BUF[3] == 'd')//判斷是否為IAP指令
{
RxCmdFlag = 1; //接收到更新APP代碼指令,標志位置1
RxDataLength = 0; //清空指令長度,防止影響后面計算APP代碼大小
printf("準備接收app程序,請?zhí)砑觔in文件!\r\n"); //準備好接收bin文件,等待用戶添加
}
else
{
CodeUpdateFlag = 0;
AppCodeLength = 0;
printf("指令錯誤!\r\n"); //未接收到IAP更新指令,其他任何串口發(fā)送數(shù)據(jù)都認為指令錯誤
}
}
else if(RxCmdFlag == 1 && RxDataLength > 10)//接收APP程序
{
CodeUpdateFlag = 1; //代碼更新標志位置位,用于應用程序代碼接收完成后寫FLASH
RxCmdFlag = 0;
AppCodeLength = USART_RX_CNT;
printf("APP程序接收完成!\r\n");
printf("程序大小:%dBytes\r\n",AppCodeLength);
}
else
{
RxDataLength = 0;
printf("文件或指令錯誤!\r\n"); //如果代碼大小不足10Bytes,認為沒有正確添加bin文件
}
RxDataCount = 0;
USART_RX_CNT = 0;
}
else
{
RxDataCount = USART_RX_CNT;
}
}
delay_ms(10); //給以串口中斷的時間,判斷是否接收完成
if(CodeUpdateFlag) //代碼更新標志位置位
{
CodeUpdateFlag = 0;
if(AppCodeLength)
{
printf("程序更新中...\r\n");
if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x08000000) //判斷代碼合法性
{
printf("正在下載程序!\r\n");
iap_write_appbin(FLASH_APP1_ADDR,USART_RX_BUF,AppCodeLength); //新代碼寫入FLASH
AppRunFlag = 1;
}
else
{
printf("程序更新失敗,請檢查bin文件是否正確!\r\n");
printf("跳轉到原有應用程序!\r\n");
iap_load_app(FLASH_APP1_ADDR); //執(zhí)行FLASH APP代碼
}
}
else
{
printf("沒有程序可以更新!\r\n");
}
}
if(AppRunFlag) //App運行標志置位
{
printf("開始運行程序!\r\n");
delay_ms(10);
if(((*(vu32*)(FLASH_APP1_ADDR + 4)) & 0xFF000000) == 0x08000000) //判斷代碼合法性
{
AppRunFlag = 0;
STMFLASH_Write(ADDR_CodeWriteFlag,IAPFlagBuf,2); //寫入APP代碼標志
//RCC_DeInit(); //關閉外設
//__disable_irq();
iap_load_app(FLASH_APP1_ADDR); //執(zhí)行FLASH APP代碼
}
else
{
printf("應用程序錯誤!\r\n");
}
}
}
else
{
printf("已有一個應用程序!\r\n");
printf("開始運行程序!\r\n");
delay_ms(10);
iap_load_app(FLASH_APP1_ADDR); //執(zhí)行FLASH APP代碼
}
}
}
-
main函數(shù)中,選擇一塊不使用的flash區(qū)域來保存跳轉標志位。
我選擇的是0x8070000開始的區(qū)域,整個flash大小是512k也就是0x8080000,所以最后我又留了64k的大小來存放,所以留給APP程序的就是0x08010000到0x08070000的地址,也是就384k大小空間,記住這個0x08010000到0x08070000,后面APP程序要考。 -
每次進入main函數(shù)都要先讀這個地址的值,若等于0x11則直接跳轉到APP,因為在跳轉APP之前是要對這個地址進行寫0x11的,所以跳轉到APP后這個地址是0x11,開機啟動會自動進入APP。
到此似乎思路就清晰了,原子通過按鍵來操作接收,下載。我這里通過指令來接收下載,其他的判斷棧頂?shù)刂返暮戏ㄐ?,看兩遍代碼也能明白了。
APP程序
接下來就是APP程序的操作部分了。
要從IAP跳轉到APP,APP的程序也需要修改。首先就是程序的起始地址和大小。
前面的IAP從0x8000000到0x8010000,所以我們的APP程序只能從0x8010000開始,而0x8070000到0x8080000又要保存跳轉標志位,那么APP的地址就只能是0x8010000到0x8070000了,既然這樣,那就拉滿吧,所以大小設置我選擇0x60000。
然后就是輸出bin文件了,要用串口傳輸不能用hex文件,所以在USER界面
選擇如圖所示操作,方框里填的是E:\keil5\ARM\ARMCC\bin\fromelf.exe --bin -o …\output\Project.bin …\output\Project.axf 注意有空格,當然這個也得根據(jù)自己的文件位置更改。
下一步就是更改偏移地址了,這里只需要在主函數(shù)里加
SCB->VTOR=FLASH_BASE|0x10000; 即可,我這里偏移0x10000
到此就能編譯生成bin文件,通過IAP程序,使用串口來完成升級了。
但是后續(xù)要升級怎么辦,我們還得從APP跳轉到IAP來。所以在APP程序中還需要接收指令來跳轉到IAP去。
這里Receive_Data_Point3是接收到的字節(jié)長度,我設置的跳轉指令是APPTOIAP!共9位。
然后就是跳轉前還需將存放標志位得地址寫入0x00,前面IAP中會對該地址得值進行判斷是否是0x11,是的話就又跳回來了。然后有大佬踩坑后,得知跳轉到IAP直接用NVIC_SystemReset(); 即可。
到此就基本完成了。
第一次寫博客,也是為了記錄和學習。語言不通順還請多諒解,如有錯誤的地方也請多加指正。同時有疑問的同學也可以評論留言,歡迎討論交流。文章來源:http://www.zghlxwxcb.cn/news/detail-686537.html
可能出現(xiàn)的問題解決方法:
在后續(xù)的測試中發(fā)現(xiàn),串口接收bin文件時還沒接收完就進入到了寫入flash的動作,導致有很大的概率程序升級失敗。分析了半天原因可能是,接收緩存在10ms內沒收到數(shù)據(jù)就默認接收完畢進入寫入跳轉了,我使用的9600波特率,在將下圖中的延時增加到100ms后,沒有再出現(xiàn)問題了。文章來源地址http://www.zghlxwxcb.cn/news/detail-686537.html
到了這里,關于STM32F1 IAP在線升級功能實現(xiàn)(使用串口)及心得的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!