分享

基于蓝牙的STM32 IAP在线升级

 rookie 2022-05-08 发布于北京

最近开发的一个小项目须要支持蓝牙在线升级,今天便详细地了解一番。蓝牙在线升级的方式,流程如图
在这里插入图片描述
流程解释: 产品的最新程序放在云端的服务器上,并将程序更新的提醒经过手机APP推送给用户,当用户点击程序更新时,APP将程序下载至手机上,并经过蓝牙传输到STM32上,这时单片机解析到的指令为程序更新,便触发IAP在线刷新程序。web

要实现这一功能,必须经过单片机的串口IAP在线升级功能。
1.什么是IAP?编程

IAP(In Application Programming)即在应用编程,IAP 是用户本身的程序在运行过程当中对User Flash 的部分区域进行烧写,目的是为了在产品发布后能够方便地经过预留的通讯口对产品中的固件程序进行更新升级。缓存

因此要实现IAP功能,固件程序须分为两个代码,即引导程序(BootLoader)和用户程序(APP)。
1.引导程序(BootLoader):只执行串口数据的接收、烧写程序并将程序执行地址跳转至用户程序段(此代码只能经过JTAG或SWD烧写)
2.用户程序(APP):执行用户所要实现的程序(此代码经过串口接收,IAP烧写入单片机flash中)服务器

2.STM32程序的启动方式
为何能将程序分红两个程序分别下载,要理解这个,咱们有必要了解一下STM32的启动方式,由于我用的是STM32F103C8T6,因此就以这个型号为例:
STM32上电或者复位后,代码区始终从0x00000000开始,三种启动模式其实就是将各自存储空间的地址映射到0x00000000中。其启动模式由BOOT0和BOOT1引脚的电平高低来控制。
在这里插入图片描述
一、BOOT1=x BOOT0=0:从Flash启动,将主Flash地址0x08000000映射到0x00000000,这样代码启动以后就至关于从0x08000000开始,这是正常的工做模式。
二、BOOT1=0 BOOT0=1:从系统存储器启动。首先控制BOOT0 BOOT1管脚,复位后,STM32与上述两种方式相似,从系统存储器地址0x1FFF F000开始执行代码,这种模式启动的程序功能由厂家设置。
三、BOOT1=1 BOOT0=1: 从RAM启动,将RAM地址0x20000000映射到0x00000000,这样代码启动以后就至关于从0x20000000开始,这种模式能够用于调试。
这里,我默认使用的模式是主闪存控制器启动,也就是从Flash启动。app

3. Flash在STM32内存空间的定义
STM32单片机内存空间有明确的定义,
在这里插入图片描述
查看STM32F103C8T6可知,其Flash是分配在空间从0x0800 0000到0x0801FFFF,最大的空间为127KByte。因此在主闪存控制器启动模式下,STM32一上电,单片机先将0x0800 0000映射到代码区,而后从0x0800 0000开始执行程序。
在进入main函数前,单片机还作了如下处理(不须要本身编写代码,由单片机内部自动执行)
在这里插入图片描述
ortex-M3上电后来到复位中断(已将前4个字节的值存入MSP堆栈指针),转到__main标号,完成RW段的移动、ZI段的初始化,创建堆栈,初始化库函数,而后跳转到main函数,自此就开始执行咱们编写的C程序。svg

4.引导程序和用户程序内存空间的划分
咱们知道,单片机默认是从0x0800 0000开始执行的,其过程:
在这里插入图片描述
1.STM32 在复位后,先从 0X08000004 地址取出复位中断向量的地址,并跳
转到复位中断服务程序,如图标号①所示;
2.在复位中断服务程序执行完以后,会跳转到咱们的main 函数,如图标号②所示;
3.而咱们的 main 函数通常都是一个死循环,在 main 函数执行过程当中,若是收到中断请求(发生重中断),此时 STM32 强制将 PC 指针指回中断向量表处,如图标号③所示;
4.而后,根据中断源进入相应的中断服务程序,如图标号④所示;
5.在执行完中断服务程序之后,程序再次返回 main 函数执行,如图标号⑤所示。函数

而咱们将FLash的内存空间分为引导程序和用户程序,其0x0800 0000~0x0800 2000,8KByte的空间做为BootLoader,将0x0800 2000 ~0x0801 FFFF,共120KByte做为用户空间(STM32F103C8T6实际只有64KByte Flash,用户空间为56KByte)。
在这里插入图片描述
经过此种方式后,STM32执行程序的流程变为:
在这里插入图片描述工具

  1. STM32 复位后,仍是从 0X08000004 地址取出复位中断向量的地址,并跳转到复位中断服务程序,在运行完复位中断服务程序以后跳转到 IAP 的 main 函数,如图标号①所示。
  2. 此部分同图 47.1.1 同样;在执行完 IAP 之后(即将新的 APP 代码写入 STM32的 FLASH,灰底部分。
  3. 新程序的复位中断向量起始地址为 0X08000004+N+M),跳转至新写入程序的复位向量表,取出新程序的复位中断向量的地址,并跳转执行新程序的复位中断服务程序,随后跳转至新程序的 main 函数,如图标号②和③所示,
  4. 一样 main 函数为一个死循环,而且注意到此时 STM32 的 FLASH,在不一样位置上,共有两个中断向量表。

5.引导程序的设计
(1)首先,实现串口中断函数,蓝牙经过串口将数据传输到单片机上,由于STM32F103C8T6的RAM只有8KByte,因此上位机每次发送的数据为1KByte,单片机烧写完后再发送下1KByte数据。ui

extern uint8_t usart_buf[1024+8];
//数据长度(2B)  数据(1KB)  序号(2B)  [CRC(4B)]
extern uint16_t buf_cnt;
extern uint8_t bootStatus;

void USART1_IRQHandler(void)
{
	uint8_t temp = 0;
	if( (USART_GetFlagStatus(USART1, USART_IT_RXNE) != RESET) )
	{
		temp = (uint8_t)USART_ReceiveData(USART1);
		if(++buf_cnt < (1024+8)  && ((buf_cnt & 0x8000) != 0x8000))
		{
			usart_buf[buf_cnt - 1] = temp;
		}
		else buf_cnt |= 0x8000;
	}
}

(2)在main函数中,对接收到的数据进行判断设计

if(flag)      //是否串口是否有接收到数据
{
	printf("开始更新固件r\n");	
	// 判断APP程序的起始地址是否为0X08XXXXXX
 	if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x08000000)
	{	 
		iap_write_appbin(FLASH_APP1_ADDR,USART_RX_BUF,applenth);//更新	FLASHFLASH代码	
		printf("固件更新成功\r\n");		
	}
	else 
	{
		printf("非固件程序\r\n");
	}
	flag = 0;

(3)若是检测到APP程序,进入FLASH烧写函数

// 从addr起烧录程序

void Iap_Write(uint32_t addr)
{
	uint16_t temp = 0;
	uint16_t data_size = 0;    //烧录大小
	uint16_t data_len = 0;	   //数据长度
	uint16_t index = 0;         //数据块索引
	uint32_t addr_now = addr;	    //写入地址
	uint8_t data_write[1024] = {0};	//数据缓冲
	//准备烧录,数据大小为1K
	while(1)
	{
		while((buf_cnt & 0x8000) == 0);		//等待数据接收完毕
		//解析数据
		data_len = (uint16_t)usart_buf[1] << 8 | usart_buf[0];	//获取data有效长度
		for(temp = 2; temp < data_len + 2; temp++)	//从第二位开始拷贝数据
		{
			data_write[temp - 2] = usart_buf[temp];
		}
		index = usart_buf[1024 + 3] << 8 + usart_buf[1024 + 2];			//获取索引
		//开始写数据
		if(data_len < 1024)	//写入剩下的数据
		{
			if(data_len % 2 != 0)data_len += 1;
			STMFLASH_Write(addr_now, (uint16_t *)data_write, data_len / 2);		//
			data_size += 1;
			STMFLASH_Write(IAP_INFO, &data_len, 1);
			putString("OK\n");
			break;
		}
		else
		{
			STMFLASH_Write(addr_now, (uint16_t*)data_write, data_len / 2);
			data_size += 1;
			addr_now += 1024;	//下一个1K
			buf_cnt = 0;
			for(temp = 0; temp < USART_BUF_SIZE; temp++)usart_buf[temp] = 0;
			putString("Next\n");		
		}
	}		//烧录完成
	//清空串口缓存
	buf_cnt = 0;
	for(temp = 0; temp < USART_BUF_SIZE; temp++)usart_buf[temp] = 0;
}

(4)烧录好APP程序后,引导程序将PC指针地址指向APP程序的起始地址,即0x0800 2000;要对PC指针进行操做,需使用MSR汇编指令来操做。

//设置栈顶地址
//addr:栈顶地址
__asm void MSR_MSP(u32 addr) 
{
    MSR MSP, r0 			//set Main Stack value
    BX r14
}

而后执行PC指针跳转函数

void Iap_load(uint32_t addr)
{
	if(((*(vu32*)addr) & 0x2FFE0000) == 0x20000000)		
	{
		jump = (iapfun) *(vu32*)(addr + 4);					//强制转化为函数
		MSR_MSP(*(vu32*)addr);
		jump();
	}
	else 
	{
		printf("Error\n");
		while(1);
	}
}

(5)最后,main函数的实现就很简单了,以轮询的方式读取串口数据,而后烧写FLASH,最后跳转至APP程序

while(1)
{
	while((readBootTime() != 0)  && (flag == 0))			//以轮询的方式读取串口
	{
		if(Iap_wait() == 8)
		{
			USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);	
			flag = 1;
		}
	}
	if(flag)
	{
		IAP_WRITE();
		flag = 0;
	}
	else 
		IAP_LOAD();       //跳转至APP程序
	}

6.用户程序APP的实现
用户程序实现比较简单,只须要对地址进行设计一下就能够,因此我就使用点亮LED来做为APP程序
在这里插入图片描述
图中 IROM1 的起始地址(Start)通常为 0X08000000,大小(Size)为 0X10000,
即从 0X08000000 开始的64K 空间为咱们的程序存储(由于咱们STM32F103C8T6 的 FLASH大小是 64K)。而图中,咱们设置起始地址(Start)为 0X0800 2000,即偏移量为 0X2000字节),于是,留给 APP 用的 FLASH 空间(Size)只有0X8000(56K 字节)大小了。设置好 Start 和 Szie,就完成 APP 程序的起始地址设置。

(2) 设置APP程序的中断向量表的偏移量
在 systemInit 函数中的,设置中断向量表的偏移量

SCB->VTOR = FLASH_BASE | 0x2000;

以上设置完成以后,点击rebuild按钮从新编译,生成LED.hex
(3)生成APP程序bin文件
咱们经过 MDK 自带的格式转换工具 fromelf.exe,来实现.axf 文件到.bin 文件的转换。该工具在 MDK 的安装目录\ARM\BIN40 文件夹里面
在这里插入图片描述
咱们就能够在 MDK 编译成功以后,调用 fromelf.exe,根据当前工程的 LED.axf,生成一个 LED.bin 的文件。
7.APP程序的蓝牙在线升级
因为公司云端服务器还没搭好,我就先本身的电脑蓝牙与产品蓝牙链接,而后经过用QT写的串口调试助手发送至STM32中,完成APP程序的升级。
在这里插入图片描述 至此,基于蓝牙的STM32 IAP在线升级就完成了!

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多