搞嵌入式开发的兄弟们都懂,产品出厂后固件升级是个刚需。今天咱们聊个DSP28335的串口升级方案,直接上干货不墨迹
这玩意儿驻留在0x3F8000起始的Flash区域,核心就干三件事:检测升级标志、接收数据包、烧写用户程序。注意这里包序号用int类型,理论支持21亿个包,按512字节分包的话,最大支持1TB的固件——虽然DSP根本用不上这么大,但架构上确实没限制。这套方案在工业现场跑了两年多,经历过电压不稳、电磁干扰等毒打,稳定性妥妥的。提供bootloader源代码,用户工程源代码,上位机以及上位机源代码。上
DSP28335串口升级方案 分包发送,可实时升级,也可以上电阶段升级,无工程大小限制。 提供bootloader源代码,用户工程源代码,上位机以及上位机源代码。 提供使用说明,通信协议。
先看bootloader设计思路。这玩意儿驻留在0x3F8000起始的Flash区域,核心就干三件事:检测升级标志、接收数据包、烧写用户程序。关键跳转逻辑长这样:
void (*UserCode)(void);
UserCode = (void (*)(void))0x3F4000; //用户程序起始地址
if(升级标志有效){
执行升级流程();
}else{
asm(" LB #0x3F4000"); //硬核跳转
}
这里有个骚操作——直接把函数指针指向用户程序入口地址,用LB指令强制跳转。注意用户工程需要修改cmd文件偏移量,不然绝对地址对不上直接凉凉。
用户工程配置重点在中断向量表重映射。DSP这货的中断向量默认在低地址,得用下面这波操作:
#pragma DATA_SECTION(interruptVecs, "userVecs")
extern void (* const interruptVecs[])(void);
void main(){
MemCopy(&intvecs, &interruptVecs, sizeof(interruptVecs)); //复制中断向量
InitPieCtrl();
//...其他初始化
}
配合cmd文件里userVecs段的地址偏移,完美避开bootloader占用的空间。
上位机用C#写的,核心是分包算法。每次发512字节,带CRC16校验:
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();
}
注意这里包序号用int类型,理论支持21亿个包,按512字节分包的话,最大支持1TB的固件——虽然DSP根本用不上这么大,但架构上确实没限制。
通信协议里有个魔鬼细节:超时重传机制。bootloader里用SCI的FIFO中断配合定时器:
#pragma CODE_SECTION(sciFifoIsr, "ramfuncs");
void sciFifoIsr(){
TimerReset(SCI_TIMER);
//...收数据
}
void TimerIsr(){
if(超时未收到新包){
向上位机要重传();
}
}
把中断服务函数放到RAM执行是为了防止Flash擦写时中断卡死,这个坑当年可是让我熬夜调了三天。
实测升级速度约115200bps下10KB/s,烧写256KB程序大概半分钟。当然实际用的时候建议加个进度条显示,不然用户以为卡死了反手就是个差评。
最后说下工程适配秘籍:
- 用户工程编译前改cmd文件里的FLASH起始地址
- 全局搜索所有包含FLASH地址的代码,特别是DSP28xxx_GlobalVariableDefs.c里的
- 用MemCopy代替直接初始化,防止bootloader被覆盖
这套方案在工业现场跑了两年多,经历过电压不稳、电磁干扰等毒打,稳定性妥妥的。源码包里有《祖传防变砖指南》,照着操作基本不会翻车。

更多推荐



所有评论(0)