一、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、没有初始化堆栈、没有对齐都会进入硬件错误中断中;
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去计算校验值是会出错的。