当前位置: 首页 > news >正文

STM32笔记(串口IAP升级)

一、IAP简介

IAP(In Application Programming)即在应用编程, IAP 是用户自己的程序在运行过程中对
User Flash 的部分区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信口对产
品中的固件程序进行更新升级。

通常实现 IAP 功能时,即用户程序运行中作自身的更新操作,需要在设计固件程序时编写两部分代码,第一部分程序不执行正常的功能操作,而只是通过某种通信方式(如 USB、 USART)接收程序或数据,执行对第二部分代码的更新;第二部分代码才是真正的功能代码。

两部分代码都同时烧录在 User Flash 中,当芯片上电后,首先是第一部分代码开始运行,它作如下操作:

  1. 检查是否需要对第二部分代码进行更新;
  2. 如果不需要更新则转到第二部分代码执行;
  3. 执行更新操作;
  4. 跳转到第二部分代码执行;

第一部分代码必须通过其它手段,如 JTAG 或 ISP 烧入;二部分代码可以使用第一部分代码 IAP 功能烧入,也可以和第一部分代码一起烧入,以后需要程序更新时再通过第一部分 IAP代码更新。

将第一部分项目代码称之为 Bootloader 程序,第二部分代码称之为 APP 程序,他们存放在STM32F4 FLASH 的不同地址范围,一般从最低地址区开始存放 Bootloader,紧跟其后的就是APP 程序(注意,如果 FLASH 容量足够,是可以设计很多 APP 程序的,同时还可以分两个bank区)。这样我们就是要实现 2 个程序: Bootloader 和 APP。

STM32F4 的 APP 程序不仅可以放到 FLASH 里面运行,也可以放到 SRAM 里面运行。

二、正常的程序运行流程

内部闪存(FLASH)地址起始于 0x08000000,一般情况下,程序文件就从此地址开始写入。

程序启动后,将首先从“中断向量表”取出复位中断向量执行复位中断程序完成启动,而这张“中断向量表”的起始地址是 0x08000004,当中断来临, 内部硬件机制亦会自动将 PC 指针定位到“中断向量表”处,并根据中断源取出对应的中断向量执行中断服务程序。

复位后,先从 0X08000004 地址取出复位中断向量的地址,并跳转到复位中断服务程序,如图标号①所示;在复位中断服务程序执行完之后,会跳转到我们的 main 函数,如图标号②所示;而我们的 main 函数一般都是一个死循环,在 main 函数执行过程中,如果收到中断请求(发生重中断), PC 指针指回中断向量表处,如图标号③所示;然后,根据中断源进入相应的中断服务程序,如图标号④所示;在执行完中断服务程序以后,程序再次返回 main 函数执行,如图标号⑤所示。

三、IAP 程序的运行流程

程序还是从 0X08000004 地址取出复位中断向量的地址,并跳转到复位中断服务程序,在运行完复位中断服务程序之后跳转到 IAP 的 main 函数

在执行完 IAP 以后(即将新的 APP 代码写入 FLASH,灰底部分。新程序的复位中断向量起始地址为 0X08000004+N+M),跳转至新写入程序的复位向量表,取出新程序的复位中断向量的地址,并跳转执行新程序的复位中断服务程序,随后跳转至新程序的 main 函数,如图标号②和③所示,同样 main 函数为一个死循环,但在不同位置上,共有两个中断向量表。

通俗讲:其实就是在正常运行的程序(APP程序)之前加上一个程序(bootloader程序), bootloader程序运行完之后再运行APP程序,两个是独立的个体,都需要有自己的中断。所以bootloader程序运行完之后就需将中断向量表位置移到APP程序的位置,不然APP程序发生中断响应回去执行bootloader程序的中断程序。

IAP 程序必须满足两个要求:

  1. 新程序必须在 IAP 程序之后的某个偏移量为 x 的地址开始;
  2. 必须将新程序的中断向量表相应的移动,移动的偏移量为 x;

四、运用实例

bootloader程序开机的时候先显示提示信息,然后等待串口输入接收 APP 程序(无校验,一次性接收),在串口接收到 APP 程序之后,即可执行 IAP。

按下 KEY1 按键,将串口接收到的 APP 程序存放到 STM32F103 的 FLASH,之后再按 KEY2 既可以跳转执行这个 FLASH APP 程序。

Bootloader 程序:

main.c

#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_key.h" 
#include "bsp_TiMbase.h"
#include "bsp_usart.h"/* 用于获取接收数据的计数 */
uint32_t USART_RX_CNT; /* 串口接收的数据保存在ram的0X20001000地址中 */
uint8_t  USART_RX_BUF[USART_REC_LEN] __attribute__ ((at(0X20001000)));/* 接收数据函数处理 */
void Receive_App_Data(void)
{static uint16_t oldcount = 0;	static uint16_t applenth = 0;static uint16_t com_delay = 0;if (com_delay < 1000){com_delay++;}else{if (USART_RX_CNT) {if (oldcount == USART_RX_CNT){applenth = USART_RX_CNT;printf("Receiving APP data succeeded.\r\n");printf("Code len: %dBytes\r\n",applenth);oldcount = 0;USART_RX_CNT = 0;}else{oldcount = USART_RX_CNT;}}}/* 扫描Key1, 将接收的app数据写进内部flash中 */if(Key_Scan(KEY1_GPIO_PORT, KEY1_GPIO_PIN) == KEY_ON){if(applenth){printf("Start updating firmware...\r\n");	/* 判断APP程序的复位地址是否在范围内 */if(((*(volatile uint32_t *)(0x20001000 + 4)) & 0xFF000000) == 0x08000000){	 Iap_Write_Appbin(FLASH_APP1_ADDR, USART_RX_BUF, applenth);  printf("Firmware update complete!\r\n");	}else { printf("Non-flash applications!\r\n");}}else {printf("There is no firmware to update!\r\n");}	} 	
}int main(void)
{USART_Config();LED_GPIO_Config();Key_GPIO_Config();BASIC_TIM_Init();printf("IAP Test !\n");while(1){if( Key_Scan(KEY2_GPIO_PORT, KEY2_GPIO_PIN) == KEY_ON){printf("Start APP code!\r\n");if(((*(volatile uint32_t *)(FLASH_APP1_ADDR + 4)) & 0xFF000000)==0x08000000){	 printf("success\n");/* 跳转到App程序中,最好放在主循环里,不要放在定时器里 */Iap_Load_App(FLASH_APP1_ADDR);}else {printf("Non-flash applications cannot be executed!\r\n");}} }
}

主要运行跳转到app的函数。

iap.c

#include "iap.h"   uint16_t iapbuf[1024]; 	//2K字节缓存
iapfun jump_to_app;/* 以每2k字节进行写入 */
void Iap_Write_Appbin(uint32_t appxaddr, uint8_t *appbuf, uint32_t appsize)
{uint16_t t;uint16_t i=0;uint16_t temp;uint32_t fwaddr = appxaddr;//当前写入的地址uint8_t *dfu = appbuf;for(t = 0; t < appsize; t += 2){						    temp = (u16)dfu[1] << 8;temp += (u16)dfu[0];	  dfu += 2;//偏移2个字节iapbuf[i++] = temp;	    if(i == 1024){i = 0;Flash_Write(fwaddr, iapbuf, 1024);	fwaddr += 2048;//偏移2048  16=2*8.所以要乘以2.}}if (i)Flash_Write(fwaddr, iapbuf, i);//将最后的一些内容字节写进去. 
}void Iap_Load_App(uint32_t appxaddr)
{/* 检查栈顶地址是否合法 */if(((*(volatile uint32_t *)appxaddr) & 0x2FFE0000) == 0x20000000)	{ /* 关中断 */__disable_irq(); /* 关闭外设时钟 */RCC->APB1ENR = 0;RCC->APB2ENR = 0;SysTick->CTRL = 0; /* 设置主堆栈指针 */__set_MSP(*(volatile uint32_t *)appxaddr);/* APP程序的复位地址 */jump_to_app = (iapfun) * (volatile uint32_t *)(appxaddr + 4);jump_to_app();}
}	/*********************************************END OF FILE**********************/

主要是将app程序写入flash,和跳转到app操作。

注意:一定要关中断和关闭外设(由于本程序用了TIM,所以关了中断,没用也可以不关,但最好是关)

串口中断-:

// 串口中断服务函数
void DEBUG_USART_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(USART_RX_CNT < USART_REC_LEN){USART_RX_BUF[USART_RX_CNT] = ucTemp;USART_RX_CNT++;			 									     }}	 
}

主要是接收app程序。

定时器中断-bsp_TiMbase.c:

void  BASIC_TIM_IRQHandler (void)
{if (TIM_GetITStatus( BASIC_TIM, TIM_IT_Update) != RESET ) {	LED_Test();Receive_App_Data();TIM_ClearITPendingBit(BASIC_TIM , TIM_FLAG_Update);  		 }		 	
}

运行LED闪烁程序和接收app数据并写入到flash里。

bootloader程序大小的确定:

双击工程得到.map文件看到该程序占用10.99kb大小。

使用11kb进行计算得出:2c00,但我使用了5000,为了方便扩展,当然你可以设置该大小。

APP 程序:

#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_TiMbase.h"volatile uint32_t time = 0; // ms 计时变量 int main(void)
{SCB->VTOR = 0x08005000;__enable_irq();LED_GPIO_Config();BASIC_TIM_Init();while(1){if ( time == 1000 ) /* 1000 * 1 ms = 1s 时间到 */{time = 0; LED1_TOGGLE; }        }
}

主要使1s LED闪烁一次

/* 设置中断向量表的偏移量 */ 
SCB->VTOR = 0x08005000;
/* 使能中断 */
__enable_irq();

这两个必须要加上,否则无法跳转。

用XCOM进行app文件的发送:

按下KEY1:

按下KEY2:

五、源代码地址

https://gitee.com/xu-fuyong/stm32f103-iap

相关文章:

STM32笔记(串口IAP升级)

一、IAP简介 IAP&#xff08;In Application Programming&#xff09;即在应用编程&#xff0c; IAP 是用户自己的程序在运行过程中对 User Flash 的部分区域进行烧写&#xff0c;目的是为了在产品发布后可以方便地通过预留的通信口对产 品中的固件程序进行更新升级。 通常实…...

C++网络编程:select IO多路复用及TCP服务器开发

C网络编程&#xff1a;使用select实现IO多路复用 一、什么是 IO 多路复用&#xff1f;二、IO多路复用器 select三、相关接口3.1、fd_set 结构体3.2、宏和函数 四、select 实现 TCP 服务器五、总结 一、什么是 IO 多路复用&#xff1f; 在网络编程中&#xff0c;最容易想到的并…...

部署 L2JMobius 天堂2芙蕾雅版本

首先下载所需要的服务器端 “L2J_Mobius.zip” 和芙蕾雅客户端&#xff08;三个压缩文件&#xff09;&#xff0c; 我的网盘下载&#xff1a;https://pan.baidu.com/s/1XdlcCFPvXnzfwFoVK7Sn7Q?pwdavd4 所有文件都在“芙蕾雅”目录下&#xff0c;也可以加入企鹅交流裙 87470…...

C#开发合集

用C#轻松搞定m3u8视频下载与合并 嘿&#xff0c;程序员们&#xff01;今天咱们来聊聊如何用C#写个小程序&#xff0c;轻松下载和合并m3u8视频文件。没错&#xff0c;就是那种分段的流媒体视频。准备好了吗&#xff1f;让我们开始吧&#xff01; 准备工作 在动手之前&#xf…...

鸿蒙面试 --- 性能优化

性能优化可以从三个方面入手 感知流畅、渲染性能、运行性能 感知流畅 在应用开发中&#xff0c;动画可以为用户界面增添生动、流畅的交互效果&#xff0c;提升用户对应用的好感度。然而&#xff0c;滥用动画也会导致应用性能下降&#xff0c;消耗过多的系统资源&#xff0c;…...

React的基础知识:Context

1. Context 在 React 中&#xff0c;Context 提供了一种通过组件树传递数据的方式&#xff0c;无需手动在每个层级传递 props。这在处理一些全局应用状态时非常有用&#xff0c;比如用户认证、主题、语言偏好等。 如何使用 Context 创建 Context&#xff1a;首先&#xff0c;…...

微知-lspci访问到指定的PCIe设备的几种方式?(lspci -s bus;lspci -d devices)

通过bdf号查看 -s &#xff08;bus&#xff09; lspci -s 03:00.0通过vendor id或者device id等设备查看 -d &#xff08;device&#xff09; lspci -d 15b3: #这里是vendor号&#xff0c;所以在前面 lspci -d :1021 #这里是设备号&#xff0c;所以要:在前vendorid和deviceid…...

【Kubernetes 集群核心概念:Pod】pod生命周期介绍【五】

5.1 Pod生命周期 Pod的生命周期指的是从Pod创建到终止的整个过程。它分为以下两种常见情况&#xff1a; 长期运行Pod&#xff1a; 例如运行HTTP服务的Pod&#xff0c;它在正常情况下会一直运行&#xff0c;但可以手动删除或终止。短期运行Pod&#xff1a; 例如执行计算任务的…...

c++的虚继承说明、案例、代码

虚继承的基本概念 在 C 中&#xff0c;虚继承主要用于解决多继承时可能出现的菱形继承问题。菱形继承是指一个类有两个&#xff08;或更多&#xff09;子类&#xff0c;而这两个子类又同时继承自一个共同的基类&#xff0c;当这些子类又被另一个类继承时&#xff0c;就形成了菱…...

小米PC电脑手机互联互通,小米妙享,小米电脑管家,老款小米笔记本怎么使用,其他品牌笔记本怎么使用,一分钟教会你

说在前面 之前我们体验过妙享中心&#xff0c;里面就有互联互通的全部能力&#xff0c;现在有了小米电脑管家&#xff0c;老款的笔记本竟然用不了&#xff0c;也可以理解&#xff0c;毕竟老款笔记本做系统研发的时候没有预留适配的文件补丁&#xff0c;至于其他品牌的winPC小米…...

介绍SSD硬盘

SSD硬盘&#xff08;固态硬盘&#xff0c;Solid State Drive&#xff09;是一种利用闪存技术存储数据的存储设备&#xff0c;与传统的机械硬盘&#xff08;HDD&#xff09;不同&#xff0c;SSD没有任何活动部件&#xff0c;因此其性能和耐用性较为优越。以下是SSD硬盘的一些主要…...

CMAKE常用命令详解

NDK List基本用法 Get–获取列表中指定索引的元素 list(Get list_name index output_var)解释 list_name: 要操作集合的名称index: 要取得的元素下标output_var: 保存从集合中取得元素的结果 栗子 list(GET mylist 0 first_element) # 获取第一个元素APPEND–在列表末尾…...

Vue3的通灵之术Teleport

前言 近期Vue3更新了一些新的内容&#xff0c;我都还没有一个一个仔细去看&#xff0c;但是还是有必要去解读一下新内容的。就先从Teleport 开始吧。 官方对 Teleport 的解释是&#xff1a;<Teleport> 是一个内置组件&#xff0c;它可以将一个组件内部的一部分模板“传…...

ue5第三人称闯关游戏学习(一)

视频资料38 - Compilers and Editors_哔哩哔哩_bilibili 上一个第一人称射击项目做完 接下来要更深入学习。 引入资产与C来创建第三人称闯关游戏 这次要引入的资产有两个分别是 Unreal Learning Kit&#xff1a;Game和stylized character kit: casual 01 不过有个比较麻…...

IIC 随机写+多次写 可以控制写几次

verilog module icc_tx#(parameter SIZE 2 , //用来控制写多少次 比如地址是0000 一个地址只能存放8bit数据 超出指针就会到下一个地址0001parameter CLK_DIV 50_000_000 ,parameter SPEED 100_000 ,parameter LED 50 )( input wire c…...

controller中的参数注解@Param @RequestParam和@RequestBody的不同

现在controller中有个方法&#xff1a;&#xff08;LoginUserRequest是一个用户类对象&#xff09; PostMapping("/test/phone")public Result validPhone(LoginUserRequest loginUserRequest) {return Result.success(loginUserRequest);}现在讨论Param("login…...

手搓人工智能-最优化算法(1)最速梯度下降法,及推导过程

“Men pass away, but their deeds abide.” 人终有一死&#xff0c;但是他们的业绩将永存。 ——奥古斯坦-路易柯西 目录 前言 简单函数求极值 复杂函数梯度法求极值 泰勒展开 梯度&#xff0c;Nabla算子 Cauchy-Schwarz不等式 梯度下降算法 算法流程 梯度下降法…...

多目标优化算法——多目标粒子群优化算法(MOPSO)

Handling Multiple Objectives With Particle Swarm Optimization&#xff08;多目标粒子群优化算法&#xff09; 一、摘要&#xff1a; 本文提出了一种将帕累托优势引入粒子群优化算法的方法&#xff0c;使该算法能够处理具有多个目标函数的问题。与目前其他将粒子群算法扩展…...

Swift——自动引用计数ARC

ARC ARC是swift使用的一种管理应用程序内存的机制&#xff0c;对于C语言我们知道&#xff0c;当我们申请一块空间&#xff0c;通常需要手动释放&#xff0c;不然会造成空间浪费&#xff0c;而有了ARC机制&#xff0c;你无需考虑内存的管理&#xff0c;因为ARC会在类的实例不再…...

【Quarkus】基于CDI和拦截器实现AOP功能(进阶版)

Quarkus 基于CDI和拦截器实现AOP功能&#xff08;进阶版&#xff09; 拦截器的属性成员拦截器的重复使用基于属性成员和重复使用的拦截器的发消息案例 本节来了解一下拦截器高级特性&#xff08;拦截器的重复使用和属性成员&#xff09;&#xff0c;官网说明&#xff1a;https:…...

React 第五十五节 Router 中 useAsyncError的使用详解

前言 useAsyncError 是 React Router v6.4 引入的一个钩子&#xff0c;用于处理异步操作&#xff08;如数据加载&#xff09;中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误&#xff1a;捕获在 loader 或 action 中发生的异步错误替…...

Linux 文件类型,目录与路径,文件与目录管理

文件类型 后面的字符表示文件类型标志 普通文件&#xff1a;-&#xff08;纯文本文件&#xff0c;二进制文件&#xff0c;数据格式文件&#xff09; 如文本文件、图片、程序文件等。 目录文件&#xff1a;d&#xff08;directory&#xff09; 用来存放其他文件或子目录。 设备…...

Flask RESTful 示例

目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题&#xff1a; 下面创建一个简单的Flask RESTful API示例。首先&#xff0c;我们需要创建环境&#xff0c;安装必要的依赖&#xff0c;然后…...

【配置 YOLOX 用于按目录分类的图片数据集】

现在的图标点选越来越多&#xff0c;如何一步解决&#xff0c;采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集&#xff08;每个目录代表一个类别&#xff0c;目录下是该类别的所有图片&#xff09;&#xff0c;你需要进行以下配置步骤&#x…...

GitHub 趋势日报 (2025年06月08日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践

6月5日&#xff0c;2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席&#xff0c;并作《智能体在安全领域的应用实践》主题演讲&#xff0c;分享了在智能体在安全领域的突破性实践。他指出&#xff0c;百度通过将安全能力…...

python报错No module named ‘tensorflow.keras‘

是由于不同版本的tensorflow下的keras所在的路径不同&#xff0c;结合所安装的tensorflow的目录结构修改from语句即可。 原语句&#xff1a; from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后&#xff1a; from tensorflow.python.keras.lay…...

技术栈RabbitMq的介绍和使用

目录 1. 什么是消息队列&#xff1f;2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...

深度学习水论文:mamba+图像增强

&#x1f9c0;当前视觉领域对高效长序列建模需求激增&#xff0c;对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模&#xff0c;以及动态计算优势&#xff0c;在图像质量提升和细节恢复方面有难以替代的作用。 &#x1f9c0;因此短时间内&#xff0c;就有不…...

Web中间件--tomcat学习

Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机&#xff0c;它可以执行Java字节码。Java虚拟机是Java平台的一部分&#xff0c;Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...