日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

stm32 IAP 程序编写心得

發(fā)布時(shí)間:2023/12/16 编程问答 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 stm32 IAP 程序编写心得 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

stm32 IAP 程序編寫心得

    • 前言
    • IAP簡(jiǎn)介及原理
    • IAP設(shè)計(jì)過程
    • 完整的IAP功能
    • 功能的實(shí)現(xiàn)與遇到的問題
    • 附錄:程序源碼

前言

大家好這里是小白,因?yàn)閷?shí)在受不了一些網(wǎng)上代碼和某些開發(fā)板例程的荼毒,決定開個(gè)blog吐槽一下學(xué)習(xí)和工作中踩過的一些雷和遇到的一些坑。博主目前主要是做硬件和嵌入式設(shè)計(jì)的,主要使用的STM32F1XX和STM32F4XX并配合Inter的FPGA做一些程控和信號(hào)處理,具體行業(yè)就不多說了,有興趣可以點(diǎn)進(jìn)我的頭像看一哈,或者私信我交流一些設(shè)計(jì)心得。

IAP簡(jiǎn)介及原理

IAP是在線應(yīng)用編程(In Application Programming)的簡(jiǎn)稱,簡(jiǎn)單來說IAP的目的就是實(shí)現(xiàn)在線刷程序。在剛開始學(xué)習(xí)STM32的時(shí)候,我們只能通過J-LINK或者ST-LINK實(shí)現(xiàn)程序燒寫,隨著學(xué)習(xí)的深入,我們也可能會(huì)接觸到使用ST官方的BOOTLOADER GUI進(jìn)行程序下載,但是上述的這幾種方式在燒寫程序時(shí)要么需要使用特殊的下載工具,要么需要跳片改變STM32 boot0腳的狀態(tài)并使用專用的軟件,并不適合生產(chǎn)批量燒寫,因此就有了IAP這種下載方式。
IAP的原理在STM32資料手冊(cè)和很多博主都有介紹,有需要的朋友可以參考下面兩個(gè)資料:
STM32+IAP實(shí)現(xiàn)原理: https://blog.csdn.net/wzy15965343032/article/details/88545225.
正點(diǎn)原子STM32探索者開發(fā)板IAP: https://www.bilibili.com/video/BV1Rx411R75t?p=91.
理論上IAP可以實(shí)現(xiàn)包括USART,CAN,SPI,IIC等任意一種通訊方式實(shí)現(xiàn)在線編程。
廢話不多說直接說正題,由于工作需要,最近我也在編寫基于USART的IAP實(shí)現(xiàn)方案,參考了網(wǎng)上一些資料后總覺得不太理想,因此分享一下我在編寫IAP時(shí)遇到的一些問題以及解決方案。

IAP設(shè)計(jì)過程

目前我在網(wǎng)上找到兩個(gè)相關(guān)的IAP設(shè)計(jì)例程,都是基于ymodem協(xié)議的,一個(gè)就是正點(diǎn)原子的IAP串口實(shí)現(xiàn)方案,另一個(gè)不知道算不算官方的例程,看函數(shù)介紹寫的是MCD Application Team。
第一個(gè)例程我就不放了,大家可去正點(diǎn)原子官網(wǎng)下載。第二個(gè)例程采用的是超級(jí)終端控制的,但是需要上位機(jī)添加bin文件(這里我沒有找到相應(yīng)的上位機(jī),理論上可以使用老司機(jī)常用的那個(gè)SSCOM42,只不過字符輸入需要手動(dòng)點(diǎn)擊發(fā)送),我會(huì)在文章末尾放上下載鏈接,大家可根據(jù)需求自行下載。
IAP設(shè)計(jì)過程本質(zhì)上就是編寫兩段(或多段)代碼,一段是IAP代碼,另一段是實(shí)際的APP應(yīng)用程序代碼,編寫完成后將IAP燒寫入STM32FLASH的最前端,再將APP應(yīng)用程序編譯后生成bin文件。然后將IAP程序燒錄至FLASH中,隨后根據(jù)需要將要使用的應(yīng)用程序的bin文件通過USART(通訊)的方式發(fā)送給單片機(jī),并存入單片機(jī)的FLASH。注意這里的應(yīng)用程序存放要偏移一定的地址,偏移大小是設(shè)置的IAP程序的大小,否則會(huì)擦除掉IAP程序?qū)е绿D(zhuǎn)失敗或者IAP僅能使用一次。
比如我使用的是stm32f407單片機(jī),該單片機(jī)一共12個(gè)SECTOR共1MB的FLASH存儲(chǔ)空間,我一般是這樣分配的,SECTOR 0用于存放IAP代碼,SECTOR 1-SECTOR 4用于存放APP應(yīng)用程序代碼(如果不夠可以往后擴(kuò)展),SECTOR5-SECTOR 10用于存放需掉電保存的系統(tǒng)參數(shù),最后SECTOR11存放一些關(guān)鍵的系統(tǒng)標(biāo)志(這個(gè)后面會(huì)提到)。


APP應(yīng)用程序我這里使用原子的UCOSII的移植例程,通過點(diǎn)亮兩個(gè)LED燈檢查IAP程序是否燒錄進(jìn)去,APP應(yīng)用程序需要設(shè)置FLASH地址,并且需要設(shè)置APP應(yīng)用程序的中斷向量偏移。



最后再選擇MDK自帶的fromelf應(yīng)用程序(在KEIL的安裝路徑下面,點(diǎn)擊后面的小文件夾添加),生成bin文件就可以使用上位機(jī)更新程序了。

這里注意 --bin - o…是手動(dòng)添加的,OBJ后面的文件名是你的項(xiàng)目output文件名。如我的項(xiàng)目output名稱是UCOS-1,所以我這里應(yīng)該填:
D:\Keil5\ARM\ARMCC\bin\fromelf.exe --bin -o …\OBJ\UCOS-1.bin …\OBJ\UCOS-1.axf
以上工作完成就可以使用上位機(jī)更新APP應(yīng)用程序了,首先將IAP程序燒錄進(jìn)單片機(jī),打開串口助手,將你的電路板與電腦連接,選擇打開文件,將編譯好的bin文件添加,點(diǎn)擊發(fā)送文件,APP程序就寫入了相應(yīng)的FLASH中。

完整的IAP功能

以上就是IAP編程的完整過程,也是原子開發(fā)板例程中具有的功能,但是正當(dāng)我想測(cè)試的時(shí)候發(fā)現(xiàn)一個(gè)問題,原子的例程是通過按鍵實(shí)現(xiàn)跳轉(zhuǎn)的,而我設(shè)計(jì)的電路板,沒有按鍵!!
然后我又看了看原子例程代碼,想了想我的需求,發(fā)現(xiàn)這離我想要的IAP代碼功能還差很多,于是我決定掏出我的陳年老代碼重構(gòu)一下改成IAP代碼。
相比于原子的開發(fā)歷程,我需要我的IAP程序具有以下幾個(gè)功能:
1.在IAP程序中,通過特定串口指令進(jìn)入IAP模式,然后再添加和發(fā)送APP應(yīng)用程序的bin文件,防止發(fā)送的數(shù)據(jù),也被寫入FLASH。
2.可以實(shí)現(xiàn)APP應(yīng)用程序跳轉(zhuǎn)回IAP程序,實(shí)現(xiàn)程序的重新燒錄,這一點(diǎn)也是通過特定的串口指令來實(shí)現(xiàn)的。
3.上電判斷是否已有APP應(yīng)用程序,如果已有程序,自動(dòng)跳轉(zhuǎn)APP程序運(yùn)行。
4.由APP應(yīng)用程序跳轉(zhuǎn)回IAP后,IAP程序判斷本次運(yùn)行是重新上電還是由APP程序跳轉(zhuǎn)而來,如果是由APP跳轉(zhuǎn)而來,則用戶需要重新燒錄程序,先將已有的應(yīng)用程序自動(dòng)擦除,等待串口指令進(jìn)入IAP模式。
具備了以上功能,IAP程序才算完整。

功能的實(shí)現(xiàn)與遇到的問題

以上功能并不難實(shí)現(xiàn),有興趣的朋友可以試著自己做一下,我這里來簡(jiǎn)單說一下我的做法。
1.首先針對(duì)第一點(diǎn),設(shè)置一個(gè)串口指令標(biāo)志位,只有收到了特定的指令(此處我設(shè)置的是十六進(jìn)制A5 5A 31 42 0D),標(biāo)志位置位,才可以開始接收bin文件,否則無論收到什么都顯示指令錯(cuò)誤。

if(USART_RX_CNT) {if(RxDataCount == USART_RX_CNT) //串口沒有再收到新數(shù)據(jù){RxDataLength = USART_RX_CNT;if(RxCmdFlag == 0 && RxDataLength == 5) //接收IAP指令{if(USART_RX_BUF[0] == 0xA5 && USART_RX_BUF[1] == 0x5A && USART_RX_BUF[2] == 0x31 && USART_RX_BUF[3] == 0x42 && USART_RX_BUF[4] == 0x0D){RxCmdFlag = 1; //接收到更新APP代碼指令,標(biāo)志位置位RxDataLength = 0; //清空指令長(zhǎng)度,防止影響后面計(jì)算APP代碼大小printf("Ready to recieve app code,please add a binary file!\r\n"); //準(zhǔn)備好接收bin文件,等待用戶添加}else{CodeUpdateFlag = 0;AppCodeLength = 0;printf("Command Error!\r\n"); //未接收到IAP更新指令,其他任何串口發(fā)送數(shù)據(jù)都認(rèn)為指令錯(cuò)誤}}else if(RxCmdFlag == 1 && RxDataLength > 10)//接收IAP程序{CodeUpdateFlag = 1; //代碼更新標(biāo)志位置位,用于應(yīng)用程序代碼接收完成后寫FLASHRxCmdFlag = 0;AppCodeLength = USART_RX_CNT;printf("App code recieve complete!\r\n");printf("Code size:%dBytes\r\n",AppCodeLength);}else{RxDataLength = 0;printf("Not correct file or command!\r\n"); //如果代碼大小不足10Bytes,認(rèn)為沒有正確添加bin文件}RxDataCount = 0;USART_RX_CNT = 0;}else {RxDataCount = USART_RX_CNT;} }

2.這個(gè)部分需要重點(diǎn)注意一下,我在測(cè)試跳轉(zhuǎn)回IAP的時(shí)候,原本是在APP應(yīng)用程序其中一個(gè)LED_TASK中做了個(gè)計(jì)數(shù),計(jì)數(shù)10次之后自動(dòng)跳轉(zhuǎn)回IAP程序,到這里是沒有問題的,但當(dāng)我再次發(fā)送bin文件時(shí)就發(fā)生了問題。

void led0_task(void *pdata) {u8 count = 0;while(1){LED0=0;delay_ms(80);LED0=1;delay_ms(920);if(count > 10){count = 0;printf("跳轉(zhuǎn)回IAP程序!\r\n");delay_ms(10);iap_load_app(STM32_FLASH_BASE);}else{count ++;}} }

這里使用與IAP代碼里使用的iap_load_app()函數(shù)是可以跳轉(zhuǎn)回IAP代碼的,但是當(dāng)我再次發(fā)送應(yīng)用程序bin文件時(shí),并沒有顯示跳轉(zhuǎn)APP程序,于是我掛了仿真器對(duì)IAP代碼進(jìn)行仿真,發(fā)現(xiàn)在我再次發(fā)送bin文件時(shí),系統(tǒng)發(fā)生了錯(cuò)誤進(jìn)入了死循環(huán)。

void HardFault_Handler(void) {/* Go to infinite loop when Hard Fault exception occurs */while (1){} }

這個(gè)問題我試了好久,也沒有發(fā)現(xiàn)明顯的邏輯錯(cuò)誤,就是找不到原因,于是我去網(wǎng)上查IAP的資料,發(fā)現(xiàn)大多數(shù)都是IAP功能的介紹,沒有什么具體示例。經(jīng)過長(zhǎng)時(shí)間查找,我終于看到了一篇文章,原來是我在跳轉(zhuǎn)回IAP程序之后沒有復(fù)位RCC時(shí)鐘和NVIC中斷,導(dǎo)致時(shí)鐘或中斷發(fā)生錯(cuò)亂。所以在跳轉(zhuǎn)回IAP程序后,配置時(shí)鐘之前要添加RCC_DeInit()函數(shù),在配置中斷前要添加NVIC_DeInit (),先將時(shí)鐘和中斷關(guān)閉之后再重新配置,當(dāng)然這里也可以使用stm32庫(kù)函數(shù)里面的系統(tǒng)復(fù)位函數(shù)來讓程序重新從頭開始運(yùn)行,我這里使用的就是這種方法(別問,問就是偷懶)。

if(Jump_IAP_FLAG){Jump_IAP_FLAG = 0;delay_ms(10);if(((*(vu32*)(STM32_FLASH_BASE + 4)) & 0xFF000000) == 0x08000000) //判斷代碼合法性{STMFLASH_Write(ADDR_CodeWriteFlag,IAPFlagBuf,2); //寫IAP跳轉(zhuǎn)標(biāo)志位delay_ms(100);printf("Start Running IAP code!\r\n");NVIC_SystemReset(); //注意此處跳轉(zhuǎn)不是使用iap_load_app,而是將系統(tǒng)復(fù)位}else {printf("IAP code is illegal!\r\n"); }}

3.解決了上述兩個(gè)問題,第三點(diǎn)和第四點(diǎn)就好實(shí)現(xiàn)了,正如前文所說,我在SECTOR11中的連續(xù)兩個(gè)地址存放了兩個(gè)標(biāo)志量。

#define ADDR_CodeWriteFlag ((u32)0x080E1000) //IAP程序更新標(biāo)志位存放地址,如果是0x55,說明已經(jīng)有應(yīng)用程序 #define ADDR_JumpToIAPFlag ((u32)0x080E1001) //IAP程序跳轉(zhuǎn)標(biāo)志位,如果是0x55,說明是從APP跳轉(zhuǎn)回IAP程序

在將bin文件寫入FLASH之后跳轉(zhuǎn)APP應(yīng)用程序之前,連續(xù)從ADDR_CodeWriteFlag地址連續(xù)寫入兩個(gè)標(biāo)志量0x55和0x00,跳轉(zhuǎn)之后如果系統(tǒng)復(fù)位或者重新上電,在運(yùn)行IAP程序時(shí)會(huì)首先讀取ADDR_CodeWriteFlag地址是否等于0x55,如果等于則直接跳轉(zhuǎn)APP程序運(yùn)行,如果不相等,等待用戶燒錄程序再繼續(xù)運(yùn)行。

JumpToAPPFlag = FLASH_ReadWord(ADDR_CodeWriteFlag); //讀取APP寫入標(biāo)志位,判斷是否已有程序 if(JumpToAPPFlag != 0x55) //判斷是否已有APP程序,如果已有,跳轉(zhuǎn)至APP程序運(yùn)行 {} else {printf("There is an app code!\r\n");printf("Start running app code!\r\n");delay_ms(10);IAP_Load_AppCode(FLASH_APPCODE_ADDR); //執(zhí)行FLASH APP代碼 }

同樣由APP跳轉(zhuǎn)回IAP之前,我會(huì)重新在上述地址連續(xù)寫入0x00和0x55,這樣跳轉(zhuǎn)回IAP之后既不會(huì)再跳轉(zhuǎn)回APP,也可以判定ADDR_JumpToIAPFlag位的值,如果該位等于0x55,說明是從APP程序跳轉(zhuǎn)回來的,因此會(huì)先擦除存放APP代碼的扇區(qū),這里我是用的是SECTOR1-SECTOR4。

if(JumpToIAPFlag == 0x55) //判斷是否從APP程序跳轉(zhuǎn)回來,如果是擦除已有APP程序 {STMFLASH_Erase(); JumpToIAPFlag = 0x00; //清跳轉(zhuǎn)標(biāo)志位,防止不停擦除FLASH }

以上就是我IAP編寫的歷程和一些心得,希望對(duì)大家有幫助吧,下面放上源碼,有需要的朋友自取,STM32F1系的單片機(jī)也可以使用,但是要修改一下各系統(tǒng)函數(shù)的初始化配置(F1和F4在配置上還是有少許區(qū)別的,這個(gè)有需求的話我也可以給大家開個(gè)貼交流一下)。

附錄:程序源碼

CSDN: https://download.csdn.net/download/suzuzhizhen/15611524?spm=1001.2014.3001.5503.
GitHub:https://github.com/baiyiqing-LC/stm32f4xx_usart_IAP

總結(jié)

以上是生活随笔為你收集整理的stm32 IAP 程序编写心得的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。