• 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中开总中断的方式是不够的,要具体到使用的外设才能正常跳转;

    1. #define MAIN_USER_FLASH_BEGIN 0x8004000 //用户程序存储地址
    2. typedef void (*RESET_FUNCTION )(void); //复位函数模型
    3. //从BOOT程序跳转到APP程序
    4. void boot_jump_app(void)
    5. {
    6. uint32_t jump_addr=*((__IO uint32_t *)(MAIN_USER_FLASH_BEGIN+4));
    7. RESET_FUNCTION Reset=(RESET_FUNCTION)jump_addr;
    8. __set_MSP(*(__IO uint32_t*)MAIN_USER_FLASH_BEGIN);
    9. Reset();
    10. }
    11. int main()
    12. {
    13. board_init(); //有使用到TIM2
    14. TIM_Cmd(TIM2,DISABLE); //关闭定时器
    15. TIM_DeInit(TIM2); //复位定时器
    16. __disable_irq(); //关中断
    17. boot_jump_app(); //从BOOT程序跳转到APP程序
    18. while(1);
    19. }

    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时钟;

    1. //用户程序存放地址(也是编译后的下载文件存放中断向量的flash地址)
    2. #define APPLICATION_ADDRESS ((uint32_t)0x08004000)
    3. //RAM中的中断向量
    4. __IO uint32_t VectorTable[48] __attribute__((at(0x20000000)));
    5. int main()
    6. {
    7. u8 i;
    8. //必须开启SYSCFG时钟
    9. RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
    10. //从flash中复制中断向量到ram
    11. for(i = 0; i < 48; i++){
    12. VectorTable[i] = *(__IO uint32_t*)(APPLICATION_ADDRESS + (i<<2));
    13. }
    14. //中断向量映射到RAM(开始地址处)
    15. SYSCFG_MemoryRemapConfig(SYSCFG_MemoryRemap_SRAM);
    16. __enable_irq();
    17. board_init();
    18. while(1);
    19. }

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

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

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

    1. //软件复位函数
    2. void System_Reset(void)
    3. {
    4. __disable_irq(); //关中断
    5. NVIC_SystemReset(); //软件复位
    6. }

     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、流程图

  • 相关阅读:
    muduo库中实现Protbuf编码器与消息分发器
    grid项目属性之grid-area&justify-self/align-self
    vue 常见问题
    JAVA8-Stream的使用
    【云原生 | 从零开始学Kubernetes】二、使用kubeadm搭建K8S集群
    智慧公厕解决方案易集成好使用的智能硬件
    java计算机毕业设计桂林餐饮服务平台源码+mysql数据库+系统+lw文档+部署
    非常全的一份Python爬虫的Xpath博文
    Word控件Spire.Doc 【页面设置】教程(11) ;如何在 C# 中设置 Word 文档的页面大小
    【uni-app从入门到实战】条件编译、导航学习
  • 原文地址:https://blog.csdn.net/E2242/article/details/126162800