资讯动态

IAP串口升级教程

发布时间:2026/10/18 14:19:33 来源:尧图企业网站定制
前言本教程从基础开始讲解bootloader IAP的原理和实现对于想尽快实现IAP功能的学习者可以从本文的第六节或第七节开始。一、认识bootloaderbootloader是什么bootloader是嵌入式系统中芯片上电复位后最先运行的程序他是一个引导程序。在有bootloader的项目中芯片的flash存储空间至少分为两个区bootloader区和APP区它们分别存储对应的程序。APP区用户自己写的业务功能代码如电灯程序等BOOTLOADER区用于负责引导程序如何启动如是直接运行APP区的用户程序还是需要先进行其他操作IAP升级等。bootloader的作用OAT远程升级、串口升级、USB升级等的能力底层需要一段程序来接收新的固件写入芯片flash并控制启动新程序。合理的设计之下即使APP升级失败设备任然会停留在bootloader中等待重新升级不会无法恢复比只有一个APP的业务更加容易恢复。二、STM32的启动文件和启动流程启动文件的功能在STM32工程中有一个“startup_stm32f103xb.s”类似的启动汇编文件。启动文件是芯片从复位到main()之间的引导程序。它主要提供1.中断向量表告诉cpu复位后栈的位置和指令执行开始位置和中断去哪里处理。2.ResetHandler完成上电后早期的初始化。3.提供中断默认处理函数为中断准备入口。启动流程上电复位 startup_stm32fxxxxb.s startup_stm32f103xb.c(SystenInit) main.c(main)中断向量表向量表是一张入口地址表cup复位、发生异常和中断时会按照编号到这张表里取对应的函数地址然后跳转。三、STM32内存映射什么是内存映射Cortex-M使用统一编址Flash、RAM、外设寄存器都有使用同一套32位地址空间cpu访问地址时总线会把它路由到对应的存储和外设。1.0x0800xxxx读flash里的代码和常量2.0x2000xxxx读写SRAM里的变量、堆和栈3.0x4000xxxx读写外设寄存器内存映射就是不同硬件资源被安排到那些地址区间常见的Cortex-M3/4类STM32F1/F4为例地址空间大致如下Flash区程序烧入区特点掉电数据不丢失运行时主要取值、读常量在线改写需要按页或扇区擦除后再编程复位后正常从Flash启动向量表通常在0x08000000;初始MSP栈顶0x08000000Reset_Hardler地址0x08000004后面为异常和中断入口等SRAM变量和栈存放区SRAM一般从0x20000000开始是程序运行时的工作台。示意如下外设与内核写GPIO-ODR、USART1-DR时本质时读写某个固定地址这些地址落在外设区常见0x40000000起。和Flash/SARM的区别Flash/SARM存代码和数据外设地址的读写会立刻影响硬件行为能力。内存映射和启动流程Flash存放长期保存和执行的东西RAM存放运行中会发生改变的东西外设/内核存放控制芯片行为的寄存器四、连接脚本Keil中的散列文件在Kile中连接器靠什么决定代码进Flash变量进RAM靠的就是分散加载文件散列文件.sct.sct文件.sct是连接器看内存布局的说明数它不在芯片上电后执行而是在连接阶段告诉连接器Flash、RAM从哪里开始有多大向量表、代码、常量放哪里可读写变量在哪里它主要负责程序的各个部分放在哪里连接后程序通常会形成这样的布局.sct文件在Keil中开启的方式常见.sct文件写法本文以stm32f103c8t6为例; ************************************************************* ; *** Scatter-Loading Description File generated by uVision *** ; ************************************************************* LR_IROM1 0x08008000 0x00008000 { ; load region size_region ER_IROM1 0x08008000 0x00008000 { ; load address execution address *.o (RESET, First) *(InRoot$$Sections) .ANY (RO) .ANY (XO) } RW_IRAM1 0x20000000 0x00005000 { ; RW data .ANY (RW ZI) } }.sct文件的核心结构加载域管烧到哪里执行域管运行时在哪里加载域LR_IROM1 0x08008000 0x00008000解析LR_IROM1加载域名字0x08008000起始地址Flash起点0x00008000 大小Flash执行域ER_IROM1 0x08008000 0x00008000 { ; load address execution address *.o (RESET, First) *(InRoot$$Sections) .ANY (RO) .ANY (XO) }解析ER_IROM1 0x08008000 0x00008000 从0x08008000起大小为32kb*.o (RESET, First)把启动文件里的RESET断向量表放到最前面*(InRoot$$Sections)Keil/RAM库需要的根区段通常保留.ANY (RO)其他只读内容代码常量等.ANY (XO)其他可执行相关的操作RAM执行域:RW_IRAM1 0x20000000 0x00005000 { ; RW data .ANY (RW ZI) }解析RW_IRAM1 0x20000000 0x00005000从0x20000000 起大小为32kbRWReadWrite有初值的可读写变量大致对应.dataZIZero Init启动时清零的变量大致对应.bss所以 int g 100; 这类通常进RWint g;或ing 0这类通常进ZI五、Flash编程Flash主要存放代码和常量的地方掉电不丢失。写之前需要先擦徐擦除和写入都有最小单位不能当普通数组随便改。Flash的擦除、写入和读取读取读取Flash直接按照地址读即可例如0x0800xxxx就能拿到代码和常量擦除把某一块flash恢复到可写状态常见表现为0xFF通常按页或者扇区擦除具体看芯片型号写入把数据数据放到已擦除过的区域不同系列对写入宽度有要求如半字字等或更大STM32f103用的是页一页大小2KB0x800Flash编程的基本流程一次典型的Flash写入如下1.关闭中断确保flash写过程中不会被打断2.解锁flash3.擦除目标页或扇区4.按对其要求写入数据5.读回校验6.锁定flash代码代码实战我们用STM32CubeMX生成一个带串口1的工程用来实现Flash的操作相关代码实现见百度网盘 中的”01_flash“工程文件通过网盘分享的文件IAP代码链接: https://pan.baidu.com/s/1zwN_RA0Dx9qH34rlIOTltg?pwdmaob 提取码: maobSTM32CubeMX基础工程生成Flash功能代码编写配置串口打印重定向代码实现1.修改编写usart.c文件 从定向串口打印功能新增以下代码#include stdio.h // printf重定向新增以下代码/*** brief 串口重定向函数* param ch 要发送的字符* return 发送的字符*/int fputc(int ch, FILE *f){HAL_UART_Transmit(huart1, (uint8_t *)ch, 1, 0xFFFF);return ch;}2.修改编写main.c文件实现Flash编程新增以下代码#include stdio.h // printf重定向Flash编程实现新增以下代码定义要操作的Faish的地址信息#define FLASH_ADDR 0x08010000 // 存储区域起始地址 追后一页新增以下代码Flash操作代码printf(STM32F103C8T6:Flash erasure, writing and reading operations\r\n);HAL_Delay(10);// 定义变量FLASH_EraseInitTypeDef erase; // 定义flashsh 擦除写入结构体uint32_t page_error 0; // 定义flash擦除错误变量uint32_t data 0x12345678; // 定义要写入flash的数据uint32_t read_data 0; // 定义读取flash的数据变量//1.解锁flashHAL_FLASH_Unlock();//2.擦除flash参数配置erase.TypeErase FLASH_TYPEERASE_PAGES; // 擦除类型为页擦除erase.PageAddress FLASH_ADDR; // 擦除起始地址erase.NbPages 1; // 擦除页数为1// //3.调用擦除函数HAL_FLASHEx_Erase(erase, page_error);//4.写入数据HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, FLASH_ADDR, data);//5.锁定flashHAL_FLASH_Lock();//6.读取数据read_data *(uint32_t*)FLASH_ADDR;printf(read_data %x\r\n, read_data);Flash编程结束使用串口工具验证 已读取到我们写入的数据六、实现一个简单的BootLoader在开始之前我们需要对齐一个概念要实现bootloader程序跳转我们将要准备两个工程一个是bootloader工程用来编写跳转到app代码的功能代码一个是app工程用来编程实际的业务功能如显示代码点灯代码等。所以我们要创建两个工程分别为bootloader工程和app工程。并将芯片Flash空间分为两个部分分别用来存储bootloader和app代码。bootloader分区bootloader占Flash的起始地址复位入口固定在0x08000000app不能再从0x08000000开始不然下载程序时会将bootloader程序覆盖掉本教程以stm32f103c8t664kb Flash为例切成两部分区域大小起始地址结束地址bootloader32kb0x80000x080000000x08008000app32kb0x80000x080080000x08010000合计64kb0x100000x080000000x08010000Flash分区完成之后结构图bootloader占启动权App占领业务权复位后一定先跑bootloader通过检查后跳到0x08008000跑AppApp没有向量表而是向量表跟着app搬到了0x08008000。0x08008000是App的初始SP0x08008004是App的Reset_Handlerboot跳转时读取的就是这连个字App跑起来之后中断向量表也要指向这张表也就是把SCB-VTOR改成0x08008000。代码实战接下来我们开始编写代码和实战配置。相关代码实现见百度网盘 中的”02_bootloader_1“工程文件通过网盘分享的文件IAP代码链接: https://pan.baidu.com/s/1zwN_RA0Dx9qH34rlIOTltg?pwdmaob 提取码: maob1.生成两个带串口的基础工程文件文件我们用STM32CubeMX软件生成两个基础工程并开启串口1后面用来打印和验证信息STM32CubeMX的相关配置和代码生成请见目录第5节Flash编程-代码实战-STM32CubeMX基础工程生成串口重定向打印的相关配置和代码生成请见目录第5节Flash编程-代码实战-功能代码编写实现-配置串口重定向代码实现2.编写和配置bootloader工程文件代码①先配置好Klie②配置连接脚本.sct文件开启.sct文件配置信息按照注释要求修改 ; ************************************************************* ; *** Scatter-Loading Description File generated by uVision *** ; ************************************************************* ;配置按照bootloader的flash起始地址和大小修改 0x08000000起始地址 0x0000800032KB大小 LR_IROM1 0x08000000 0x00008000 { ; load region size_region ;配置按照bootloader的flash起始地址和大小修改 0x08000000起始地址 0x0000800032KB大小 ER_IROM1 0x08000000 0x00008000 { ; load address execution address *.o (RESET, First) *(InRoot$$Sections) .ANY (RO) .ANY (XO) } ;基本不需要修改 RW_IRAM1 0x20000000 0x00005000 { ; RW data .ANY (RW ZI) } }③添加mian.c文件代码添加基础配置// 定义需求变量 typedef void (*app_fun_t)(void); // 函数指针用于指向APP地址/* USER CODE BEGIN PV */ // 定义需求宏 #define BOOTLOADER_ADDR 0x08000000u // 引导加载器起始地址 #define BOOTLOADER_SIZE 0x00008000u // 引导加载器大小 #define APP_ADDR 0x08008000u // APP起始地址 #define APP_SIZE 0x00008000u // APP大小 #define SRAM_START_ADDR 0x20000000u // SRAM起始地址 #define SRAM_END_ADDR 0x20005000u // SRAM结束地址(stm32f103c8t6 srom大小为20k) #define APP_FLASH_START_ADDR 0x08008000u // APP Flash起始地址(非必须定义) #define APP_FLASH_END_ADDR 0x080010000u // APP Flash结束地址(非必须定义) #define APP_STACK_MIN 0x20000000u // APP栈起始地址最小值 #define APP_STACK_MAX 0x20005000u // APP栈起始地址最大值 /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------/ void SystemClock_Config(void); / USER CODE BEGIN PFP */ // 定义需求函数 static uint8_t APP_IsReady(uint32_t app_addr); // 检测APP是否准备好(校验APP地址) static void JUMP_TO_APP(uint32_t app_addr); // 跳转到APP /* USER CODE END PFP */实现函数功能/* USER CODE BEGIN 0 */ // 实现需求函数 /** brief 检测APP是否准备好(校验APP地址) param app_addr: APP起始地址 retval 1: APP准备好, 0: APP未准备好 */ static uint8_t APP_IsReady(uint32_t app_addr) { //取出APP首地址和APP首地址4地址的值 uint32_t app_sp *(volatile uint32_t *)(app_addr 0u); // APP首地址的值(栈指针,取出向量表的第一个值) uint32_t app_pc *(volatile uint32_t *)(app_addr 4u); // APP首地址4的值() //判断APP首地址的值是否在SRAM范围内 /* 检查MSP栈指针 app_spz 栈指针需大于SRAM起始地址且小于SRAM结束地址 */ if((app_sp APP_STACK_MIN) || (app_sp APP_STACK_MAX)) { return 0; // APP未准备好 } //检查Reset_Handler地址必须在APP范围内且最低位为1(Thumb指令集) /* 检查PC寄存器 app_pc 需大于APP Flash起始地址且小于APP Flash结束地址 */ if((app_pc APP_ADDR) || (app_pc (APP_ADDR APP_SIZE)) || ((app_pc 0x1u) 0u)) // 判断APP首地址4的值是否在APP范围内且最低位为1(Thumb指令集) { return 0; // APP未准备好 } return 1; // APP准备好 } /** brief 跳转到APP param app_addr: APP起始地址 retval None */ static void JUMP_TO_APP(uint32_t app_addr) { //取出APP首地址和APP首地址4地址的值 uint32_t app_sp *(volatile uint32_t *)(app_addr 0u); // APP首地址的值(栈指针,取出向量表的第一个值) uint32_t app_pc *(volatile uint32_t *)(app_addr 4u); // APP首地址4的值() app_fun_t app_reset; // 定义函数指针变量,用于指向APP地址 // 1.关闭总中断 __disable_irq(); // 2.关闭SysTick定时器 SysTick-CTRL 0; // 关闭SysTick定时器 SysTick-LOAD 0; // 停止SysTick定时器 SysTick-VAL 0; // 停止SysTick定时器 // 3.重定向向量表 SCB-VTOR app_addr; // 4.切换主栈 __set_MSP(app_sp); // 5.跳转到APP(指向APP地址) app_reset (app_fun_t)app_pc; // 6.运行APP app_reset(); while(1) { // 死循环 正常不会运行到这里 } } /* USER CODE END 0 */在mian函数中实现bootloader跳转app的功能/* USER CODE BEGIN 2 */ printf(bootloader start!!\r\n); // 判断APP是否准备好 if(APP_IsReady(APP_ADDR)!0) { printf(APP is ready jumping......!!\r\n); HAL_Delay(50); // 跳转到APP JUMP_TO_APP(APP_ADDR); } /* USER CODE END 2 */BootLoader工程代码编写完成3.编写和配置app工程文件代码注意这一步需要完成Klie配置见上文本章第6节 代码实战-2.编写和配置bootloader工程文件代码-①先配置好Klieapp工程相对来说比较简单我们就只需要配置.sct文件和打印一段话来检验是否跳转到该代码同配置bootloader的.sct文件一样打开.sct文件。; ************************************************************* ; *** Scatter-Loading Description File generated by uVision *** ; ************************************************************* ; 这里注意这里的起始地址时接着bootloader的地址 也就是起始地址0x08000000 加 bootloader 分配的 ; Flash大小0x08000 所以是0x08008000同样的app分配的地址也是32KB 所以配置 0x00008000 LR_IROM1 0x08008000 0x00008000 { ; load region size_region ER_IROM1 0x08008000 0x00008000 { ; load address execution address *.o (RESET, First) *(InRoot$$Sections) .ANY (RO) .ANY (XO) } ;这里保持不变 RW_IRAM1 0x20000000 0x00005000 { ; RW data .ANY (RW ZI) } }接下来我们在main文件的main函数中打印一段话/* USER CODE BEGIN 2 */ printf(APP Start! STM32f103c8t6.\r\n); /* USER CODE END 2 */app工程代码配置王完成4.测试跳转功能我们分别编译下载bootloader代码和app代码到单片机中打开串口工具看打印信息可以看到串口打印出了bootloader和app两个工程代码的信息说明bootloader成功跳转到app代码七、IAP实现串口升级IAP实现串口升级功能原理与上一节实现的原理相当于bootloader加flash功能在跳转到app程序时先判断是否升级若升级则进行Flash擦除重新写入程序操作。接下来我们来实现它们把。相关代码实现见百度网盘 中的”03_bootloader_IAP_Ymodem“工程文件通过网盘分享的文件IAP代码链接: https://pan.baidu.com/s/1zwN_RA0Dx9qH34rlIOTltg?pwdmaob 提取码: maob工具准备实现串口升级我们需要借助一个支持Ymodem通讯协议的工具来进行文件传输 我是用的是 Xshell 7需要的可查看我的另一篇博客 Xshell 7的安装。Xshell 7安装指导指南-CSDN博客1.生成两个带串口的基础工程文件文件我们用STM32CubeMX软件生成两个基础工程并开启串口1后面用来打印和验证信息STM32CubeMX的相关配置和代码生成请见目录第5节Flash编程-代码实战-STM32CubeMX基础工程生成串口重定向打印的相关配置和代码生成请见目录第5节Flash编程-代码实战-功能代码编写实现-配置串口重定向代码实现2.bootlader工程的Ymodem升级协议的移植什么是YmodemYmodem是串口传输协议Xmodem的升级版上位机把.bin文件按协议拆成数据包发送除去bootlader按照协议收包边收边写入Flash。自己写和【定长包协议相比】对比项之定义协议Ymodem文件名/大小需要自己定首包自带包长需要自己定128B/1024B校验自己定CRC标准CRC16上位机要自己写Xshell/SecureCRT等直接发Boot代码短稍长但是可以直接用开源传输逻辑数据包格式startSOH0x01(128字节数据接收)STX0x02(1024字节数据接收)number包序号012.....~number包序号取反用于校验data有效数据包0是文件名和大小包1起是内容CRC16位CRC校验高字节在前常用控制字节符号值含义SOH0x01128数据包STX0x021024数据包EOT0x04传输结束ACK0x06确认VAK0x15否认要求重发CA0x18连续两个表示终止CRC16/‘C’0x43收方请求CRC16模式stm32ymodem官方下载地址https://www.st.com/en/embedded-software/stsw-stm32008.html#get-software我已移植好大家可以直接百度网盘下载我的源代码复制Ymodem文件夹到自己的工程中3.编写和配置bootloaderYmodem工程文件代码移植完成代码结构如下图注意这一步需要完成Klie配置见上文本章第6节 代码实战-2.编写和配置bootloader工程文件代码-①先配置好Klie配置.sct文件关于配置连接脚本.sct文件的操作请参考本章第6节中的-2.编写和配置bootloader工程文件代码-②配置连接脚本.sct文件。; ************************************************************* ; *** Scatter-Loading Description File generated by uVision *** ; ************************************************************* LR_IROM1 0x08000000 0x00008000 { ; load region size_region ER_IROM1 0x08000000 0x00008000 { ; load address execution address *.o (RESET, First) *(InRoot$$Sections) .ANY (RO) .ANY (XO) } RW_IRAM1 0x20000000 0x00005000 { ; RW data .ANY (RW ZI) } }Ymodem移植文件代码修改说明打开三个文件1修改common.h文件的串口配置这个配置修改为自己的串口如你用的是串口则 UartHandle 配置为huart22.修改flash_if.h文件这里必须配置成真实的app flash的起始地址和大小需和app中的.sct文件配置的一致。Ymodem移植文件完成main.c文件编程添加基础配置这里和第6章节差不多但是我们注释了 main.c文件的函数如下// static uint8_t APP_IsReady(uint32_t app_addr); // 检测APP是否准备好(校验APP地址)因为现在不需要这个函数去检测app区了有移植过来的Ymodem来处理了。我们将Ymodem的头文件添加进来#include flash_if.h // flash接口#include ymodem.h // ymodem接口具体main,c文件代码/* USER CODE BEGIN Header */ /** ****************************************************************************** * file : main.c * brief : Main program body ****************************************************************************** * attention * * Copyright (c) 2026 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include main.h #include usart.h #include gpio.h /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include stdio.h // printf重定向 #include flash_if.h // flash接口 #include ymodem.h // ymodem接口 /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ // 定义需求变量 typedef void (*app_fun_t)(void); // 函数指针用于指向APP地址 /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ // 定义需求宏 #define BOOTLOADER_ADDR 0x08000000u // 引导加载器起始地址 #define BOOTLOADER_SIZE 0x00008000u // 引导加载器大小 #define APP_ADDR 0x08008000u // APP起始地址 * #define APP_SIZE 0x00008000u // APP大小* #define SRAM_START_ADDR 0x20000000u // SRAM起始地址 #define SRAM_END_ADDR 0x20005000u // SRAM结束地址(stm32f103c8t6 srom大小为20k) #define APP_FLASH_START_ADDR 0x08008000u // APP Flash起始地址 #define APP_FLASH_END_ADDR 0x080010000u // APP Flash结束地址 #define APP_STACK_MIN 0x20000000u // APP栈起始地址最小值* #define APP_STACK_MAX 0x20005000u // APP栈起始地址最大值* /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ // 定义需求函数 // static uint8_t APP_IsReady(uint32_t app_addr); // 检测APP是否准备好(校验APP地址) static void JUMP_TO_APP(uint32_t app_addr); // 跳转到APP /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ // 实现需求函数 /** * brief 检测APP是否准备好(校验APP地址) * param app_addr: APP起始地址 * retval 1: APP准备好, 0: APP未准备好 */ // static uint8_t APP_IsReady(uint32_t app_addr) // { // //取出APP首地址和APP首地址4地址的值 // uint32_t app_sp *(volatile uint32_t *)(app_addr 0u); // APP首地址的值(栈指针,取出向量表的第一个值) // uint32_t app_pc *(volatile uint32_t *)(app_addr 4u); // APP首地址4的值() // //判断APP首地址的值是否在SRAM范围内 // /* 检查MSP栈指针 app_spz 栈指针需大于SRAM起始地址且小于SRAM结束地址 */ // if((app_sp APP_STACK_MIN) || (app_sp APP_STACK_MAX)) // { // return 0; // APP未准备好 // } // //检查Reset_Handler地址必须在APP范围内且最低位为1(Thumb指令集) // /* 检查PC寄存器 app_pc 需大于APP Flash起始地址且小于APP Flash结束地址 */ // if((app_pc APP_ADDR) || (app_pc (APP_ADDR APP_SIZE)) || ((app_pc 0x1u) 0u)) // 判断APP首地址4的值是否在APP范围内且最低位为1(Thumb指令集) // { // return 0; // APP未准备好 // } // return 1; // APP准备好 // } /** * brief 跳转到APP * param app_addr: APP起始地址 * retval None */ static void JUMP_TO_APP(uint32_t app_addr) { //取出APP首地址和APP首地址4地址的值 uint32_t app_sp *(volatile uint32_t *)(app_addr 0u); // APP首地址的值(栈指针,取出向量表的第一个值) uint32_t app_pc *(volatile uint32_t *)(app_addr 4u); // APP首地址4的值() app_fun_t app_reset; // 定义函数指针变量,用于指向APP地址 // 1.关闭总中断 __disable_irq(); // 2.关闭SysTick定时器 SysTick-CTRL 0; // 关闭SysTick定时器 SysTick-LOAD 0; // 停止SysTick定时器 SysTick-VAL 0; // 停止SysTick定时器 // 3.重定向向量表 SCB-VTOR app_addr; // 4.切换主栈 __set_MSP(app_sp); // 5.跳转到APP(指向APP地址) app_reset (app_fun_t)app_pc; // 6.运行APP app_reset(); while(1) { // 死循环 正常不会运行到这里 } } /* USER CODE END 0 */ /** * brief The application entry point. * retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_USART1_UART_Init(); /* USER CODE BEGIN 2 */ //变量定义 uint32_t fw_size 0u; // 存储APP文件大小 uint8_t key 0u; // 存储按键值 //初始化flash_if FLASH_If_Init(); //显示引导加载器信息打印 printf(\r\n Bootloader IAP \r\n); printf(Boot 0x08000000, App 0x%081X\r\n,(unsigned long)APP_ADDR); printf(1: Ymodem download (success - reset)\r\n); printf(3: Jump to APP\r\n); printf(Waiting command......\r\n); printf(****Input 1; -Update the firmware\r\n); printf(****Input 3; -Run the APP program\r\n); // 判断APP是否准备好 // if(APP_IsReady(APP_ADDR)!0) // { // printf(APP_yomdem is ready jumping......!!\r\n); // HAL_Delay(50); // // 跳转到APP // JUMP_TO_APP(APP_ADDR); // } /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ //1.等待串口命令无超时 if(HAL_UART_Receive(huart1, key, 1u, HAL_MAX_DELAY) ! HAL_OK) { continue; //继续等待 } //2.判断按键值 if (key 1) //按键1 启动Ymodem下载 { // 接收固件文件 printf(waiting Ymodem file......\r\n); fw_size 0u; if (Ymodem_Receive(fw_size) COM_OK) { printf(Download OK,size%1u,name%s\r\n, (unsigned long)fw_size,(char *)aFileName); printf(Resetting......\r\n); HAL_Delay(100); //升级成功后系统复位重新进boot/再跳 app 由后续策略决定 NVIC_SystemReset(); } else { printf(Download failed waiting command ...\r\n); } } else if (key 3) //按键3 跳转到APP { // 跳转到APP printf(Jump to APP......\r\n); HAL_Delay(100); JUMP_TO_APP(APP_ADDR); } } /* USER CODE END 3 */ } /** * brief System Clock Configuration * retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.HSIState RCC_HSI_ON; RCC_OscInitStruct.PLL.PLLState RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL RCC_PLL_MUL2; if (HAL_RCC_OscConfig(RCC_OscInitStruct) ! HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(RCC_ClkInitStruct, FLASH_LATENCY_0) ! HAL_OK) { Error_Handler(); } } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ /** * brief This function is executed in case of error occurrence. * retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * param file: pointer to the source file name * param line: assert_param error line source number * retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, ex: printf(Wrong parameters value: file %s on line %d\r\n, file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */4.编写和配置appYmodem工程文件代码注意这一步需要完成Klie配置见上文本章第6节 代码实战-2.编写和配置bootloader工程文件代码-①先配置好KlieappYmodem工程代码和第6章-代码实战-编写和配置app工程文件代码一致无需做其他修改5.IAP升级操作app_ymodem工程生成.bin文件 串口烧入的文件将下面一段规则按照图片所示黏贴到输入框内fromelf --bin --output SLL.bin #L重新编译注意是完整编译整个工程是rebuild不是Build不然会生成失败编译完成后会生成.bin文件后面升级就是用这个文件烧入使用Xshell进行IAP升级先将编写好的bootloader_ymodem烧入进单片机1.打开 Xshell新建一个会话 名称自己随便取用默认的也行协议选择SERIAL2.配置串口信息选串口端口号需要和你自己的串口配置一致3.ymodem配置保持默认即可点击确定。4传输文件重启单片机打印出boolloader引导串口输入1进入升级模式串口输入3不升级直接运行现有的app程序输入1进行升级串口一直打印CCCCC..... 等待文件传入在黑设区域右击鼠标选择传输-选择YMODEM(Y)-用YMODEM发送选择app_ymodem工程生成的.bin文件即可发送完成单片机会重启又回到bootloader程序我们输入3跳转到我们烧入的.bin文件查看打印信息是否是和我们编程的一致输入3验证app程序是否下载成功可以看到下图打印的信息和我们编写的打印信息一致IAP升级所有功能完成结尾说明此教程完全有作者自己操作实现瑕疵不可避免欢迎指出错误和改进方式。如有任何问题欢迎留言我将全力尽力解答。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑