一、IAP系统架构设计
1.1 Flash分区规划(64KB Flash)
STM32F103C8T6 Flash布局:
┌─────────────────────────────────┐ 0x08000000
│ BootLoader (8KB) │
│ 功能:固件校验、跳转、升级 │
│ 地址:0x08000000 - 0x08001FFF │
├─────────────────────────────────┤ 0x08002000
│ App (52KB) │
│ 功能:主应用程序 │
│ 地址:0x08002000 - 0x0800FFFF │
├─────────────────────────────────┤ 0x08010000
│ App备份区 (4KB) │
│ 功能:升级失败时回滚 │
│ 地址:0x08010000 - 0x08010FFF │
└─────────────────────────────────┘
1.2 系统启动流程
上电复位
↓
BootLoader启动
↓
检查升级标志
↓
├─→ 有升级请求 → 擦除App区 → 接收新固件 → 校验 → 写入Flash
↓
├─→ 无升级请求 → 检查App完整性 → 跳转至App
↓
App运行
↓
收到升级命令 → 设置升级标志 → 软复位 → 进入BootLoader
二、BootLoader源码
2.1 bootloader.h
/**
* @file bootloader.h
* @brief STM32F103C8T6 BootLoader头文件
*/
#ifndef __BOOTLOADER_H
#define __BOOTLOADER_H
#include "stm32f10x.h"
#include <string.h>
/* Flash配置 */
#define BOOTLOADER_ADDR 0x08000000 // BootLoader起始地址
#define APP_ADDR 0x08002000 // App起始地址
#define BACKUP_ADDR 0x08010000 // 备份区起始地址
#define BOOTLOADER_SIZE 0x2000 // 8KB
#define APP_SIZE 0xD000 // 52KB
#define PAGE_SIZE 1024 // Flash页大小
/* 升级标志 */
#define UPGRADE_MAGIC 0x55AA55AA // 升级魔数
#define NORMAL_BOOT 0x00000000 // 正常启动
#define UPGRADE_REQUEST 0x12345678 // 升级请求
#define UPGRADE_SUCCESS 0x87654321 // 升级成功
#define UPGRADE_FAILED 0xABCDEFAB // 升级失败
/* 通信协议 */
#define FRAME_HEAD 0xAA55 // 帧头
#define CMD_START_UPGRADE 0x01 // 开始升级
#define CMD_DATA_PACKET 0x02 // 数据包
#define CMD_END_UPGRADE 0x03 // 结束升级
#define CMD_GET_VERSION 0x04 // 获取版本
#define CMD_JUMP_TO_APP 0x05 // 跳转到App
/* 状态定义 */
typedef enum {
BL_OK = 0,
BL_ERROR,
BL_TIMEOUT,
BL_CRC_ERROR,
BL_FLASH_ERROR,
BL_INVALID_PARAM
} BL_Status;
/* 升级信息结构体 */
typedef struct {
uint32_t magic; // 魔数
uint32_t app_size; // App大小
uint32_t app_crc; // App CRC32校验
uint32_t upgrade_flag; // 升级标志
uint32_t version; // 版本号
} Upgrade_Info_t;
/* 数据包结构 */
typedef struct {
uint16_t head; // 帧头 0xAA55
uint8_t cmd; // 命令
uint16_t length; // 数据长度
uint8_t data[256]; // 数据
uint16_t crc16; // CRC16校验
} Packet_t;
/* 全局变量 */
extern Upgrade_Info_t upgrade_info;
extern uint8_t rx_buffer[264];
extern uint16_t rx_index;
/* 函数声明 */
void BootLoader_Init(void);
BL_Status BootLoader_CheckUpgrade(void);
BL_Status BootLoader_EraseApp(void);
BL_Status BootLoader_WriteFlash(uint32_t addr, uint8_t *data, uint16_t len);
BL_Status BootLoader_VerifyApp(uint32_t addr, uint32_t size, uint32_t crc);
void BootLoader_JumpToApp(void);
BL_Status BootLoader_ProcessPacket(Packet_t *packet);
void BootLoader_SendResponse(uint8_t cmd, uint8_t status);
uint32_t BootLoader_GetVersion(void);
#endif
2.2 bootloader.c
/**
* @file bootloader.c
* @brief BootLoader主程序
*/
#include "bootloader.h"
#include "uart.h"
#include "flash.h"
#include "crc.h"
#include "delay.h"
/* 全局变量 */
Upgrade_Info_t upgrade_info __attribute__((section(".noinit")));
uint8_t rx_buffer[264];
uint16_t rx_index = 0;
uint32_t received_bytes = 0;
uint32_t total_packets = 0;
uint32_t current_packet = 0;
/* 版本信息 */
#define BOOTLOADER_VERSION 0x0100 // V1.0
/* BootLoader初始化 */
void BootLoader_Init(void)
{
/* 系统时钟初始化 */
SystemInit();
/* 延时初始化 */
Delay_Init();
/* 串口初始化(115200-8-N-1) */
UART_Init(115200);
/* Flash初始化 */
Flash_Init();
/* 读取升级信息 */
Flash_Read(APP_ADDR - sizeof(Upgrade_Info_t), (uint8_t *)&upgrade_info, sizeof(Upgrade_Info_t));
/* 打印启动信息 */
UART_SendString("BootLoader V1.0 Started\r\n");
UART_SendString("Press 'u' to enter upgrade mode within 3 seconds...\r\n");
}
/* 检查是否需要升级 */
BL_Status BootLoader_CheckUpgrade(void)
{
uint8_t key;
uint32_t timeout = 3000; // 3秒超时
/* 检查升级标志 */
if (upgrade_info.upgrade_flag == UPGRADE_REQUEST) {
UART_SendString("Upgrade flag detected!\r\n");
return BL_OK;
}
/* 等待用户输入升级命令 */
while (timeout--) {
if (UART_ReceiveByte(&key)) {
if (key == 'u' || key == 'U') {
UART_SendString("Enter upgrade mode...\r\n");
return BL_OK;
}
}
Delay_Ms(1);
}
return BL_ERROR;
}
/* 擦除App区 */
BL_Status BootLoader_EraseApp(void)
{
uint32_t addr;
uint8_t result;
UART_SendString("Erasing App area...\r\n");
/* 擦除App区(从APP_ADDR开始,共52KB) */
for (addr = APP_ADDR; addr < APP_ADDR + APP_SIZE; addr += PAGE_SIZE) {
result = Flash_ErasePage(addr);
if (result != FLASH_OK) {
UART_SendString("Erase failed!\r\n");
return BL_FLASH_ERROR;
}
/* 打印进度 */
if ((addr - APP_ADDR) % (10 * PAGE_SIZE) == 0) {
UART_SendChar('.');
}
}
UART_SendString("\r\nErase completed!\r\n");
return BL_OK;
}
/* 写Flash */
BL_Status BootLoader_WriteFlash(uint32_t addr, uint8_t *data, uint16_t len)
{
uint8_t result;
result = Flash_Write(addr, data, len);
if (result != FLASH_OK) {
return BL_FLASH_ERROR;
}
return BL_OK;
}
/* 校验App */
BL_Status BootLoader_VerifyApp(uint32_t addr, uint32_t size, uint32_t crc)
{
uint32_t calc_crc = 0;
uint8_t buffer[256];
uint32_t i, read_len;
UART_SendString("Verifying App...\r\n");
/* 计算CRC32 */
for (i = 0; i < size; i += 256) {
read_len = (size - i) > 256 ? 256 : (size - i);
Flash_Read(addr + i, buffer, read_len);
calc_crc = CRC32_Update(calc_crc, buffer, read_len);
}
/* 比较CRC */
if (calc_crc != crc) {
UART_SendString("CRC check failed!\r\n");
UART_SendString("Expected: ");
UART_SendHex(crc);
UART_SendString(", Calculated: ");
UART_SendHex(calc_crc);
UART_SendString("\r\n");
return BL_CRC_ERROR;
}
UART_SendString("CRC check passed!\r\n");
return BL_OK;
}
/* 跳转到App */
void BootLoader_JumpToApp(void)
{
typedef void (*pFunction)(void);
pFunction JumpToApplication;
uint32_t JumpAddress;
UART_SendString("Jumping to App...\r\n");
Delay_Ms(100);
/* 关闭所有中断 */
__disable_irq();
/* 检查App栈顶地址是否在合法范围 */
if (((*(__IO uint32_t*)APP_ADDR) & 0x2FFE0000) == 0x20000000) {
/* 设置主堆栈指针 */
__set_MSP(*(__IO uint32_t*)APP_ADDR);
/* 获取复位处理函数地址 */
JumpAddress = *(__IO uint32_t*)(APP_ADDR + 4);
JumpToApplication = (pFunction)JumpAddress;
/* 跳转到App */
JumpToApplication();
}
else {
UART_SendString("Invalid App stack pointer!\r\n");
while (1);
}
}
/* 处理数据包 */
BL_Status BootLoader_ProcessPacket(Packet_t *packet)
{
static uint32_t write_addr = APP_ADDR;
BL_Status status = BL_OK;
switch (packet->cmd) {
case CMD_START_UPGRADE:
/* 开始升级命令 */
if (packet->length != 8) {
return BL_INVALID_PARAM;
}
/* 解析升级信息 */
upgrade_info.app_size = *(uint32_t*)&packet->data[0];
upgrade_info.app_crc = *(uint32_t*)&packet->data[4];
upgrade_info.upgrade_flag = UPGRADE_REQUEST;
upgrade_info.version++;
/* 擦除App区 */
status = BootLoader_EraseApp();
if (status != BL_OK) {
return status;
}
/* 重置写地址 */
write_addr = APP_ADDR;
received_bytes = 0;
total_packets = (upgrade_info.app_size + 255) / 256;
current_packet = 0;
UART_SendString("Upgrade started. Size: ");
UART_SendNumber(upgrade_info.app_size);
UART_SendString(" bytes\r\n");
break;
case CMD_DATA_PACKET:
/* 数据包 */
if (received_bytes + packet->length > upgrade_info.app_size) {
return BL_INVALID_PARAM;
}
/* 写入Flash */
status = BootLoader_WriteFlash(write_addr, packet->data, packet->length);
if (status != BL_OK) {
return status;
}
write_addr += packet->length;
received_bytes += packet->length;
current_packet++;
/* 发送确认 */
BootLoader_SendResponse(CMD_DATA_PACKET, BL_OK);
/* 打印进度 */
if (current_packet % 10 == 0) {
UART_SendChar('.');
}
break;
case CMD_END_UPGRADE:
/* 结束升级命令 */
UART_SendString("\r\nUpgrade finished. Verifying...\r\n");
/* 校验App */
status = BootLoader_VerifyApp(APP_ADDR, upgrade_info.app_size, upgrade_info.app_crc);
if (status != BL_OK) {
upgrade_info.upgrade_flag = UPGRADE_FAILED;
return status;
}
/* 更新升级信息 */
upgrade_info.upgrade_flag = UPGRADE_SUCCESS;
Flash_Write(APP_ADDR - sizeof(Upgrade_Info_t),
(uint8_t *)&upgrade_info, sizeof(Upgrade_Info_t));
UART_SendString("Upgrade success!\r\n");
BootLoader_SendResponse(CMD_END_UPGRADE, BL_OK);
break;
case CMD_GET_VERSION:
/* 获取版本 */
BootLoader_SendResponse(CMD_GET_VERSION, BOOTLOADER_VERSION);
break;
case CMD_JUMP_TO_APP:
/* 跳转到App */
BootLoader_JumpToApp();
break;
default:
return BL_INVALID_PARAM;
}
return BL_OK;
}
/* 发送响应 */
void BootLoader_SendResponse(uint8_t cmd, uint8_t status)
{
Packet_t response;
response.head = FRAME_HEAD;
response.cmd = cmd;
response.length = 1;
response.data[0] = status;
response.crc16 = CRC16_Calc((uint8_t*)&response, 5);
UART_SendData((uint8_t*)&response, 7);
}
/* 获取BootLoader版本 */
uint32_t BootLoader_GetVersion(void)
{
return BOOTLOADER_VERSION;
}
/* 主循环 */
int main(void)
{
Packet_t packet;
uint16_t crc16;
BL_Status status;
/* 初始化 */
BootLoader_Init();
/* 检查是否需要升级 */
status = BootLoader_CheckUpgrade();
if (status == BL_OK) {
/* 进入升级模式 */
UART_SendString("Waiting for firmware...\r\n");
while (1) {
/* 接收数据包 */
if (UART_ReceivePacket(&packet)) {
/* 校验CRC16 */
crc16 = CRC16_Calc((uint8_t*)&packet, packet.length + 5);
if (crc16 != packet.crc16) {
BootLoader_SendResponse(packet.cmd, BL_CRC_ERROR);
continue;
}
/* 处理数据包 */
status = BootLoader_ProcessPacket(&packet);
if (status != BL_OK) {
BootLoader_SendResponse(packet.cmd, status);
break;
}
}
}
}
else {
/* 直接跳转到App */
BootLoader_JumpToApp();
}
while (1);
}
三、Flash驱动(flash.c)
/**
* @file flash.c
* @brief STM32F103 Flash操作驱动
*/
#include "flash.h"
/* Flash解锁 */
void Flash_Init(void)
{
/* 解锁Flash */
FLASH->KEYR = 0x45670123;
FLASH->KEYR = 0xCDEF89AB;
}
/* 擦除页 */
uint8_t Flash_ErasePage(uint32_t addr)
{
/* 等待Flash就绪 */
while (FLASH->SR & FLASH_SR_BSY);
/* 检查地址对齐 */
if (addr % PAGE_SIZE != 0) {
return FLASH_ERROR;
}
/* 设置页擦除 */
FLASH->CR |= FLASH_CR_PER;
FLASH->AR = addr;
FLASH->CR |= FLASH_CR_STRT;
/* 等待擦除完成 */
while (FLASH->SR & FLASH_SR_BSY);
/* 清除标志 */
FLASH->SR |= FLASH_SR_EOP;
FLASH->CR &= ~FLASH_CR_PER;
return FLASH_OK;
}
/* 写半字(16位) */
uint8_t Flash_WriteHalfWord(uint32_t addr, uint16_t data)
{
/* 等待Flash就绪 */
while (FLASH->SR & FLASH_SR_BSY);
/* 设置编程 */
FLASH->CR |= FLASH_CR_PG;
/* 写入半字 */
*(__IO uint16_t*)addr = data;
/* 等待写入完成 */
while (FLASH->SR & FLASH_SR_BSY);
/* 清除标志 */
FLASH->SR |= FLASH_SR_EOP;
FLASH->CR &= ~FLASH_CR_PG;
/* 校验 */
if (*(__IO uint16_t*)addr != data) {
return FLASH_ERROR;
}
return FLASH_OK;
}
/* 写数据(任意长度) */
uint8_t Flash_Write(uint32_t addr, uint8_t *data, uint16_t len)
{
uint16_t i;
uint16_t halfword;
uint8_t result;
for (i = 0; i < len; i += 2) {
/* 组合成半字 */
halfword = data[i];
if (i + 1 < len) {
halfword |= (data[i + 1] << 8);
}
else {
halfword |= 0xFF00; // 补齐
}
/* 写入Flash */
result = Flash_WriteHalfWord(addr + i, halfword);
if (result != FLASH_OK) {
return FLASH_ERROR;
}
}
return FLASH_OK;
}
/* 读数据 */
void Flash_Read(uint32_t addr, uint8_t *buffer, uint16_t len)
{
memcpy(buffer, (void*)addr, len);
}
四、App端实现(关键点)
4.1 App工程配置
修改链接脚本(.ld文件):
/* STM32F103C8T6 链接脚本 */
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K
FLASH (rx) : ORIGIN = 0x08002000, LENGTH = 52K /* 从8KB开始 */
}
/* 中断向量表偏移 */
SCB->VTOR = FLASH_BASE | 0x2000; /* 偏移8KB */
4.2 App中设置升级标志
/* 在App中触发升级 */
void App_RequestUpgrade(void)
{
Upgrade_Info_t upgrade_info;
/* 读取当前升级信息 */
Flash_Read(APP_ADDR - sizeof(Upgrade_Info_t),
(uint8_t *)&upgrade_info, sizeof(Upgrade_Info_t));
/* 设置升级标志 */
upgrade_info.upgrade_flag = UPGRADE_REQUEST;
/* 写入Flash */
Flash_Write(APP_ADDR - sizeof(Upgrade_Info_t),
(uint8_t *)&upgrade_info, sizeof(Upgrade_Info_t));
/* 软复位 */
NVIC_SystemReset();
}
4.3 App中断向量表重映射
/* 在App的main函数开头添加 */
int main(void)
{
/* 重映射中断向量表 */
SCB->VTOR = FLASH_BASE | 0x2000; /* 偏移8KB */
/* 其余初始化... */
}
五、通信协议实现
5.1 串口接收协议
/* 串口接收数据包 */
uint8_t UART_ReceivePacket(Packet_t *packet)
{
static uint8_t state = 0;
static uint16_t index = 0;
uint8_t byte;
while (UART_ReceiveByte(&byte)) {
switch (state) {
case 0: /* 等待帧头低字节 */
if (byte == (FRAME_HEAD & 0xFF)) {
state = 1;
index = 0;
}
break;
case 1: /* 等待帧头高字节 */
if (byte == (FRAME_HEAD >> 8)) {
state = 2;
}
else {
state = 0;
}
break;
case 2: /* 命令 */
packet->cmd = byte;
state = 3;
break;
case 3: /* 长度低字节 */
packet->length = byte;
state = 4;
break;
case 4: /* 长度高字节 */
packet->length |= (byte << 8);
if (packet->length > 256) {
state = 0; /* 长度错误 */
}
else {
state = 5;
index = 0;
}
break;
case 5: /* 数据 */
packet->data[index++] = byte;
if (index >= packet->length) {
state = 6;
}
break;
case 6: /* CRC16低字节 */
packet->crc16 = byte;
state = 7;
break;
case 7: /* CRC16高字节 */
packet->crc16 |= (byte << 8);
state = 0;
return 1; /* 成功接收一包 */
default:
state = 0;
break;
}
}
return 0;
}
参考代码 STM32F103C8T6实现IAP、BootLoader功能 www.youwenfan.com/contentali/56300.html
六、升级工具(Python脚本)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
STM32F103 IAP升级工具
"""
import serial
import time
import binascii
import crcmod
class STM32_IAP:
def __init__(self, port, baudrate=115200):
self.ser = serial.Serial(port, baudrate, timeout=1)
self.crc16 = crcmod.mkCrcFun(0x18005, rev=True, initCrc=0xFFFF)
def send_packet(self, cmd, data=b''):
"""发送数据包"""
head = b'\x55\xAA'
length = len(data).to_bytes(2, 'little')
packet = head + bytes([cmd]) + length + data
crc = self.crc16(packet).to_bytes(2, 'little')
packet += crc
self.ser.write(packet)
return self.wait_response()
def wait_response(self, timeout=5):
"""等待响应"""
start = time.time()
while time.time() - start < timeout:
if self.ser.in_waiting >= 7:
response = self.ser.read(7)
if response[0] == 0x55 and response[1] == 0xAA:
return response[4] # 返回状态字节
return None
def upgrade_firmware(self, bin_file):
"""升级固件"""
print(f"Upgrading {bin_file}...")
# 读取bin文件
with open(bin_file, 'rb') as f:
firmware = f.read()
# 发送开始升级命令
size = len(firmware).to_bytes(4, 'little')
crc = self.crc32(firmware).to_bytes(4, 'little')
self.send_packet(0x01, size + crc)
# 分包发送数据
packet_size = 256
for i in range(0, len(firmware), packet_size):
chunk = firmware[i:i+packet_size]
self.send_packet(0x02, chunk)
print(f"Progress: {(i+len(chunk))/len(firmware)*100:.1f}%")
# 发送结束命令
self.send_packet(0x03)
print("Upgrade completed!")
if __name__ == "__main__":
iap = STM32_IAP('COM3', 115200)
iap.upgrade_firmware('app.bin')
七、Keil工程配置
7.1 BootLoader工程设置
Target:
- Device: STM32F103C8T6
- Clock: 8MHz HSE → 72MHz PLL
Linker:
- IROM1: 0x08000000, 0x2000 (8KB)
- IRAM1: 0x20000000, 0x5000 (20KB)
Output:
- Name of Executable: bootloader
- Create HEX File: Yes
7.2 App工程设置
Target:
- Device: STM32F103C8T6
- Clock: 8MHz HSE → 72MHz PLL
Linker:
- IROM1: 0x08002000, 0xD000 (52KB)
- IRAM1: 0x20000000, 0x5000 (20KB)
Output:
- Name of Executable: app
- Create HEX File: Yes
八、调试与测试
8.1 测试方法
- 编译BootLoader → 烧录到0x08000000
- 编译App → 生成app.bin
- 运行升级工具 → 发送app.bin
- 观察串口输出 → 确认升级成功
- 重启设备 → 验证App正常运行
8.2 常见问题
| 问题 | 原因 | 解决 |
|---|---|---|
| 跳转失败 | VTOR未设置 | 在App中设置SCB->VTOR = 0x08002000 |
| 升级卡死 | 看门狗未关闭 | 升级前关闭IWDG |
| CRC错误 | 字节序问题 | 统一使用小端格式 |
| Flash写失败 | 未解锁 | 先执行FLASH_Unlock() |
九、安全增强(可选)
/* 添加AES加密 */
#include "aes.h"
/* 解密固件 */
void Decrypt_Firmware(uint8_t *data, uint32_t len)
{
AES_CTX ctx;
uint8_t key[16] = {
0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,
0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10};
AES_Init(&ctx, key, 128);
AES_Decrypt(&ctx, data, len);
}