STM32远程升级(IAP)

1、烧写程序三种方式

嵌入式系统的主要升级方式有ICP、IAP、ISP。

  1. ISP(In-SystemProgramming)为在系统中编程,出厂时ST公司已经在ROM中写入了BootLoader,通过通信接口就可以擦除与编程用户程序;
  2. IAP(In-ApplicationProgram)为在应用程序中编程,用户在运行程序的时候,通过BootLoader程序对指定的Flash区域进行烧写,除了ST公司出厂时写入的BootLoader外,用户可以自己写入一个BootLoader程序来运行存储在Flash中指定区域的程序,在程序的运行过程中我们就可以通过任何通讯方式,把接收到的bin文件写入到指定区域的Flash中,然后进行应用程序的升级;
  3. ICP(In-CircuitProgramming)为在电路编程,这是一种串行编程方式,可以使用JTAG或者SWD接口来对STM32进行程序烧写。

2、远程升级实现

首先,我的思路是将STM32的FLASH区域化分为四大区域,例如我使用的是STM32F103VET6,该芯片的FLASH区域有512K。第一块区域大小为50K,用来存放BOOTLOADER程序。第二块与第三块区域可以每个分200K大小,用来存放用户应用程序一(APP1)与用户应用程序二(APP2)。第四部分就用来存放各种标志位,比如当前运程的程序标志、应用程序版本标志等等。如果当前是运行在APP1下,那么接收到的升级程序就要写在APP2的区域内。我采用的是MQTT协议来实现远程升级,设备端使用Wi-Fi连接至互联网,通过MQTT协议连接至云端服务器。这里用的MQTT服务器为EMQX,并使用python作为另一个客户端(paho-mqtt)连接至服务器,这样就实现了python写的客户端与设备终端之间的通讯。

终端设备程序流程图(示意):
在这里插入图片描述

(1) BOOTLOADER

BOOTLOADER程序程序负责根据升级FLAG去跳转至对应的APP程序,没有其他工作。在编译时要制定FLASH空间,分配50K的大小。跳转的程序就不列出来了,有很多可以参考的例程。
在这里插入图片描述

(2)用户应用程序一(APP1)

使用ESP8266模块连接Wi-Fi,并通过MQTT协议连接至云端服务器。由于我的应用程序中使用了ucOSIII与emwin所以,bin文件会比较大。此时使用MQTT协议一次发送数据包过大可能会引起数据丢失等错误。所以为了避免这些错误。经测试,一次发送4K左右的数据包时可以保证数据的完整度。所以当bin文件大小为200K左右时,需要将其分成50个数据包来发送数据。如果要实现多台设备终端升级时,可以考虑先订阅一个升级包,收到该主题的消息后就取消订阅。这样两个设备之间不会相互影响。
APP1的地址要避开其他。
在这里插入图片描述
一定要在main函数中加入这条。放在最前面,设置中断向量表的偏移。

SCB->VTOR = FLASH_BASE | 0x0800C800;

(2)用户应用程序二(APP2)

同APP1
在这里插入图片描述
同样要在main函数中首先设置中断向量偏移。

SCB->VTOR = FLASH_BASE | 0x803E800;

3、Bootloader程序

1.首先先给主函数的例子

// 设置FLASH保存地址(必须为偶数,且其值要大于本代码所占用FLASH的大小)
// Bootloader 程序空间50k
/* 第一个APP的起始位置 IAP程序空间留有(100k)每一个APP大小为200K */
#define FLASH_APP1_ADDR	0X0800C800
/* 第二个APP的起始位置为250K */
#define FLASH_APP2_ADDR	0X0803E800
/* APP_FLAG存储起始位置为450K */
#define FLASH_FLAG_ADDR	0X08070800

#define Flag_Size 5
int main(void)
{
	USARTx_Init();
	uint8_t Read_Flag[5];

	STMFLASH_Read(FLASH_FLAG_ADDR,(uint16_t *)Read_Flag,Flag_Size);
	while(1)
	{
		if(strcmp((char *)Read_Flag,"0") == 0)
		{
			printf("开始执行用户代码(app1)!\n");
			if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)
			{
				iap_load_app(FLASH_APP1_ADDR);
			}
			else
				printf("FLASH地址有误!\n");
		
		}
		if(strcmp((char *)Read_Flag,"1") == 0)
		{
			printf("开始执行用户代码(app2)!\n");
			if(((*(vu32*)(FLASH_APP2_ADDR+4))&0xFF000000)==0x08000000)
			{
				iap_load_app(FLASH_APP2_ADDR);
			}
			else
				printf("FLASH地址有误!\n");
		}
	}
}

2.iap_load_app()函数

typedef void (*iapfun) (void);  
iapfun jump2app;
void iap_load_app(uint32_t app_addr)
{
		// 检查栈顶地址是否合法.
	if(((*(vu32*)app_addr)&0x2FFE0000)==0x20000000)	
	{
		jump2app = (iapfun)*(vu32*)(app_addr + 4);
		MSR_MSP(*(vu32*)app_addr);
		jump2app();
	}
}

3.这样就完成每次读取(FLASH_FLAG_ADDR 0X08070800)这个地址的存放的数据,然后根据读出的标志是0还是1来跳转至相应的APP区域执行程序。

4.关于升级文件的接收

首先我们的单片机要连接到EMQ X服务器,然后订阅相关的升级Topic。当单片机发送升级请求时,使用python编写的MQTT客户端接收到请求,根据所要升级的文件大小,将其分成若干个小包(我这里测试过每包分为4K的大小单片机可以正常接收数据)。每个小包为一个Topic,然后将数据发送至单片机。单片机每接收一个topic,经过检查后将其写入FLASH。接收完所有的升级包后,将FLASH_FLAG_ADDR中的标志位置0或1,通过软复位来进入bootloader程序。然后bootloader程序根据标志位跳转相应的APP区。

4.后期还会继续加更完整程序(狗头)

关于MQTT程序https://blog.csdn.net/qq_40893726/article/details/109449151
在这篇文章有介绍

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值