DSP28035串口升级方案(标志位触发)代码功能深度解析

DSP28035串口升级方案(标志位触发)代码功能深度解析 DSP28035的can升级方案 提供源代码测试用固件。 上位机采用c#开发。 说明 一、介绍 1、测试平台介绍采用M新动力的DSP28035开发板CAN口使用GPIO30\31。波特率为500K。 2、28035__APP为测试用的用户代码ccs10.3.1工程参考其CMD配置。 3、28035_Bootloader_CAN为bootloader源代码ccs10.3.1工程 4、SWJ为上位机采用VS2013开发C#语言。 5、测试使用的是周立功的USBCAN-IIcan盒如果用一些国产可以兼容周立功的则更换这里面的ControlCAN.dll即可。 6、升级的app工程需要生成hex去升级具体参考我给的工程的设置。 7、BootLoader代码只有D400这一个灯1s闪烁一次 APP代码D400\401\402三个灯同时200ms闪烁一次。 8、目前跳转时间设置为5s 9、协议的注释在上位机源代码中。一、方案核心定位与代码体系DSP28035串口升级方案标志位触发版是一套基于Flash标志位判定的固件升级系统区别于传统时间窗口触发方式通过在特定Flash扇区写入预设标志位实现升级流程的精准触发。整个代码体系包含三大核心模块F28035bootloadersciv3BootLoader底层代码、F28035apptextV2/F28035APPTEXT双用户示例代码、CodeProgram上位机交互代码三者通过标准化串口协议与Flash操作逻辑协同实现从“升级触发-数据传输-固件烧录-程序跳转”的全流程自动化。二、底层硬件与开发环境依赖1. 硬件基础与资源分配核心芯片TI TMS320F28035C2000系列32位定点DSP具备128KB Flash存储空间支持串口SCI通信与分区擦写。串口资源使用GPIO28SCI-RX、GPIO29SCI-TX引脚波特率固定为115200bps硬件层面需确保上下位机波特率、数据位、停止位、校验位完全匹配默认8N1模式。Flash分区规划代码核心存储逻辑| Flash分区 | 地址范围 | 功能定位 | 代码关联模块 ||-----------|----------------|---------------------------|----------------------------|| FLASHA | 0x3F7000 - 0x3F7FFF | BootLoader程序存储区 | F28035bootloadersciv3 || FlashC-G | 0x3F8000 - 0x3FEFFF | 用户APP程序存储区 | F28035apptextV2/APP_TEXT || FlashH | 0x3FF000 - 0x3FFFFF | 升级标志位存储区 | 所有模块标志位读写 |2. 开发环境与工具链DSP工程基于Code Composer StudioCCS10.3.1编译器选用TI v20.2.4.LTS需确保版本不低于此否则缺失C2000 Hex Utility选项无法生成升级所需Hex文件。上位机工程基于Visual Studio 2013支持C/C编译内置串口通信库与Hex文件解析模块提供编译好的CodeProgram.exe可执行程序源码包含协议解析与数据发送逻辑。三、BootLoader代码F28035_bootloader_sci_v3功能解析BootLoader作为系统启动的“第一道门”其核心职责是判定启动流向执行APP或进入升级模式并实现串口数据接收与Flash烧录。代码逻辑围绕“标志位检测-串口交互-Flash操作”三大核心展开。1. 启动流程核心代码主循环逻辑// 简化版启动判定代码实际代码包含Flash初始化与硬件配置 void main(void) { // 1. 系统初始化关闭中断、配置时钟、初始化SCI与Flash控制器 InitSysCtrl(); // 系统时钟与外设使能核心时钟设为60MHz DINT; // 关闭全局中断防止Flash操作被打断 InitFlash(); // Flash控制器初始化解锁、设置等待周期 InitSci(); // SCI串口初始化波特率1152008N1模式 // 2. 升级标志位检测读取FlashH扇区0x3E8000地址的标志位 Uint16 UpdateFlag[2]; Flash_RD(0x3E8000, UpdateFlag, 0x02); // 从0x3E8000读取2个16位数据 // 3. 判定启动流向标志位为0x12340xABCD时进入升级模式否则跳转至APP if((UpdateFlag[0] 0x1234) (UpdateFlag[1] 0xABCD)) { // 3.1 进入升级模式点亮D400灯1s闪烁状态指示等待上位机数据 UpdateMode_Entry(); } else { // 3.2 跳转至APP验证APP首地址有效性合法则跳转到FlashCAPP起始区 if(VerifyAppValid(0x3F8000) TRUE) { JumpToApp(0x3F8000); // 强制设置PC指针为APP起始地址 } else { // APP无效D400灯常亮进入死循环 GpioDataRegs.GPBSET.bit.GPIO34 1; // 假设D400对应GPIO34 while(1); } } }2. 核心子函数功能拆解1Flash操作函数标志位读写核心Flash操作是升级方案的底层支撑代码中封装了擦除FlashErase、读取FlashRD、写入Flash_WR三个核心接口需严格遵循F28035的Flash操作时序防止擦写失败导致硬件损坏。FlashErase扇区擦除cUint16 FlashErase(Uint16 SectorNum, Uint16 *FlashStatus) {// 1. 解锁Flash擦除权限F28035 Flash默认锁定需写解锁码FlashRegs.FWKEY.bit.WRKEY 0xA5; // 写密钥解锁FlashRegs.FUNLOCK.bit.FUNLOCK 1; // 解锁功能寄存器// 2. 选择擦除扇区此处SectorNumH扇区对应0x3FF000FlashRegs.FSECT.bit.SECTOR SectorNum;// 3. 触发擦除命令并等待完成FlashRegs.FCMD.bit.CMD 0x03; // 擦除命令FlashRegs.FWKEY.bit.WRKEY 0xA5; // 确认命令while(FlashRegs.FBUSY.bit.BUSY 1); // 等待擦除完成BUSY位为0表示结束// 4. 检查擦除状态0表示成功非0表示错误*FlashStatus FlashRegs.FSTATUS.bit.STATUS;if(*FlashStatus ! 0) {return FAIL;}return SUCCESS;}FlashWR数据写入cUint16 FlashWR(Uint32 DestAddr, Uint16SrcData, Uint16 DataLen) {Uint16 i;Uint16FlashDest (Uint16 *)DestAddr; // 目标地址指针FlashH扇区// 1. 解锁Flash写入权限DSP28035的can升级方案 提供源代码测试用固件。 上位机采用c#开发。 说明 一、介绍 1、测试平台介绍采用M新动力的DSP28035开发板CAN口使用GPIO30\31。波特率为500K。 2、28035__APP为测试用的用户代码ccs10.3.1工程参考其CMD配置。 3、28035_Bootloader_CAN为bootloader源代码ccs10.3.1工程 4、SWJ为上位机采用VS2013开发C#语言。 5、测试使用的是周立功的USBCAN-IIcan盒如果用一些国产可以兼容周立功的则更换这里面的ControlCAN.dll即可。 6、升级的app工程需要生成hex去升级具体参考我给的工程的设置。 7、BootLoader代码只有D400这一个灯1s闪烁一次 APP代码D400\401\402三个灯同时200ms闪烁一次。 8、目前跳转时间设置为5s 9、协议的注释在上位机源代码中。FlashRegs.FWKEY.bit.WRKEY 0xA5;FlashRegs.FUNLOCK.bit.FUNLOCK 1;// 2. 逐字写入数据F28035 Flash按16位字写入不可字节操作for(i0; iFlashDest[i] SrcData[i]; // 写入16位数据FlashRegs.FWKEY.bit.WRKEY 0xA5; // 确认写入while(FlashRegs.FBUSY.bit.BUSY 1); // 等待单字写入完成// 校验写入结果防止数据错误if(FlashDest[i] ! SrcData[i]) {return FAIL;}}return SUCCESS;}2SCI串口通信函数上位机数据交互SCI模块负责接收上位机发送的APP Hex文件数据代码中实现“指令解析-数据缓存-Flash烧录”的流水线处理核心是SciRx_ISR串口接收中断服务函数。volatile Uint16 SciRxBuffer[2048]; // 串口接收缓存2KB匹配上位机数据包大小 volatile Uint16 SciRxCnt 0; // 接收数据计数 interrupt void SciRx_ISR(void) { // 1. 读取串口接收数据SCI数据寄存器 Uint16 RxData SciaRegs.SCIRXBUF.bit.SAR; // 2. 缓存数据直到缓存满或收到结束标志 if(SciRxCnt 2048) { SciRxBuffer[SciRxCnt] RxData; // 3. 检测数据包结束标志假设上位机尾帧标志为0xBB if(RxData 0xBB) { // 数据包接收完成触发Flash烧录 Flash_WriteFromBuffer(0x3F8000 (CurrentPage*2048), (Uint16 *)SciRxBuffer, SciRxCnt); SciRxCnt 0; // 重置缓存计数准备接收下一包 CurrentPage; // 切换到下一页FlashC-G扇区分页存储 } } // 4. 清除中断标志避免重复触发 SciaRegs.SCIFFRX.bit.RXFFOVRCLR 1; // 清除溢出标志 SciaRegs.SCIFFRX.bit.RXFFINTCLR 1; // 清除接收中断标志 PieCtrlRegs.PIEACK.all PIEACK_GROUP9; // 确认PIE中断SCI属于PIE组9 }3程序跳转函数BootLoader→APP当标志位不满足升级条件时BootLoader需跳转到APP程序执行核心是通过修改程序计数器PC实现强制跳转需确保APP首地址为合法的中断向量表F28035复位后从0x3FFFC0读取复位向量。void JumpToApp(Uint32 AppStartAddr) { // 1. 关闭所有中断防止跳转过程中被中断打断 DINT; IER 0x0000; // 禁用CPU中断 IFR 0x0000; // 清除中断标志 // 2. 定义函数指针指向APP首地址的复位向量 typedef void (*AppFuncPtr)(void); AppFuncPtr JumpToAppFunc (AppFuncPtr)*(Uint32 *)(AppStartAddr 0x40); // 注F28035 APP首地址如0x3F80000x40为复位向量地址存储APP入口函数 // 3. 执行跳转调用函数指针进入APP主函数 JumpToAppFunc(); }四、用户APP代码F28035_app_text_V2/APP_TEXT功能解析用户APP代码不仅实现业务逻辑如三灯闪烁更核心的是升级触发功能——即接收上位机握手指令后写入升级标志位并复位CPU让系统重启后进入BootLoader升级模式。两个示例代码V2与基础版的核心差异在于业务逻辑复杂度但升级触发逻辑完全一致。1. 升级触发核心代码握手指令响应APP需在主循环中监听串口握手指令上位机发送的升级请求收到指令后执行“擦除标志位扇区-写入标志位-复位CPU”三步操作代码如下void main(void) { // 1. 系统初始化与BootLoader一致包含时钟、GPIO、SCI、中断配置 InitSysCtrl(); InitGpio(); // 配置GPIO34D400、GPIO35D401、GPIO36D402为输出 InitSci(); InitPieCtrl(); // 初始化PIE中断控制器 InitInterrupts();// 使能SCI接收中断 // 2. 业务逻辑主循环三灯200ms闪烁 监听升级指令 while(1) { // 2.1 三灯闪烁业务逻辑D400/401/402同时亮200ms灭200ms GpioDataRegs.GPBSET.all 0x000000E0; // 点亮三灯假设对应GPIO34-36bit34-361 DELAY_US(200000); // 延时200msF28035时钟60MHzDELAY_US精度1us GpioDataRegs.GPBCLEAR.all 0x000000E0;// 熄灭三灯 DELAY_US(200000); // 2.2 检查是否收到升级握手指令假设握手指令为0x55AA if(CheckUpgradeCmd() TRUE) { // 执行升级触发擦除FlashH 写标志位 复位 TriggerUpgrade(); } } } // 检查上位机握手指令SCI接收数据解析 Uint16 CheckUpgradeCmd(void) { if(SciRxCnt 2) { // 握手指令为2字节0x55 0xAA if((SciRxBuffer[0] 0x55) (SciRxBuffer[1] 0xAA)) { SciRxCnt 0; // 清除接收计数避免重复触发 return TRUE; } } return FALSE; } // 升级触发执行函数核心写入标志位 void TriggerUpgrade(void) { Uint16 Flash_Status; Uint16 UpdateFlag[2] {0x1234, 0xABCD}; // 预设升级标志位 // 1. 关闭中断防止Flash操作被中断 DINT; // 2. 擦除FlashH扇区必须先擦除Flash只能从1写0擦除后全为1 Flash_Erase(SECTORH, Flash_Status); // SECTORH为H扇区宏定义需在头文件中声明 if(Flash_Status ! SUCCESS) { EINT; return; // 擦除失败退出触发流程 } // 3. 写入升级标志位地址0x3E8000即FlashH扇区起始地址 Flash_WR(0x3E8000, UpdateFlag, 0x02); // 写入2个16位数据0x1234和0xABCD // 4. 使能中断并复位CPU复位后BootLoader会检测标志位 EINT; RESET_CPU(); // 复位CPU调用F28035复位函数或直接写复位寄存器 }2. GPIO灯控逻辑状态指示差异化APP代码与BootLoader的灯控逻辑是区分两者运行状态的关键具体实现如下BootLoader灯控D400单灯1s闪烁cvoid UpdateModeEntry(void) {while(1) {GpioDataRegs.GPBSET.bit.GPIO34 1; // 点亮D400DELAYUS(1000000); // 延时1sGpioDataRegs.GPBCLEAR.bit.GPIO34 1;// 熄灭D400DELAY_US(1000000);}}APP灯控D400/401/402三灯200ms闪烁如上文主循环中代码所示通过GPBSET置1和GPBCLEAR清0寄存器操作GPIO实现多灯同步闪烁。五、上位机代码CodeProgram功能解析上位机代码基于VS2013开发包含源码与编译好的可执行程序核心职责是“发送握手指令-传输APP Hex文件-监控升级进度”其代码逻辑围绕“串口管理-文件解析-协议交互”展开。1. 串口初始化与握手指令发送上位机需先与DSP建立串口连接再发送握手指令触发APP的升级流程核心代码C#示例如下using System.IO.Ports; // 串口操作命名空间 public class UpgradeTool { private SerialPort serialPort; // 串口对象 private const int BaudRate 115200; // 固定波特率 private const string UpgradeCmd \x55\xAA; // 升级握手指令0x550xAA // 串口初始化 public bool InitSerialPort(string portName) { try { serialPort new SerialPort(portName, BaudRate, Parity.None, 8, StopBits.One); serialPort.ReadTimeout 5000; // 读取超时5s serialPort.WriteTimeout 5000; // 写入超时5s serialPort.Open(); // 打开串口 return true; } catch(Exception ex) { MessageBox.Show(串口打开失败 ex.Message); return false; } } // 发送升级握手指令并等待ACK public bool SendUpgradeCmd() { if(!serialPort.IsOpen) return false; try { // 1. 发送握手指令2字节 serialPort.Write(UpgradeCmd); // 2. 等待DSP回复ACK假设ACK为0xAA55 byte[] ackBuffer new byte[2]; int readLen serialPort.Read(ackBuffer, 0, 2); // 3. 验证ACK是否正确 if(readLen 2 ackBuffer[0] 0xAA ackBuffer[1] 0x55) { MessageBox.Show(握手指令发送成功DSP进入升级模式); return true; } else { MessageBox.Show(握手指令响应失败); return false; } } catch(Exception ex) { MessageBox.Show(发送指令异常 ex.Message); return false; } } }2. Hex文件解析与数据传输上位机需将用户APP的Hex文件解析为二进制数据再按固定数据包格式如2KB/包通过串口发送给BootLoader核心是Hex文件解析逻辑Hex文件为ASCII格式需转换为二进制// 解析Hex文件返回二进制数据数组 public byte[] ParseHexFile(string hexFilePath) { Listbyte binData new Listbyte(); string[] hexLines File.ReadAllLines(hexFilePath); // 按行读取Hex文件 foreach(string line in hexLines) { if(string.IsNullOrEmpty(line) || !line.StartsWith(:)) continue; // Hex行以:开头 // Hex行格式:LLAAAATT[DD...DD]CC其中LL数据长度AAAA地址TT类型DD数据CC校验 int dataLen Convert.ToInt32(line.Substring(1, 2), 16); // 数据长度16进制转10进制 string dataStr line.Substring(9, dataLen * 2); // 数据段跳过前9个字符:LLAAAAAT // 将数据段ASCII字符串转换为二进制字节 for(int i0; idataStr.Length; i2) { string byteStr dataStr.Substring(i, 2); byte dataByte Convert.ToByte(byteStr, 16); binData.Add(dataByte); } } return binData.ToArray(); // 返回二进制数据 } // 发送二进制数据按2KB包分割匹配BootLoader接收缓存 public bool SendBinData(byte[] binData) { if(binData.Length 0) return false; const int PacketSize 2048; // 数据包大小2KB int totalPackets (binData.Length PacketSize - 1) / PacketSize; // 总包数 int currentPacket 0; foreach(int i0; itotalPackets; i) { // 1. 分割数据包最后一包可能小于2KB int startIdx i * PacketSize; int endIdx Math.Min(startIdx PacketSize, binData.Length); int currentLen endIdx - startIdx; byte[] packet new byte[currentLen 4]; // 数据包格式帧头(1B)包号(1B)数据校验(2B) // 2. 构造数据包 packet[0] 0x5A; // 帧头固定为0x5ABootLoader识别用 packet[1] (byte)i; // 包号0开始BootLoader按包号写入Flash Array.Copy(binData, startIdx, packet, 2, currentLen); // 复制数据 ushort checkSum CalculateCheckSum(packet, 2 currentLen); // 计算校验和帧头包号数据 packet[2 currentLen] (byte)(checkSum 8); // 校验和高8位 packet[3 currentLen] (byte)(checkSum 0xFF);// 校验和低8位 // 3. 发送数据包并等待BootLoader确认 serialPort.Write(packet, 0, packet.Length); byte[] ack new byte[1]; int readAck serialPort.Read(ack, 0, 1); if(readAck ! 1 || ack[0] ! 0x0A) { // 假设确认码为0x0A MessageBox.Show($第{i}包发送失败); return false; } // 4. 更新进度UI层面显示升级进度 currentPacket; UpdateProgress((int)((float)currentPacket / totalPackets * 100)); } MessageBox.Show(升级完成); return true; } // 计算校验和累加和校验BootLoader端需相同逻辑校验 private ushort CalculateCheckSum(byte[] data, int len) { ushort sum 0; for(int i0; ilen; i) { sum data[i]; } return sum; }六、核心技术与算法总结1. 标志位触发机制方案核心差异化技术原理通过在FlashH扇区写入特定值0x12340xABCD作为升级触发标志BootLoader启动时读取该标志位判定是否进入升级模式替代传统“固定时间窗口如启动3s内按按键”触发方式。优势触发更精准仅需上位机发送指令、无需硬件按键、支持远程升级适用于工业场景中DSP设备无法物理接触的情况。2. Flash分区操作技术扇区擦除与写入时序F28035 Flash采用“先擦除后写入”机制擦除时需解锁、选择扇区、触发命令并等待完成写入时需逐字16位操作且需校验写入结果防止数据错误。地址规划严格划分BootLoaderFLASHA、APPFlashC-G、标志位FlashH区域避免程序覆盖如BootLoader不可被APP擦写标志位区域独立于程序区。3. 串口通信协议数据可靠性保障帧结构设计数据包采用“帧头包号数据校验和”格式帧头0x5A用于BootLoader识别有效数据包号用于按序写入Flash校验和累加和用于检测数据传输错误。中断驱动接收DSP端采用SCI接收中断SciRx_ISR实现数据实时接收避免查询方式导致的CPU资源浪费确保2KB数据包完整接收。4. 程序跳转技术PC指针强制修改通过函数指针指向APP的复位向量地址APP首地址0x40调用函数指针实现PC跳转需提前关闭所有中断防止跳转过程中中断导致程序跑飞。APP有效性验证BootLoader跳转前需验证APP首地址的中断向量表是否合法如检查复位向量是否为非0值避免跳转到无效代码导致系统崩溃。七、代码使用与调试注意事项BootLoader首次烧录必须通过JTAG仿真器烧录F28035bootloadersci_v3至FLASHA烧录后移除仿真器后续APP升级通过串口完成。APP工程配置在CCS中需将APP的链接地址设为FlashC起始地址0x3F8000并通过C2000 Hex Utility生成Intel Hex格式文件上位机仅支持Hex文件解析。串口接线确保DSP的GPIO28SCI-RX接上位机串口TXGPIO29SCI-TX接上位机串口RX交叉接线不可直连否则无法通信。标志位清除升级完成后BootLoader需擦除FlashH扇区的标志位否则下次启动仍会进入升级模式可在APP首次运行时添加“擦除标志位”逻辑。调试技巧若升级失败可通过灯控状态判断故障点如D400单灯常亮表示APP无效D400 1s闪烁表示BootLoader等待升级数据并结合上位机日志排查串口通信问题。八、总结本方案通过“标志位触发”实现DSP28035的串口升级核心代码围绕Flash操作、串口通信、程序跳转三大技术展开BootLoader负责启动判定与数据烧录APP负责业务逻辑与升级触发上位机负责指令发送与文件传输。代码设计中严格遵循F28035的硬件特性如Flash时序、SCI中断机制通过校验和、中断驱动、有效性验证等手段保障升级可靠性适用于工业控制、电力电子等需要远程固件更新的DSP应用场景。