STM32 ——bootloader IAP
文章目录
- 1. IAP 简介
- 1.1 程序升级流程
- 1.1.1 程序运行流程原理
- 1.2 单片机User Flash 代码布局
- 1.3 `.hex` 文件转`.bin`
- 2. YModem 协议简介
- 3. APP 应用程序
- 3.1 设置APP 的烧录/运行扇区
- 3.2 在APP 中添加跳转IAP 接口
- 3.3 在IAP 中修改通讯串口
- 4. python 脚本使用
本文使用单片机型号:STM32F104xx
1. IAP 简介
IAP(In Application Programming) 是用户自己的程序在运行过程中对 User Flash 的部分区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信口对产品中的固件程序进行更新升级。为了实现 IAP 功能,系统将分为 bootloader 和 app 两部分。bootloader 部分实现 app 升级功能和跳转,app 部分实现系统核心功能并能触发升级。
1.1 程序升级流程
- 上电后先运行IAP 代码,检查是否需要对APP 部分代码进行更新
- 如果需要更新则执行更新程序 / 如果不需要就转到步骤3
- 设置系统调度,跳转到用户应用程序运行
1.1.1 程序运行流程原理
- IAP、APP 成功烧录到单片机中;
- IAP、APP 代码配置正确;
- 单片机正常上电;
- 运行IAP 程序,检查EEPROM 中指定位置是否为指定标志数据;
如:EEPROM 指定地址为1000 的位置数据是否为
0x1B;
- 是则跳转到IAP 代码,开始烧录/升级APP 代码,代码烧录完成后,EEPROM 中指定位置数据恢复到默认跳转APP 代码的标志数据;
- 否则跳转到APP 代码运行;
另外,在运行APP 代码过程中,输入指定指令,可跳转到步骤4(IAP 代码)
1.2 单片机User Flash 代码布局

1.3 .hex 文件转.bin
在Keil 中下图位置添加代码fromelf.exe --bin -o "$L@L.bin" "#L",编译后,可在.hex 同文件夹下生成.bin 文件;

2. YModem 协议简介
【点击跳转百度百科】YModem 是⼀种⽂件传输的协议,由XModem协议演变⽽来的,每包数据可以达到1024字节,是⼀个⾮常⾼效的⽂件传输协议。
- YModem 协议:
- 起始帧:
SOH + 00 + FF + filename + filesize + NULL + CRCH + CRCL
起始帧是⽂件传输发送端发的第⼀条重要消息.
filename表示传输⽂件的⽂件名.
filesize表示需要传输⽂件的⼤⼩.
CRCH + CRCL 表示整条帧(去掉前三个字节)的CRC16校验. - 数据帧格式:
STX/SOH + [编号] + 编号的反码 + data[0] + data[1] + data[2] + … + CRCH + CRCL
SOH 表示有128个字节, 有的也只⽤SOH传输数据.
STX 表示有1024个字节.
CRCH + CRCL 表示整条帧(去掉前三个字节)的CRC16校验.如果传输最后⼀条字节不⾜128个字节, 则⽤1A填充 - 结束帧的数据格式:
SOH + 00 + FF + NULL + NULL + … + NULL + CRCH + CRCL
- 起始帧:
使用Ymodem协议可以保证我们的传输数据安全,避免因为丢包等原因导致错误的bin(用户程序)烧录到FLASH,导致单片机运行异常跑飞等等
3. APP 应用程序
3.1 设置APP 的烧录/运行扇区
- 在APP 代码初始化前,添加以下代码:
NVIC_SetVectorTable(NVIC_VectTab_FLASH,(0x08003000));
- 在Target 设置中,设置对应的扇区位置:

- 在Debug 设置中,选择擦除扇区:

3.2 在APP 中添加跳转IAP 接口
除了3.1 步骤的操作外,在APP 中也需要添加一个跳转到IAP 到接口,以实现在APP 代码运行中进入IAP 代码升级APP 代码的功能;
- 在串口通讯函数中,添加跳转指令:
if(strcmp("UPLOAD FIRMWARE",ptr)==0){Get_TO_BootLoader();return;}
#define UPLOAD_FLAG_ADDR 1000
void Get_TO_BootLoader(void)
{IIC_24C256_Write_Byte(0XA0,UPLOAD_FLAG_ADDR,4,0X1B); // 在板载EEPROM 指定位置中改变标志数据,使得单片机重启后进入IAP 代码printf("0x%X\r\n",IIC_24C256_Read_Byte(0XA0,UPLOAD_FLAG_ADDR,4)); printf("MCU Into Upload firmware Pass\r\n@_@");soft_reset(); // 单片机重启
}void soft_reset(void)
{//关闭所有中断__set_FAULTMASK(1);//单片机复位NVIC_SystemReset();
}
3.3 在IAP 中修改通讯串口
int main(void)
{int i;IAP_Init(); // <--- 进入IIC_Init(); ...
}
void IAP_Init(void)
{USART_InitTypeDef USART_InitStructure;/* USART resources configuration (Clock, GPIO pins and USART registers) ----*//* USART configured as follow:- BaudRate = 115200 baud - Word Length = 8 Bits- One Stop Bit- No parity- Hardware flow control disabled (RTS and CTS signals)- Receive and transmit enabled*/USART_InitStructure.USART_BaudRate = 115200;USART_InitStructure.USART_WordLength = USART_WordLength_8b;USART_InitStructure.USART_StopBits = USART_StopBits_1;USART_InitStructure.USART_Parity = USART_Parity_No;USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;STM_EVAL_COMInit(COM1, &USART_InitStructure); // <--- 进入STM_EVAL_COMInit 的函数本体}
void STM_EVAL_COMInit(COM_TypeDef COM, USART_InitTypeDef* USART_InitStruct)
{GPIO_InitTypeDef GPIO_InitStructure;/* Enable GPIO clock */RCC_APB2PeriphClockCmd(COM_TX_PORT_CLK[COM] | COM_RX_PORT_CLK[COM] | RCC_APB2Periph_AFIO, ENABLE); // <--- 选择COM_TX_PORT_CLK进入其定义/* Enable UART clock */if (COM == COM1){RCC_APB1PeriphClockCmd(COM_USART_CLK[COM], ENABLE); }else{RCC_APB2PeriphClockCmd(COM_USART_CLK[COM], ENABLE);}/* Configure USART Tx as alternate function push-pull */GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = COM_TX_PIN[COM];GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(COM_TX_PORT[COM], &GPIO_InitStructure);/* Configure USART Rx as input floating */GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_InitStructure.GPIO_Pin = COM_RX_PIN[COM];GPIO_Init(COM_RX_PORT[COM], &GPIO_InitStructure);/* USART configuration */USART_Init(COM_USART[COM], USART_InitStruct);/* Enable USART */USART_Cmd(COM_USART[COM], ENABLE);
}
USART_TypeDef* COM_USART[COMn] = {EVAL_COM1, EVAL_COM2}; // <--- 选择EVAL_COM1进入其定义GPIO_TypeDef* COM_TX_PORT[COMn] = {EVAL_COM1_TX_GPIO_PORT, EVAL_COM2_TX_GPIO_PORT};GPIO_TypeDef* COM_RX_PORT[COMn] = {EVAL_COM1_RX_GPIO_PORT, EVAL_COM2_RX_GPIO_PORT};const uint32_t COM_USART_CLK[COMn] = {EVAL_COM1_CLK, EVAL_COM2_CLK};const uint32_t COM_TX_PORT_CLK[COMn] = {EVAL_COM1_TX_GPIO_CLK, EVAL_COM2_TX_GPIO_CLK};const uint32_t COM_RX_PORT_CLK[COMn] = {EVAL_COM1_RX_GPIO_CLK, EVAL_COM2_RX_GPIO_CLK};const uint16_t COM_TX_PIN[COMn] = {EVAL_COM1_TX_PIN, EVAL_COM2_TX_PIN};const uint16_t COM_RX_PIN[COMn] = {EVAL_COM1_RX_PIN, EVAL_COM2_RX_PIN};
// 修改对应的串口和Pin 引脚
#define EVAL_COM1 USART2 // 如改为串口4,就把该项改为USART4,以下项同理
#define EVAL_COM1_CLK RCC_APB1Periph_USART2
#define EVAL_COM1_TX_PIN GPIO_Pin_2
#define EVAL_COM1_TX_GPIO_PORT GPIOA
#define EVAL_COM1_TX_GPIO_CLK RCC_APB2Periph_GPIOA
#define EVAL_COM1_RX_PIN GPIO_Pin_3
#define EVAL_COM1_RX_GPIO_PORT GPIOA
#define EVAL_COM1_RX_GPIO_CLK RCC_APB2Periph_GPIOA
#define EVAL_COM1_IRQn USART2_IRQn
- 最后编译下载到单片机调试;
4. python 脚本使用
- 待烧录的
.bin文件根据“使用说明”,已特定前缀命名,放置到对应文件夹中; - 打开mac 电脑“终端”,将python 脚本
iap.py拖到终端窗口,敲回车;

- 终端显示以下界面,表示
.bin文件已成功烧录到单片机;

本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
