基于STM32F103C8T6的双区脱机烧录器开发实战
最近在嵌入式开发社区里,脱机烧录器的需求明显升温。不少开发者反馈,在产线环境或现场调试时,频繁连接电脑烧录程序既低效又不专业。今天我们就来深度剖析如何用STM32F103C8T6搭配W25Q64 Flash芯片,打造一个支持双程序存储的智能脱机烧录器。
1. 硬件架构设计与元器件选型
1.1 核心控制器选型
选择STM32F103C8T6作为主控主要基于三点考量:
- Cortex-M3内核的72MHz主频足够处理SWD协议通信
- 内置64KB Flash和20KB SRAM满足固件运行需求
- 丰富的GPIO和USART接口便于扩展
关键参数对比表:
| 型号 | 主频 | Flash | SRAM | 封装 | 价格(¥) |
|---|---|---|---|---|---|
| STM32F103C8T6 | 72MHz | 64KB | 20KB | LQFP48 | 12.5 |
| STM32F103CBT6 | 72MHz | 128KB | 20KB | LQFP48 | 15.8 |
| GD32F103C8T6 | 108MHz | 64KB | 20KB | LQFP48 | 9.8 |
提示:GD32虽然性价比高,但SWD时序兼容性可能存在问题,建议优先选择ST原厂芯片
1.2 存储方案设计
W25Q64(8MB SPI Flash)的选型考虑:
- 页编程时间仅0.7ms,擦除速度快
- 支持10万次擦写周期
- 兼容3.3V供电系统
电路设计要点:
// SPI引脚配置示例 GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.Pin = GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);2. SWD协议引擎实现
2.1 CMSIS-DAP精简移植
传统CMSIS-DAP需要USB支持,我们将其简化为纯SWD实现:
关键函数裁剪:
- 保留
swd_transfer_retry() - 重写
swd_init()适配GPIO模拟 - 优化
target_flash_program_page()
- 保留
时序控制要点:
; 典型SWD时序(72MHz下) SWDIO_HIGH: MOVS r0, #1 STRB r0, [r1, #GPIO_ODR] NOP NOP SWDIO_LOW: MOVS r0, #0 STRB r0, [r1, #GPIO_ODR] NOP2.2 目标芯片识别流程
通过SWD读取IDCODE实现自动识别:
- 发送至少50个SWCLK周期复位线序
- 发送JTAG-to-SWD切换序列
- 读取IDR寄存器获取芯片型号
常见STM32 IDCODE对照:
| 芯片型号 | IDCODE | 备注 |
|---|---|---|
| STM32F103x8 | 0x1BA01477 | 包括C8T6 |
| STM32F103xB | 0x1BA01477 | 包括CBT6 |
| STM32F401xC | 0x2BA01477 |
3. 双区存储管理系统
3.1 Flash空间划分方案
W25Q64的8MB空间分配策略:
区0(0x000000-0x3FFFFF):
- 4MB存储空间
- 包含文件头(芯片类型+文件大小)
- HEX文件原始数据
区1(0x400000-0x7FFFFF):
- 镜像区0结构
- 独立校验信息
注意:每个存储区前4字节保留为文件长度标识
3.2 动态加载机制
实现代码分块读取和烧录的关键逻辑:
void flash_program_chunk(uint32_t addr, uint8_t *buf, uint32_t size) { target_flash_init(addr); while(size > 0) { uint32_t chunk = (size > 1024) ? 1024 : size; if(target_flash_program_page(addr, buf, chunk) != 0) { // 错误处理 } addr += chunk; buf += chunk; size -= chunk; } target_flash_uninit(); }4. 串口通信协议设计
4.1 自定义轻量级协议
为提高可靠性,设计基于帧头的通信格式:
| 偏移 | 长度 | 内容 | 说明 |
|---|---|---|---|
| 0 | 1 | 0xAA | 帧头标识 |
| 1 | 1 | 命令类型 | 0x01:写入 0x02:读取 |
| 2 | 2 | 数据长度 | 大端格式 |
| 4 | N | 有效载荷 | 实际数据 |
| N+4 | 2 | CRC16 | 校验码 |
典型交互流程:
- 上位机发送"YHSWDDOWN"握手信号
- 设备回复"READY"
- 传输HEX文件分块数据
- 设备返回"ACK"+进度百分比
4.2 错误恢复机制
实现三重保障:
- 每帧数据CRC校验
- 关键操作应答超时重传
- Flash写入前预校验
# 上位机校验示例(Python) def calc_crc(data): crc = 0xFFFF for byte in data: crc ^= byte for _ in range(8): if crc & 0x0001: crc >>= 1 crc ^= 0xA001 else: crc >>= 1 return crc5. 完整工程实现要点
5.1 关键外设初始化顺序
- 系统时钟配置(优先使用内部HSI)
- GPIO和中断初始化
- SPI Flash接口初始化
- USART通信模块启动
- SWD GPIO配置
5.2 内存优化技巧
针对资源受限的C8T6型号:
- 使用
__attribute__((section(".ccmram")))将缓冲区放在CCM内存 - 启用-Os优化等级减少代码体积
- 关键函数添加
__inline提示
内存占用分析:
Program Size: Code=39256 RO-data=672 RW-data=168 ZI-data=20486. 实战调试经验
在面包板搭建原型时,遇到SWD通信不稳定的问题,最终发现是:
- 未添加10kΩ上拉电阻到SWDIO
- SWCLK走线过长引起信号振铃
- 电源滤波不足导致电压波动
改进措施:
- 在SWDIO和SWCLK上添加22pF电容滤波
- 缩短信号线长度至5cm内
- 增加100nF去耦电容靠近MCU
烧录速度实测数据:
| 文件大小 | 单次烧录时间 | 脱机烧录时间 |
|---|---|---|
| 32KB | 1.2s | 1.8s |
| 64KB | 2.3s | 3.1s |
| 128KB | 4.7s | 6.4s |