文章目录
- 一、设计思路
- **整体框架设计**
- **各文件/模块功能解析**
- 1. `main.c`(主程序入口,核心控制)
- 2. 隐含的核心模块(框架中未展示但必备)
- **设计亮点**
- 二、代码
- bootloader.h
- bootloader.c
- flash.c
- main.c
一、设计思路
整体框架设计
该Bootloader采用分层模块化架构,核心逻辑可分为入口控制层、核心功能层和硬件适配层,整体流程如下:
- 系统初始化 → 2. 检查启动条件(进入Bootloader还是跳转应用) → 3. 执行对应流程(Bootloader模式/应用程序)
- 内置故障处理机制,确保极端情况下系统可恢复
各文件/模块功能解析
1. main.c
(主程序入口,核心控制)
这是Bootloader的入口文件,负责统筹整个启动流程,关键功能包括:
-
系统初始化:
- 调用
system_init()
:完成单片机底层硬件初始化(如时钟、GPIO、中断向量表设置等),为Bootloader运行提供基础环境。 - 调用
bootloader_init()
:初始化Bootloader核心组件(通信模块、Flash驱动、校验模块等)。
- 调用
-
启动条件判断:
bootloader_check_enter_condition()
:决定系统进入Bootloader模式还是直接启动应用程序。
常见判断条件:按键触发(用户主动升级)、应用程序校验失败(自动进入修复模式)、特定寄存器标志(远程升级指令)等。
-
分支执行:
- 若需进入Bootloader:调用
bootloader_run()
进入升级主循环(接收固件、校验、写入Flash等)。 - 若无需进入:调用
bootloader_jump_to_app()
跳转到用户应用程序(核心功能,需关闭Bootloader占用的外设和中断,确保应用正常运行)。
- 若需进入Bootloader:调用
-
故障兜底:
- 重定义
HardFault_Handler()
:当系统发生致命错误(如硬件故障、应用程序崩溃)时,强制进入Bootloader模式,避免设备变砖,提高可靠性。
- 重定义
2. 隐含的核心模块(框架中未展示但必备)
根据Bootloader的功能描述,完整框架还包含以下关键模块(通常在 bootloader.h
及对应源文件中实现):
-
通信模块:
- 支持UART、SPI等多种接口,负责与上位机(如PC工具、网关)通信,接收升级命令和固件数据。
- 实现数据帧解析、超时重传、校验和等机制,确保固件传输可靠(尤其在工业环境等干扰较强的场景)。
-
Flash操作模块:
- 封装Flash擦除、写入、读取等底层操作,适配不同单片机的Flash特性(如扇区大小、保护机制)。
- 包含擦写失败重试逻辑,避免因瞬时干扰导致Flash损坏。
-
固件校验模块:
- 采用CRC32等算法验证接收到的固件完整性,防止因传输错误导致的升级失败。
- 支持版本检查功能,避免降级或重复升级(可根据需求开启)。
-
安全与保护模块:
- 内存边界检查:防止固件写入超出应用程序分区的地址(避免覆盖Bootloader自身)。
- 可选加密/解密功能:对传输的固件进行加密,防止固件被窃取或篡改(适合商业设备)。
-
失败恢复机制:
- 双分区设计:预留两个应用程序分区(当前分区+备份分区),升级失败时自动回滚到备份分区。
- 升级标志位:在Flash中设置升级状态标志(如“升级中”“升级完成”),若中途断电,重启后可根据标志位恢复流程。
设计亮点
-
高可靠性:
- 故障兜底:硬错误中断强制进入Bootloader,避免设备无法启动。
- 多重校验:从通信到写入全流程校验,减少升级失败概率。
-
多场景适配:
- 多通信接口:支持UART(有线)、SPI(外设扩展)等,可根据硬件场景选择。
- 灵活的启动条件:兼顾用户主动升级和自动修复(如应用损坏时)。
-
可扩展性:
- 模块化设计:各功能模块解耦,便于添加新通信方式(如CAN、以太网)或安全功能(如加密)。
- 硬件无关性:核心逻辑与硬件分离,移植到不同单片机时只需修改底层驱动。
该框架适合工业控制、物联网设备等对稳定性要求高的场景,通过分层设计和容错机制,平衡了可靠性与灵活性。实际使用时,需根据具体单片机型号(如STM32、GD32等)适配Flash驱动和外设初始化代码。
二、代码
bootloader.h
#ifndef BOOTLOADER_H
#define BOOTLOADER_H#include <stdint.h>
#include <stdbool.h>
#include <string.h>// 配置参数 - 根据实际硬件修改
#define APP_START_ADDR 0x08008000 // 应用程序起始地址
#define BOOTLOADER_VERSION "1.2.0" // Bootloader版本
#define MAX_FIRMWARE_SIZE (512 * 1024) // 最大固件大小
#define COMM_BUFFER_SIZE 1024 // 通信缓冲区大小
#define CRC32_POLYNOMIAL 0xEDB88320UL // CRC32多项式// 固件信息结构体
typedef struct {uint32_t magic; // 魔术字,用于验证uint32_t firmware_size; // 固件大小uint32_t crc32; // 固件CRC32校验值uint8_t version[16]; // 固件版本uint8_t reserved[12]; // 预留空间
} firmware_info_t;// 通信命令
typedef enum {CMD_GET_VERSION = 0x01, // 获取版本信息CMD_START_UPDATE = 0x02, // 开始更新CMD_SEND_DATA = 0x03, // 发送数据CMD_VERIFY = 0x04, // 验证固件CMD_BOOT_APP = 0x05, // 启动应用程序CMD_ERASE = 0x06, // 擦除FlashCMD_NACK = 0xFF, // 否定应答CMD_ACK = 0x00 // 肯定应答
} comm_command_t;// 升级状态
typedef enum {BOOT_IDLE = 0, // 空闲状态BOOT_WAITING, // 等待数据BOOT_RECEIVING, // 接收中BOOT_VERIFYING, // 验证中BOOT_FLASHING, // 烧写中BOOT_COMPLETE, // 完成BOOT_ERROR // 错误
} boot_state_t;// 通信接口类型
typedef enum {COMM_UART = 0, // UART通信COMM_SPI = 1 // SPI通信
} comm_interface_t;// 错误代码
typedef enum {ERROR_NONE = 0, // 无错误ERROR_CRC = 1, // CRC错误ERROR_SIZE = 2, // 大小错误ERROR_FLASH = 3, // Flash错误ERROR_COMM = 4, // 通信错误ERROR_VERIFY = 5, // 验证错误ERROR_INVALID_CMD = 6 // 无效命令
} error_code_t;// 函数声明
void bootloader_init(void);
bool bootloader_check_enter_condition(void);
void bootloader_run(void);
void bootloader_jump_to_app(void);
uint32_t crc32_calculate(const uint8_t *data, uint32_t length);
error_code_t flash_erase_sector(uint32_t address);
error_code_t flash_write_data(uint32_t address, const uint8_t *data, uint32_t length);#endif // BOOTLOADER_H
bootloader.c
#include "bootloader.h"
#include "uart.h"
#include "spi.h"
#include "flash.h"
#include "gpio.h"
#include "timer.h"// 全局变量
static boot_state_t current_state = BOOT_IDLE;
static comm_interface_t current_interface = COMM_UART;
static uint8_t comm_buffer[COMM_BUFFER_SIZE];
static uint8_t *firmware_buffer = (uint8_t *)APP_START_ADDR; // 固件缓冲区
static firmware_info_t current_firmware_info;
static uint32_t received_bytes = 0;
static uint32_t timeout_counter = 0;// 检查是否进入Bootloader模式
bool bootloader_check_enter_condition(void) {// 条件1: 特定引脚被拉低if (gpio_read_boot_pin() == 0) {return true;}// 条件2: 应用程序区域为空if (*(volatile uint32_t *)APP_START_ADDR == 0xFFFFFFFF) {return true;}// 条件3: 应用程序请求更新 (通过特定内存位置标记)volatile uint32_t *update_flag = (volatile uint32_t *)0x20000000; // 示例地址if (*update_flag == 0xDEADBEEF) {*update_flag = 0x00000000; // 清除标志return true;}return false;
}// 初始化Bootloader
void bootloader_init(void) {// 初始化硬件gpio_init();uart_init(115200); // 默认UART波特率spi_init();flash_init();timer_init(10); // 10ms定时器// 初始化状态current_state = BOOT_IDLE;received_bytes = 0;timeout_counter = 0;memset(¤t_firmware_info, 0, sizeof(firmware_info_t));// 输出启动信息uart_send_string("Bootloader " BOOTLOADER_VERSION " started\r\n");uart_send_string("Press and hold BOOT pin to enter update mode\r\n");
}// CRC32计算
uint32_t crc32_calculate(const uint8_t *data, uint32_t length) {uint32_t crc = 0xFFFFFFFFUL;for (uint32_t i = 0; i < length; i++) {crc ^= data[i];for (uint8_t j = 0; j < 8; j++) {if (crc & 1) {crc = (crc >> 1) ^ CRC32_POLYNOMIAL;} else {crc >>= 1;}}}return crc ^ 0xFFFFFFFFUL;
}// 发送响应
static void send_response(comm_command_t cmd, error_code_t error) {uint8_t response[3] = {0xAA, (uint8_t)cmd, (uint8_t)error};if (current_interface == COMM_UART) {uart_send_bytes(response, 3);} else {spi_send_bytes(response, 3);}
}// 处理接收到的命令
static void process_command(uint8_t *data, uint16_t length) {if (length < 1) {send_response(CMD_NACK, ERROR_INVALID_CMD);return;}comm_command_t cmd = (comm_command_t)data[0];error_code_t error = ERROR_NONE;switch (cmd) {case CMD_GET_VERSION: {// 发送Bootloader版本uint8_t response[32];response[0] = 0xAA;response[1] = CMD_GET_VERSION;strncpy((char *)&response[2], BOOTLOADER_VERSION, sizeof(response) - 2);response[sizeof(response) - 1] = '\0';if (current_interface == COMM_UART) {uart_send_bytes(response, strlen((char *)response) + 2);} else {spi_send_bytes(response, strlen((char *)response) + 2);}break;}case CMD_START_UPDATE: {if (length < sizeof(firmware_info_t) + 1) {error = ERROR_INVALID_CMD;break;}// 复制固件信息memcpy(¤t_firmware_info, &data[1], sizeof(firmware_info_t));// 检查魔术字if (current_firmware_info.magic != 0x12345678) {error = ERROR_VERIFY;break;}// 检查固件大小if (current_firmware_info.firmware_size > MAX_FIRMWARE_SIZE) {error = ERROR_SIZE;break;}// 擦除应用程序区域error = flash_erase_sector(APP_START_ADDR);if (error == ERROR_NONE) {current_state = BOOT_WAITING;received_bytes = 0;uart_send_string("Ready to receive firmware...\r\n");}break;}case CMD_SEND_DATA: {if (current_state != BOOT_WAITING && current_state != BOOT_RECEIVING) {error = ERROR_INVALID_CMD;break;}if (length < 2) { // 至少1字节命令 + 1字节数据error = ERROR_COMM;break;}uint16_t data_len = length - 1; // 减去命令字节// 检查是否超出固件大小if (received_bytes + data_len > current_firmware_info.firmware_size) {error = ERROR_SIZE;break;}// 写入数据到缓冲区memcpy(&firmware_buffer[received_bytes], &data[1], data_len);received_bytes += data_len;current_state = BOOT_RECEIVING;timeout_counter = 0; // 重置超时计数器uart_send_stringf("Received %d/%d bytes\r\n", received_bytes, current_firmware_info.firmware_size);break;}case CMD_VERIFY: {if (current_state != BOOT_RECEIVING) {error = ERROR_INVALID_CMD;break;}// 检查是否接收完整if (received_bytes != current_firmware_info.firmware_size) {error = ERROR_SIZE;break;}// 计算CRC32并验证uint32_t calculated_crc = crc32_calculate(firmware_buffer, current_firmware_info.firmware_size);if (calculated_crc != current_firmware_info.crc32) {error = ERROR_CRC;break;}current_state = BOOT_VERIFYING;uart_send_string("Firmware verified successfully\r\n");break;}case CMD_BOOT_APP: {if (current_state == BOOT_VERIFYING || current_state == BOOT_COMPLETE) {uart_send_string("Jumping to application...\r\n");send_response(CMD_BOOT_APP, ERROR_NONE);// 短延时确保数据发送完成for (volatile int i = 0; i < 1000000; i++);bootloader_jump_to_app();} else {error = ERROR_INVALID_CMD;}break;}default:error = ERROR_INVALID_CMD;break;}send_response(cmd, error);
}// 处理通信数据
static void process_communication(void) {uint16_t bytes_received = 0;// 检查UART数据if (current_interface == COMM_UART) {bytes_received = uart_receive_bytes(comm_buffer, COMM_BUFFER_SIZE);}// 检查SPI数据else if (current_interface == COMM_SPI) {bytes_received = spi_receive_bytes(comm_buffer, COMM_BUFFER_SIZE);}if (bytes_received > 0) {process_command(comm_buffer, bytes_received);timeout_counter = 0; // 重置超时计数器}
}// 定时器回调函数 - 处理超时
void timer_callback(void) {if (current_state != BOOT_IDLE) {timeout_counter++;// 5秒超时 (500 * 10ms)if (timeout_counter >= 500) {uart_send_string("Timeout occurred\r\n");current_state = BOOT_IDLE;timeout_counter = 0;}}
}// 运行Bootloader主循环
void bootloader_run(void) {uart_send_string("Entering bootloader mode\r\n");while (1) {// 处理通信process_communication();// 状态机处理switch (current_state) {case BOOT_VERIFYING:// 验证完成,等待启动应用程序current_state = BOOT_COMPLETE;uart_send_string("Update completed. Send BOOT_APP command to start application\r\n");break;case BOOT_ERROR:// 错误状态,重置uart_send_string("Error occurred. Resetting...\r\n");current_state = BOOT_IDLE;break;default:break;}// 空闲状态下检查是否自动启动应用程序if (current_state == BOOT_IDLE) {timeout_counter++;// 3秒超时自动启动应用 (300 * 10ms)if (timeout_counter >= 300) {uart_send_string("Timeout, booting application...\r\n");bootloader_jump_to_app();}}}
}// 跳转到应用程序
void bootloader_jump_to_app(void) {// 检查应用程序是否存在if ((*(volatile uint32_t *)APP_START_ADDR) & 0x2FFE0000) {// 关闭所有外设uart_deinit();spi_deinit();timer_deinit();// 设置栈指针和程序计数器void (*app_reset_handler)(void) = (void *)*(volatile uint32_t *)(APP_START_ADDR + 4);// 设置主栈指针__set_MSP(*(volatile uint32_t *)APP_START_ADDR);// 跳转到应用程序app_reset_handler();}// 如果应用程序不存在,停留在Bootloaderuart_send_string("No valid application found\r\n");while (1);
}
flash.c
#include "bootloader.h"
#include "stm32f4xx_hal.h" // 根据实际MCU修改// 初始化Flash
void flash_init(void) {// 初始化Flash外设HAL_FLASH_Unlock();// 清除所有标志__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);HAL_FLASH_Lock();
}// 擦除Flash扇区
error_code_t flash_erase_sector(uint32_t address) {// 检查地址是否在应用程序区域if (address < APP_START_ADDR) {return ERROR_FLASH;}FLASH_EraseInitTypeDef erase_init;uint32_t sector_error;error_code_t result = ERROR_NONE;// 解锁Flashif (HAL_FLASH_Unlock() != HAL_OK) {return ERROR_FLASH;}// 确定要擦除的扇区// 注意:不同MCU的扇区划分不同,需要根据实际情况修改if (address < 0x08020000) {erase_init.Sector = FLASH_SECTOR_2;} else if (address < 0x08040000) {erase_init.Sector = FLASH_SECTOR_3;} else {// 根据实际情况添加更多扇区判断erase_init.Sector = FLASH_SECTOR_4;}erase_init.TypeErase = FLASH_TYPEERASE_SECTORS;erase_init.VoltageRange = FLASH_VOLTAGE_RANGE_3;erase_init.NbSectors = 1; // 可以根据需要擦除多个扇区// 执行擦除if (HAL_FLASHEx_Erase(&erase_init, §or_error) != HAL_OK) {result = ERROR_FLASH;}// 锁定FlashHAL_FLASH_Lock();return result;
}// 写入数据到Flash
error_code_t flash_write_data(uint32_t address, const uint8_t *data, uint32_t length) {// 检查地址和长度if (address < APP_START_ADDR || (address + length) > (APP_START_ADDR + MAX_FIRMWARE_SIZE)) {return ERROR_FLASH;}error_code_t result = ERROR_NONE;uint32_t i = 0;// 解锁Flashif (HAL_FLASH_Unlock() != HAL_OK) {return ERROR_FLASH;}// 按字写入数据while (i < length) {uint32_t data_word;// 处理最后几个字节(不足4字节)if (length - i < 4) {data_word = 0;memcpy(&data_word, &data[i], length - i);} else {memcpy(&data_word, &data[i], 4);}// 写入数据if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, address + i, data_word) != HAL_OK) {result = ERROR_FLASH;break;}i += 4;}// 锁定FlashHAL_FLASH_Lock();return result;
}
main.c
#include "bootloader.h"
#include "system.h"int main(void) {// 初始化系统system_init();// 初始化Bootloaderbootloader_init();// 检查是否需要进入Bootloader模式if (bootloader_check_enter_condition()) {// 进入Bootloader主循环bootloader_run();} else {// 直接跳转到应用程序bootloader_jump_to_app();}// 正常情况下不会到达这里while (1);
}// 重定向异常处理到Bootloader
void HardFault_Handler(void) {// 发生硬错误时,尝试进入Bootloaderbootloader_init();bootloader_run();while (1);
}