1. 项目背景与核心挑战
去年接手了一个工业控制器的远程升级需求,客户要求在不拆机的情况下通过CAN总线完成DSP28335固件更新。本以为就是个常规的IAP(In-Application Programming)功能移植,结果从HEX文件解析到双DSP通信,整整踩了两周的坑。最崩溃的时候,实验室的两块DSP开发板都被我锁死了,示波器的屏幕上全是CAN总线的错误帧。
这个项目的核心难点在于:
- DSP28335的Flash需要同时存放引导程序(Boot2)和应用程序(App),必须精确划分地址空间
- 从Boot2跳转到App时,需要处理中断关闭、流水线清空等底层细节
- LabVIEW上位机需要正确解析Intel HEX格式,并处理16位地址偏移问题
- 双DSP通过CAN通信时,波特率容错和错误恢复机制尤为关键
2. 下位机系统架构设计
2.1 Flash空间规划
在CCS6.1.3中,通过修改CMD文件将Flash划分为两个独立区域:
code复制MEMORY {
BOOT2 : origin = 0x3D000, length = 0x800 /* 2KB引导区 */
APP : origin = 0x3D800, length = 0x2800 /* 10KB应用区 */
}
关键点在于:
- Boot2区需要包含Flash操作驱动和最小化CAN协议栈
- App区起始地址必须16字节对齐,否则跳转时会触发异常
- 两个区域的代码段必须使用#pragma CODE_SECTION明确指定存放位置
2.2 二次引导程序实现
Boot2的核心逻辑流程:
- 初始化PLL和时钟系统
- 配置CAN控制器(波特率250kbps)
- 检测GPIO引脚判断是否进入升级模式
- 若收到升级命令,则擦除App区并写入新固件
- 校验通过后跳转到App入口地址
跳转前必须执行的保护操作:
c复制__asm(" CLRC INTM"); //关闭所有中断
__asm(" SPM 0"); //清空指令流水线
__asm(" MOVW DP, #0");//重置数据页指针
__asm(" LB _c_int00"); //跳转到App的C环境初始化
3. HEX文件解析与CAN通信协议
3.1 LabVIEW上位机解析HEX文件
标准Intel HEX文件格式示例:
code复制:020000040003F7
:100000000A0000000D0000001000000013000000E0
:00000001FF
在LabVIEW中需要特殊处理:
- 遇到04类型记录(扩展线性地址)时更新基地址
- 将16位地址与基地址相加得到实际物理地址
- 对数据记录进行CRC16校验(多项式0x8005)
- 过滤掉文件结束记录(01类型)
3.2 CAN通信协议设计
自定义的升级协议帧格式:
| 字节偏移 | 内容 | 说明 |
|---|---|---|
| 0 | 0xA5 | 帧头标识 |
| 1 | 序列号 | 0-255循环计数 |
| 2-3 | 数据长度 | 当前帧有效数据长度 |
| 4-5 | 地址高位 | 写入地址高16位 |
| 6-7 | 地址低位 | 写入地址低16位 |
| 8-N | 数据 | 有效数据(最大8字节) |
| N+1-N+2 | CRC16 | 校验和 |
关键参数:
- CAN ID:0x181(发送),0x281(接收)
- 波特率:250kbps(BRP=9)
- 超时时间:500ms
- 重试次数:3次
4. 双DSP通信的坑与解决方案
4.1 波特率同步问题
现象:节点2经常收不到数据,用CAN分析仪抓包发现误码率高达30%
根本原因:
- 两个DSP的晶振精度不同(一个7.3728MHz±100ppm,另一个±300ppm)
- CANBTC寄存器配置相同但实际波特率偏差3%
解决方案:
- 在Boot2中增加自适应波特率检测功能
- 通过发送0x55AA测试帧测量实际位时间
- 动态调整BRP值直到误差<1%
4.2 意外断电恢复机制
当升级过程中断电会导致:
- Flash写入不完整
- Boot2区可能损坏
设计的应急方案:
- 在Flash最后256字节备份关键参数
- 上电时检测GPIO12引脚电平
- 如果拉低则进入紧急恢复模式(通过CAN烧写Boot2)
恢复流程代码片段:
c复制if(GpioDataRegs.GPADAT.bit.GPIO12 == 0){
CAN_Init(125000); //低速模式提高可靠性
while(!ReceiveFullImage()){
Flash_Erase(BOOT2_START, 2048);
Flash_Write(BOOT2_START, g_RecvBuf, 2048);
}
}
5. 调试工具与技巧
5.1 必备工具清单
- CAN分析仪(推荐PCAN-USB)
- CCS6.1.3的Memory Browser功能
- 示波器(观察CAN_H/CAN_L差分信号)
- 自制HEX转BIN工具(带地址偏移补偿)
5.2 典型问题排查指南
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 跳转后死机 | 中断未关闭 | 跳转前执行CLRC INTM |
| CAN通信不稳定 | 终端电阻未接 | 在120Ω端接电阻 |
| HEX解析错误 | 地址偏移处理错误 | 检查04记录处理逻辑 |
| Flash写入失败 | 等待周期不足 | 调整FBANKWAIT.RANDWAIT值 |
| 双机通信不同步 | 波特率容差过大 | 启用自适应波特率检测 |
6. 关键代码实现细节
6.1 Flash驱动优化
原始Flash写入需要60ms/页,通过以下优化降至20ms:
c复制void Flash_Write(uint32_t addr, uint16_t *data, uint16_t length){
EALLOW;
FlashRegs.FOPT.bit.ENPIPE = 1; //启用流水线模式
FlashRegs.FBANKWAIT.bit.RANDWAIT = 5; //优化等待周期
FlashRegs.FSTDBYWAIT.bit.STDBYWAIT = 0x1FF;
EDIS;
// 实际写入操作
memcpy((void*)addr, data, length);
}
6.2 CAN中断处理
高效的双缓冲接收机制:
c复制interrupt void CAN_ISR(void){
uint16_t mailbox = CanaRegs.CANRMP.all;
if(mailbox & 0x1){ //邮箱1接收中断
g_CAN_RxBuf[g_RxIndex++] = CanaMboxes.MBOX1.MDRL.all;
CanaRegs.CANRMP.bit.RMP1 = 1; //清除中断标志
if(g_RxIndex >= 8){
PostTask(UPDATE_TASK);
}
}
}
7. 项目总结与改进方向
经过这个项目,总结了几个重要经验:
- 任何对Flash的操作都必须放在RAM中执行
- CAN通信至少要实现重传和超时机制
- 跳转地址必须进行严格的边界检查
下一步优化计划:
- 增加AES-128固件加密功能
- 实现差分升级减少数据传输量
- 开发基于Qt的跨平台上位机
最让我自豪的是,最终方案在工业现场连续运行6个月,完成了37次远程升级,成功率达到100%。那些通宵调试的日子,最终都化为了控制器上稳定闪烁的LED指示灯。