1. DSP28335串口升级方案概述
在嵌入式产品开发中,固件升级功能是刚需中的刚需。想象一下产品出厂后发现bug或者需要增加新功能,如果每次都要拆机用JTAG烧写,那工程师怕是要跑断腿。今天给大家分享一个在工业现场打磨两年多的DSP28335串口升级方案,这个方案最大的特点就是稳如老狗——经历过电压波动、电磁干扰等各种恶劣环境的考验。
这套方案包含完整的工具链:
- Bootloader源码(支持实时升级和上电升级)
- 用户工程模板(含中断向量重映射方案)
- 上位机工具(含C#源码)
- 详细通信协议文档
技术指标方面,采用115200bps波特率时实测传输速度约10KB/s,烧写256KB程序约需半分钟。虽然看起来不算快,但在工业场景下稳定性远比速度重要。方案采用512字节数据分包传输,配合CRC16校验和超时重传机制,确保数据传输万无一失。
2. Bootloader设计与实现
2.1 存储空间规划
Bootloader驻留在Flash的0x3F8000起始区域,这个地址选择很有讲究:
- 位于DSP28335的Flash存储区末尾(根据芯片手册,Flash结束于0x3F7FFF)
- 预留足够空间存放Bootloader代码(通常不超过32KB)
- 用户程序从0x3F4000开始,中间预留256KB空间给用户程序
c复制/* 关键跳转逻辑 */
void (*UserCode)(void);
UserCode = (void (*)(void))0x3F4000; // 用户程序入口地址
if(升级标志有效){
执行升级流程();
}else{
asm(" LB #0x3F4000"); // 绝对地址跳转
}
注意:使用LB指令(长跳转)而非BL指令,是因为我们需要绝对地址跳转而不是相对跳转。这个细节在早期的调试中曾导致跳转失败。
2.2 升级流程设计
完整的升级流程包含以下步骤:
- 上位机发送升级开始命令
- Bootloader擦除用户程序区(0x3F4000开始)
- 按512字节分包接收数据,每包校验CRC
- 收到完整程序后校验MD5
- 更新升级标志位
- 跳转到用户程序
特别要注意的是Flash擦写期间的电源管理:
- 关闭所有不必要的外设时钟
- 禁用看门狗(防止擦写过程中复位)
- 在关键操作前增加电压检测(防止低电压下误操作)
3. 用户工程适配要点
3.1 中断向量重映射
DSP28335的中断向量默认位于0x000000开始的存储器空间,这与Bootloader存在冲突。解决方案是重映射中断向量到用户程序空间:
c复制#pragma DATA_SECTION(interruptVecs, "userVecs")
extern void (* const interruptVecs[])(void);
void main(){
MemCopy(&intvecs, &interruptVecs, sizeof(interruptVecs));
InitPieCtrl();
//...其他初始化
}
对应的CMD文件修改示例:
code复制MEMORY {
PAGE 0:
USER_VECS : origin = 0x3F4000, length = 0x000100
FLASH : origin = 0x3F4100, length = 0x00FEF00
...
}
SECTIONS {
userVecs : > USER_VECS, PAGE = 0
.text : > FLASH, PAGE = 0
...
}
3.2 全局变量初始化陷阱
很多工程师会忽略的一个坑是全局变量的初始化。常规做法是在main()之前由编译器自动初始化,但这会覆盖Bootloader区域。解决方案:
- 修改DSP28xxx_GlobalVariableDefs.c文件
- 将所有初始化代码移到main()函数内
- 使用MemCopy代替直接赋值
c复制// 错误做法(会导致Bootloader被覆盖)
int g_Config = 0x1234;
// 正确做法
int g_Config;
void main(){
g_Config = 0x1234; // 手动初始化
// 或者
MemCopy(&initial_value, &g_Config, sizeof(g_Config));
}
4. 上位机设计与通信协议
4.1 数据包格式设计
上位机采用C#开发,核心是可靠的数据分包算法。每个数据包包含:
- 2字节包头(0xAA55)
- 4字节包序号(支持21亿个包)
- 1字节数据长度(固定512字节)
- 数据载荷
- 2字节CRC16校验
csharp复制byte[] CreatePacket(int pkgNo, byte[] data){
var buffer = new List<byte>();
buffer.AddRange(new byte[]{0xAA, 0x55}); // 包头
buffer.AddRange(BitConverter.GetBytes(pkgNo));
buffer.Add((byte)data.Length);
buffer.AddRange(data);
ushort crc = CalcCRC16(buffer.ToArray());
buffer.AddRange(BitConverter.GetBytes(crc));
return buffer.ToArray();
}
4.2 超时重传机制
Bootloader中使用SCI的FIFO中断配合定时器实现可靠传输:
c复制#pragma CODE_SECTION(sciFifoIsr, "ramfuncs");
void sciFifoIsr(){
TimerReset(SCI_TIMER); // 收到数据后重置超时计时器
//...处理接收数据
}
void TimerIsr(){
if(超时未收到新包){
发送NAK请求重传();
}
}
这里有两个关键点:
- 中断服务函数必须放在RAM执行(防止Flash擦写时中断卡死)
- 超时时间建议设置为3倍的单包传输时间(115200bps下约50ms)
5. 工业现场实战经验
5.1 电磁兼容性处理
在变频器等强干扰环境中,我们总结了以下经验:
- 串口线必须使用双绞屏蔽线,屏蔽层单端接地
- 在TX/RX线上串联33Ω电阻并并联100pF电容到地
- 软件上增加异常脉冲过滤算法(连续3个相同数据才确认有效)
5.2 异常处理方案
针对各种异常情况,Bootloader需要做特别处理:
| 异常类型 | 处理方案 | 恢复方法 |
|---|---|---|
| 断电重启 | 检查升级标志 | 重新开始传输 |
| 数据校验错误 | 请求重传 | 自动重试3次 |
| Flash写入失败 | 标记坏块 | 跳过坏块地址 |
| 电压过低 | 暂停操作 | 等待电压恢复 |
5.3 性能优化技巧
- Flash擦除加速:先擦除整个扇区(8KB),而不是按页擦除
- 数据传输优化:使用DMA配合SCI的FIFO,减少CPU开销
- 进度显示:每完成5%发送一次进度信息给上位机
6. 防变砖指南
经历过多次现场翻车后,我们总结出以下保命秘籍:
- 双备份机制:保留上一版固件,升级失败自动回滚
- 硬件写保护:在Flash关键区域设置写保护位
- 心跳检测:升级过程中每10秒检测一次硬件状态
- 最小系统检测:确保时钟、电源、复位电路正常
c复制void SafeUpgrade(){
if(检测电压() < 3.0V) return ERROR;
if(检测时钟() != 150MHz) return ERROR;
if(校验备份区() != PASS) return ERROR;
// 只有所有检查通过才执行升级
执行升级流程();
}
这套方案在工业现场稳定运行两年多,升级成功率保持在99.9%以上。最后提醒大家:生产前务必在不同环境条件下(高低温、电压波动)进行充分测试,毕竟现场变砖的代价可比实验室高得多。