STM32串口IAP升级
1.分区
L073RZ 192K + 6K
分区 | 大小 |
---|---|
Bootloader | 64K |
APP | 72K |
Backup | 72K |
Memory | 6K |
分区 | 大小 | 备注 |
Bootloader | 64K | 引导程序 |
APP | 72K | App运行 |
Backup | 72K | APP备份 |
Memory | 6K | 数据存储 |
2.工程创建
1)BOOT
a.IR0M1
Bootloader的起始地址: 0x8000000
Bootloader的Size: 0x9000
b.定义
#define APPLICATION_ADDRESS (uint32_t)0x801E000 /* Start user code address */
#define BACKUP_APPADDRESS (uint32_t)0x800C000 /* Backup code : APP2 */
c.APP跳转
pFunction JumpToApplication;
uint32_t JumpAddress = 0;
void ExecApplication(void)
{ __disable_irq(); /* 在跳转APP之前,关闭中断。 *//* Test if user code is programmed starting from address "APPLICATION_ADDRESS" */if (((*(__IO uint32_t*)APPLICATION_ADDRESS) & 0x2FFE0000 ) == 0x20000000){/* Jump to user application */JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);JumpToApplication = (pFunction) JumpAddress;/* Initialize user application's Stack Pointer */__set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);JumpToApplication();}
}
d.需启用CRC校验
2)APP
设置中断向量表偏移地址:
#include "system_stm32l0xx.c"/*!< Uncomment the following line if you need to relocate your vector Table inInternal SRAM. */
/* #define VECT_TAB_SRAM */
#ifdef SOTA_APP1
#define VECT_TAB_OFFSET 0X0C000U#else
#ifdef SOTA_APP2
#define VECT_TAB_OFFSET 0X1E000U#else
#define VECT_TAB_OFFSET 0x00U /*!< Vector Table base offset field.This value must be a multiple of 0x100. */
#endif
#endif
keil的bin文件生成
C:\Keil_v5\ARM\ARMCC\bin\fromelf.exe --bin -o ./Objects/@L.bin ./Objects/@L.axf |
APP1
a.IR0M1
Bootloader的起始地址: 0x801E000
Bootloader的Size: 0x12000
b.APP1作为更新区,更新的固件存在此处并运行。
APP2
a.IR0M1
Bootloader的起始地址: 0x801E000
Bootloader的Size: 0x12000
b.APP2作为备份区,单片机一上电进入的是APP2,如果进行了固件升级则上电进入APP1,APP2一直不变。
3.总体流程
Bootloader程序设计思路:
单片机上电进入Bootloader,先判断升级标志是否需要升级固件,不需要则判断APP2中断向量表是否正确,后跳转至APP2即备份区;需要就进入等待接收文件状态。
接收完更新文件后,将数据写入APP1地址,清除标志,重启单片机进入APP1。
APP程序设计思路:
系统初始化后,调用__enable_irq();打开中断总开关,因为Bootloader里面关闭了中断总开关。在while(1)中等待接收串口升级的标志。
注意: 在往flash里写数据之前,应先关闭中断__disable_irq();,写完后再开启;
在进入跳转函数之前,将用到的外设反向初始化。
4.FLASH擦除写入过程过程
关中断 __disable_irq(); 解锁 HAL_FLASH_Unlock(); 计算页数 页擦除/写入 HAL_FLASHEx_Erase / HAL_FLASH_Program上锁 HAL_FLASH_Lock();开中断 __enable_irq();
5.Ymodem协议
YModem是一种文件传输的协议,每包数据可以达到1024字节。文件大于32K建议使用STX(1024)。字符 ASCII码16进制
SOH 0x01
STX 0x02
ACK 0x06
NAK 0x15
EOT 0x04
C 0x43
1).起始帧
SOH + 00 + FF + filename + filesize + NULL + CRCH + CRCL
起始帧是文件传输发送端发的第一条重要消息.
filename表示传输文件的文件名.
filesize表示需要传输文件的大小.
CRCH + CRCL 表示整条帧(去掉前三个字节)的CRC16校验.
2).数据帧格式
STX/SOH + [编号] + 编号的反码 + data[0] + data[1] + data[2] + … + CRCH + CRCL
SOH 表示有128个字节, 有的也只用SOH传输数据.
STX 表示有1024个字节.
CRCH + CRCL 表示整条帧(去掉前三个字节)的CRC16校验.
如果传输最后一条字节不足128个字节, 则用1A填充
3).结束帧的数据格式
SOH + 00 + FF + NULL + NULL + … + NULL + CRCH + CRCL
Ymodem协议解释来自:
https://blog.csdn.net/weixin_41294615/article/details/104652105
ST官方例程
https://www.st.com/content/st_com/zh/products/embedded-software/mcu-mpu-embedded-software/stm32-embedded-software/stm32cube-expansion-packages/x-cube-iap-usart.html
解读一下例程:
两个工程 IAP_Binary_Template、IAP_Main,IAP_Binary_Template是APP程序,IAP_Main是BOOT。
BOOT的主函数主要是,判断是进入升级还是直接跳入APP
int main(void)
{ HAL_Init();/* Configure the system clock to 32 MHz */SystemClock_Config();/* Initialize Key Button mounted on STM32L073Z-EVAL board */BSP_PB_Init(BUTTON_TAMPER, BUTTON_MODE_GPIO);/* Test if Key push-button on STM32L073Z-EVAL Board is pressed */if (BSP_PB_GetState(BUTTON_TAMPER) == GPIO_PIN_SET)/* 判断按键是否为按下 */{ /* Initialise Flash */FLASH_If_Init();/* Execute the IAP driver in order to reprogram the Flash */IAP_Init();/* 外设初始化 *//* Display main menu */Main_Menu ();/* 选择菜单 重要 */}/* Keep the user application running */else{/* Test if user code is programmed starting from address "APPLICATION_ADDRESS" */if (((*(__IO uint32_t*)APPLICATION_ADDRESS) & 0x2FFE0000 ) == 0x20000000){/* Jump to user application */JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);JumpToApplication = (pFunction) JumpAddress;/* Initialize user application's Stack Pointer */__set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);JumpToApplication();}}while (1){}
}