单片机的iap升级.思路流程与杂乱知识

一、iap框架

1、单片机从基地址启动

  • 引导区代码启动 --> 跳转到boot --> 没有检测到标志位-->跳转到app -->检查是否发生代码升级事件 -->给flash写入标志 --> 系统重启 -->引导区代码启动 -->跳转到boot -->检测到标志位 -->启动接收 -->从新的地址启动app

2、单片机从基地址启动

  • boot区代码启动 --> 没有检测到标志位 -->跳转到app -->检查到发生代码升级事件 -->给flash写入标志 --> 系统重启 --> boot区代码启动 --> 检测到标志位 -->启动接收 -->覆盖原app代码 --> 启动app

3、单片机从基地址启动

  • app代码启动 -->检测到代码升级事件 -->跳转到接收代码 -->启动接收 -->系统重启

二、isp与iap区别

1、iap:

  • (In Application Programming)即在应用编程,IAP是用户自己的程序在运行过程中对User Flash的部分区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信口对产品中的固件程序进行更新升级。通常实现IAP功能时,即用户 程序运行中作自身的更新操作,需要在设计固件程序时编写两个项目代码,第一个项目程序不执行正常的功能操作,而只是通过某种通信方式(如USB、USART)接收程序或数据,执行对第二部分代码的更新;第二个项目代码才是真正的功能代码.

2、isp:

  • 在系统编程,一般来说通过串口对程序进行更新. 不需要专门的烧录器,如JLINK.一般不考虑产品卖出去之后的功能更新。

三、代码的跳转

1、sp指针

  • sp指针值就是当前sram的堆栈地址,在编译的hex文件可以清晰的看出:sp指针的起始值就在hex文件的开头。

单片机的iap升级.思路流程与杂乱知识_第1张图片

  • 其保存在烧录后的flash中代码段的最开始。利用这种特性,常用来检测boot跳转到app时,app代码还在不在。在代码跳转时这是很重要的性质。在网上抄iap源码时可以看到利用这性质作检查的代码段,就不要把它删除了。
  • hex文件可以给出很多信息,其解读方式为:notpadd++打开的文件的冒号的后四字节:黑色字节的字节长度+flash内的烧录地址(高)+flash内的烧录地址(高字节)+flash内的烧录地址(低)+记录类型。如下:
    :+10+0000+00+18F09FE518F09FE518F09FE518F09FE5+C0+\r\n 
    Start Code+Byte count +Address +Record type +Data +Checksum+endline 
    注:记录类型Record type有6种: 
        00:数据记录 
        01:文件结束记录 
        02:扩展段地址记录 
        03:开始段地址记录 
        04:扩展线性地址记录 
        05:开始线性地址记录
  • 当然会看到一些特殊的比如上图中的:“ :+02+0000+04+0000+FA ”,依旧满足Start Code+Byte count +Address +Record type +Data +Checksum+endline公式。重要的看到“ 04 ”这代表着“ 扩展线性地址记录 ”其实就是记录着代码在mcu的flash中代码的偏移地址。从上图中一眼就能看出代码存放在0x08000000这个空间后面。0x0800是mcu厂商固定flash地址(arm内核),0x00是自己设置的偏移地址“ :+02+0000+04+0000+FA ”中的“ 0000 ”。
  • 针对sp指针的初始化目前只有两种个方法:使用软件重启函数重启代码,这可以清除某些标志位。从boot跳转到app时由于栈用得不多,所以app的运行不会进入HardFaultHandler()也就是硬件错误函数里。但从app跳到boot又跳回app这时候一些比较大型的系统就有很大的概率进入硬件错误函数。这是因为栈没有释放,多次的进入app使得栈溢出。第二种是手动设置,通过调用__set_PSP()跟__set_MSP()来手动设置psp、msp <-gd32f303的库->。
  • m3内核(arm的其他内核待考察)的sp指针有psp跟msp两种,在普通的前后台系统中,这两者无需分辨,但是在rtos系统中要注意这两个区别:中断会使用msp指针,线程代码使用psp指针。在做iap跳转时要注意对这两个sp指针的差异从而正确初始化sp指针。具体看
  • 对于m0内核来说,其中断向量表是48字(192字节)也就是0xC0个字节,其在flash的地址同样在代码段的起始位置,使用j-link工具回读flash可以查看到其结构。
一般来说在编写iap时,
 1、没有初始化堆栈、没有对齐都会进入硬件错误中断中;
 2、没有关闭总中断、清理外设会导致代码运行有莫名的难以预测的问题;
 3、boot代码跟app代码的时钟设置不一致会跳转后卡死;

2、pc指针

  • pc指针总是指向下一个要执行的指令
  • 在执行代码跳转后改变的就是这个指针指向的值。如果跳转后续没有做其他的初始化会导致奇奇怪怪的问题。

3、空间的划分

  • 值得注意的是m3内核的空间不是随意划分的。比如gd32f303扇区大小为2kB,每次写都必须先擦除整个扇区数据。无论是boot代码还是app代码都应该放在每个扇区的开头。这样规避了在boot中烧写flash代码时增加的隐含操作,可以一定程度减少开发时间。当然,这不是要求必须放在扇区开头的原因:m3的内核里,代码的偏移必须是0x200(512)的整数倍,也就是代码必须以0x200的整数倍的空间地址开始摆放。

4、重置中断向量表

  • 需要关注一下这个函数:nvic_vector_table_set(),这个是设置中断向量表的,要求这个函数必须在app代码的main函数开头被正确的调用且只调用一次。不然在app里的中断触发时调用的是bootloader里的中断处理函数

5、跳转代码例示

if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000)//查看待跳转地址的程序存在与否 
{ 
    JumpAddress = *( __IO uint32_t* )( ApplicationAddress + 4 ); //用户代码区第二个字存储为新程序起始地址(新程序复位向量指针) 
    Jump_To_Application = ( pFunction ) JumpAddress; __set_PSP(*(__IO uint32_t *)ApplicationAddress); 
    __set_CONTROL(0); //把psp修改成msp 
    __set_MSP( *( __IO uint32_t* ) ApplicationAddress ); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址) 
    Jump_To_Application(); //设置PC指针为新程序复位中断函数的地址 
}

6、校验

  • 在boot程序中给flash写入app代码时要注意,与代码无关的部分保持为0xFF,若给它清零,虽然运行不出错,但是在上位机jlink去计算校验值是会出错的。

你可能感兴趣的:(单片机,嵌入式硬件,arm,物联网)