1. DSP28335在线升级技术概述
在工业控制和电力电子领域,TI的DSP28335凭借其出色的实时性能和丰富的外设资源,一直是电机控制、光伏逆变器等应用的首选处理器。但在实际产品迭代中,我们经常面临一个现实问题:如何在不拆机的情况下完成固件更新?这就是Bootloader技术的用武之地。
我经历过多次现场升级的"血泪史":早期采用JTAG烧录器逐个设备升级,不仅效率低下,还容易因接触不良导致升级失败。后来改用串口ISP方案,虽然解决了拆机问题,但升级速度慢如蜗牛(115200bps速率下升级100KB固件需要近10分钟)。直到开发出基于CAN总线的多节点并行升级方案,才真正实现了"分钟级"的产线批量更新。
2. Bootloader设计核心原理
2.1 存储器分区策略
DSP28335的Flash分为多个扇区(Sector),每个扇区大小16KB。我们的分区方案如下:
| 地址范围 | 用途 | 大小 |
|---|---|---|
| 0x3F8000-0x3FBFFF | Bootloader代码区 | 16KB |
| 0x3FC000-0x3FFFFF | 参数存储区 | 16KB |
| 0x000000-0x3F7FFF | 应用程序区 | 248KB |
关键提示:TI官方例程默认将Flash起始地址分配给应用程序,但在Bootloader方案中需要反转这个布局,确保Bootloader始终位于固定地址。
2.2 启动流程重定向
芯片上电后执行流程如下:
- 从0x3FFFC0读取复位向量
- 跳转到Bootloader入口(_c_int00)
- 检查升级标志位和校验码
- 若需升级则进入下载模式,否则跳转到0x000000执行应用
实现跳转的关键汇编指令:
assembly复制 MOVW DP, #0
MOV @0, #0x0000 ; 设置跳转地址低16位
MOV @1, #0x0000 ; 设置跳转地址高16位
LB 0x0000 ; 长跳转到应用程序
2.3 通信协议设计
我们采用改良的XMODEM-1K协议,主要改进包括:
- 增加0x55AA前缀字作为帧同步
- 每包数据1024字节+4字节CRC32校验
- 支持滑动窗口协议实现断点续传
典型通信时序:
code复制Host: 发送升级请求(0xA5)
Device: 回复ACK(0x06)
Host: 发送数据包头(包含固件大小、CRC等)
Device: 校验通过后回复ACK
[循环传输数据包...]
Host: 发送结束包(0x04)
Device: 写入升级标志后复位
3. 具体实现步骤
3.1 开发环境搭建
需要准备的软件工具:
- Code Composer Studio v6+(配置Flash API)
- UniFlash工具(生成HEX格式固件)
- CAN分析仪(如PCAN-USB Pro)
- 自制上位机软件(C#实现)
关键配置步骤:
c复制// 在CMD文件中重定义存储器
MEMORY
{
BOOT_ROM : origin = 0x3F8000, length = 0x004000
APP_ROM : origin = 0x000000, length = 0x3F8000
}
// 链接器配置
SECTIONS
{
.bootloader : {} > BOOT_ROM
.text : {} > APP_ROM
...
}
3.2 Flash操作关键代码
擦除扇区示例:
c复制void Flash_Erase(uint32_t addr) {
EALLOW;
FlashRegs.FPWR.bit.PWR = 3; // 设置编程电压
FlashRegs.FBANKWAIT.bit.PAGEWAIT = 5; // 等待周期
FlashRegs.FSTDBYWAIT.bit.STDBYWAIT = 0x3FF;
Flash_Service(&FlashRegs, FLASH_ERASE, (uint16_t*)addr);
EDIS;
}
写入数据注意事项:
- 必须按64位对齐写入
- 每次写入前需要先擦除整个扇区
- 建议在RAM中运行Flash API(避免从Flash执行擦写操作)
3.3 应用程序改造要点
应用程序需要做以下适配:
- 修改链接文件将代码起始地址设为0x000000
- 中断向量表重映射:
c复制#pragma DATA_SECTION(PieVectTable, "PieVectTableFile");
volatile struct PIE_VECT_TABLE PieVectTable;
- 在main()首行添加延迟:
c复制DELAY_US(1000); // 等待Bootloader完全退出
4. 实战问题排查指南
4.1 典型故障现象及解决方案
| 故障现象 | 可能原因 | 解决方案 |
|---|---|---|
| 无法进入Bootloader模式 | GPIO引脚配置冲突 | 检查启动配置引脚上拉电阻 |
| 升级中途卡死 | CAN总线负载过高 | 降低波特率至500kbps以下 |
| 校验失败 | Flash写入未完成 | 增加写入后的延迟时间 |
| 跳转后程序跑飞 | 应用程序向量表未重映射 | 检查PieVectTable初始化 |
4.2 性能优化技巧
- 加速传输:
- 启用CAN FD模式(需硬件支持)
- 采用压缩算法(如LZ77)减小传输量
- 批量写入Flash(每次写满整个扇区)
- 安全增强:
c复制// 在应用程序首部添加签名块
#pragma DATA_SECTION(appHeader, ".appHeader")
const struct {
uint32_t magic; // 0x55AA1234
uint32_t crc32;
uint32_t version;
} appHeader = {0x55AA1234, 0, 0x0100};
- 功耗管理:
c复制// 升级过程中关闭外设时钟
SysCtrlRegs.PCLKCR0.bit.ADCENCLK = 0;
SysCtrlRegs.PCLKCR1.bit.EPWMENCLK = 0;
5. 进阶开发方向
对于需要更高可靠性的场景,建议实现以下功能:
- 双Bank切换:
- 将应用程序区分成BankA/B两个副本
- 通过标志位决定启动哪个Bank
- 新固件写入非活动Bank,验证通过后切换
- 差分升级:
python复制# 上位机生成差分包示例
def generate_diff(old_bin, new_bin):
from bsdiff4 import diff
with open(old_bin, 'rb') as f1, open(new_bin, 'rb') as f2:
old = f1.read()
new = f2.read()
patch = diff(old, new)
return patch
- 远程升级安全:
- 采用AES-256加密固件
- 添加数字签名验证(ECDSA算法)
- 实现回滚计数机制(防止反复刷写)
在实际项目中,我们通过CAN总线升级200个节点的完整系统仅需15分钟,相比传统方式效率提升20倍以上。这个过程中最深的体会是:Bootloader的稳定性比功能丰富更重要。建议首次实现时先确保基础传输可靠,再逐步添加高级功能。
