STM32固件升級設計——串口IAP升級(基于YMODEM協議)

目錄

一、功能描述?

1、BootLoader部分:

2、APP部分:

二、BootLoader程序制作

1、分區定義

2、 主函數

3、YMODEM協議的實現

4、程序跳轉

三、APP程序制作

四、工程配置(默認KEIL5)

五、運行測試?

結束語


概述

????????IAP(In Application Programming)即在應用中編程,允許在應用程序運行時更新或切換固件。STM32通過修改MSP(主堆棧指針)和PC(程序計數器)實現從不同地址啟動,包括Flash或RAM地址。默認情況下,嵌入式程序以連續二進制形式燒錄到STM32的可尋址Flash區域。若Flash容量足夠存儲多個完整程序,每個程序獨立且完整,上電后可通過修改MSP值選擇不同程序入口,從而實現多固件切換或升級。

?????????BootLoader(引導加載程序)是嵌入式系統或計算機啟動時運行的一段小型程序,負責初始化硬件、加載操作系統內核并將其控制權移交。它是系統從關機狀態到操作系統完全運行之間的橋梁。

????????所以,固件升級的基本思路是將stm32的flash劃分為若干個區域,其中包括BootLoader區域和APP區等,將各自的程序寫到對應的flash區域里,本文將采用YMODEM協議來接收固件,只重實踐不講原理。

一、功能描述?

????????使用STM32的串口總線和Ymodem協議實現串口IAP升級程序。將FLASH分為3個部分。
分區介紹:
????????本文使用stm32f103vet6,flash是512k,sector是1k,BootLoader整個代碼編譯下來有11K左右,所以使用0x08000000~0x00002FFF,SETTING主要存放升級標志位,使用0x08003000~0x00003FFF,剩下的FLASH將分為兩個部分都用來存放代碼,APP使用0x08004000~0x0807C000。(我這里已經極致壓縮FLASH了,如果大家要移植的話,肯定要修改各個區域大小的)

區域起始地址區域大小功能
BOOT0x080000000x00003000(12k)存放BootLoader程序
SETTING0x080030000x00001000(4k)存放升級標志位/其它掉電不丟失標志位
APP0x080040000x0007C000(496k)存放產品主程序

tips:?選擇以上分區方式的好處是在上電瞬間可以觸發升級,在進入主程序運行期間也可以觸發升級,這樣就算升級了錯誤的程序,只要復位了再次進行升級就行,就不會導致變磚了。如果選擇在主程序執行接收升級文件步驟,那如果程序有誤,就很大概率會變磚,當然也有辦法,那就是加校驗或者將bin文件加密,這部分就不做了。

1、BootLoader部分:
  • 運行程序時首先從SETTING區域讀取升級標志位,如果需要升級就一直用串口發送字符C(Ymodem協議的一部分),否則就直接跳轉到APP。
  • 上電長按KEY1并復位,直接設置升級標志位進入升級,一直發送字符C,用xshell來發送升級bin文件。先將APP區域的程序全部擦除掉,然后xshell會按照Ymodem協議發送128或1024字節給MCU,MCU分別寫入到APP區域內后復位即可實現升級程序,具體請查看本文源碼。
2、APP部分:

????????該部分需要在程序起始設置中斷向量跳轉指針,為了符合需求我用串口接收到‘1’就會設置程序更新標志位,復位后進入BootLoader升級。(大家也可以設置一個串口命令或者其它觸發方式,只要能寫更新標志位和復位就行)

二、BootLoader程序制作

????????主要包含了YMODEM協議的驅動代碼。

1、分區定義
#define FLASH_SECTOR_SIZE       1024   //MCU sector size
#define FLASH_SECTOR_NUM        512    // 512k
#define FLASH_START_ADDR        ((uint32_t)0x08000000)
#define FLASH_END_ADDR          ((uint32_t)(0x08000000 + FLASH_SECTOR_NUM * FLASH_SECTOR_SIZE))#define BOOT_SECTOR_ADDR            0x08000000      // BOOT sector start address
#define BOOT_SECTOR_SIZE            0x3000
#define SETTING_SECTOR_ADDR         0x08003000      // APP設置的boot升級標志位
#define SETTING_SECTOR_SIZE         0x1000
#define APP_SECTOR_ADDR             0x08004000      // APP sector start address 
#define APP_SECTOR_SIZE             0x7C000         // APP sector size 
#define APP_ERASE_SECTORS        (APP_SECTOR_SIZE / FLASH_SECTOR_SIZE)   //507904/1024=496#define SETTING_BOOT_STATE      0x08003000
#define START_PROGRAM_STATE     2
#define UPDATE_PROGRAM_STATE    3
#define UPDATE_SUCCESS_STATE    4typedef enum {NONE = 0,BUSY,START_PROGRAM,    //進入APP主程序或者有更新就執行更新UPDATE_PROGRAM,   //進入更新UPDATE_SUCCESS    //更新成功寫標志位
}process_status;      //更新狀態
2、 主函數

????????這部分包含了升級的所有狀態,該框架可以說對比上一篇文章是完全不變的,在UPDATE_PROGRAM需要一直發送字符C來請求數據,具體看代碼。

static void iap_process(void)
{process_status process;process = get_boot_state();switch (process) {case NONE:break;case START_PROGRAM:printf("start app...\r\n");delay_ms(50);if ((((*(vu32*)(APP_SECTOR_ADDR+4))&0xFF000000)==0x08000000)&&(!iap_load_app(APP_SECTOR_ADDR))) {printf("no program\r\n");delay_ms(1000);}printf("start app failed\r\n");break;case UPDATE_PROGRAM:ymodem_c();LED1_TOGGLE;delay_ms(1000);break;case UPDATE_SUCCESS:write_setting_boot_state(START_PROGRAM_STATE);NVIC_SystemReset();break;default:break;}
}int main(void)
{LED_GPIO_Config();ymodem_init();Key_GPIO_Config();printf("jin ru boot\r\n");set_boot_state(read_setting_boot_state());if(Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == 1){set_boot_state(UPDATE_PROGRAM);}while(1){iap_process();}
}
3、YMODEM協議的實現

? ? ? ? 接收固件按幀來接收的,默認使用1024個字節每幀接收,起始幀和結束幀(YMODEM_SOH)都不傳輸數據,數據幀(YMODEM_STX)以1024個字節接收固件,所以只需要接收YMODEM_STX,至于串口收發、隊列和定時器的配置就不說了,只可以意會不可言傳,反正根據YMODEM協議發完一幀需要MCU給應答才會發第二幀。

推薦以下兩篇文章學習YMODEM協議

Ymodem協議詳解-CSDN博客

Ymodem文件傳輸協議_ymodem協議-CSDN博客

#define YMODEM_SOH		0x01  // start of 128
#define YMODEM_STX		0x02  // start of 1024
#define YMODEM_EOT		0x04  // end of transmission
#define YMODEM_ACK		0x06  // positive acknowledge
#define YMODEM_NAK		0x15  // negative acknowledge
#define YMODEM_CA		0x18  // cancel終止
#define YMODEM_C		0x43  // control character 'C'
#define YMODEM_END      0x4F  // control character 'O'關閉傳輸
void ymodem_ack(void) 
{uint8_t buf;buf = YMODEM_ACK;Usart_Send_Data(&buf, 1);
}void ymodem_nack(void) 
{uint8_t buf;buf = YMODEM_NAK;Usart_Send_Data(&buf, 1);
}void ymodem_c(void) 
{uint8_t buf;buf = YMODEM_C;Usart_Send_Data(&buf, 1);
}void ymodem_end(void)
{uint8_t buf;buf = YMODEM_END;Usart_Send_Data(&buf, 1);
}void ymodem_start(ymodem_callback cb) 
{if (ymodem.status == 0) {ymodem.cb = cb;}
}void ymodem_recv(download_buf_t *p) 
{uint8_t type = p->data[0];switch (ymodem.status) {case 0:if (type == YMODEM_SOH) {ymodem.process = BUSY;ymodem.addr = APP_SECTOR_ADDR;mcu_flash_erase(ymodem.addr, APP_ERASE_SECTORS);ymodem_ack();ymodem_c();ymodem.status++;}break;case 1:if (type == YMODEM_SOH || type == YMODEM_STX) {if (type == YMODEM_SOH)//起始幀、結束幀這里可以不操作flash{mcu_flash_write(ymodem.addr, &p->data[3], 128);ymodem.addr += 128;}else {mcu_flash_write(ymodem.addr, &p->data[3], 1024);ymodem.addr += 1024;}ymodem_ack();}else if (type == YMODEM_EOT) {ymodem_nack();ymodem.status++;}else {ymodem.status = 0;}break;case 2:if (type == YMODEM_EOT) {ymodem_ack();ymodem_c();ymodem.status++;}break;case 3:if (type == YMODEM_SOH) {ymodem_ack();ymodem_end();ymodem.status = 0;ymodem.process = UPDATE_SUCCESS;}}p->len = 0;
}
4、程序跳轉

????????跳轉這部分網上也很多,基本沒什么區別,關于中斷可能要注意一下,可能跳轉之前,某些外設中斷是開啟的,跳轉之后,中斷產生了,但是APP代碼中沒有處理對應該中斷的中斷處理函數,所以就可能會直接死機。

tips:本文的開發環境只有64k ram,所以需要特別注意這里,雖然bootloader的ram不太可能會超。

if (((*(__IO uint32_t*)appxaddr) & 0x2FFF0000 ) == 0x20000000)?

詳細參考以下文章:

關于STM32單片機IAP升級中if(((*(__IO uint32_t*)ulAddr_App) & 0x2FFE0000) == 0x20000000)語句的理解-CSDN博客

uint8_t iap_load_app(uint32_t appxaddr)
{uint8_t i;uint32_t jump_addr;if (((*(__IO uint32_t*)appxaddr) & 0x2FFF0000 ) == 0x20000000) {  jump_addr = *(__IO uint32_t*) (appxaddr + 4);  jump2app = (iapfun)jump_addr;  /* 關閉所有中斷,清除所有中斷掛起標志 */  for (i = 0; i < 8; i++){NVIC->ICER[i]=0xFFFFFFFF;NVIC->ICPR[i]=0xFFFFFFFF;}	__set_MSP(*(__IO uint32_t*)appxaddr);  jump2app();return 1;}return 0;
}	

三、APP程序制作

????????這部分需要設置一下flash的偏移量,為了滿足需求,還需要在串口中斷函數添加觸發更新標志位。

NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x4000);
// 串口中斷服務函數
void USART1_IRQHandler(void)
{uint8_t ucTemp;if(USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET){		ucTemp = USART_ReceiveData(DEBUG_USARTx);USART_SendData(DEBUG_USARTx,ucTemp);  if(ucTemp=='1'){write_setting_boot_state(UPDATE_PROGRAM_STATE);NVIC_SystemReset();}}	 
}

四、工程配置(默認KEIL5)

BootLoader部分:0x08000000~0x08002FFF

?

APP部分:0x08004000~0x0807C000

?

五、運行測試?

? ? ? ? 用串口助手執行升級操作,我用Xshell 8來操作(用其它支持YMODEM協議的串口助手也可以)。

1、配置Xshell 8。點擊文件新建->點擊連接->修改名稱(隨便改)和協議(選SERIAL)->點擊串口->修改常規設置確認(圖不截了,不會自己百度了解一下)

2、直接跳轉APP。根據下圖看到首先進來boot,然后直接跳轉APP了。

?

?3、進入更新。按下KEY1按鍵后復位板子會重新進入boot,然后進入更新,MCU會一直發送字符C請求數據,根據下圖發送bin文件給MCU接收。

可以看到已經更新成功,根據需求,大家也可以在主程序發送字符1給單片機也可以實現該功能。

tips: 測試完有個瑕疵,每次更新程序都需要把APP的全部扇區都擦除掉,那樣很費時間,當然也有解決的辦法,就是YMODEM協議可以抓取數據包的長度,根據數據包的長度去擦除固定的扇區,但是需要琢磨一下,我懶得弄,在我看來這個瑕疵是可以接受的。

結束語

????????以上基于YMODEM協議的串口IAP升級功能已實現,這只是其中的一種升級方式,后面大家看到的也希望可以得到大家的指點或者互動,感謝各位亦菲彥祖們了。下面給出了完整工程,也包含了另一種分區(BOOT+SETTING+APP+DOWNLOAD)的代碼。

完整代碼下載地址:

基于YMODEM協議的串口IAP升級固件資源-CSDN下載

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/88099.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/88099.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/88099.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Cookie(搭配domain)/Session(搭配HttpServletRequest+HttpSession)

各位看官&#xff0c;大家早安午安晚安呀~~~如果您覺得這篇文章對您有幫助的話歡迎您一鍵三連&#xff0c;小編盡全力做到更好 歡迎您分享給更多人哦今天我們來學習&#xff1a;Cookie/Session1.Cookie/Session的簡述我們在講解HTTP協議的時候已經講解過Cookie了HTTP 協議自身是…

240.搜索二維矩陣Ⅱ

純暴力有點太唐了&#xff0c;不過竟然能過&#xff1b;還有行和列的表示我一直搞反了。。。class Solution {public boolean searchMatrix(int[][] matrix, int target) {for(int i 0 ;i<matrix.length;i){for(int j 0 ;j<matrix[0].length;j){if(matrix[i][j]target)…

【計算機組成原理】-CPU章節學習篇—筆記隨筆

計算機組成原理 CPU 章節知識點總結&#xff08;適用于 408 考研&#xff09;? 一、CPU 的功能與基本結構? 1.1 CPU 的功能? CPU&#xff08;中央處理器&#xff09;是計算機的核心部件&#xff0c;主要功能包括&#xff1a;? 指令控制&#xff1a;程序的順序執行&#xff…

公用測控裝置的功能

公用測控裝置在電力系統中廣泛應用于變電站的高壓開關單元、變壓器本體及低壓側等對象。它集測量、控制、保護于一體&#xff0c;確保電網的安全、穩定運行。公用測控裝置采用高性能硬件架構&#xff0c;如32位微控制器和獨立AD采樣技術&#xff0c;結合軟件算法&#xff0c;實…

數據庫|達夢DM數據庫配置實例步驟

哈嘍&#xff0c;你好啊&#xff0c;我是雷工&#xff01; 最近因為一個信創項目&#xff0c;要使用達夢DM數據庫&#xff0c; 因此安裝了達夢DM數據庫來學習使用&#xff0c; 上一節中記錄了安裝Windows版本達夢DM數據庫的過程步驟&#xff0c; 這一節緊接著上一節《數據庫…

三十一、【高級特性篇】接口用例參數化與關聯:實現上下文數據傳遞

三十一、【高級特性篇】接口用例參數化與關聯:實現上下文數據傳遞 前言準備工作第一部分:后端數據模型調整1. 升級 `TestCase` 模型2. 生成并應用數據庫遷移3. 更新 `TestCaseSerializer`第二部分:后端測試執行器強化1. 修改 `execute_api_test_case` 函數2. 修改 Celery 任…

PCA通過“找最大方差方向”實現降維,本質是用更少的變量捕捉原始數據的主要模式

什么事 PCA(主成分分析) PCA(主成分分析)的原理與通俗舉例 PCA 是什么? PCA(Principal Component Analysis)是一種常用的降維算法,核心目標是將高維數據映射到低維空間,同時盡可能保留原始數據的關鍵信息(方差最大的方向)。 核心原理:找“最能代表數據的方向”…

JAVA synchronized關鍵字涉及的Monitor對象中 EntryList和WaitSet工作機制

在Java的synchronized同步機制中&#xff0c;Monitor對象的EntryList和WaitSet是兩個關鍵隊列&#xff0c;它們分別管理不同狀態的線程。下面我將詳細解釋它們的工作原理&#xff0c;并提供代碼示例說明。 EntryList&#xff08;鎖競爭隊列&#xff09; 作用機制 EntryList保…

js-day10

JS學習之旅-day101. 作用域1.1 局部作用域1.2 全局作用域1.3 作用域鏈1.4 JS垃圾回收機制&#xff08;GC&#xff09;1.5 閉包1.6 變量提升2. 函數進階2.1 函數提升2.2 函數參數2.3 箭頭函數3. 解構賦值3.1 數組解構3.2 對象解構4. 數組遍歷4.1 forEach4.2 filter1. 作用域 作…

智能數字式毫秒計在實際生活場景中的應用

在電力領域&#xff0c;SYN5307型數字毫秒表可精準監測特高壓變電站斷路器合閘時間差&#xff0c;定位繼電保護裝置信號延遲&#xff1b;工業自動化中&#xff0c;優化汽車焊裝線時序、提升半導體晶圓切割良率&#xff1b;科研計量上&#xff0c;助力量子通信同步校準&#xff…

Java面試基礎:概念

1. Java的特點跨平臺性&#xff1a;Java的 “編寫一次&#xff0c;運行無處不在” 是其最大的特點之一。Java編譯器將源代碼編譯成字節碼(bytecode)&#xff0c;該字節碼可以在任何安裝了Java虛擬機(JVM)的系統上運行。面向對象&#xff1a;Java是一門嚴格的面向對象編程語言&a…

PyQt5高級窗口控件詳解:停靠窗口、多文檔界面與滾動條

掌握PyQt5的高級窗口控件&#xff0c;讓你的GUI應用具備專業級的布局與交互體驗 在PyQt5應用開發中&#xff0c;高效管理窗口布局和實現復雜交互功能是提升用戶體驗的關鍵。本文將深入解析三個核心高級控件&#xff1a;停靠窗口&#xff08;QDockWidget&#xff09;、多文檔界面…

50天50個小項目 (Vue3 + Tailwindcss V4) ? | DrawingApp(畫板組件)

&#x1f4c5; 我們繼續 50 個小項目挑戰&#xff01;—— DrawingApp組件 倉庫地址&#xff1a;https://github.com/SunACong/50-vue-projects 項目預覽地址&#xff1a;https://50-vue-projects.vercel.app/ 使用 Vue 3 的 Composition API&#xff08;<script setup>…

Eureka、Nacos、LoadBalance、OpenFeign?之間的區別聯系和協作 (附代碼講解)

這篇文章聊聊微服務里的這幾個老伙計&#xff1a;Eureka、Nacos、LoadBalance、OpenFeign。咱們做微服務開發&#xff0c;總會跟這幾個組件打交道&#xff1a;Eureka、Nacos、Spring Cloud LoadBalancer、OpenFeign。它們各司其職又互相配合&#xff0c;今天就把它們的關系、用…

JavaSE-繼承

繼承&#xff08;inheritance&#xff09;繼承的意義我們首先來看下面兩個類&#xff1a;public class Dog {public String name;public int age;public void eat(){System.out.println(this.name"正在吃飯");}public void bark(){System.out.println(this.name"…

第二屆虛擬現實、圖像和信號處理國際學術會議(VRISP 2025)

重要信息 官網&#xff1a;www.icvisp.net 時間&#xff1a;2025年8月1-3日 地點&#xff1a;中國-長沙 簡介 近年來&#xff0c;虛擬現實技術取得了顯著進步&#xff0c;與5G、云計算和物聯網等新一代信息技術的融合加速&#xff0c;推動了其在硬件、軟件和內容應用等方面…

SpringBoot+Mybatis+MySQL+Vue+ElementUI前后端分離版:整體布局、架構調整(二)

目錄 一、前言 二、后端調整 1.實體類調整 2.菜單相關接口 3.用戶相關接口 4.新增工具類 5.新增菜單樹返回類 6.配置類、攔截器 三、前端調整 1.請求調整 2.頁面布局、樣式調整 1.user.vue 2.index.vue 3.請求攔截 四、開發過程中的問題 五、附&#xff1a…

vue3官方文檔學習心得

這幾天抽空把vue3的文檔整個看了一遍。簡介 | Vue.js 23年寫過一個vue2的項目&#xff0c;24年寫了一個vue3的項目&#xff0c;頁面功能比較簡單&#xff0c;用幾個簡單的API&#xff0c;watch、watchEffect、ref、reactive就能實現的業務功能。 寫了幾年的react的&#xff0…

Pycharm恢復默認設置,配置導致復制粘貼等不能使用

在file 種找到manage IDE settings在manage IDE settings中找到restore default settings

【王樹森推薦系統】召回12:曝光過濾 Bloom Filter

概述 曝光過濾通常是在召回階段做&#xff0c;具體的方法就是用 Bloom Filter 曝光過濾問題 如果用戶看過某個物品&#xff0c;則不再把該物品曝光給用戶。原因是同一個物品重復曝光給用戶會損害用戶體驗&#xff0c;但也不是所有推薦系統都有曝光過濾&#xff0c;像 youtube 這…