STM32 + CubeMX + 串口 + IAP升级

这篇文章分享一个简单的串口IAP Demo,实现使用串口更新我们自己的App程序。

目录

  • 一、IAP简介
  • 二、Stm32CubeMx配置
  • 三、Boot代码及配置
    • 1、代码
    • 2、配置
  • 四、App代码及配置
    • 1、代码
    • 2、配置
  • 五、效果展示

一、IAP简介

IAP介绍可以在网上找找,相关资料很多,如:
https://blog.csdn.net/ba_wang_mao/article/details/110401656

二、Stm32CubeMx配置

1、RCC开启外部高速时钟(略)
2、配置STLink调试口(略)
3、配置串口方便调试输出(略)
4、配置工程名、生成路径,之后生成工程(略)
(1-4步的基础配置可以参考前面的文章《STM32基础工程模板创建》)
Boot和App的工程配置是一样的,配置好时钟、usart1即可

三、Boot代码及配置

1、代码

#include <stdio.h>
#include "string.h"
#include "stdio.h"#define NVIC_VectTab_RAM_Start       ((uint32_t)0x20000000)							//RAM起始地址
#define NVIC_VectTab_RAM_End         ((uint32_t)0x20020000)							//RAM结束地址,大小为128K,根据自己的实际芯片大小修改
#define NVIC_VectTab_FLASH           ((uint32_t)0x08000000)							//Flash起始地址
#define BOOT_SIZE							       0x3000							//Boot大小,12KB,0--12页,共128页
#define ApplicationAddress	         (NVIC_VectTab_FLASH + BOOT_SIZE)   			//App的起始地址
#define USER_FLASH_PAGES	           52      										//App所占大小,52KB                      
//#define FLASH_PAGE_SIZE	           1024											//每页所占的字节大小,和系统库定义重复了,注释掉
#define	UPDATE_CMD	                 "update"										//升级擦除指令
#define FLASH_USER_END_ADDR	\((FLASH_PAGE_SIZE*USER_FLASH_PAGES)+ApplicationAddress)					//App结束地址typedef  void (*iapfun)(void);
void SystemClock_Config(void);int fputc(int ch, FILE *f)
{HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);return ch;
}iapfun jump2app; 
unsigned int count2 = 0;
unsigned char datatemp[256] = {0};
unsigned char boot_flag = 0;
unsigned char time_out_flag = 0;int main(void)
{unsigned char i;HAL_Init();SystemClock_Config();MX_GPIO_Init();MX_USART1_UART_Init();printf("boot start\r\n");printf("input \"update\" to erasure user flash, or wait 10s to start user app\r\n");for(i = 0; i<10; i++){//上电后每秒阻塞接收升级指令,连续10秒未收到则跳转App,10秒内收到则接收App数据并写入HAL_UART_Receive(&huart1, datatemp, 256, 1000); if(strstr((const char *)datatemp, UPDATE_CMD) != NULL){// 擦除App区域FLASH_EraseInitTypeDef EraseInitStruct;unsigned int PageError;HAL_FLASH_Unlock();EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;EraseInitStruct.PageAddress = ApplicationAddress;EraseInitStruct.NbPages = USER_FLASH_PAGES;if(HAL_FLASHEx_Erase(&EraseInitStruct, &PageError) != HAL_OK){HAL_FLASH_Lock();printf("Erase fail at:0x%x\n\r",PageError);return 0;}HAL_FLASH_Lock();boot_flag = 1;printf("Erase OK\n\r");				break;			}}if(boot_flag == 1){HAL_StatusTypeDef	temp;unsigned int Address;unsigned int data_32;unsigned char j = 0;printf("ready to receive bin, please send in 30s\n\r");Address = ApplicationAddress;		temp = HAL_UART_Receive(&huart1, datatemp, 256, 30*1000); if(temp == HAL_TIMEOUT){//阻塞30S,未收到App数据则退出printf("time out, end wait to receive bin\n\r");return 0;}else if(temp == HAL_OK){//收到则循环接收,每秒阻塞接收256字节,并写入Flashwhile(1){unsigned char i;				HAL_FLASH_Unlock();for(i=0; i<64; i++){data_32 = *(unsigned int *)(&datatemp[i<<2]);if(Address < FLASH_USER_END_ADDR){if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, data_32)==HAL_OK){Address = Address + 4;}else {HAL_FLASH_Lock();printf("write fail at: 0x%x\n\r", Address);return 0;}						}					}HAL_FLASH_Lock();	printf("write 256 btye OK: 0x%x\t%d\n\r",Address,j++);//最后一包数据不足256字节时会接收超时,防止app数据不完整,不足256字节数据接收完以后处理一次,下一次接收超时认为接收完成,跳转Apptemp = HAL_UART_Receive(&huart1, datatemp, 256, 2*1000); if(temp == HAL_TIMEOUT){time_out_flag++;if(time_out_flag == 2){printf("End write OK\n\r");goto START_APP;}}}}}	else if(boot_flag == 0)  //没有收到升级命令{	
START_APP:		printf("start user app\n\r");	HAL_Delay(10);/*判断App的栈顶指针是否合法(即是否有App)ApplicationAddress为App在flash中的地址,APP把中断向量表ApplicationAddress开始的位置,而中断向量表前4字节存储的是栈顶地址这里的目的是判断App的栈顶指针是否在0x20000000到0x2001FFFF之间,在的话就认为有App,不在就没有*/printf("ApplicationAddress:%0x\r\n", (*(unsigned int *)ApplicationAddress));if(((*(unsigned int *)ApplicationAddress)>= NVIC_VectTab_RAM_Start) &&((*(unsigned int *)ApplicationAddress)<= NVIC_VectTab_RAM_End)){	// disable irq, if use this, must enable irq at app//__disable_irq(); //(ApplicationAddress+4)放的是中断向量表的第二项“复位地址”__set_MSP(*(unsigned int *)ApplicationAddress); jump2app=(iapfun)*(unsigned int *)(ApplicationAddress+4);jump2app();	}else{printf("no user app\n\r");return 0;}}while (1){}
}

2、配置

在这里插入图片描述

四、App代码及配置

1、代码

#include <stdio.h>#define NVIC_VectTab_FLASH           ((uint32_t)0x08000000)					//Flash起始地址
#define BOOT_SIZE					 0x3000									//Boot大小void SystemClock_Config(void);
int fputc(int ch, FILE *f)
{HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);return ch;
}int main(void)
{uint32_t Count = 0;/* 设置中断向量偏移地址 */SCB->VTOR = NVIC_VectTab_FLASH | BOOT_SIZE;HAL_Init();SystemClock_Config();MX_GPIO_Init();MX_USART1_UART_Init();printf("\n\rapp start\n\r");while (1){HAL_Delay(10);if(Count%100 == 0){printf("this is app!\t%d\n\r",Count/100);}Count++;}
}

2、配置

在这里插入图片描述
在这里插入图片描述

//添加这条命令生成bin文件
$K\ARM\ARMCC\bin\fromelf.exe --bin --output=Bin\@L.bin !L

五、效果展示

1、先使用Keil将Boot程序烧录进单片机,串口提示请在10s内输入升级命令
在这里插入图片描述
2、串口发送“update”命令,提示成功,在30s内发送bin文件
在这里插入图片描述
3、配置串口发送延时为100ms.因为stm32是接收一包写一包的,比较耗时。
在这里插入图片描述
4、选择并发送文件,开始打印接收升级日志
在这里插入图片描述
在这里插入图片描述
5、全部接收完成后会跳转到App,并打印App内的日志
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/887849.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

A051-基于Spring Boot的网络海鲜市场系统的设计与实现

&#x1f64a;作者简介&#xff1a;在校研究生&#xff0c;拥有计算机专业的研究生开发团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339; 赠送计算机毕业设计600…

《String类》

目录 一、定义与概述 二、创建字符串对象 2.1 直接赋值 2.2 使用构造函数 三、字符串的不可变性 四、常用方法 4.1 String对象的比较 4.1.1 比较是否引用同一个对象 4.1.2 boolean equals(Object anObject)方法&#xff1a;按照字典序比较 4.1.3 int compareTo(Strin…

【python使用kazoo连ZooKeeper基础使用】

from kazoo.client import KazooClient, KazooState from kazoo.exceptions import NoNodeError,NodeExistsError,NotEmptyError import json# 创建 KazooClient 实例&#xff0c;连接到 ZooKeeper 服务器 zk KazooClient(hosts127.0.0.1:2181) zk.start()# 定义节点路径 path…

I/O流综合练习题

(1)要编写一个dog.properties nametom age5 colorred (2)编写Dog 类(name,age,color)创建一个dog对象&#xff0c;读取dog.properties 用相应的内容完 成属性初始化,并输出 (3)将创建的Dog 对象 &#xff0c;序列化到 文件 dog.dat 文件 package chapter19.Properties;import …

vue多页面应用集成时权限处理问题

在多页面应用&#xff08;MPA&#xff09;中&#xff0c;权限管理通常会涉及到每个页面的访问控制、身份验证、以及权限校验。以下是几种常见的权限处理方式&#xff1a; 1. 前端路由权限控制 原理&#xff1a;虽然是多页面应用&#xff0c;通常每个页面会独立加载和渲染&…

泷羽sec-蓝队基础之网络七层杀伤链 (下)学习笔记

声明&#xff01; 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团队无关&a…

《C++ Primer Plus》学习笔记|第7章-函数——C++编程模块 (未完结)

文章目录 7.1 函数的基本知识7.1.1 定义函数7.1.2 函数原型和函数调用 7.2 函数参数和按值传递7.3 函数与数组7.3.5 指针与const7.4 函数和二维数组 7.10 函数指针1. 函数的地址2. 声明函数指针3&#xff0e;使用指针来调用函数 7.12 复习题1&#xff0e; 使用函数的3个步骤是什…

Flink CDC Connector开发指南:逻辑复制协议实战与性能优化

目录 1、PostgreSQL的数据同步原理 2、Debezium定义的数据库和Flink通讯的消息格式 3、Gauss100 OLTP的数据同步方案 3.1 通过 JDBC 拉取数据 3.2 Gauss100 OLTP 逻辑复制插件Socket通讯 3.2.1 开发逻辑复制插件 编译逻辑复制插件 配置逻辑复制工具 启动逻辑复制服务 …

探讨播客的生态系统

最近对播客发生了兴趣&#xff0c;从而引起了对播客背后的技术&#xff0c;生态的关注。本文谈谈播客背后的技术生态系统。 播客很简单 播客&#xff08;podcast&#xff09;本质上就是以语音的方式发布信息。它和博客非常类似。如果将CSDN 网站上的文字加一个语音播报。CSDN …

@bytemd/vue掘金markdown插件预览内容有误

vue项目使用bytemd/vue 来预览字符串格式的markdown内容&#xff0c;总会多出如图的一段代码&#xff0c; 请问有没有大佬知道为什么&#xff1f; 很急&#xff0c;求教&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;

windows下编译IEC 61850依赖库

windows下编译IEC 61850依赖库 0、引言1、环境准备2、源码下载3、下载WpdPack4、生成vs解决方案5、在VS上对解决方案进行编译 0、引言 最近刚好在学习IEC 61850的一些标准规范&#xff0c;主要包含了两大块协议&#xff1a;MMS和GOOSE。61850是一个非常强大的协议&#xff0c;…

实现对图片或者视频增加隐藏水印和提取水印

好久好久没有写博客了&#xff0c;最近看见一个很有意思的文章&#xff1a;小心你的电脑被窃听&#xff0c;就是说在一些公司&#xff0c;截图都会存在水印&#xff0c;方便溯源&#xff0c;然后出于技术的好奇&#xff0c;我在github上搜了一下&#xff0c;还真有相关的github…

demo专业的一些无聊联想

手印分析年龄之形态学方法初探镜子表面汗潜指印的拍摄 实验采用普通配光检验法(暗视场配光照相法和垂直定向反射配光照相法)和短波紫外反射照相法对镜面上的汗潜指印进行拍摄, 拍摄效果如图2所示。三种方法均取得了较好的拍摄效果, 其中效果最好的是采用暗视场配光照相法拍摄…

科技为翼 助残向新 高德地图无障碍导航规划突破1.5亿次

今年12月03日是第33个国际残疾人日。在当下科技发展日新月异的时代&#xff0c;如何让残障人士共享科技红利、平等地参与社会生活&#xff0c;成为当前社会关注的热点。 中国有超过8500万残障人士&#xff0c;其中超过2400万为肢残人群&#xff0c;视力障碍残疾人数超过1700万…

C++类的自动转换和强制类型转换

目录 一、类型转换 二、转换函数 一、类型转换 C⽀持内置类型隐式类型转换为类类型对象&#xff0c;需要有相关内置类型为参数的构造函数 简单说就是可以将内置类型转化为自定义类型 示例&#xff1a; class Test { public:Test(int n1 0):num1(n1){}void pr…

什么是sfp,onu,​为什么PON(​俗称“光猫”​)模块使用SC光纤接口

在现代网络设备中&#xff0c;我们经常会看到SFP或SFP接口的身影&#xff0c;这些接口有时被简称为光口&#xff0c;但这个称呼并不严谨。有些厂商则称之为多功能口或多用途口&#xff0c;然而这对于不了解的人来说可能还是一头雾水。SFP&#xff0c;即Small Form-Factor Plugg…

【Linux】线程池设计 + 策略模式

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;Linux 目录 一&#xff1a;&#x1f525; 线程池 1-1 ⽇志与策略模式1-2 线程池设计1-3 线程安全的单例模式1-3-1 什么是单例模式1-3-2 单例模式的特点1-3-3 饿汉实现⽅式和懒汉实现⽅式1-3-4 饿汉…

flutter项目AndroidiOS自动打包脚本

从业数年余,开发出身,经数载努力位项目经理,因环境欠佳,终失业.失业达七月有余,几经周转,现又从开发,既回原点亦从始.并非与诸位抢食,仅为糊口,望海涵!因从头开始,所经之处皆为新奇,遂处处留痕以备日后之需. 自动打包脚本原文地址:https://zhuanlan.zhihu.com/p/481472311 转…

挂载本地目录到k8s的pod实现持久化存储

本地目录实现持久化存储 容器是无状态的,每次重启都是新的进程,但是我们需要将一些状态数据如配置、用户数据等存到本地来方便新的容器可以拿到历史状态。先创建一个目录来存放数据,并且挂载到minikube虚拟机内(不是pod里面)。注意要新开一个终端来调用,这个命令会阻塞,…

循环神经网络:从基础到应用的深度解析

&#x1f35b;循环神经网络&#xff08;RNN&#xff09;概述 循环神经网络&#xff08;Recurrent Neural Network, RNN&#xff09;是一种能够处理时序数据或序列数据的深度学习模型。不同于传统的前馈神经网络&#xff0c;RNN具有内存单元&#xff0c;能够捕捉序列中前后信息…