bootloader实现要点
bootloader 设计要点
1.分配Flash空间,一部分用于bootloader 一部分用于 APP ,这里bootloader分配了7K空间,APP分配了121K空间
2.需要准备一个通讯接口,可以是串口,也可以时can等
3.准备mcu的内部flash驱动,可以实现连续读写的函数,flash擦除在写函数内部自动判定
4.bootloader内部延时函数不要使用systick的中断实现,否则进入freertos后创建任务立即出现野指针问题,导致hardfault
5.跳转到app复位函数
实现流程
- 设备开机初始化gpio用于数据led便于提示用
- 初始化串口,串口使用接收中断+空闲中断的方式
- 初始化flash
- 检测是否收到升级指令,收到进入app升级状态,等待app数据,未收到app数据等待超时时进入app
- 收到app数据,首先检测数据头尾和检验,成功后将数据写入app地址区域0x8007800开始的地址空间,app收到的数据每次都是1K,当收到的数据小于1K代码代表app数据发送完成
- 进入appp
软件升级流程:
- mcu开机
- 上位机发送0xaa,0xaa,0x55,0x55
- mcu开始擦除本底app用到的flash存储区域
- 擦除完成后进入升级状态
- 上位机开始发送数据包
- mcu接收数据包,并等待一个完成的数据包,每个数据包固定大小
- mcu检验
- 保存数据到flash
- 返回数据处理完成
- 上位机发送下一包数据
- 最后一包小于1031个字节说明发送完成
- 跳转到app
数据包结构;累计 2+2+N+2+1 个字节,
0xaa,0xaa 数据id2个字节(高位,低位), 数据*N 0xaa,0xaa ,异或检验
- 正常数据包1031个字节
- 小于1031个字节的就是 最后一个数据包
2个核心的bootloader函数
//设置栈顶地址
//addr:栈顶地址
__asm void MSR_MSP(uint32_t addr)
{
MSR MSP, r0 //set Main Stack value
BX r14
}
iapfun jump2app;
//跳转到应用程序段
//appxaddr:用户代码起始地址.
void iap_load_app(uint32_t appxaddr)
{
if(((*(uint32_t*)appxaddr)&0x2FFE0000)==0x20000000) //检查栈顶地址是否合法.
{
jump2app=(iapfun)*(uint32_t*)(appxaddr+4); //用户代码区第二个字为程序开始地址(复位地址)
MSR_MSP(*(uint32_t*)appxaddr); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
jump2app(); //跳转到APP.
}
}
APP实现要点
-
需要设置中断偏移地址
在main函数入口就要立即设置,避免中断产生导致hardfault
SCB->VTOR = FLASH_BASE | 0X7800;
-
设置app程序的内存空间
0x8007800
0x18800
-
生成app
C:\Keil_v5\ARM\ARMCC\bin\fromelf.exe --bin -o N32L406MBL7_APP.bin .\Objects\N32L406MBL7.axf
上位机实现
现目前上位机实现
-
4个0xaa 0xaa 0xaa 0xaa 让mcu进入升级状态
-
在升级状态下数据格式
上位机发: 0xaa,0xaa 数据id2个字节(高位,低位), 数据*N 0xaa,0xaa ,异或检验
下位机收到回复:立即发送回复帧
格式 2个0xaa 0xaa 开头+收到数据id+2个 0xaa 0xaa 结束
-
读取bin文件,然后按照下发格式组包
使用方法
- 选择一个串口打开
- 打开app bin文件
- mcu开机有10S升级等待时间,电机计入bootloader
- 开始升级
本次这个升级工具相较于之前的N32L40x的工具实现了数据失败后重新传输的功能