1. DSP28335 Bootloader升级方案概述
在工业控制和电力电子领域,DSP28335凭借其出色的实时性能和丰富的外设资源,一直是众多工程师的首选方案。而固件在线升级能力,则是现代化设备不可或缺的核心功能。本文将详细介绍基于CAN总线的DSP28335 Bootloader实现方案,这套方案已经在多个工业现场稳定运行超过3年,累计完成超过10万次安全升级。
传统的JTAG烧录方式需要开箱操作,不仅效率低下,而且对于安装在复杂环境中的设备几乎无法实施。相比之下,CAN总线Bootloader方案具有以下显著优势:
- 升级过程无需拆机,通过设备现有CAN接口即可完成
- 支持远程批量升级,极大降低维护成本
- 升级速度可达1Mbps,1MB固件仅需30秒左右
- 内置校验和回滚机制,确保升级失败自动恢复
2. Bootloader核心原理与实现
2.1 启动流程设计
DSP28335的Bootloader本质上是一段存储在Flash起始位置的常驻程序,其核心职责是决定启动路径。我们设计的双模式启动流程如下:
- 上电后首先运行Bootloader
- 检查升级标志位(0x8000地址)
- 若检测到升级指令(0xAA55AA55),进入CAN升级模式
- 否则跳转到用户应用程序(0x3F8000地址)
这个设计的关键在于标志位的设置与保护。在实际项目中,我们采用了以下保护措施:
- 标志位区域单独划分在Flash Sector 0
- 应用程序擦写时跳过该扇区
- 添加ECC校验防止数据异常
2.2 内存布局规划
合理的内存规划是Bootloader稳定运行的基础。以下是经过验证的内存分配方案:
| 区域 | 起始地址 | 大小 | 用途 |
|---|---|---|---|
| Bootloader | 0x3F0000 | 32KB | 存储Bootloader代码 |
| 标志位区 | 0x3F8000 | 256B | 存储升级标志和关键参数 |
| 应用程序 | 0x3F8100 | 480KB | 用户程序存储区 |
| 备份区 | 0x3FF000 | 64KB | 旧版本备份区 |
注意:DSP28335的Flash分为多个扇区,擦除时必须按扇区操作。建议在CMD文件中明确定义各区域边界,避免越界擦除。
2.3 关键代码实现
Bootloader的核心跳转逻辑实现如下:
c复制// 检查升级标志位
volatile unsigned long *pFlag = (unsigned long *)0x3F8000;
if(*pFlag == 0xAA55AA55) {
// 初始化CAN通信
CAN_Init(1000000); // 1Mbps
Enter_Update_Mode();
} else {
// 检查应用程序有效性
if(Check_App_Valid(0x3F8100)) {
// 重映射中断向量
EALLOW;
PieVectTable.EINT1 = (PINT)(0x3F8100 + 0x0D);
// 更多向量重映射...
EDIS;
// 跳转到应用程序
asm(" LB #0x3F8100");
} else {
// 应用程序无效,进入紧急模式
System_Halt();
}
}
这段代码有几个关键点需要注意:
- 使用volatile防止编译器优化标志位读取
- 跳转前必须完成中断向量重映射
- 应用程序需进行CRC或校验和验证
3. CAN通信协议设计
3.1 数据帧格式
我们设计了专用的通信协议确保传输可靠性,每帧CAN数据包含:
| 字段 | 长度 | 说明 |
|---|---|---|
| 帧类型 | 1字节 | 0x01:数据帧 0x02:ACK 0x03:NAK |
| 序列号 | 2字节 | 数据包序号(0~65535) |
| 地址 | 4字节 | Flash写入地址 |
| 数据 | 0-8字节 | 有效载荷 |
| CRC16 | 2字节 | 数据校验码 |
这种设计可以实现:
- 最大支持64KB数据块传输
- 每帧独立校验确保数据完整
- 支持重传机制
3.2 通信状态机
Bootloader与上位机的交互采用状态机模式:
- 握手阶段:上位机发送SYNC帧,Bootloader回应设备信息
- 准备阶段:上位机发送固件信息(大小、版本等)
- 传输阶段:分块传输固件数据
- 验证阶段:校验整个固件完整性
- 完成阶段:设置启动标志,准备重启
c复制typedef enum {
STATE_IDLE,
STATE_SYNC,
STATE_READY,
STATE_TRANSFER,
STATE_VERIFY,
STATE_FINISH
} Bootloader_State;
// 状态处理函数示例
void Handle_Transfer_State(CAN_Frame *frame) {
if(frame->seq == current_seq) {
Program_Flash(frame->addr, frame->data);
Send_ACK(frame->seq);
current_seq++;
} else if(frame->seq < current_seq) {
// 重复包,直接ACK
Send_ACK(frame->seq);
} else {
// 乱序包,请求重传
Send_NAK(current_seq);
}
}
3.3 错误处理机制
可靠的升级系统必须具备完善的错误处理:
- 超时重传:每个数据包等待ACK超时时间为500ms,最多重试3次
- 数据校验:每帧数据包含CRC16校验,错误立即丢弃
- 断点续传:记录最后成功接收的包序号,中断后可恢复
- 回滚机制:升级失败自动恢复备份版本
4. Flash操作关键细节
4.1 安全擦写流程
DSP28335的Flash操作需要严格遵守时序:
c复制void Flash_Erase(uint32_t addr, uint16_t length) {
EALLOW;
// 禁用看门狗
DisableDog();
// 配置Flash时序
FlashRegs.FBANKWAIT.bit.PAGEWAIT = 3;
FlashRegs.FBANKWAIT.bit.RANDWAIT = 1;
FlashRegs.FOTPWAIT.bit.OTPWAIT = 5;
// 必须关闭流水线模式
MemCfgRegs.FlashRegs.acceso.bit.ENPIPE = 0;
// 执行擦除
Fapi_issueAsyncCommandWithAddress(Fapi_EraseSector, addr);
while(Fapi_checkFsmForReady() != Fapi_Status_FsmReady);
EDIS;
}
警告:Flash操作期间必须禁止中断,否则会导致程序崩溃。建议将关键Flash操作代码复制到RAM中执行。
4.2 数据编程技巧
Flash编程需要注意以下要点:
- 必须按64位(2x32位)对齐写入
- 每次编程前需要擦除整个扇区
- 编程电压必须稳定(建议在3.3V±5%范围内)
优化后的编程函数示例:
c复制#pragma CODE_SECTION(Flash_Program, "ramfuncs");
void Flash_Program(uint32_t addr, uint32_t *data) {
EALLOW;
DisableDog();
// 等待Flash就绪
while(Fapi_checkFsmForReady() != Fapi_Status_FsmReady);
// 设置编程模式
Fapi_setActiveFlashBank(addr);
// 执行编程
Fapi_issueProgrammingCommand(
(uint32 *)addr, data, 2,
0, 0, Fapi_AutoEccGeneration);
// 等待完成
while(Fapi_checkFsmForReady() != Fapi_Status_FsmReady);
EDIS;
}
5. 上位机软件实现
5.1 核心功能设计
基于C#的上位机软件主要功能模块:
- 固件解析:解析.out或.hex文件,提取有效代码段
- 通信管理:CAN设备初始化、数据包调度
- 进度控制:分块传输、超时管理
- 日志记录:详细记录升级过程,便于问题排查
关键传输逻辑:
csharp复制public async Task StartUpgrade()
{
// 初始化CAN设备
var can = new ZlgCanDevice();
await can.Init(1_000_000);
// 发送同步帧
await can.SendSyncFrame();
// 等待设备响应
var devInfo = await WaitDeviceResponse();
// 分块传输
var chunks = SplitFirmware();
foreach(var chunk in chunks)
{
int retry = 3;
bool success = false;
while(retry-- > 0 && !success)
{
await can.SendDataFrame(chunk);
success = await WaitAck(chunk.Seq);
}
if(!success) throw new TimeoutException();
// 更新进度
UpdateProgress(chunk.Seq * 100 / chunks.Count);
}
// 发送完成指令
await can.SendFinishFrame();
}
5.2 异常处理设计
上位机需要处理以下常见异常:
- CAN设备未连接:自动检测设备状态,提示用户
- 目标无响应:超时后重试,超过阈值报错
- 数据校验失败:自动重发错误数据包
- 电源异常:检测电压波动,异常时中止升级
6. 实战经验与避坑指南
6.1 常见问题排查
-
跳转失败问题
- 现象:Bootloader执行跳转后系统挂起
- 原因:应用程序中断向量未正确重映射
- 解决:在应用程序初始化时重新配置PIE向量表
-
CAN通信不稳定
- 现象:数据包频繁丢失
- 原因:终端电阻未正确配置或波特率不匹配
- 解决:确保总线两端有120Ω终端电阻,精确计算波特率
-
Flash编程错误
- 现象:编程后数据校验失败
- 原因:电压不稳或时序配置不当
- 解决:检查电源质量,调整Flash等待状态
6.2 性能优化技巧
-
RAM运行加速
将频繁执行的代码(如CAN中断服务程序)放到RAM中:c复制#pragma CODE_SECTION(CAN_ISR, "ramfuncs"); __interrupt void CAN_ISR(void) { // 中断处理代码 } -
批量编程优化
将多个64位数据合并编程,减少擦写次数:c复制#define BATCH_SIZE 8 uint32_t buffer[BATCH_SIZE * 2]; // 收集足够数据后批量编程 Flash_Program_Batch(addr, buffer, BATCH_SIZE); -
差分升级实现
仅传输有变化的扇区,大幅减少升级时间:csharp复制// 上位机计算差异 var diff = CompareFirmware(oldVer, newVer); SendOnlyChangedSectors(diff);
7. 扩展应用与变体方案
7.1 多核DSP升级
对于F2838x等多核DSP,升级方案需要特别处理:
- 主从核协调:通过IPC机制同步升级状态
- 核间依赖管理:确保固件版本兼容
- 独立验证:每个核单独校验自身固件
7.2 无线升级方案
结合无线模块实现远程升级:
- 4G/WiFi转CAN网关设计
- 低功耗模式下的唤醒机制
- 空中升级(OTA)安全加密
7.3 工厂生产模式
适配生产线需求的特殊优化:
- 高速批量编程(并行多个CAN节点)
- 自动序列号写入
- 生产测试一体化流程
在实际项目中,我们曾遇到一个棘手问题:某批设备在高温环境下升级失败率陡增。经过深入分析发现是Flash编程时序未考虑温度补偿。最终通过动态调整等待状态解决了这个问题,这也提醒我们在设计Bootloader时必须充分考虑实际工作环境。
