M0_IAP

关于IAP之前写过几篇,刚开始计划,没搞成就换了工作,遗憾,现在刚好又需要使用到IAP的功能,所以继续更新,必成。

0、前言

一个物联网的项目,主要就是MCU+4G模块,MCU通过AT指令使用4G模块,利用MQTT协议连接阿里云平台,嵌入式和后端通过阿里云平台交互数据;

型号是HK32F030R8T6,M0内核,内部Flash大小64K,SRAM大小10K;

IAP升级的流程是:平台把要更新的代码分成一个个包,通过阿里云平台给4G模块,4G模块再转发到MCU的串口,所以总体就是一个串口IAP的功能;

1、阶段1——分配BOOT和APP,能够从BOOT跳转到APP

1.1、分配BOOT的ROM

KEIL中配置ROM,起始地址0x0800 0000,Size是0x4000,预留16K,如下图:

1.2、分配APP的ROM和RAM

KEIL中配置ROM,起始地址0x0800 4000,Size是0xB800,也就是46K,加上上面的16K一共62K,剩余的2K用作保存数据;

RAM的起始地址为0x2000 0100,Size是0x2700,0x100+0x2700=0x2800也就是10K的RAM,至于为什么不从0x2000 0000开始,是因为M0不能将中断向量表映射到FLASH的0x0800 4000,但是可以映射到RAM,所以这部分0x100就是给APP的中断向量用的,如下图:

0x2000 0000到0x2000 0100这段内存,实际使用的大小是48个32位的空间也就是48*4=0xC0,所以配置RAM的起始地址你也可以写成0x2000 00C0;

Cortex-M0内核中断向量共有48个,在“startup_stm32f0xx.s”函数中__Vectors到__Vectors_End可以看到;

 1.3、BOOT的代码

主要是实现BOOT到APP跳转部分的代码,此外解决了由于BOOT中使用外设中断(现在只用了定时器)导致跳转到APP后无法执行的问题,在BOOT中关总中断,在APP中开总中断的方式是不够的,要具体到使用的外设才能正常跳转;

#define MAIN_USER_FLASH_BEGIN     0x8004000     //用户程序存储地址
 
typedef void (*RESET_FUNCTION )(void);          //复位函数模型
 
//从BOOT程序跳转到APP程序
void boot_jump_app(void)
{
   uint32_t jump_addr=*((__IO uint32_t *)(MAIN_USER_FLASH_BEGIN+4));
   RESET_FUNCTION Reset=(RESET_FUNCTION)jump_addr;
   __set_MSP(*(__IO uint32_t*)MAIN_USER_FLASH_BEGIN);
   Reset();
}

int main()
{	
	board_init();            //有使用到TIM2
	
	TIM_Cmd(TIM2,DISABLE);	 //关闭定时器	
	TIM_DeInit(TIM2);	     //复位定时器
	__disable_irq();		 //关中断

	boot_jump_app();         //从BOOT程序跳转到APP程序
	
	while(1);	
}

1.4、APP的代码部分

配置中断向量表、开总中断;

编译后的APP程序,48个中断向量就在bin文件的起始位置,所以把APP程序下载到0x0800 4000的位置后,理论上要将这个中断向量映射到0x0800 4000,这也是M3的做法,但是M0不能将中断向量表映射到FLASH的0x0800 4000,不过可以映射到RAM,所以就复制0x0800 4000开始的中断向量到0x2000 0000,然后将中断向量映射到RAM;

由于映射时使用了SYSCFG_MemoryRemapConfig函数,所以要开启SYSCFG时钟;

//用户程序存放地址(也是编译后的下载文件存放中断向量的flash地址)
#define APPLICATION_ADDRESS ((uint32_t)0x08004000)
 
//RAM中的中断向量
__IO uint32_t VectorTable[48] __attribute__((at(0x20000000)));

int main()
{
	u8 i;
		
	//必须开启SYSCFG时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
	
	//从flash中复制中断向量到ram
	for(i = 0; i < 48; i++){
		VectorTable[i] = *(__IO uint32_t*)(APPLICATION_ADDRESS + (i<<2));
	}
	//中断向量映射到RAM(开始地址处)
	SYSCFG_MemoryRemapConfig(SYSCFG_MemoryRemap_SRAM);

	__enable_irq();
	
	board_init();	
    while(1);
}

2、利用软件复位实现APP到BOOT的跳转

在上面的基础上让APP在运行一段时间后,跳转到BOOT,只需要在APP中定时个10s确定APP已经可以正常运行了,然后执行软件复位;

现象呢就是BOOT运行几秒跳转APP,APP运行几秒BOOT,这样循环,测试的程序就是定时器加LED,运行明了而且测试使用外设中断的情况;

//软件复位函数
void System_Reset(void)
{	
	__disable_irq();		//关中断
	NVIC_SystemReset(); 	//软件复位
}

 3、串口能正常收发数据、可以将数据写入芯片flash

之前文章有,而且这些知识非常基本,与IAP本身的功能也不是很相关,不花篇幅;

IAP_4_串口通讯准备_小老虎_IOT的博客-CSDN博客

IAP_5_读取内部Flash的数据_小老虎_IOT的博客-CSDN博客_如何读取flash中的数据

IAP_6_内部Flash写_小老虎_IOT的博客-CSDN博客


4、IAP更新的思路

BOOT程序:

检测flash中更新标志位是否为更新,如果更新,进入串口收发、接收bin文件的流程,如果不需要更新,直接跳转到APP;

更新的情况,在接收完bin文件之后,清除更新标志位,复位,再次进入BOOT程序判断不需要更新,跳转到APP;

APP程序:

正常运行程序,收到IAP指令后,将flash中的更新标志位置位,然后跳转到BOOT;


5、单片机与平台的通信问题

5.1、分包

bin文件为30K左右,太大了不可能一次性发送的,MQTT也不支持,就算平台可以一次发送单片机也没办法一次性接收缓存bin文件再写入flash的,所以要分包,分包不大不小最好,现在是以512字节分包;

最后一个包可能是不足512字节的,所以每个包带上传输的数据量,方便操作;

5.2、CRC16校验

要保证发送和接收数据的一致性,bin文件有任何错误都可能导致升级失败,最可怕的是如果不校验你压根不知道升级失败了;

5.2、应答

写入flash是很耗时间的,平台要等单片机处理完,发送ACK1后再发送下一个包;

同时,如果出现CRC错误,可以发送ACK0,让平台重发;

以及,如果丢包,超时未收到ACK1,也要进行重发;

5.3、APP与BOOT的过渡

APP端在收到IAP升级的指令后,往往不能直接跳转到BOOT,比如APP中还有数据正在处理中,想等待一次完整的处理完成;

BOOT中也有一些初始化的操作,可能比较耗时,比如连接阿里云平台等;

所以跳转到BOOT并准备好了以后,发送“READY TO UPDATE”,再让平台发送更新的程序。

5.4、单片机如何知道更新完成、平台如何知道更新成功

如果知道bin文件一共被分成了多少个包,每成功处理完一个包计数,最后一个包处理完成,也就是更新完成了;

所以,可以事先发送bin文件的信息,比如版本,包总数,再进行分包发送;

当平台收到最后一个包返回的ACK1后,可以记为更新成功;


6、流程图

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值