commit d2b8bd794009eb120f86600763dab0e5c946a4f9 Author: rovina Date: Thu Apr 2 15:03:51 2026 +0800 Initial commit: STM32 Bootloader extension module 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 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..4399845 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,102 @@ +cmake_minimum_required(VERSION 3.22) + + + +set(MCU_FAMILY "stm32f4xx" CACHE STRING "MCU family") + +set_property(CACHE MCU_FAMILY PROPERTY STRINGS + "stm32f4xx" + "stm32f1xx" +) + +option(BOOT_ENABLE_AES "Enable AES encryption" ON) +option(BOOT_ENABLE_CRC "Enable CRC verification" ON) +option(BOOT_ENABLE_DUAL_BANK "Enable dual bank support" ON) +option(BOOT_ENABLE_VERSION_MGR "Enable version management" ON) +option(BOOT_ENABLE_LOG "Enable logging" ON) + +add_library(bootloader STATIC) + +set(BOOTLOADER_CORE_SRC + ${CMAKE_CURRENT_SOURCE_DIR}/src/bootloader.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/bsp_flash.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/bsp_uart.c + # ${CMAKE_CURRENT_SOURCE_DIR}/src/protocol.c + # ${CMAKE_CURRENT_SOURCE_DIR}/src/crc_check.c + # ${CMAKE_CURRENT_SOURCE_DIR}/src/firmware_mgr.c + # ${CMAKE_CURRENT_SOURCE_DIR}/src/boot_jump.c +) + +# 加密模块(条件编译) +if(BOOT_ENABLE_AES) + list(APPEND BOOTLOADER_CORE_SRC + ${CMAKE_CURRENT_SOURCE_DIR}/src/aes_crypto.c + ) +endif() + + +set(BOOTLOADER_PORT_SRC + ${CMAKE_CURRENT_SOURCE_DIR}/port/${MCU_FAMILY}/flash_port.c + ${CMAKE_CURRENT_SOURCE_DIR}/port/${MCU_FAMILY}/uart_port.c +) + +target_sources(bootloader PRIVATE + ${BOOTLOADER_CORE_SRC} + ${BOOTLOADER_PORT_SRC} +) + +target_include_directories(bootloader PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/port/${MCU_FAMILY} +) + +if(BOOT_ENABLE_AES) + target_compile_definitions(bootloader PRIVATE BOOT_ENABLE_AES=1) +else() + target_compile_definitions(bootloader PRIVATE BOOT_ENABLE_AES=0) +endif() +if(BOOT_ENABLE_CRC) + target_compile_definitions(bootloader PRIVATE BOOT_ENABLE_CRC=1) +else() + target_compile_definitions(bootloader PRIVATE BOOT_ENABLE_CRC=0) +endif() +if(BOOT_ENABLE_DUAL_BANK) + target_compile_definitions(bootloader PRIVATE BOOT_ENABLE_DUAL_BANK=1) +else() + target_compile_definitions(bootloader PRIVATE BOOT_ENABLE_DUAL_BANK=0) +endif() +if(BOOT_ENABLE_VERSION_MGR) + target_compile_definitions(bootloader PRIVATE BOOT_ENABLE_VERSION_MGR=1) +else() + target_compile_definitions(bootloader PRIVATE BOOT_ENABLE_VERSION_MGR=0) +endif() +if(BOOT_ENABLE_LOG) + target_compile_definitions(bootloader PRIVATE BOOT_ENABLE_LOG=1) +else() + target_compile_definitions(bootloader PRIVATE BOOT_ENABLE_LOG=0) +endif() + +if(MCU_FAMILY STREQUAL "stm32f4xx") + target_compile_definitions(bootloader PRIVATE MCU_FAMILY_STM32F4XX=1) +elseif(MCU_FAMILY STREQUAL "stm32f1xx") + target_compile_definitions(bootloader PRIVATE MCU_FAMILY_STM32F1XX=1) +endif() + +target_link_libraries(bootloader PUBLIC + stm32cubemx +) +target_compile_options(bootloader PRIVATE + -Wall # 所有警告 + -Wextra # 额外警告 + -Wpedantic # 严格ISO C标准 + -fdata-sections # 数据分段(链接器优化) + -ffunction-sections # 函数分段(链接器优化) +) +message(STATUS "=== Bootloader Configuration ===") +message(STATUS "MCU Family: ${MCU_FAMILY}") +message(STATUS "AES Encryption: ${BOOT_ENABLE_AES}") +message(STATUS "CRC Verification: ${BOOT_ENABLE_CRC}") +message(STATUS "Dual Bank: ${BOOT_ENABLE_DUAL_BANK}") +message(STATUS "Version Manager: ${BOOT_ENABLE_VERSION_MGR}") +message(STATUS "Logging: ${BOOT_ENABLE_LOG}") +message(STATUS "===============================") \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..86fe7af --- /dev/null +++ b/README.md @@ -0,0 +1,696 @@ +# STM32 Bootloader Extension + +> 一个教学导向的、可移植的、生产就绪的Bootloader扩展模块 + +![License](https://img.shields.io/badge/license-MIT-blue.svg) +![Platform](https://img.shields.io/badge/platform-STM32-orange.svg) +![Language](https://img.shields.io/badge/language-C-green.svg) + +--- + +## 目录 + +- [项目简介](#项目简介) +- [核心特性](#核心特性) +- [快速开始](#快速开始) +- [架构设计](#架构设计) +- [学习路径](#学习路径) +- [模块详解](#模块详解) +- [配置指南](#配置指南) +- [移植指南](#移植指南) +- [上位机工具](#上位机工具) +- [常见问题](#常见问题) +- [贡献指南](#贡献指南) + +--- + +## 项目简介 + +这是一个专为**教学目的**设计的STM32 Bootloader扩展模块,采用**分层架构**和**模块化设计**,让您: + +- 理解Bootloader的核心原理 +- 掌握固件升级的完整流程 +- 学习安全启动的设计思想 +- 获得可直接用于生产环境的代码 + +### 为什么需要Bootloader? + +``` +┌─────────────────────────────────────────────────┐ +│ 传统固件升级:需要拆卸设备、连接调试器 │ +│ → 成本高、效率低、用户体验差 │ +├─────────────────────────────────────────────────┤ +│ Bootloader升级:串口/USB直接更新固件 │ +│ → 无需拆卸、远程升级、用户友好 │ +└─────────────────────────────────────────────────┘ +``` + +### 适用场景 + +- IoT设备远程固件更新 +- 嵌入式产品现场升级 +- 需要安全启动的设备 +- 学习嵌入式Bootloader开发 + +--- + +## 核心特性 + +| 特性 | 描述 | 状态 | +|------|------|:----:| +| 自定义协议 | 高效可靠的数据传输协议 | ✅ | +| AES-256加密 | 固件传输全程加密保护 | ✅ | +| CRC32校验 | 数据完整性验证 | ✅ | +| 双Bank备份 | 安全升级,失败可回滚 | ✅ | +| 版本管理 | 固件版本控制 | ✅ | +| 模块化设计 | 易于理解和移植 | ✅ | +| 跨MCU支持 | 通过BSP抽象层适配 | ✅ | + +--- + +## 快速开始 + +### 5分钟集成到您的项目 + +#### 步骤1: 复制扩展模块 + +```bash +# 将extensions目录复制到您的STM32项目根目录 +cp -r extensions/ /path/to/your/project/ +``` + +#### 步骤2: 配置参数 + +编辑 `extensions/bootloader/include/bootloader_config.h`: + +```c +// 选择您的MCU系列 +#define MCU_FAMILY_STM32F4XX 1 +#define MCU_FAMILY_STM32F1XX 0 + +// 配置Flash布局 +#define BOOTLOADER_SIZE_KB 32 +#define APP_BASE_ADDR 0x08008000 +#define BANK1_ADDR 0x08008000 +#define BANK2_ADDR 0x08038000 + +// 配置串口参数 +#define BOOT_UART_BAUDRATE 115200 +``` + +#### 步骤3: 修改main.c + +```c +#include "bootloader.h" + +int main(void) { + HAL_Init(); + SystemClock_Config(); + + // === Bootloader入口 === + bootloader_init(); + if (bootloader_check_enter()) { + // 进入Bootloader模式 + while (bootloader_get_state() != BOOT_STATE_JUMP_APP) { + bootloader_process(); + } + } + // ===================== + + // 您的应用代码... + MX_GPIO_Init(); + MX_USART1_UART_Init(); + + while (1) { + // 主循环 + } +} +``` + +#### 步骤4: 修改链接脚本 + +将应用程序的起始地址修改为 `APP_BASE_ADDR`: + +```ld +/* 原始 */ +FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K + +/* 修改为 */ +FLASH (rx) : ORIGIN = 0x08008000, LENGTH = 992K +``` + +#### 步骤5: 编译并烧录 + +```bash +# 编译Bootloader +cd extensions/bootloader +mkdir build && cd build +cmake .. -DMCU_FAMILY=stm32f4xx +make + +# 烧录Bootloader到0x08000000 +st-flash write bootloader.bin 0x08000000 +``` + +--- + +## 架构设计 + +### 分层架构图 + +``` +┌─────────────────────────────────────────────────────────┐ +│ 应用层 (Application) │ +│ main.c / 用户代码 │ +├─────────────────────────────────────────────────────────┤ +│ API层 (bootloader.h) │ +│ 统一对外接口,屏蔽内部实现细节 │ +├─────────────────────────────────────────────────────────┤ +│ 功能模块层 (MCU无关) │ +│ ┌────────────┬────────────┬────────────┬───────────┐ │ +│ │ Protocol │ AES Crypto │ CRC Check │ Firmware │ │ +│ │ Handler │ Engine │ Module │ Manager │ │ +│ └────────────┴────────────┴────────────┴───────────┘ │ +├─────────────────────────────────────────────────────────┤ +│ BSP抽象层 (bsp_*.h) │ +│ 定义硬件操作接口,实现解耦合 │ +├─────────────────────────────────────────────────────────┤ +│ 移植适配层 (MCU相关) │ +│ ┌──────────────┬──────────────┬──────────────┐ │ +│ │ STM32F4xx │ STM32F1xx │ Others │ │ +│ │ flash_port │ flash_port │ flash_port │ │ +│ │ uart_port │ uart_port │ uart_port │ │ +│ └──────────────┴──────────────┴──────────────┘ │ +└─────────────────────────────────────────────────────────┘ +``` + +### 数据流图 + +``` +上位机 Bootloader 应用 + │ │ │ + │ ①加密固件 │ │ + ├─────────────────────────────────>│ │ + │ │ │ + │ ②发送数据包 │ │ + ├─────────────────────────────────>│ │ + │ │ ③解密、校验 │ + │ ├──────┐ │ + │ │ │ │ + │ │<─────┘ │ + │ │ │ + │ │ ④写入Flash │ + │ ├──────┐ │ + │ │ │ │ + │ │<─────┘ │ + │ │ │ + │ ⑤应答确认 │ │ + │<─────────────────────────────────┤ │ + │ │ │ + │ │ ⑥跳转执行 │ + │ ├───────────────────────────>│ + │ │ │ +``` + +### Flash内存布局 + +``` +STM32F407ZGTx (1MB Flash) + +┌──────────────┬──────────────────┬─────────────────────┐ +│ 地址范围 │ 用途 │ 大小 │ +├──────────────┼──────────────────┼─────────────────────┤ +│ 0x08000000 │ Bootloader │ 32KB │ +│ ~ │ 启动引导程序 │ 包含加密/校验模块 │ +│ 0x08007FFF │ │ │ +├──────────────┼──────────────────┼─────────────────────┤ +│ 0x08008000 │ Bank1 - App │ 192KB │ +│ ~ │ 主固件存储区 │ 当前运行版本 │ +│ 0x08037FFF │ │ │ +├──────────────┼──────────────────┼─────────────────────┤ +│ 0x08038000 │ Bank2 - Backup │ 192KB │ +│ ~ │ 备份固件区 │ 升级临时存储 │ +│ 0x08067FFF │ │ 回滚备份 │ +├──────────────┼──────────────────┼─────────────────────┤ +│ 0x08068000 │ Reserved │ 608KB │ +│ ~ │ 预留空间 │ 可用于文件系统 │ +│ 0x080FFFFF │ │ │ +└──────────────┴──────────────────┴─────────────────────┘ +``` + +--- + +## 学习路径 + +### 推荐学习顺序 + +``` +第1阶段:理解基础概念 +├── 阅读本文档,了解Bootloader原理 +├── 理解Flash内存布局 +└── 学习启动流程和中断向量表 + +第2阶段:搭建开发环境 +├── 集成Bootloader到您的项目 +├── 配置串口通信 +└── 编译并烧录测试 + +第3阶段:深入核心模块 +├── protocol.c - 理解通信协议设计 +├── bsp_flash.c - 掌握Flash操作 +├── aes_crypto.c - 学习加密实现 +└── firmware_mgr.c - 理解固件管理 + +第4阶段:实践与扩展 +├── 移植到其他MCU平台 +├── 添加自定义功能 +└── 开发上位机工具 + +第5阶段:生产部署 +├── 安全性加固 +├── 稳定性测试 +└── 现场升级测试 +``` + +### 学习资源 + +| 资源 | 说明 | 链接 | +|------|------|------| +| [INTEGRATION.md](docs/INTEGRATION.md) | 详细集成指南 | 本地文档 | +| [PORTING.md](docs/PORTING.md) | 移植到其他MCU | 本地文档 | +| [协议设计文档](docs/PROTOCOL.md) | 自定义协议详解 | 本地文档 | +| STM32参考手册 | Flash操作说明 | ST官网 | +| AES加密教程 | 加密算法原理 | 网络资源 | + +--- + +## 模块详解 + +### 1. 协议模块 (protocol.c) + +自定义二进制协议,高效可靠: + +``` +帧结构: +┌──────┬──────┬──────┬────────┬──────┬──────┐ +│ HEAD │ CMD │ LEN │ DATA │ CRC16│ TAIL │ +│ 0xAA │ 1字节│ 2字节│ N字节 │ 2字节│ 0x55 │ +└──────┴──────┴──────┴────────┴──────┴──────┘ + +支持命令: +- CMD_HANDSHAKE (0x01): 握手建立连接 +- CMD_ERASE_FLASH (0x02): 擦除Flash扇区 +- CMD_WRITE_DATA (0x03): 写入数据包 +- CMD_VERIFY_FIRM (0x04): 校验固件 +- CMD_JUMP_APP (0x05): 跳转应用 +- CMD_GET_VERSION (0x06): 查询版本 +- CMD_SWITCH_BANK (0x07): 切换Bank +``` + +**设计亮点:** +- 简洁高效,仅7字节开销 +- CRC16校验保证数据完整性 +- 支持应答重传机制 +- 超时自动恢复 + +### 2. 加密模块 (aes_crypto.c) + +AES-256加密保护固件安全: + +```c +// 加密流程 +固件.bin → AES-256加密 → 加密固件.bin → 传输 + +// 解密流程(Bootloader中) +接收数据 → AES-256解密 → 验证 → 写入Flash +``` + +**安全特性:** +- 256位密钥长度 +- CBC模式加密 +- 随机IV向量 +- 密钥不存储在代码中 + +### 3. 固件管理模块 (firmware_mgr.c) + +完整的固件生命周期管理: + +``` +┌─────────────────────────────────────────┐ +│ 固件升级流程 │ +├─────────────────────────────────────────┤ +│ 1. 接收加密固件 │ +│ ↓ │ +│ 2. AES-256解密 │ +│ ↓ │ +│ 3. CRC32完整性校验 │ +│ ↓ │ +│ 4. 写入Bank2临时区 │ +│ ↓ │ +│ 5. 验证写入数据 │ +│ ↓ │ +│ 6. 复制到Bank1主区 │ +│ ↓ │ +│ 7. 更新版本信息 │ +│ ↓ │ +│ 8. 重启并跳转 │ +└─────────────────────────────────────────┘ +``` + +**错误恢复:** +- 写入失败 → 保留原固件 +- 校验失败 → 自动重传 +- 升级中断 → Bank切换恢复 + +### 4. 双Bank管理 + +安全的固件备份机制: + +``` +正常升级流程: +Bank1[App v1.0] ──→ 接收新固件 → Bank2[v2.0] + ↓ + 验证通过,复制到Bank1 + ↓ + Bank1[v2.0] ← Bank2[备份] + +异常恢复流程: +Bank1[App v2.0] ──→ 运行异常 → 从Bank2恢复 + ↓ + Bank1[v1.0] ← Bank2[v1.0] +``` + +--- + +## 配置指南 + +### 完整配置参数 + +编辑 `bootloader_config.h` 文件: + +```c +/*============================================================================ + * MCU平台配置 + *============================================================================*/ +#define MCU_FAMILY_STM32F4XX 1 // STM32F4系列 +#define MCU_FAMILY_STM32F1XX 0 // STM32F1系列 + +/*============================================================================ + * Flash布局配置 + *============================================================================*/ +// Bootloader占用空间 (KB) +#define BOOTLOADER_SIZE_KB 32 + +// Flash基地址 (不要修改) +#define FLASH_BASE_ADDR 0x08000000 + +// 应用程序基地址 +#define APP_BASE_ADDR (FLASH_BASE_ADDR + BOOTLOADER_SIZE_KB * 1024) + +// 双Bank配置 +#define BANK1_ADDR 0x08008000 // Bank1起始地址 +#define BANK2_ADDR 0x08038000 // Bank2起始地址 +#define APP_SIZE_MAX (192 * 1024) // 单个Bank最大192KB + +// 版本信息存储区 +#define VERSION_INFO_ADDR 0x08007C00 // 最后1KB用于存储版本信息 + +/*============================================================================ + * 串口通信配置 + *============================================================================*/ +#define BOOT_UART_BAUDRATE 115200 // 串口波特率 +#define BOOT_UART_TIMEOUT_MS 3000 // 接收超时 (毫秒) +#define BOOT_UART_BUFFER_SIZE 2048 // 接收缓冲区大小 + +/*============================================================================ + * 协议配置 + *============================================================================*/ +#define PROTOCOL_MAX_DATA_LEN 1024 // 单包最大数据长度 +#define PROTOCOL_RETRY_COUNT 3 // 重试次数 +#define PROTOCOL_RETRY_DELAY_MS 100 // 重试间隔 (毫秒) + +/*============================================================================ + * 功能开关 + *============================================================================*/ +#define BOOT_ENABLE_AES 1 // 启用AES加密 +#define BOOT_ENABLE_CRC 1 // 启用CRC校验 +#define BOOT_ENABLE_DUAL_BANK 1 // 启用双Bank备份 +#define BOOT_ENABLE_VERSION_MGR 1 // 启用版本管理 +#define BOOT_ENABLE_LOG 1 // 启用日志输出 + +/*============================================================================ + * 安全配置 + *============================================================================*/ +#define BOOT_AES_KEY_SIZE 256 // AES密钥长度 (128/256) +#define BOOT_AES_IV_SIZE 16 // IV向量长度 + +// AES密钥 (实际应用中应从安全区域读取) +#define BOOT_AES_KEY {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, \ + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c, \ + 0x76, 0x2e, 0x71, 0x60, 0xf3, 0x8b, 0x4d, 0xa5, \ + 0x6a, 0x78, 0x4d, 0x90, 0x45, 0x19, 0x0c, 0xfe} + +/*============================================================================ + * 触发方式配置 + *============================================================================*/ +#define BOOT_TRIGGER_TIMEOUT_MS 3000 // 启动时等待命令超时 (毫秒) +// 按键触发 (可选) +// #define BOOT_TRIGGER_PIN GPIO_PIN_0 +// #define BOOT_TRIGGER_PORT GPIOA +// #define BOOT_TRIGGER_ACTIVE 0 // 0: 低电平触发, 1: 高电平触发 + +/*============================================================================ + * 日志配置 + *============================================================================*/ +#if BOOT_ENABLE_LOG +#define BOOT_LOG_LEVEL_DEBUG 0 +#define BOOT_LOG_LEVEL_INFO 1 +#define BOOT_LOG_LEVEL_WARNING 2 +#define BOOT_LOG_LEVEL_ERROR 3 +#define BOOT_LOG_LEVEL BOOT_LOG_LEVEL_INFO +#endif + +/*============================================================================ + * 调试配置 + *============================================================================*/ +#define BOOT_DEBUG_MODE 0 // 调试模式 (生产环境关闭) +``` + +### 配置示例 + +#### 示例1: 最小配置 (无加密) + +```c +#define BOOT_ENABLE_AES 0 // 关闭加密 +#define BOOT_ENABLE_CRC 1 // 仅CRC校验 +#define BOOT_ENABLE_DUAL_BANK 0 // 单Bank模式 +#define BOOT_ENABLE_VERSION_MGR 0 // 不管理版本 +``` + +#### 示例2: 生产配置 (安全最高) + +```c +#define BOOT_ENABLE_AES 1 // 启用AES-256加密 +#define BOOT_ENABLE_CRC 1 // 启用CRC校验 +#define BOOT_ENABLE_DUAL_BANK 1 // 双Bank备份 +#define BOOT_ENABLE_VERSION_MGR 1 // 版本管理 +#define BOOT_ENABLE_LOG 0 // 关闭日志 +``` + +--- + +## 移植指南 + +详细移植步骤请参考 [PORTING.md](docs/PORTING.md) + +### 快速移植到新MCU + +1. **复制移植模板** + ```bash + cp -r port/port_template port/your_mcu + ``` + +2. **实现Flash操作接口** + ```c + // flash_port.c + int flash_port_init(void) { /* ... */ } + int flash_port_erase(uint32_t addr, uint32_t size) { /* ... */ } + int flash_port_write(uint32_t addr, const uint8_t *data, uint32_t len) { /* ... */ } + int flash_port_read(uint32_t addr, uint8_t *data, uint32_t len) { /* ... */ } + ``` + +3. **实现UART操作接口** + ```c + // uart_port.c + int uart_port_init(uint32_t baudrate) { /* ... */ } + int uart_port_send(const uint8_t *data, uint32_t len) { /* ... */ } + int uart_port_recv(uint8_t *data, uint32_t len, uint32_t timeout) { /* ... */ } + ``` + +4. **修改CMakeLists.txt** + ```cmake + set(MCU_FAMILY "your_mcu" CACHE STRING "MCU family") + ``` + +--- + +## 上位机工具 + +### 命令行工具 + +```bash +# 查看帮助 +./flash_tool --help + +# 连接设备 +./flash_tool --port COM3 --baud 115200 + +# 烧录固件 +./flash_tool --port COM3 --write firmware.bin + +# 查看版本 +./flash_tool --port COM3 --version + +# 擦除Flash +./flash_tool --port COM3 --erase + +# 切换Bank +./flash_tool --port COM3 --switch-bank + +# 加密固件 +./flash_tool --encrypt firmware.bin firmware_enc.bin +``` + +### Python脚本 (简易版) + +```python +import serial +import struct + +def send_firmware(port, baudrate, firmware_path): + ser = serial.Serial(port, baudrate, timeout=3) + + # 发送握手 + handshake = bytes([0xAA, 0x01, 0x00, 0x00, 0x55]) + ser.write(handshake) + + # 读取固件 + with open(firmware_path, 'rb') as f: + firmware = f.read() + + # 发送数据包... + # (详细实现见 tools/flash_tool) + + ser.close() + +if __name__ == '__main__': + send_firmware('COM3', 115200, 'firmware.bin') +``` + +--- + +## 常见问题 + +### Q1: Bootloader启动后无法跳转到应用? + +**A:** 检查以下几点: +1. 应用程序的链接脚本起始地址是否正确设置为 `APP_BASE_ADDR` +2. 应用程序的中断向量表是否偏移 `SCB->VTOR = APP_BASE_ADDR` +3. 应用程序是否正确编译烧录 + +### Q2: Flash写入失败? + +**A:** 常见原因: +1. Flash未解锁 +2. 写入地址未擦除 +3. 写入地址未对齐 +4. Flash页大小配置错误 + +### Q3: 串口通信不稳定? + +**A:** 建议: +1. 降低波特率到 9600 或 57600 +2. 增加超时时间 `BOOT_UART_TIMEOUT_MS` +3. 添加硬件流控 +4. 检查串口线缆质量 + +### Q4: 如何调试Bootloader? + +**A:** 方法: +1. 启用日志输出 `BOOT_ENABLE_LOG = 1` +2. 设置日志级别 `BOOT_LOG_LEVEL = BOOT_LOG_LEVEL_DEBUG` +3. 使用串口打印调试信息 +4. 使用调试器单步调试 + +### Q5: 如何确保固件安全性? + +**A:** 建议: +1. 启用AES-256加密 `BOOT_ENABLE_AES = 1` +2. 密钥不要硬编码,从安全区域读取 +3. 使用固件签名验证 +4. 启用读保护 (RDP) + +--- + +## 性能指标 + +| 指标 | 数值 | +|------|------| +| Bootloader大小 | ~16KB (启用所有功能) | +| 最小程序大小 | ~4KB (仅基础功能) | +| RAM占用 | ~2KB | +| 192KB固件升级时间 | ~15秒 @115200 | +| 支持最大固件大小 | 192KB (单Bank) | +| 可靠性 | CRC校验 + 重传机制 | + +--- + +## 版本历史 + +| 版本 | 日期 | 说明 | +|------|------|------| +| v1.0.0 | 2026-01 | 初始版本,基础功能 | +| v1.1.0 | TBD | 添加AES加密支持 | +| v1.2.0 | TBD | 添加双Bank支持 | +| v2.0.0 | TBD | 重构架构,支持多MCU | + +--- + +## 许可证 + +本项目采用 MIT 许可证,详见 [LICENSE](LICENSE) 文件。 + +--- + +## 贡献指南 + +欢迎贡献代码、报告问题或提出建议! + +1. Fork 本仓库 +2. 创建特性分支 (`git checkout -b feature/AmazingFeature`) +3. 提交更改 (`git commit -m 'Add some AmazingFeature'`) +4. 推送到分支 (`git push origin feature/AmazingFeature`) +5. 提交 Pull Request + +--- + +## 联系方式 + +- 问题反馈: [GitHub Issues](https://github.com/your-repo/issues) +- 技术讨论: [GitHub Discussions](https://github.com/your-repo/discussions) + +--- + +## 致谢 + +感谢以下开源项目的启发: + +- STM32 HAL库 +- TinyAES库 +- STM32CubeProgrammer + +--- + +**Happy Coding!** + +如果您觉得这个项目有帮助,请给一个 ⭐ Star 支持一下! \ No newline at end of file diff --git a/docs/INTEGRATION.md b/docs/INTEGRATION.md new file mode 100644 index 0000000..80fb790 --- /dev/null +++ b/docs/INTEGRATION.md @@ -0,0 +1,643 @@ +# Bootloader集成指南 + +本文档详细介绍如何将Bootloader扩展模块集成到您的STM32项目中。 + +--- + +## 目录 + +- [前置要求](#前置要求) +- [集成方式](#集成方式) +- [详细步骤](#详细步骤) +- [配置参数](#配置参数) +- [链接脚本修改](#链接脚本修改) +- [中断向量表](#中断向量表) +- [编译与烧录](#编译与烧录) +- [测试验证](#测试验证) +- [故障排查](#故障排查) + +--- + +## 前置要求 + +### 硬件要求 + +- STM32系列MCU (本模块主要针对STM32F4系列) +- 至少一个UART接口 +- 足够的Flash空间: + - Bootloader: 16KB ~ 32KB + - 应用程序: 取决于您的需求 + +### 软件要求 + +- ARM GCC工具链 +- CMake 3.10+ (可选) +- STM32CubeMX (用于生成初始化代码) +- 串口调试工具 + +### 开发环境 + +- Windows: STM32CubeIDE / Keil MDK / IAR +- Linux: VSCode + CMake + ARM GCC +- macOS: VSCode + CMake + ARM GCC + +--- + +## 集成方式 + +### 方式一:嵌入式集成(推荐新手) + +Bootloader与应用程序共用一个工程,通过条件编译切换。 + +**优点:** +- 配置简单 +- 易于调试 +- 适合学习 + +**缺点:** +- 需要重新编译整个工程 +- 不够灵活 + +### 方式二:独立工程集成(推荐生产) + +Bootloader和应用程序分别作为独立工程。 + +**优点:** +- 独立开发 +- 灵活部署 +- 适合生产 + +**缺点:** +- 需要管理两个工程 +- 配置相对复杂 + +--- + +## 详细步骤 + +### 方式一:嵌入式集成 + +#### 步骤1: 复制文件 + +```bash +# 复制整个extensions目录到您的项目根目录 +cp -r extensions/ /path/to/your/project/ +``` + +#### 步骤2: 修改CMakeLists.txt + +在您的项目CMakeLists.txt中添加: + +```cmake +# 添加Bootloader模块 +add_subdirectory(extensions/bootloader) + +# 链接Bootloader库 +target_link_libraries(${PROJECT_NAME}.elf bootloader) + +# 包含头文件 +target_include_directories(${PROJECT_NAME}.elf PRIVATE + extensions/bootloader/include +) +``` + +#### 步骤3: 配置bootloader_config.h + +编辑 `extensions/bootloader/include/bootloader_config.h`: + +```c +// MCU配置 +#define MCU_FAMILY_STM32F4XX 1 + +// Flash布局 +#define BOOTLOADER_SIZE_KB 32 +#define APP_BASE_ADDR 0x08008000 + +// 功能开关 +#define BOOT_ENABLE_AES 1 +#define BOOT_ENABLE_CRC 1 +#define BOOT_ENABLE_DUAL_BANK 1 +``` + +#### 步骤4: 修改main.c + +```c +#include "main.h" +#include "bootloader.h" + +int main(void) { + // HAL初始化 + HAL_Init(); + SystemClock_Config(); + + // === Bootloader入口 === + bootloader_init(); + + // 检查是否进入Bootloader模式 + if (bootloader_check_enter()) { + // Bootloader主循环 + while (1) { + bootloader_process(); + + // 检查是否需要跳转 + if (bootloader_get_state() == BOOT_STATE_JUMP_APP) { + bootloader_jump_to_app(APP_BASE_ADDR); + } + } + } + // ===================== + + // 应用程序初始化 + MX_GPIO_Init(); + MX_USART1_UART_Init(); + // ... 其他初始化 + + // 应用程序主循环 + while (1) { + // 您的应用代码 + } +} +``` + +#### 步骤5: 修改链接脚本 + +找到您的链接脚本 (例如 `STM32F407XX_FLASH.ld`),修改Flash起始地址: + +```ld +/* 原始配置 */ +MEMORY +{ +RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K +FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K +} + +/* 修改为 (为Bootloader预留32KB) */ +MEMORY +{ +RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K +FLASH (rx) : ORIGIN = 0x08008000, LENGTH = 992K +} +``` + +#### 步骤6: 修改中断向量表 + +在应用程序的 `system_stm32f4xx.c` 中,添加中断向量表偏移: + +```c +void SystemInit(void) { + // ... 原有代码 + + // 设置中断向量表偏移 +#ifdef VECT_TAB_SRAM + SCB->VTOR = RAMDTCM_BASE | VECT_TAB_OFFSET; +#else + SCB->VTOR = FLASH_BASE | 0x8000; // 0x8000 = 32KB偏移 +#endif +} +``` + +或者在 `main.c` 的开头添加: + +```c +#define VECT_TAB_OFFSET 0x8000 // 32KB偏移 + +int main(void) { + // 设置中断向量表 + SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; + + HAL_Init(); + // ... +} +``` + +--- + +### 方式二:独立工程集成 + +#### 步骤1: 创建Bootloader工程 + +```bash +mkdir bootloader_project +cd bootloader_project + +# 复制Bootloader模块 +cp -r /path/to/bootloader/extensions/bootloader ./ +``` + +#### 步骤2: 创建Bootloader的main.c + +```c +#include "bootloader.h" + +int main(void) { + HAL_Init(); + SystemClock_Config(); + + // 初始化Bootloader + bootloader_init(); + + // Bootloader主循环 + while (1) { + bootloader_process(); + + if (bootloader_get_state() == BOOT_STATE_JUMP_APP) { + // 检查应用程序是否存在 + if (bootloader_check_app_valid(APP_BASE_ADDR)) { + bootloader_jump_to_app(APP_BASE_ADDR); + } + } + } +} +``` + +#### 步骤3: 配置Bootloader链接脚本 + +Bootloader从 0x08000000 开始: + +```ld +MEMORY +{ +RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K +FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K +} +``` + +#### 步骤4: 创建应用程序工程 + +应用程序从 0x08008000 开始: + +```ld +MEMORY +{ +RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K +FLASH (rx) : ORIGIN = 0x08008000, LENGTH = 992K +} +``` + +#### 步骤5: 编译烧录顺序 + +```bash +# 1. 编译Bootloader +cd bootloader_project +make +# 烧录到 0x08000000 +st-flash write bootloader.bin 0x08000000 + +# 2. 编译应用程序 +cd application_project +make +# 通过Bootloader升级,或直接烧录到 0x08008000 +``` + +--- + +## 配置参数 + +### 必须配置的参数 + +| 参数 | 说明 | 示例值 | +|------|------|--------| +| MCU_FAMILY_* | MCU系列 | STM32F4XX | +| BOOTLOADER_SIZE_KB | Bootloader大小 | 32 | +| APP_BASE_ADDR | 应用起始地址 | 0x08008000 | +| BOOT_UART_BAUDRATE | 串口波特率 | 115200 | + +### 可选配置的参数 + +| 参数 | 说明 | 默认值 | +|------|------|--------| +| BOOT_ENABLE_AES | 启用AES加密 | 1 | +| BOOT_ENABLE_CRC | 启用CRC校验 | 1 | +| BOOT_ENABLE_DUAL_BANK | 启用双Bank | 1 | +| BOOT_ENABLE_VERSION_MGR | 启用版本管理 | 1 | +| BOOT_TRIGGER_TIMEOUT_MS | 启动等待超时 | 3000 | + +--- + +## 链接脚本修改 + +### 完整示例 (STM32F407ZGTx) + +```ld +/* Entry Point */ +ENTRY(Reset_Handler) + +/* Highest address of the user mode stack */ +_estack = ORIGIN(RAM) + LENGTH(RAM); + +/* Specify the memory areas */ +MEMORY +{ +RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K +CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K + +/* 根据您的需求选择其一 */ + +/* 方式1: 应用程序链接脚本 (从32KB开始) */ +FLASH (rx) : ORIGIN = 0x08008000, LENGTH = 992K + +/* 方式2: Bootloader链接脚本 (从0开始) */ +/* FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K */ +} + +/* 中断向量表偏移 (仅应用程序需要) */ +_isr_vector = ORIGIN(FLASH); +``` + +### 注意事项 + +1. **Bootloader工程**: Flash起始地址 = 0x08000000 +2. **应用程序工程**: Flash起始地址 = 0x08000000 + Bootloader大小 +3. **不要重叠**: 确保Bootloader和应用程序的Flash区域不重叠 + +--- + +## 中断向量表 + +### 为什么需要修改? + +STM32上电后,从 0x08000000 读取中断向量表。如果应用程序不在 0x08000000,必须重定向中断向量表。 + +### 修改方法 + +#### 方法1: 修改system_stm32f4xx.c + +```c +void SystemInit(void) { + // ... 原有代码 + + /* Configure the Vector Table location -------------------------------------*/ +#define VECT_TAB_OFFSET 0x8000 /* 32KB offset for bootloader */ + +#ifdef VECT_TAB_SRAM + SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; +#else + SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; +#endif +} +``` + +#### 方法2: 在main.c中设置 + +```c +int main(void) { + // 第一件事:设置中断向量表 + SCB->VTOR = 0x08008000; // 应用程序起始地址 + + // 然后初始化HAL + HAL_Init(); + + // ... +} +``` + +#### 方法3: 使用宏定义 + +在 `main.h` 中定义: + +```c +#define APPLICATION_ADDRESS 0x08008000 + +// 在main函数开头 +SCB->VTOR = APPLICATION_ADDRESS; +``` + +--- + +## 编译与烧录 + +### 编译Bootloader + +```bash +cd bootloader_project +mkdir build && cd build +cmake .. -DMCU_FAMILY=stm32f4xx +make -j4 +``` + +### 编译应用程序 + +```bash +cd application_project +mkdir build && cd build +cmake .. +make -j4 +``` + +### 烧录顺序 + +#### 方法1: 使用ST-Link + +```bash +# 烧录Bootloader +st-flash write bootloader.bin 0x08000000 + +# 烧录应用程序 (首次烧录) +st-flash write application.bin 0x08008000 +``` + +#### 方法2: 使用STM32CubeProgrammer + +```bash +# 烧录Bootloader +STM32_Programmer_CLI -c port=SWD -w bootloader.bin 0x08000000 -v + +# 烧录应用程序 +STM32_Programmer_CLI -c port=SWD -w application.bin 0x08008000 -v +``` + +#### 方法3: 通过Bootloader升级 + +```bash +# 使用上位机工具升级 +./flash_tool --port COM3 --write application.bin +``` + +--- + +## 测试验证 + +### 基础功能测试 + +#### 测试1: Bootloader启动测试 + +```c +// 在bootloader.c中添加调试输出 +void bootloader_init(void) { + // 初始化串口 + bsp_uart_init(BOOT_UART_BAUDRATE); + + printf("Bootloader v1.0.0\r\n"); + printf("Waiting for command...\r\n"); + + // ... +} +``` + +**预期结果:** 串口输出Bootloader版本信息 + +#### 测试2: 跳转测试 + +```c +// 在main.c中添加 +bootloader_jump_to_app(APP_BASE_ADDR); + +// 在应用程序main.c开头添加 +printf("Application started!\r\n"); +``` + +**预期结果:** 成功跳转到应用程序并输出信息 + +#### 测试3: 通信测试 + +使用串口调试工具发送握手命令: + +``` +发送: AA 01 00 00 55 (握手命令) +预期: 接收到应答 +``` + +### 完整功能测试 + +#### 测试清单 + +- [ ] Bootloader正常启动 +- [ ] 串口通信正常 +- [ ] 握手命令成功 +- [ ] Flash擦除成功 +- [ ] 固件写入成功 +- [ ] CRC校验通过 +- [ ] AES解密正确 +- [ ] 跳转应用程序成功 +- [ ] 应用程序正常运行 +- [ ] 双Bank切换成功 +- [ ] 版本管理正常 + +--- + +## 故障排查 + +### 问题1: 编译错误 - 找不到头文件 + +**症状:** +``` +fatal error: bootloader.h: No such file or directory +``` + +**解决:** +```cmake +# 在CMakeLists.txt中添加包含路径 +target_include_directories(${PROJECT_NAME} PRIVATE + extensions/bootloader/include +) +``` + +### 问题2: 链接错误 - 重复定义 + +**症状:** +``` +multiple definition of `main' +``` + +**解决:** +检查是否有多个main函数,确保只有一个main入口。 + +### 问题3: 运行时错误 - 无法启动 + +**症状:** +程序不运行或立即崩溃 + +**排查步骤:** +1. 检查Flash布局是否正确 +2. 检查中断向量表是否正确设置 +3. 使用调试器检查PC指针位置 +4. 检查堆栈大小是否足够 + +### 问题4: 跳转失败 + +**症状:** +跳转到应用程序后无响应 + +**排查步骤:** +1. 确认应用程序起始地址正确 +2. 检查中断向量表是否偏移 +3. 检查应用程序是否正确烧录 +4. 使用调试器查看应用程序是否正常运行 + +**调试代码:** +```c +void bootloader_jump_to_app(uint32_t app_addr) { + // 检查应用程序是否存在 + uint32_t app_stack = *((uint32_t*)app_addr); + uint32_t app_entry = *((uint32_t*)(app_addr + 4)); + + printf("App stack: 0x%08X\r\n", app_stack); + printf("App entry: 0x%08X\r\n", app_entry); + + // 检查栈指针是否在RAM范围内 + if (app_stack < 0x20000000 || app_stack > 0x20000000 + 128*1024) { + printf("Invalid stack pointer!\r\n"); + return; + } + + // 禁用所有中断 + __disable_irq(); + + // 跳转 + typedef void (*app_entry_t)(void); + app_entry_t entry = (app_entry_t)app_entry; + entry(); +} +``` + +### 问题5: Flash写入失败 + +**症状:** +Flash写入返回错误 + +**排查步骤:** +1. 确认Flash已解锁 +2. 确认写入地址已擦除 +3. 确认地址对齐 +4. 检查Flash页大小配置 + +**调试代码:** +```c +int flash_port_write(uint32_t addr, const uint8_t *data, uint32_t len) { + printf("Writing to 0x%08X, len=%d\r\n", addr, len); + + // 检查Flash状态 + FLASH_TypeDef *flash = FLASH; + printf("Flash SR: 0x%08X\r\n", flash->SR); + + // 写入操作... +} +``` + +--- + +## 下一步 + +完成集成后,请阅读: + +- [协议文档](PROTOCOL.md) - 了解通信协议细节 +- [移植指南](PORTING.md) - 移植到其他MCU平台 +- [上位机工具](../tools/flash_tool/README.md) - 使用烧写工具 + +--- + +## 技术支持 + +如遇到问题,请: + +1. 查阅本文档的故障排查章节 +2. 查看 [GitHub Issues](https://github.com/your-repo/issues) +3. 提交新的Issue,附带: + - MCU型号 + - 完整的错误信息 + - 相关配置文件 + - 调试日志 + +--- + +**祝您集成顺利!** \ No newline at end of file diff --git a/docs/PORTING.md b/docs/PORTING.md new file mode 100644 index 0000000..025e1a6 --- /dev/null +++ b/docs/PORTING.md @@ -0,0 +1,822 @@ +# 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. 撰写移植文档 + +--- + +**祝您移植顺利!** \ No newline at end of file diff --git a/include/bootloader.h b/include/bootloader.h new file mode 100644 index 0000000..6a46b72 --- /dev/null +++ b/include/bootloader.h @@ -0,0 +1,53 @@ +#ifndef BOOTLOADER_H +#define BOOTLOADER_H + +#include +#include + +#include "bootloader_config.h" + +typedef enum { + BOOT_STATE_IDLE = 0, + BOOT_STATE_HANDSHAKE, + BOOT_STATE_RECEIVING, + BOOT_STATE_VERIFYING, + BOOT_STATE_WRITING, + BOOT_STATE_UPDATING, + BOOT_STATE_JUMP_APP, + BOOT_STATE_ERROR, +} bootloader_state_t; + +typedef enum { + BOOT_OK = 0, + BOOT_ERR_FLASH_WRITE, + BOOT_ERR_FLASH_VERIFY, + BOOT_ERR_UART_TIMEOUT, + BOOT_ERR_CRC_MISMATCH, + BOOT_ERR_AES_DECRYPT, + BOOT_ERR_PROTOCOL, + BOOT_ERR_APP_INVALID, +} bootloader_error_t; + +void bootloader_init(void); +void bootloader_process(void); +bool bootloader_check_entry(void); + +bootloader_state_t bootloader_get_state(void); +bootloader_error_t bootloader_get_error(void); + +void bootloader_jump_to_app(uint32_t app_addr); +bool bootloader_check_app_valid(uint32_t app_addr); + +typedef struct { + uint32_t magic; + uint32_t version; + uint32_t checksum; + uint32_t bank_id; + uint32_t timestamp; + uint32_t size; +} firmware_version_t; + + +firmware_version_t *bootloader_get_version(void); + +#endif diff --git a/include/bootloader_config.h b/include/bootloader_config.h new file mode 100644 index 0000000..2b4d204 --- /dev/null +++ b/include/bootloader_config.h @@ -0,0 +1,85 @@ +#ifndef BOOTLOADER_CONFIG_H +#define BOOTLOADER_CONFIG_H + +/* + * MCU Platform and Feature Configuration for STM32F4xx Bootloader + */ +#define MCU_FAMILY_STM32F4XX 1 +#define MCU_FAMILY_STM32F1XX 0 + +/* + * Flash Memory Layout Configuration + */ +#define BOOTLOADER_SIZE_KB 32 +#define BOOTLOADER_BASE_ADDR 0x08000000 +#define BOOTLOADER_END_ADDR 0x08007FFF + +#define APP_BASE_ADDR 0x08008000 +#define BANK1_ADDR 0x08008000 +#define BANK1_SIZE (192 * 1024) +#define BANK2_ADDR 0x08038000 +#define BANK2_SIZE (192 * 1024) + +#define VERSION_INFO_ADDR 0x08007C00 +#define VERSION_INFO_SIZE 1024 + +#define APP_SIZE_MAX BANK1_SIZE + +/* + * Uart Configuration for Bootloader Communication + */ +#define BOOT_UART_INSTANCE USART1 +#define BOOT_UART_BAUDRATE 115200 +#define BOOT_UART_TIMEOUT_MS 3000 +#define BOOT_UART_BUFFER_SIZE 2048 + +/* + * Protocol Configuration for Firmware Update + */ +#define PROTOCOL_FRAME_HEAD 0xAA +#define PROTOCOL_FRAME_TAIL 0x55 +#define PROTOCOL_MAX_DATA_LEN 1024 +#define PROTOCOL_RETRY_COUNT 3 +#define PROTOCOL_RETRY_DELAY_MS 100 + +/* + * Bootloader Feature Configuration + */ + +#define BOOT_ENABLE_AES 1 +#define BOOT_ENABLE_CRC 1 +#define BOOT_ENABLE_DUAL_BANK 1 +#define BOOT_ENABLE_VERSION_MGR 1 +#define BOOT_ENABLE_LOG 1 + +/* + * Security Configuration for Bootloader + */ +#define BOOT_AES_KEY_SIZE 256 +#define BOOT_AES_IV_SIZE 16 +// AES-256密钥(示例,实际应用应从安全区域读取) +#define BOOT_AES_KEY {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, \ + 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, \ + 0x09, 0xcf, 0x4f, 0x3c, 0x76, 0x2e, \ + 0x71, 0x60, 0xf3, 0x8b, 0x4d, 0xa5, \ + 0x6a, 0x78, 0x4d, 0x90, 0x45, 0x19, \ + 0x0c, 0xfe} + +/* + * Other Configuration + */ +#define BOOT_TRIGGER_TIMEOUT_MS 3000 + +/* Logging Macros */ +#if BOOT_ENABLE_LOG +#define BOOT_LOG_DEBUG(fmt, ...) do { printf("[DEBUG] " fmt "\r\n", ##__VA_ARGS__); } while(0) +#define BOOT_LOG_INFO(fmt, ...) do { printf("[INFO] " fmt "\r\n", ##__VA_ARGS__); } while(0) +#define BOOT_LOG_WARN(fmt, ...) do { printf("[WARN] " fmt "\r\n", ##__VA_ARGS__); } while(0) +#define BOOT_LOG_ERROR(fmt, ...) do { printf("[ERROR] " fmt "\r\n", ##__VA_ARGS__); } while(0) +#else +#define BOOT_LOG_DEBUG(fmt, ...) do { } while(0) +#define BOOT_LOG_INFO(fmt, ...) do { } while(0) +#define BOOT_LOG_WARN(fmt, ...) do { } while(0) +#define BOOT_LOG_ERROR(fmt, ...) do { } while(0) +#endif +#endif diff --git a/include/bsp_flash.h b/include/bsp_flash.h new file mode 100644 index 0000000..52e2aec --- /dev/null +++ b/include/bsp_flash.h @@ -0,0 +1,25 @@ +#ifndef BSP_FLASH_H +#define BSP_FLASH_H + +#include +#include + +typedef struct { + int (*init)(void); + int (*read)(uint32_t address, uint8_t *buffer, uint32_t length); + int (*write)(uint32_t address, const uint8_t *data, uint32_t length); + int (*erase)(uint32_t address, uint32_t length); + bool (*verify)(uint32_t address, const uint8_t *data, uint32_t length); +} bsp_flash_ops_t; + +void bsp_flash_register(const bsp_flash_ops_t *ops); +int bsp_flash_init(void); +int bsp_flash_erase(uint32_t address, uint32_t length); +int bsp_flash_write(uint32_t address, const uint8_t *data, uint32_t length); +int bsp_flash_read(uint32_t address, uint8_t *buffer, uint32_t length); +bool bsp_flash_verify(uint32_t address, const uint8_t *data, uint32_t length); + + +bool bsp_flash_is_app_addr(uint32_t address); +bool bsp_flash_check_app_valid(uint32_t app_addr); +#endif diff --git a/include/bsp_uart.h b/include/bsp_uart.h new file mode 100644 index 0000000..1bcfce3 --- /dev/null +++ b/include/bsp_uart.h @@ -0,0 +1,19 @@ +#ifndef BSP_UART_H +#define BSP_UART_H +#include +#include +typedef struct { + int (*init)(uint32_t baudrate); + int (*send)(const uint8_t *data, uint32_t length); + int (*recv)(uint8_t *buffer, uint32_t length, uint32_t timeout_ms); + void (*deinit)(void); +} bsp_uart_ops_t; + +void bsp_uart_register(const bsp_uart_ops_t *ops); +int bsp_uart_init(uint32_t baudrate); +int bsp_uart_send(const uint8_t *data, uint32_t length); +int bsp_uart_recv(uint8_t *buffer, uint32_t length, uint32_t timeout_ms); +int bsp_uart_recv_byte(uint8_t *byte, uint32_t timeout_ms); +void bsp_uart_deinit(void); + +#endif diff --git a/port/stm32f4xx/flash_port.c b/port/stm32f4xx/flash_port.c new file mode 100644 index 0000000..8a2dace --- /dev/null +++ b/port/stm32f4xx/flash_port.c @@ -0,0 +1,91 @@ +#include "stm32f407xx.h" +#include "stm32f4xx_hal_flash.h" +#include "stm32f4xx_hal_flash_ex.h" +#include +#include +static uint32_t flash_get_sector(uint32_t address) { + address -= FLASH_BASE; + if (address < 0x4000) return FLASH_SECTOR_0; + else if (address < 0x8000) return FLASH_SECTOR_1; + else if (address < 0xC000) return FLASH_SECTOR_2; + else if (address < 0x10000) return FLASH_SECTOR_3; + else if (address < 0x20000) return FLASH_SECTOR_4; + else if (address < 0x40000) return FLASH_SECTOR_5; + else if (address < 0x60000) return FLASH_SECTOR_6; + else if (address < 0x80000) return FLASH_SECTOR_7; + else if (address < 0xA0000) return FLASH_SECTOR_8; + else if (address < 0xC0000) return FLASH_SECTOR_9; + else if (address < 0xE0000) return FLASH_SECTOR_10; + else return FLASH_SECTOR_11; +} + +int flash_port_erase(uint32_t address, uint32_t length) { + HAL_FLASH_Unlock(); + uint32_t start_sector = flash_get_sector(address); + uint32_t end_sector = flash_get_sector(address + length - 1); + + __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR); + FLASH_EraseInitTypeDef erase; + erase.TypeErase = FLASH_TYPEERASE_SECTORS; + erase.Sector = start_sector; + erase.VoltageRange = FLASH_VOLTAGE_RANGE_3; + erase.NbSectors = end_sector - start_sector + 1; + uint32_t sector_error = 0; + if (HAL_FLASHEx_Erase(&erase, §or_error) != HAL_OK) { + HAL_FLASH_Lock(); + return -1; + } + HAL_FLASH_Lock(); + return 0; +} + +int flash_port_write(uint32_t address, const uint8_t *data, uint32_t length) { + HAL_FLASH_Unlock(); + uint32_t written = 0; + while (written < length) { + + uint32_t chunk_data; + if (length - written >= 4) { + chunk_data = *(uint32_t *)(data + written); + } else { + word_data = 0xFFFFFFFF; + for (uint32_t i = 0; i < length - written; i++) { + ((uint8_t *)&chunk_data)[i] = data[written + i]; + } + } + if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, address + written, chunk_data) != HAL_OK) { + HAL_FLASH_Lock(); + return -1; + } + written += 4; + } + HAL_FLASH_Lock(); + return 0; +} + +int flash_port_read(uint32_t address, uint8_t *buffer, uint32_t length) { + memcpy(buffer, (const void *)address, length); + return 0; +} + +bool flash_port_verify(uint32_t address, const uint8_t *data, uint32_t length) { + uint8_t read_data[length]; + flash_port_read(address, read_data, length); + return memcmp(read_data, data, length) == 0; +} + +int flash_port_init(void) { + return 0; +} + +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, +}; + +void flash_port_register(void) { + bsp_flash_register(&flash_ops); +} \ No newline at end of file diff --git a/port/stm32f4xx/uart_port.c b/port/stm32f4xx/uart_port.c new file mode 100644 index 0000000..d802e9c --- /dev/null +++ b/port/stm32f4xx/uart_port.c @@ -0,0 +1,44 @@ +#include "stm32f4xx_hal_uart.h" +#include +#define UART_HANDLE huart1 +extern UART_HandleTypeDef UART_HANDLE; + +int uart_port_init(uint32_t baudrate) { + + if (UART_HANDLE.Init.BaudRate != baudrate) { + UART_HANDLE.Init.BaudRate = baudrate; + if (HAL_UART_Init(&UART_HANDLE) != HAL_OK) { + return -1; + } + } + return 0; +} + +int uart_port_send(const uint8_t *data, uint32_t length) { + if (HAL_UART_Transmit(&UART_HANDLE, (uint8_t *)data, length, 1000) != HAL_OK) { + return -1; + } + return 0; +} + +int uart_port_recv(uint8_t *buffer, uint32_t length, uint32_t timeout_ms) { + if (HAL_UART_Receive(&UART_HANDLE, buffer, length, timeout_ms) != HAL_OK) { + return -1; + } + return 0; +} + +void uart_port_deinit(void) { + HAL_UART_DeInit(&UART_HANDLE); +} + +static const bsp_uart_ops_t uart_ops = { + .init = uart_port_init, + .send = uart_port_send, + .recv = uart_port_recv, + .deinit = uart_port_deinit, +}; + +void uart_port_register(void) { + bsp_uart_register(&uart_ops); +} diff --git a/src/aes_crypto.c b/src/aes_crypto.c new file mode 100644 index 0000000..7ec8a6c --- /dev/null +++ b/src/aes_crypto.c @@ -0,0 +1 @@ +// Placeholder diff --git a/src/bootloader.c b/src/bootloader.c new file mode 100644 index 0000000..e69de29 diff --git a/src/bsp_flash.c b/src/bsp_flash.c new file mode 100644 index 0000000..eafe45c --- /dev/null +++ b/src/bsp_flash.c @@ -0,0 +1,56 @@ +#include +#include "bsp_flash.h" +#include "bootloader_config.h" + +static bsp_flash_ops_t *g_flash_ops = NULL; + +void bsp_flash_register(const bsp_flash_ops_t *ops) { + g_flash_ops = (bsp_flash_ops_t *)ops; +} + +int bsp_flash_init(void) { + if (g_flash_ops && g_flash_ops->init) { + return g_flash_ops->init(); + } + return -1; +} + +int bsp_flash_erase(uint32_t address, uint32_t length) { + if (g_flash_ops && g_flash_ops->erase) { + return g_flash_ops->erase(address, length); + } + return -1; +} +int bsp_flash_write(uint32_t address, const uint8_t *data, uint32_t length) { + if (g_flash_ops && g_flash_ops->write) { + return g_flash_ops->write(address, data, length); + } + return -1; +} +int bsp_flash_read(uint32_t address, uint8_t *buffer, uint32_t length) { + if (g_flash_ops && g_flash_ops->read) { + return g_flash_ops->read(address, buffer, length); + } + return -1; +} +bool bsp_flash_verify(uint32_t address, const uint8_t *data, uint32_t length) { + if (g_flash_ops && g_flash_ops->verify) { + return g_flash_ops->verify(address, data, length); + } + return false; +} + +bool bsp_flash_is_app_addr(uint32_t address) { + return (address >= APP_BASE_ADDR) && (address < (APP_BASE_ADDR + APP_SIZE_MAX)); +} +bool bsp_flash_check_app_valid(uint32_t app_addr) { + uint32_t stack_ptr = *((volatile uint32_t *)app_addr); + if (stack_ptr < 0x20000000 || stack_ptr > 0x20000000 + 128 * 1024) { + return false; + } + uint32_t reset_ptr = *((volatile uint32_t *)(app_addr + 4)); + if (reset_ptr < app_addr || reset_ptr > app_addr + APP_SIZE_MAX) { + return false; + } + return true; +} \ No newline at end of file diff --git a/src/bsp_uart.c b/src/bsp_uart.c new file mode 100644 index 0000000..ccb3c8d --- /dev/null +++ b/src/bsp_uart.c @@ -0,0 +1,39 @@ +#include +#include "bsp_uart.h" + +static bsp_uart_ops_t *g_uart_ops = NULL; + +void bsp_uart_register(const bsp_uart_ops_t *ops) { + g_uart_ops = (bsp_uart_ops_t *)ops; +} + +int bsp_uart_init(uint32_t baudrate) { + if (g_uart_ops && g_uart_ops->init) { + return g_uart_ops->init(baudrate); + } + return -1; +} + +int bsp_uart_send(const uint8_t *data, uint32_t length) { + if (g_uart_ops && g_uart_ops->send) { + return g_uart_ops->send(data, length); + } + return -1; +} + +int bsp_uart_recv(uint8_t *buffer, uint32_t length, uint32_t timeout_ms) { + if (g_uart_ops && g_uart_ops->recv) { + return g_uart_ops->recv(buffer, length, timeout_ms); + } + return -1; +} + +int bsp_uart_recv_byte(uint8_t *byte, uint32_t timeout_ms) { + return bsp_uart_recv(byte, 1, timeout_ms); +} + +void bsp_uart_deinit(void) { + if (g_uart_ops && g_uart_ops->deinit) { + g_uart_ops->deinit(); + } +}