1. DSP28335 Bootloader设计背景与核心需求
在嵌入式系统开发中,Bootloader作为系统启动的第一段用户代码,承担着硬件初始化、应用程序加载和系统维护等关键职能。对于TMS320F28335这类高性能数字信号控制器而言,自定义Bootloader的设计尤为重要。这款芯片的Flash分为多个扇区(A-H),其中扇区A(0x338000-0x33FFFF)具有特殊地位——它包含了芯片复位后最先执行的启动向量。
传统开发模式下,我们通常将所有代码一次性烧录到Flash中。但在实际产品中,这种方式的局限性很明显:
- 无法进行现场固件升级
- 无法实现多应用程序切换
- 调试时需要反复全片擦除Flash
通过设计自定义Bootloader,我们可以将系统功能划分为两个独立部分:
- Bootloader程序(驻留在扇区A):负责基础硬件初始化和应用程序跳转
- 应用程序(通常放在扇区B及之后):实现具体业务功能
这种架构带来了三个显著优势:
- 现场升级能力:通过通信接口(如CAN、SCI)接收新固件并写入应用程序区域
- 安全验证机制:可在跳转前对应用程序进行CRC校验或签名验证
- 调试便利性:开发阶段可单独更新应用程序而不用修改Bootloader
2. 硬件启动流程深度解析
2.1 DSP28335的启动机制
当F28335芯片上电或复位时,其内部固化ROM中的引导加载程序会首先执行。这个阶段芯片会:
- 读取GPIO84-87的状态确定启动模式(本例使用Flash启动模式)
- 从0x3FFFC0处获取复位向量
- 跳转到0x33FFF6执行用户代码的codestart段
这个过程中有几个关键地址需要特别注意:
- 0x3FFFC0:Boot ROM的复位向量入口
- 0x33FFF6:用户Flash的codestart段(必须位于扇区A末尾)
- 0x33FFF8-0x33FFFF:CSM密码区(如果启用加密)
2.2 内存映射与段分配
F28335的存储器架构相当复杂,包含:
- Flash存储器:256KB,分为8个扇区
- SARAM:共34KB,分为多个块(L0-L7,M0-M1)
- OTP存储器:1KB,用于存储不可更改的数据
在Bootloader设计中,我们需要精心规划各段的存放位置:
c复制MEMORY {
PAGE 0: /* 程序空间 */
BOOT_ENTRY : origin = 0x338000, length = 0x007F80 /* 扇区A主区域 */
BEGIN : origin = 0x33FFF6, length = 0x000002 /* 启动向量 */
PAGE 1: /* 数据空间 */
RAMM1 : origin = 0x000500, length = 0x000300 /* 栈空间 */
RAML4 : origin = 0x00C000, length = 0x001000 /* 数据段 */
}
3. Bootloader实现关键技术
3.1 跳转前的系统状态清理
从Bootloader跳转到应用程序前,必须彻底清理系统状态,否则会导致难以排查的异常。这包括:
- 中断系统清理:
c复制DINT; // 关全局中断
IER = 0x0000; // 清CPU中断使能
IFR = 0x0000; // 清CPU中断标志
PieCtrlRegs.PIECTRL.bit.ENPIE = 0; // 禁用PIE
PieCtrlRegs.PIEACK.all = 0xFFFF; // 清所有PIE中断响应
- PIE向量表清零:
c复制Uint32 *pie_vect_ptr = (Uint32 *)0x00D00;
for(idx = 0; idx < 128; idx++) {
*pie_vect_ptr++ = 0;
}
- 栈指针重置:
asm复制MOV SP, #0x0007FF // 将栈指针指向RAMM1区域末尾
3.2 RAM函数的重定位技术
F28335的Flash访问速度较慢,因此需要将关键函数复制到RAM中运行。这通过以下步骤实现:
- 在CMD文件中定义ramfuncs段:
c复制ramfuncs : LOAD = BOOT_ENTRY,
RUN = RAML0,
LOAD_START(_RamfuncsLoadStart),
LOAD_END(_RamfuncsLoadEnd),
RUN_START(_RamfuncsRunStart),
PAGE = 0
- 在代码中执行搬运:
c复制MemCopy((Uint16*)&RamfuncsLoadStart,
(Uint16*)&RamfuncsLoadEnd,
(Uint16*)&RamfuncsRunStart);
- 关键点:
- 必须在初始化外设前完成搬运
- 搬运后所有相关函数需通过#pragma CODE_SECTION指定到ramfuncs段
- RAML0通常专用于运行ramfuncs,不要用于其他用途
4. 应用程序设计要点
4.1 应用程序的存储布局
应用程序通常存储在扇区B(0x300000开始),其CMD文件布局示例:
c复制MEMORY {
PAGE 0:
BEGIN : origin = 0x300000, length = 0x000002
APP_ENTRY : origin = 0x300002, length = 0x007FFE
...
}
SECTIONS {
codestart : > BEGIN PAGE = 0
.text : >> APP_ENTRY PAGE = 0
...
}
4.2 应用程序的初始化流程
应用程序的初始化流程与Bootloader类似,但需要注意:
- 必须重新初始化所有使用的外设
- 需要重新建立中断向量表
- RAM函数需要再次搬运(因为Bootloader的ramfuncs已被覆盖)
典型初始化代码:
c复制void main(void) {
InitSysCtrl(); // 系统时钟初始化
MemCopy(&RamfuncsLoadStart, &RamfuncsLoadEnd, &RamfuncsRunStart);
DINT;
InitPieCtrl();
IER = 0x0000;
IFR = 0x0000;
InitPieVectTable();
// 外设初始化代码...
}
5. 开发调试实战技巧
5.1 CCS工程配置要点
在CCS中配置双工程(Bootloader+APP)时需要注意:
- Bootloader工程配置:
- 链接器命令文件指定程序从扇区A开始
- 在Flash Settings中设置只擦除扇区A
- 输出文件格式选择hex或binary以便生产烧录
- 应用程序工程配置:
- 链接器命令文件指定程序从扇区B开始
- 设置只擦除应用程序使用的扇区
- 生成独立的加载文件
5.2 常见问题排查
- 跳转失败问题:
- 检查APP_CODESTART_ADDR是否正确指向应用程序入口
- 确认应用程序的codestart段已正确放置
- 验证栈指针是否已正确重置
- 运行异常问题:
- 检查中断向量表是否已正确初始化
- 确认所有使用的外设已重新初始化
- 验证RAM函数是否已正确搬运
- 编译警告处理:
c复制#10063-D entry-point symbol other than "_c_int00" specified: "code_start"
这个警告是正常的,表明我们使用了非标准的入口点。可以在工程属性中关闭特定警告:
- 右键工程 > Properties > Build > C2000 Compiler > Advanced Options > Diagnostic Options
- 添加--diag_suppress=10063
6. 进阶设计考虑
6.1 安全增强措施
- 应用程序验证:
c复制#define APP_START 0x300000
#define APP_END 0x37FFFF
Uint32 VerifyAppChecksum(void) {
Uint32 sum = 0;
Uint32 *ptr = (Uint32 *)APP_START;
while(ptr < (Uint32 *)APP_END) {
sum += *ptr++;
}
return sum;
}
- 加密启动:
- 使用CSM模块保护Flash内容
- 在跳转前验证应用程序的数字签名
- 实现安全通信协议用于固件更新
6.2 固件更新协议设计
典型的固件更新流程:
- Bootloader进入升级模式
- 通过串口接收新固件数据包
- 验证数据包完整性和真实性
- 擦除目标扇区并编程新固件
- 验证新固件并更新版本信息
协议帧示例:
code复制| 帧头(0xAA55) | 命令字 | 数据长度 | 数据区 | CRC校验 |
7. 性能优化建议
- 启动速度优化:
- 减少Bootloader中的初始化操作
- 使用DMA加速数据搬运
- 优化Flash等待状态设置
- 内存使用优化:
- 共享Bootloader和APP的栈空间
- 合理规划全局变量存储区域
- 使用动态内存分配策略
- 代码尺寸优化:
- 使用编译器的优化选项(-o3)
- 移除不必要的库函数
- 使用汇编重写关键函数
在实际项目中,我们还需要考虑生产烧录、版本管理和故障恢复等工程化问题。一个健壮的Bootloader设计可以显著提高产品的可靠性和可维护性。通过本文介绍的技术方案,开发者可以构建出满足工业级要求的DSP28335启动加载系统。
