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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

STM32的IAP在线升级

發(fā)布時間:2023/12/16 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 STM32的IAP在线升级 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

IAP簡介(簡介部分copy自正點(diǎn)原子)

IAP(In Application Programming)即在應(yīng)用編程,IAP 是用戶自己的程序在運(yùn)行過程中對User Flash 的部分區(qū)域進(jìn)行燒寫,目的是為了在產(chǎn)品發(fā)布后可以方便地通過預(yù)留的通信口對產(chǎn)品中的固件程序進(jìn)行更新升級。 通常實(shí)現(xiàn) IAP 功能時,即用戶程序運(yùn)行中作自身的更新操作,需要在設(shè)計固件程序時編寫兩個項目代碼,第一個項目程序不執(zhí)行正常的功能操作,而只是通過某種通信方式(如 USB、USART)接收程序或數(shù)據(jù),執(zhí)行對第二部分代碼的更新;第二個項目代碼才是真正的功能代碼。這兩部分項目代碼都同時燒錄在 User Flash 中,當(dāng)芯片上電后,首先是第一個項目代碼開始運(yùn)行,它作如下操作:
1)檢查是否需要對第二部分代碼進(jìn)行更新
2)如果不需要更新則轉(zhuǎn)到 4)
3)執(zhí)行更新操作
4)跳轉(zhuǎn)到第二部分代碼執(zhí)行
第一部分代碼必須通過其它手段,如 JTAG 或 ISP 燒入;第二部分代碼可以使用第一部分代碼 IAP 功能燒入,也可以和第一部分代碼一起燒入,以后需要程序更新時再通過第一部分 IAP代碼更新。
我們將第一個項目代碼稱之為 Bootloader 程序,第二個項目代碼稱之為 APP 程序,他們存放在 STM32L433FLASH 的不同地址范圍,一般從最低地址區(qū)開始存放 Bootloader,緊跟其后的就是 APP 程序(注意,如果 FLASH 容量足夠,是可以設(shè)計很多 APP 程序的,本章我們只討論一個 APP 程序的情況)。這樣我們就是要實(shí)現(xiàn) 2 個程序:Bootloader 和 APP。
STM32L433 的 APP 程序不僅可以放到 FLASH 里面運(yùn)行,也可以放到 SRAM 里面運(yùn)行,

程序思路介紹


當(dāng)產(chǎn)品定型投產(chǎn)后每一次燒錄程序都必須去修改BOOT硬件引腳的電平很不方便所以我們需要自己編寫一套BootLoader程序,也就是在單片機(jī)FLASH中編寫兩套程序,利用第一套程序通過串口接收的方式將第二套程序?qū)懺谥付ǖ刂返膬?nèi)部FLASH上,然后強(qiáng)制跳轉(zhuǎn)單片機(jī)的PC指針實(shí)現(xiàn)運(yùn)行第二套程序的方法,從理論上來說,單片機(jī)可以保存無數(shù)套程序并通過強(qiáng)制跳轉(zhuǎn)指針去運(yùn)行,當(dāng)然前提是你的FLASH足夠大。
我們稱第一套BootLoader程序?yàn)橐龑?dǎo)程序,第二套真正要運(yùn)行的為APP程序,引導(dǎo)程序只是在單片機(jī)上電時執(zhí)行一次,真正工作的是APP程序,下面我們來細(xì)說具體原理。

STM32L433內(nèi)部FLASH


我們從最底層的開始看,因?yàn)閱纹瑱C(jī)內(nèi)部的FLASH被分配的地址為0x8000000到0x8040000,結(jié)束地址減去起始地址是40000,對應(yīng)的就是262444字節(jié),除以1024剛好是256K字節(jié),這也對應(yīng)著STM32L433的flash為256K。
所以,我們引導(dǎo)程序分配56K字節(jié),APP程序分配200字節(jié)。


單片機(jī)上電之后從0x8000000這個地址開始運(yùn)行程序,STM32L4 的內(nèi)部閃存(FLASH)地址起始于 0x08000000,一般情況下,程序文件就從此地址開始寫入。此外 STM32L4 是基于 Cortex-M4 內(nèi)核的微控制器,其內(nèi)部通過一張“中斷向量表”來響應(yīng)中斷,程序啟動后,將首先從“中斷向量表”取出復(fù)位中斷向量執(zhí)行復(fù)位中斷程序完成啟動,而這張“中斷向量表”的起始地址是 0x08000004,當(dāng)中斷來臨,STM32L4 的內(nèi)部硬件機(jī)制亦會自動將 PC 指針定位到“中斷向量表”處,并根據(jù)中斷源取出對應(yīng)的中斷向量執(zhí)行中斷服務(wù)程序。在圖 中,STM32L4 在復(fù)位后,先從 0X08000004 地址取出復(fù)位中斷向量的地址,并跳轉(zhuǎn)到復(fù)位中斷服務(wù)程序,如圖標(biāo)號①所示;在復(fù)位中斷服務(wù)程序執(zhí)行完之后,會跳轉(zhuǎn)到我們的 main 函數(shù),如圖標(biāo)號②所示;而我們的 main 函數(shù)一般都是一個死循環(huán),在 main 函數(shù)執(zhí)行過程中,如果收到中斷請求(發(fā)生重中斷),此時 STM32L4 強(qiáng)制將 PC 指針指回中斷向量表處,如圖標(biāo)號③所示;然后,根據(jù)中斷源進(jìn)入相應(yīng)的中斷服務(wù)程序,如圖標(biāo)號④所示;在執(zhí)行完中斷服務(wù)程序以后,程序再次返回 main 函數(shù)執(zhí)行,如圖標(biāo)號⑤所示。
如果假如了IAP程序,那么流程就會變成:

因?yàn)槊看斡兄袛喟l(fā)生的時候都會跳轉(zhuǎn)到中斷向量表中,中斷向量表的起始地址為0x8000004,這個是程序強(qiáng)制執(zhí)行的,所以我們在APP程序的第一句,要對中斷向量表的地址進(jìn)行移位。
SCB->VTOR = FLASH_BASE | 0x10000;
在APP程序的第一句加上或操作即對APP程序的中斷向量表進(jìn)行的移位,當(dāng)中斷發(fā)生時,程序會自動跳轉(zhuǎn)至移位后的中斷向量表保證程序不會跑飛。
在引導(dǎo)程序中,接收的程序也是直接被存在單片機(jī)的SRAM中,STM32L433的SRAM有64K,所以限制了接收的最大字節(jié)數(shù),所以可以采用邊接收邊寫FLASH的辦法,最好也要對接收到的數(shù)據(jù)進(jìn)行校驗(yàn)。

程序部分

主函數(shù)

在主函數(shù)中我利用了AT指令的方法去進(jìn)入BootLoader引導(dǎo)程序,程序上電后5秒沒有收到固定的AT指令就會強(qiáng)制跳轉(zhuǎn)指針到APP應(yīng)用程序,如果收到了AT+BOOTLOADER指令程序即關(guān)閉定時器并準(zhǔn)備接收APP程序,校驗(yàn)辦法是我會在發(fā)程序之前先發(fā)送APP程序的字節(jié)數(shù),當(dāng)接收到相同字節(jié)的程序即認(rèn)為接收成功,當(dāng)然這種辦法很蠢,最好的是CRC去校驗(yàn)。

extern uint8_t u8RxBuf[]; int main(void) {/*上一次串口接收數(shù)值*/uint32_t u32oldcount = 0;/*接收到的app代碼長度*/uint32_t applenth = 0; /*上傳代碼長度*/ int32_t u32ProgramSize = 0; gtSysFlag.u8GetDataFlag = false;gtSysFlag.u8GetProgramFlag = false;gtSysFlag.u8JumpAppFlag = false;gtSysFlag.u8ProgramOkFlag = false;HAL_Init();SystemClock_Config(); //初始化系統(tǒng)時鐘為80Mdelay_init(80); //初始化延時函數(shù) 80M系統(tǒng)時鐘uart_init(115200); //初始化串口,波特率為115200TIM2_Init(5000 - 1, 8000 - 1);while(1){ /*僅支持大寫輸入*/if(gtSysFlag.u8GetDataFlag == true){if((u8RxBuf[0] == 'A') && (u8RxBuf[1] == 'T') && (u8RxBuf[2] == '+')){if((u8RxBuf[3] == 'B') && (u8RxBuf[4] == 'O') && (u8RxBuf[5] == 'O') && (u8RxBuf[6] == 'T')){/*關(guān)閉超時定時器*/__HAL_RCC_TIM2_CLK_DISABLE(); gtSysFlag.u8JumpAppFlag = false; printf("已進(jìn)入引導(dǎo)程序\r\n"); }else if((u8RxBuf[3] == 'B') && (u8RxBuf[4] == 'Y') && (u8RxBuf[5] == 'T') && (u8RxBuf[6] == 'E')){u32ProgramSize = ((u8RxBuf[7] - '0')*10000) + ((u8RxBuf[8] - '0')*1000) + ((u8RxBuf[9] - '0')*100) + ((u8RxBuf[10] - '0')*10) + ((u8RxBuf[11] - '0'));printf("代碼字節(jié)數(shù)為:%d\r\n",u32ProgramSize);} else if((u8RxBuf[3] == 'P') && (u8RxBuf[4] == 'R') && (u8RxBuf[5] == 'O') && (u8RxBuf[6] == 'G')){if((u8RxBuf[7] == 'R') && (u8RxBuf[8] == 'A') && (u8RxBuf[9] == 'M')){if(u32ProgramSize != 0){/*開始接收APP程序*/ gtSysFlag.u8GetProgramFlag = true; printf("準(zhǔn)備接收APP程序\r\n"); } else{printf("請輸入要發(fā)送的字節(jié)數(shù)\r\n");} }} else if((u8RxBuf[3] == 'W') && (u8RxBuf[4] == 'R') && (u8RxBuf[5] == 'I') && (u8RxBuf[6] == 'T')){if((u8RxBuf[7] == 'E') && (u8RxBuf[8] == 'F') && (u8RxBuf[9] == 'L')){if(applenth != 0){if(gtSysFlag.u8ProgramOkFlag == true){/*更新Flash的程序*/iap_write_appbin(FLASH_APP1_ADDR, USART_RX_BUF, applenth); //更新FLASH代碼printf("固件更新完成!\r\n"); } else {printf("請上傳代碼后進(jìn)行固件更新!\r\n");} gtSysFlag.u8ProgramOkFlag = false; }else{printf("沒有可以更新的固件!\r\n");}}} else if((u8RxBuf[3] == 'R') && (u8RxBuf[4] == 'U') && (u8RxBuf[5] == 'N')){/*開始執(zhí)行flash程序*/gtSysFlag.u8JumpAppFlag = true; } } gtSysFlag.u8GetDataFlag = false;} /*程序接收部分*/if(gtSysFlag.u8GetProgramFlag == true){ if(USART_RX_CNT != 0){if(u32oldcount == USART_RX_CNT) //新周期內(nèi),沒有收到任何數(shù)據(jù),認(rèn)為本次數(shù)據(jù)接收完成.{applenth = USART_RX_CNT;u32oldcount = 0;USART_RX_CNT = 0;printf("用戶程序接收完成!\r\n");printf("上傳代碼字節(jié)數(shù)為:%dBytes\r\n",u32ProgramSize);printf("實(shí)際代碼長度:%dBytes\r\n", applenth);if(applenth == u32ProgramSize){printf("用戶程序校驗(yàn)正確!\r\n");gtSysFlag.u8ProgramOkFlag = true;}else{printf("用戶程序校驗(yàn)錯誤,請重新上傳!\r\n");}gtSysFlag.u8GetProgramFlag = false; }else {/*將最新計數(shù)值賦值給上一次變量*/u32oldcount = USART_RX_CNT;}}delay_ms(50);}/*跳轉(zhuǎn)至APP應(yīng)用程序*/ if(gtSysFlag.u8JumpAppFlag == true){/*判斷APP程序起始地址上是否有正確數(shù)據(jù)*/if(((*(vu32*)(FLASH_APP1_ADDR + 4)) & 0xFF000000) == 0x08000000){printf("開始執(zhí)行APP程序");__HAL_RCC_TIM7_CLK_DISABLE();gtSysFlag.u8GetProgramFlag = false;iap_load_app(FLASH_APP1_ADDR);//執(zhí)行FLASH APP代碼} else{printf("FLASH無應(yīng)用程序!");gtSysFlag.u8JumpAppFlag = false;} } } }

串口接收程序

#include "usart.h" #include "delay.h" #include "sys.h" #include "timer.h" tSysFlag gtSysFlag; extern TIM_HandleTypeDef TIM7_Handler; //定時器句柄 #if 1 #pragma import(__use_no_semihosting) //標(biāo)準(zhǔn)庫需要的支持函數(shù) struct __FILE {int handle; };FILE __stdout; /*** @brief 定義_sys_exit()以避免使用半主機(jī)模式** @param void** @return void*/ void _sys_exit(int x) {x = x; } /*** @brief 重定義fputc函數(shù)** @param ch 輸出字符量* @param f 文件指針** @return void*/ int fputc(int ch, FILE *f) {while((USART1->ISR & 0X40) == 0); //循環(huán)發(fā)送,直到發(fā)送完畢USART1->TDR = (u8) ch;return ch; } #endif#if EN_USART1_RX //如果使能了接收 //串口1中斷服務(wù)程序 //注意,讀取USARTx->SR能避免莫名其妙的錯誤 u8 USART_RX_BUF[USART_REC_LEN] __attribute__ ((at(0X20001000)));//接收緩沖,最大USART_REC_LEN個字節(jié),起始地址為0X20001000. uint8_t u8RxBuf[20]; //接收狀態(tài) //bit15, 接收完成標(biāo)志 //bit14, 接收到0x0d //bit13~0, 接收到的有效字節(jié)數(shù)目 u16 USART_RX_STA = 0; //接收狀態(tài)標(biāo)記 u32 USART_RX_CNT=0; //接收的字節(jié)數(shù) uint16_t u16LoopData = 0;UART_HandleTypeDef UART1_Handler; //UART句柄void uart_init(u32 bound) {//UART 初始化設(shè)置UART1_Handler.Instance = USART1; //USART1UART1_Handler.Init.BaudRate = bound; //波特率UART1_Handler.Init.WordLength = UART_WORDLENGTH_8B; //字長為8位數(shù)據(jù)格式UART1_Handler.Init.StopBits = UART_STOPBITS_1; //一個停止位UART1_Handler.Init.Parity = UART_PARITY_NONE; //無奇偶校驗(yàn)位UART1_Handler.Init.HwFlowCtl = UART_HWCONTROL_NONE; //無硬件流控UART1_Handler.Init.Mode = UART_MODE_TX_RX; //收發(fā)模式HAL_UART_Init(&UART1_Handler); //HAL_UART_Init()會使能UART1__HAL_UART_ENABLE_IT(&UART1_Handler, UART_IT_RXNE); //開啟接收中斷HAL_NVIC_EnableIRQ(USART1_IRQn); //使能USART1中斷通道HAL_NVIC_SetPriority(USART1_IRQn, 3, 3); //搶占優(yōu)先級3,子優(yōu)先級3 }/*** @brief HAL庫串口底層初始化,時鐘使能,引腳配置,中斷配置** @param huart 串口句柄** @return void*/ void HAL_UART_MspInit(UART_HandleTypeDef *huart) {__HAL_RCC_USART1_CLK_ENABLE();__HAL_RCC_GPIOB_CLK_ENABLE();/**USART1 GPIO ConfigurationPB6 ------> USART1_TXPB7 ------> USART1_RX*/GPIO_InitTypeDef GPIO_InitStruct = {0};RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF7_USART1;HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); }/*** @brief 串口1中斷服務(wù)程序** @remark 下面代碼我們直接把中斷控制邏輯寫在中斷服務(wù)函數(shù)內(nèi)部* 說明:采用HAL庫處理邏輯,效率不高。** @param void** @return void*/ void USART1_IRQHandler(void) { uint8_t Res;static uint8_t u8Res = 0;if(gtSysFlag.u8GetProgramFlag == true){if((__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_RXNE)!=RESET)) {HAL_UART_Receive(&UART1_Handler,&Res,1,1000); if(USART_RX_CNT<USART_REC_LEN){ USART_RX_BUF[USART_RX_CNT]=Res;USART_RX_CNT++; } }} else if(gtSysFlag.u8GetProgramFlag == false){if((__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_RXNE)!=RESET)) {HAL_UART_Receive(&UART1_Handler,&Res,1,1000); u8RxBuf[u16LoopData]=Res; u16LoopData++;if(u16LoopData >= 20){u16LoopData = 0;}if(Res == 0x0D){u8Res = Res;} if((Res == 0x0A) && (u8Res == 0x0D)){u8Res = 0;u16LoopData = 0;gtSysFlag.u8GetDataFlag = true;} } } HAL_UART_IRQHandler(&UART1_Handler);__HAL_UART_ENABLE_IT(&UART1_Handler, UART_IT_RXNE); }#endif

總結(jié)

以上是生活随笔為你收集整理的STM32的IAP在线升级的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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