STM32F103 U盘升级方案设计与实现
一、系统概述
U盘升级是通过USB接口将U盘中的固件文件(.bin)读取到STM32F103内部Flash,实现程序更新的技术。本方案基于STM32F103C8T6(64KB Flash,20KB RAM),通过CH375 USB Host控制器连接U盘,结合FATFS文件系统读取升级文件,最终将文件写入应用程序区Flash,实现无缝升级。核心功能包括:U盘识别、FAT32文件解析、固件校验、Flash擦写、 bootloader跳转。
二、硬件设计
2.1 核心组件
| 模块 | 型号/参数 | 功能说明 |
|---|---|---|
| 主控 | STM32F103C8T6(Cortex-M3,72MHz) | Bootloader程序、USB Host控制、Flash擦写 |
| USB Host | CH375(USB Host控制器,SPI接口) | 转换USB Host信号为SPI,连接U盘 |
| 存储介质 | MicroSD卡/U盘(FAT32格式,≤32GB) | 存储升级文件(如UPDATE.bin) |
| 电源 | 5V USB供电(或3.3V LDO) | 为STM32、CH375、U盘供电 |
2.2 硬件连接
| 模块 | 引脚(STM32F103C8T6) | CH375引脚 | 说明 |
|---|---|---|---|
| SPI1 | PA5(SCK) | SCK | SPI时钟 |
| PA6(MISO) | MISO | SPI数据输入(CH375→STM32) | |
| PA7(MOSI) | MOSI | SPI数据输出(STM32→CH375) | |
| PA4(NSS) | CS | SPI片选(低电平有效) | |
| CH375 | INT(中断输出) | PB0(EXTI0) | U盘插入/数据就绪中断 |
| U盘 | USB_DP/USB_DM | CH375 USB接口 | 连接U盘 |
三、软件设计
3.1 系统架构
graph TD
A[Bootloader启动] --> B{检测升级请求?}
B -->|U盘插入+按键按下| C[初始化CH375+SPI]
C --> D[枚举U盘+FATFS挂载]
D --> E[读取UPDATE.bin文件]
E --> F[校验文件(CRC32)]
F --> G[擦除应用程序区Flash]
G --> H[写入新固件]
H --> I[校验Flash数据]
I --> J[跳转到应用程序]
B -->|无升级请求| J
3.2 核心模块实现
3.2.1 Bootloader分区设计
STM32F103C8T6 Flash共64KB,划分为:
- Bootloader区:0x08000000~0x08003FFF(16KB),存放升级程序;
- 应用程序区:0x08004000~0x0801FFFF(48KB),存放用户程序;
- 标志区:0x0801FFFC~0x0801FFFF(4字节),存储升级标志(0xAA55表示需升级)。
3.2.2 CH375 USB Host驱动(SPI接口)
CH375通过SPI与STM32通信,核心函数包括初始化、读写寄存器、枚举U盘:
#include "ch375spi.h"
#include "spi.h"
// SPI读写CH375寄存器
uint8_t CH375_ReadReg(uint8_t reg) {
uint8_t data;
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); // CS=0
HAL_SPI_Transmit(&hspi1, ®, 1, 100); // 发送寄存器地址
HAL_SPI_Receive(&hspi1, &data, 1, 100); // 读取数据
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); // CS=1
return data;
}
void CH375_WriteReg(uint8_t reg, uint8_t data) {
uint8_t buf[2] = {reg, data};
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
HAL_SPI_Transmit(&hspi1, buf, 2, 100);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
}
// 初始化CH375(USB Host模式)
void CH375_Init(void) {
CH375_WriteReg(CH375_MODE, 0x06); // 模式寄存器:USB Host,SPI接口
HAL_Delay(100);
uint8_t status = CH375_ReadReg(CH375_STATUS);
if (status == 0x15) printf("CH375初始化成功\r\n");
}
3.2.3 FATFS文件系统挂载(读取U盘文件)
使用FATFS R0.14b读取U盘中的UPDATE.bin:
#include "ff.h"
#include "diskio.h"
FATFS fs;
FIL file;
UINT br;
// 挂载U盘
FRESULT Mount_USB(void) {
FRESULT res = f_mount(&fs, "0:", 1); // "0:"为U盘盘符
if (res != FR_OK) printf("挂载失败: %d\r\n", res);
return res;
}
// 读取UPDATE.bin文件
uint8_t Read_UpdateFile(uint8_t *buf, uint32_t len) {
FRESULT res = f_open(&file, "0:/UPDATE.bin", FA_READ);
if (res != FR_OK) return 1;
res = f_read(&file, buf, len, &br); // 读取len字节
f_close(&file);
return (res == FR_OK && br == len) ? 0 : 1;
}
3.2.4 Flash擦写驱动(STM32内部Flash)
STM32F103 Flash擦除按页(1KB),写入按字(32位):
#include "stm32f10x_flash.h"
// 擦除应用程序区(0x08004000~0x0801FFFF)
void Flash_EraseApp(void) {
FLASH_Unlock();
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
uint32_t addr = 0x08004000;
while (addr < 0x08020000) { // 应用程序区结束地址
FLASH_ErasePage(addr);
addr += 1024; // 1KB页
}
FLASH_Lock();
}
// 写入固件数据(addr:Flash地址,buf:数据,len:字节数)
void Flash_Write(uint32_t addr, uint8_t *buf, uint32_t len) {
FLASH_Unlock();
uint32_t *src = (uint32_t*)buf;
uint32_t words = len / 4;
for (uint32_t i=0; i<words; i++) {
FLASH_ProgramWord(addr, src[i]); // 按字写入
addr += 4;
}
FLASH_Lock();
}
3.2.5 Bootloader主流程
int main(void) {
HAL_Init();
SystemClock_Config();
SPI1_Init(); // SPI1初始化(CH375通信)
CH375_Init(); // CH375初始化
Button_Init(); // 按键初始化(触发升级)
// 检测升级标志(标志区0x0801FFFC)
uint32_t flag = *(uint32_t*)0x0801FFFC;
if (flag == 0xAA55 || (Button_IsPressed() && CH375_UDiskInserted())) {
// 执行升级流程
if (Mount_USB() == FR_OK) {
uint8_t firmware[48 * 1024]; // 应用程序区大小(48KB)
if (Read_UpdateFile(firmware, sizeof(firmware)) == 0) {
Flash_EraseApp();
Flash_Write(0x08004000, firmware, sizeof(firmware));
*(uint32_t*)0x0801FFFC = 0x55AA; // 清除升级标志
NVIC_SystemReset(); // 重启
}
}
}
// 跳转到应用程序(0x08004000)
JumpToApp(0x08004000);
}
// 跳转到应用程序
void JumpToApp(uint32_t addr) {
typedef void (*AppFunc)(void);
AppFunc app = (AppFunc)*(uint32_t*)(addr + 4); // 应用程序入口地址
__set_MSP(*(uint32_t*)addr); // 设置堆栈指针
app(); // 跳转
}
参考代码 STM32F103实现U盘升级 www.youwenfan.com/contentcss/56419.html
四、关键问题与解决方案
4.1 U盘枚举失败
- 原因:CH375未正确初始化、U盘格式非FAT32、供电不足。
- 解决:用示波器测量CH375的USB_DP/DM信号,确保U盘供电稳定(5V/500mA),格式化U盘为FAT32(簇大小4KB)。
4.2 Flash写入错误
- 原因:未按页擦除直接写入、写入地址未对齐(STM32 Flash需32位对齐)。
- 解决:确保擦除后再写入,数据缓冲区按4字节对齐(
__attribute__((aligned(4))))。
4.3 升级文件校验失败
- 原因:文件损坏、读取不完整。
- 解决:添加CRC32校验(如使用STM32硬件CRC模块),读取文件后校验完整性。
五、测试步骤
- 硬件连接:按2.2节连接STM32、CH375、U盘;
- 编译Bootloader:生成.bin文件,通过ST-Link烧录至0x08000000;
- 准备升级文件:将用户程序编译为.bin,重命名为UPDATE.bin放入U盘根目录;
- 触发升级:插入U盘,按下升级按键,观察串口输出(如“升级成功”);
- 验证:重启后运行新程序,确认功能正常。
六、总结
本方案基于STM32F103和CH375实现了U盘升级,核心是Bootloader分区、USB Host控制、FATFS文件读取、Flash擦写。通过模块化设计,可扩展支持加密升级(如AES解密)、断点续传等功能。