Add a production-ready, educational bootloader extension for STM32 MCUs. Core features: - Custom binary protocol with CRC16/CRC32 verification - AES-256 encryption for firmware security - Dual-bank firmware management with rollback support - Version management and firmware validation - Modular architecture with BSP abstraction layer Project structure: - include/: Header files (bootloader.h, bsp_flash.h, bsp_uart.h) - src/: Core implementation (bootloader, protocol, crypto, firmware manager) - port/: MCU-specific adaptation layer (STM32F4xx) - docs/: Documentation (integration guide, porting guide) Supported platforms: - STM32F4xx (primary) - STM32F1xx (via porting) Quick start: 1. Copy extension module to project 2. Configure bootloader_config.h 3. Modify linker script for APP_BASE_ADDR 4. Build and flash bootloader to 0x08000000 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
822 lines
18 KiB
Markdown
822 lines
18 KiB
Markdown
# Bootloader移植指南
|
||
|
||
本文档详细介绍如何将Bootloader移植到不同的STM32系列或其他MCU平台。
|
||
|
||
---
|
||
|
||
## 目录
|
||
|
||
- [移植概述](#移植概述)
|
||
- [移植步骤](#移植步骤)
|
||
- [Flash驱动移植](#flash驱动移植)
|
||
- [UART驱动移植](#uart驱动移植)
|
||
- [配置适配](#配置适配)
|
||
- [STM32F1移植示例](#stm32f1移植示例)
|
||
- [其他MCU移植](#其他mcu移植)
|
||
- [移植验证](#移植验证)
|
||
- [常见问题](#常见问题)
|
||
|
||
---
|
||
|
||
## 移植概述
|
||
|
||
### 需要移植的内容
|
||
|
||
Bootloader采用分层设计,只有以下部分需要移植:
|
||
|
||
| 模块 | 文件 | 说明 |
|
||
|------|------|------|
|
||
| Flash驱动 | `port/<mcu>/flash_port.c` | Flash擦写读操作 |
|
||
| UART驱动 | `port/<mcu>/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 <your_mcu>
|
||
# 例如: cp -r port_template stm32f1xx
|
||
```
|
||
|
||
### 步骤2: 实现Flash驱动
|
||
|
||
编辑 `port/<your_mcu>/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/<your_mcu>/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/<your_mcu>/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 "<your_mcu>" 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. 撰写移植文档
|
||
|
||
---
|
||
|
||
**祝您移植顺利!** |