# Bootloader移植指南 本文档详细介绍如何将Bootloader移植到不同的STM32系列或其他MCU平台。 --- ## 目录 - [移植概述](#移植概述) - [移植步骤](#移植步骤) - [Flash驱动移植](#flash驱动移植) - [UART驱动移植](#uart驱动移植) - [配置适配](#配置适配) - [STM32F1移植示例](#stm32f1移植示例) - [其他MCU移植](#其他mcu移植) - [移植验证](#移植验证) - [常见问题](#常见问题) --- ## 移植概述 ### 需要移植的内容 Bootloader采用分层设计,只有以下部分需要移植: | 模块 | 文件 | 说明 | |------|------|------| | Flash驱动 | `port//flash_port.c` | Flash擦写读操作 | | UART驱动 | `port//uart_port.c` | 串口收发操作 | **其他模块(无需移植):** - 协议处理模块 - AES加密模块 - CRC校验模块 - 固件管理模块 - 跳转逻辑模块 ### BSP抽象层接口 移植前,理解BSP抽象层定义的接口: #### Flash接口 (bsp_flash.h) ```c typedef struct { int (*init)(void); // 初始化Flash int (*erase)(uint32_t addr, uint32_t size); // 擦除指定区域 int (*write)(uint32_t addr, const uint8_t *data, uint32_t len); // 写入数据 int (*read)(uint32_t addr, uint8_t *data, uint32_t len); // 读取数据 bool (*verify)(uint32_t addr, const uint8_t *data, uint32_t len); // 校验数据 } bsp_flash_ops_t; ``` #### UART接口 (bsp_uart.h) ```c typedef struct { int (*init)(uint32_t baudrate); // 初始化串口 int (*send)(const uint8_t *data, uint32_t len); // 发送数据 int (*recv)(uint8_t *data, uint32_t len, uint32_t timeout); // 接收数据 void (*deinit)(void); // 关闭串口 } bsp_uart_ops_t; ``` --- ## 移植步骤 ### 步骤1: 创建移植目录 ```bash cd extensions/bootloader/port # 复制移植模板 cp -r port_template # 例如: cp -r port_template stm32f1xx ``` ### 步骤2: 实现Flash驱动 编辑 `port//flash_port.c`,实现以下函数: ```c int flash_port_init(void) { // 解锁Flash // 配置Flash参数 // 返回成功/失败 } int flash_port_erase(uint32_t addr, uint32_t size) { // 计算需要擦除的扇区 // 逐个擦除扇区 // 返回成功/失败 } int flash_port_write(uint32_t addr, const uint8_t *data, uint32_t len) { // 写入数据到Flash // 注意地址对齐和数据长度 // 返回成功/失败 } int flash_port_read(uint32_t addr, uint8_t *data, uint32_t len) { // 从Flash读取数据 // 可以直接内存拷贝 // 返回成功/失败 } bool flash_port_verify(uint32_t addr, const uint8_t *data, uint32_t len) { // 读取Flash数据 // 与原始数据比较 // 返回是否一致 } ``` ### 步骤3: 实现UART驱动 编辑 `port//uart_port.c`,实现以下函数: ```c int uart_port_init(uint32_t baudrate) { // 配置UART参数: 波特率、数据位、停止位、校验位 // 初始化UART硬件 // 开启接收中断或DMA // 返回成功/失败 } int uart_port_send(const uint8_t *data, uint32_t len) { // 发送数据 // 可以使用HAL_UART_Transmit或自定义实现 // 等待发送完成 // 返回成功/失败 } int uart_port_recv(uint8_t *data, uint32_t len, uint32_t timeout) { // 接收数据 // 可以使用HAL_UART_Receive或中断缓冲 // 支持超时机制 // 返回接收的字节数或错误 } void uart_port_deinit(void) { // 关闭UART // 清除缓冲区 } ``` ### 步骤4: 注册BSP操作 在 `port//port.c` 中注册: ```c #include "bsp_flash.h" #include "bsp_uart.h" // Flash操作实例 static const bsp_flash_ops_t flash_ops = { .init = flash_port_init, .erase = flash_port_erase, .write = flash_port_write, .read = flash_port_read, .verify = flash_port_verify, }; // UART操作实例 static const bsp_uart_ops_t uart_ops = { .init = uart_port_init, .send = uart_port_send, .recv = uart_port_recv, .deinit = uart_port_deinit, }; // 初始化移植层 int port_init(void) { bsp_flash_register(&flash_ops); bsp_uart_register(&uart_ops); return 0; } ``` ### 步骤5: 修改CMakeLists.txt 编辑 `extensions/bootloader/CMakeLists.txt`: ```cmake # 设置MCU系列 set(MCU_FAMILY "" CACHE STRING "MCU family") # 添加移植层源文件 target_sources(bootloader PRIVATE port/${MCU_FAMILY}/flash_port.c port/${MCU_FAMILY}/uart_port.c ) # 添加头文件路径 target_include_directories(bootloader PUBLIC port/${MCU_FAMILY} ) ``` --- ## Flash驱动移植 ### STM32 Flash特性 不同STM32系列的Flash特性差异: | 系列 | Flash大小 | 扇区大小 | 页大小 | |------|----------|---------|--------| | STM32F1 | 最大512KB | 1KB/2KB/4KB | - | | STM32F4 | 最大2MB | 16KB/64KB/128KB | - | | STM32F7 | 最大2MB | 32KB/128KB/256KB | - | | STM32L4 | 最大512KB | 2KB | 256B | ### STM32F4 Flash详解 STM32F407ZGTx的Flash布局: ``` 扇区 地址范围 大小 用途 ───────────────────────────────────── 0 0x08000000 16KB Bootloader 1 0x08004000 16KB Bootloader 2 0x08008000 16KB App开始 3 0x0800C000 16KB App 4 0x08010000 64KB App 5 0x08020000 128KB App 6 0x08040000 128KB App 7 0x08060000 128KB App 8-11 0x08080000+ 128KB Reserved ``` ### Flash擦除实现 ```c int flash_port_erase(uint32_t addr, uint32_t size) { HAL_FLASH_Unlock(); // 计算起始和结束扇区 uint32_t start_sector = flash_get_sector(addr); uint32_t end_sector = flash_get_sector(addr + size - 1); // 清除错误标志 FLASH->SR = FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR; // 逐个擦除扇区 for (uint32_t sector = start_sector; sector <= end_sector; sector++) { FLASH_Erase_Sector(sector, FLASH_VOLTAGE_RANGE_3); // 等待擦除完成 while (FLASH->SR & FLASH_FLAG_BSY); // 检查错误 if (FLASH->SR & FLASH_FLAG_EOP) { FLASH->SR = FLASH_FLAG_EOP; } } HAL_FLASH_Lock(); return 0; } // 获取扇区号 (STM32F4xx) static uint32_t flash_get_sector(uint32_t addr) { addr -= FLASH_BASE; // 0x08000000 if (addr < 0x4000) return FLASH_SECTOR_0; // 16KB else if (addr < 0x8000) return FLASH_SECTOR_1; // 16KB else if (addr < 0xC000) return FLASH_SECTOR_2; // 16KB else if (addr < 0x10000) return FLASH_SECTOR_3; // 16KB else if (addr < 0x20000) return FLASH_SECTOR_4; // 64KB else if (addr < 0x40000) return FLASH_SECTOR_5; // 128KB else if (addr < 0x60000) return FLASH_SECTOR_6; // 128KB else if (addr < 0x80000) return FLASH_SECTOR_7; // 128KB else return FLASH_SECTOR_8; // 128KB+ } ``` ### Flash写入实现 STM32F4只能按32位写入: ```c int flash_port_write(uint32_t addr, const uint8_t *data, uint32_t len) { HAL_FLASH_Unlock(); uint32_t written = 0; // 按字(4字节)写入 while (written < len) { uint32_t word_data; // 构造32位数据 if (len - written >= 4) { word_data = *((uint32_t*)(data + written)); } else { // 处理不足4字节的情况 word_data = 0; for (uint32_t i = 0; i < len - written; i++) { word_data |= (data[written + i] << (i * 8)); } } // 写入Flash if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr + written, word_data) != HAL_OK) { HAL_FLASH_Lock(); return -1; } written += 4; } HAL_FLASH_Lock(); return 0; } ``` ### Flash读取实现 Flash读取可以直接内存访问: ```c int flash_port_read(uint32_t addr, uint8_t *data, uint32_t len) { // Flash可直接内存读取 memcpy(data, (uint8_t*)addr, len); return 0; } ``` --- ## UART驱动移植 ### UART配置参数 根据MCU配置UART: ```c int uart_port_init(uint32_t baudrate) { UART_HandleTypeDef huart; huart.Instance = USART1; // 或其他UART实例 huart.Init.BaudRate = baudrate; huart.Init.WordLength = UART_WORDLENGTH_8B; huart.Init.StopBits = UART_STOPBITS_1; huart.Init.Parity = UART_PARITY_NONE; huart.Init.Mode = UART_MODE_TX_RX; huart.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart) != HAL_OK) { return -1; } return 0; } ``` ### UART发送实现 ```c int uart_port_send(const uint8_t *data, uint32_t len) { extern UART_HandleTypeDef huart1; // 声明UART句柄 if (HAL_UART_Transmit(&huart1, data, len, HAL_MAX_DELAY) != HAL_OK) { return -1; } return 0; } ``` ### UART接收实现 #### 方法1: 阻塞接收 ```c int uart_port_recv(uint8_t *data, uint32_t len, uint32_t timeout) { extern UART_HandleTypeDef huart1; if (HAL_UART_Receive(&huart1, data, len, timeout) != HAL_OK) { return -1; // 超时或错误 } return len; // 返回接收的字节数 } ``` #### 方法2: 中断接收(推荐) ```c // 接收缓冲区 static uint8_t rx_buffer[2048]; static volatile uint32_t rx_index = 0; static volatile uint32_t rx_len = 0; static volatile bool rx_complete = false; int uart_port_recv(uint8_t *data, uint32_t len, uint32_t timeout) { extern UART_HandleTypeDef huart1; rx_index = 0; rx_len = len; rx_complete = false; // 启动中断接收 HAL_UART_Receive_IT(&huart1, rx_buffer, len); // 等待接收完成或超时 uint32_t start_tick = HAL_GetTick(); while (!rx_complete && (HAL_GetTick() - start_tick < timeout)) { // 等待 } if (rx_complete) { memcpy(data, rx_buffer, rx_len); return rx_len; } else { // 超时,停止接收 HAL_UART_AbortReceive_IT(&huart1); return rx_index; // 返回已接收的字节数 } } // UART接收完成回调 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart->Instance == USART1) { rx_complete = true; } } ``` --- ## 配置适配 ### MCU特定配置 在 `bootloader_config.h` 中添加MCU特定配置: ```c // MCU系列选择 #define MCU_FAMILY_STM32F4XX 1 // STM32F4系列 #define MCU_FAMILY_STM32F1XX 0 // STM32F1系列 #define MCU_FAMILY_STM32F7XX 0 // STM32F7系列 #define MCU_FAMILY_STM32L4XX 0 // STM32L4系列 // MCU时钟配置 #if MCU_FAMILY_STM32F4XX #define MCU_FLASH_LATENCY FLASH_LATENCY_5 #define MCU_VOLTAGE_RANGE FLASH_VOLTAGE_RANGE_3 #endif #if MCU_FAMILY_STM32F1XX #define MCU_FLASH_LATENCY FLASH_LATENCY_2 #endif // Flash布局 (根据MCU调整) #if MCU_FAMILY_STM32F4XX // STM32F407ZGTx (1MB Flash) #define BOOTLOADER_SIZE_KB 32 #define APP_BASE_ADDR 0x08008000 #define BANK1_ADDR 0x08008000 #define BANK2_ADDR 0x08038000 #define APP_SIZE_MAX (192 * 1024) #endif #if MCU_FAMILY_STM32F1XX // STM32F103ZETx (512KB Flash) #define BOOTLOADER_SIZE_KB 16 #define APP_BASE_ADDR 0x08004000 #define BANK1_ADDR 0x08004000 #define BANK2_ADDR 0x08024000 #define APP_SIZE_MAX (192 * 1024) #endif ``` --- ## STM32F1移植示例 ### 目录结构 ``` extensions/bootloader/port/stm32f1xx/ ├── flash_port.c ├── uart_port.c └── port.h ``` ### flash_port.c (STM32F1xx) STM32F1的Flash扇区较小且不规则: ```c #include "stm32f1xx_hal.h" #include "bsp_flash.h" int flash_port_init(void) { HAL_FLASH_Unlock(); // 清除所有错误标志 __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPERR); HAL_FLASH_Lock(); return 0; } int flash_port_erase(uint32_t addr, uint32_t size) { HAL_FLASH_Unlock(); uint32_t page_error = 0; FLASH_EraseInitTypeDef erase_init; // STM32F1按页擦除,每页1KB或2KB erase_init.TypeErase = FLASH_TYPEERASE_PAGES; erase_init.PageAddress = addr; erase_init.NbPages = (size + FLASH_PAGE_SIZE - 1) / FLASH_PAGE_SIZE; if (HAL_FLASHEx_Erase(&erase_init, &page_error) != HAL_OK) { HAL_FLASH_Lock(); return -1; } HAL_FLASH_Lock(); return 0; } int flash_port_write(uint32_t addr, const uint8_t *data, uint32_t len) { HAL_FLASH_Unlock(); uint32_t written = 0; // STM32F1按半字(2字节)写入 while (written < len) { uint16_t halfword; if (len - written >= 2) { halfword = *((uint16_t*)(data + written)); } else { halfword = data[written]; } if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, addr + written, halfword) != HAL_OK) { HAL_FLASH_Lock(); return -1; } written += 2; } HAL_FLASH_Lock(); return 0; } int flash_port_read(uint32_t addr, uint8_t *data, uint32_t len) { memcpy(data, (uint8_t*)addr, len); return 0; } bool flash_port_verify(uint32_t addr, const uint8_t *data, uint32_t len) { uint8_t flash_data[len]; flash_port_read(addr, flash_data, len); return memcmp(data, flash_data, len) == 0; } ``` ### uart_port.c (STM32F1xx) ```c #include "stm32f1xx_hal.h" #include "bsp_uart.h" extern UART_HandleTypeDef huart1; int uart_port_init(uint32_t baudrate) { // 通常在MX_USART1_UART_Init中已初始化 // 这里可以重新配置波特率 huart1.Init.BaudRate = baudrate; if (HAL_UART_Init(&huart1) != HAL_OK) { return -1; } return 0; } int uart_port_send(const uint8_t *data, uint32_t len) { return (HAL_UART_Transmit(&huart1, data, len, HAL_MAX_DELAY) == HAL_OK) ? 0 : -1; } int uart_port_recv(uint8_t *data, uint32_t len, uint32_t timeout) { return (HAL_UART_Receive(&huart1, data, len, timeout) == HAL_OK) ? len : -1; } void uart_port_deinit(void) { HAL_UART_DeInit(&huart1); } ``` --- ## 其他MCU移植 ### 移植到非STM32平台 #### ESP32移植示例 ```c // flash_port.c (ESP32) #include "esp_flash.h" int flash_port_init(void) { return esp_flash_init(); // ESP-IDF API } int flash_port_erase(uint32_t addr, uint32_t size) { return esp_flash_erase_region(addr, size); } int flash_port_write(uint32_t addr, const uint8_t *data, uint32_t len) { return esp_flash_write(addr, data, len); } int flash_port_read(uint32_t addr, uint8_t *data, uint32_t len) { return esp_flash_read(addr, data, len); } ``` #### NRF52移植示例 ```c // flash_port.c (NRF52) #include "nrf_nvmc.h" int flash_port_init(void) { return 0; // NRF52无需初始化 } int flash_port_erase(uint32_t addr, uint32_t size) { uint32_t page_start = addr / NRF_NVMC_PAGE_SIZE; uint32_t page_count = size / NRF_NVMC_PAGE_SIZE; for (uint32_t i = 0; i < page_count; i++) { nrf_nvmc_page_erase(page_start + i); } return 0; } int flash_port_write(uint32_t addr, const uint8_t *data, uint32_t len) { nrf_nvmc_write_bytes(addr, data, len); return 0; } int flash_port_read(uint32_t addr, uint8_t *data, uint32_t len) { memcpy(data, (uint8_t*)addr, len); return 0; } ``` --- ## 移植验证 ### 验证步骤 #### 1. Flash驱动验证 ```c void test_flash_driver(void) { uint8_t test_data[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; uint8_t read_data[8]; // 初始化 flash_port_init(); // 擦除 if (flash_port_erase(0x08008000, 8) == 0) { printf("Erase OK\r\n"); } // 写入 if (flash_port_write(0x08008000, test_data, 8) == 0) { printf("Write OK\r\n"); } // 读取 if (flash_port_read(0x08008000, read_data, 8) == 0) { printf("Read OK\r\n"); printf("Data: %02X %02X %02X %02X\r\n", read_data[0], read_data[1], read_data[2], read_data[3]); } // 校验 if (flash_port_verify(0x08008000, test_data, 8)) { printf("Verify OK\r\n"); } } ``` #### 2. UART驱动验证 ```c void test_uart_driver(void) { uint8_t test_data[] = "Hello Bootloader\r\n"; uint8_t recv_data[20]; // 初始化 uart_port_init(115200); // 发送 if (uart_port_send(test_data, strlen(test_data)) == 0) { printf("Send OK\r\n"); } // 接收(回显测试) int recv_len = uart_port_recv(recv_data, 20, 1000); if (recv_len > 0) { printf("Recv: %s\r\n", recv_data); } } ``` #### 3. 完整功能验证 使用上位机工具发送测试命令: ```bash # 连接设备 ./flash_tool --port COM3 --connect # 发送握手命令 ./flash_tool --port COM3 --handshake # 查询版本 ./flash_tool --port COM3 --version # 测试写入 ./flash_tool --port COM3 --test-write ``` --- ## 常见问题 ### Q1: Flash擦除失败? **原因:** - Flash未解锁 - 地址不在有效范围 - 擦除范围超出限制 **解决:** ```c // 检查Flash状态 void check_flash_status(void) { FLASH_TypeDef *flash = FLASH; printf("Flash SR: 0x%08X\r\n", flash->SR); // 清除错误标志 __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS); } ``` ### Q2: Flash写入失败? **原因:** - 写入地址未擦除 - 地址未对齐 - 数据长度不匹配 **解决:** ```c // 确保写入前已擦除 flash_port_erase(addr, len); // 确保地址对齐 addr = (addr / 4) * 4; // STM32F4按字对齐 // 确保数据长度对齐 len = ((len + 3) / 4) * 4; ``` ### Q3: UART接收不完整? **原因:** - 波特率不匹配 - 缓冲区溢出 - 中断未正确配置 **解决:** ```c // 使用中断接收 HAL_UART_Receive_IT(&huart1, rx_buffer, len); // 或使用DMA接收 HAL_UART_Receive_DMA(&huart1, rx_buffer, len); ``` ### Q4: 移植后Bootloader不启动? **排查步骤:** 1. 检查链接脚本起始地址 2. 检查中断向量表位置 3. 检查时钟配置 4. 使用调试器单步执行 --- ## 下一步 完成移植后,请: 1. 验证所有功能正常 2. 提交移植代码到项目 3. 撰写移植文档 --- **祝您移植顺利!**