DSP28335 eCAN工程实践:邮箱机制、位时序与CCS调试详解

📅 发布时间:2026/9/14 23:33:15
DSP28335 eCAN工程实践:邮箱机制、位时序与CCS调试详解
简介一套基于Code Composer Studio与TI DSP28335处理器的CAN通信实验工程面向学习嵌入式实时通信的开发者重点演示eCAN模块的初始化、报文发送与接收、ID过滤及中断处理流程。工程汇总了DSP2833x标准外设库源码、eCAN驱动实现和CCS工程配置文件环境搭建与代码定位都很直观。资源包共97个文件以C源码、H头文件、OBJ编译中间文件及CCS工程配置为主同时包含MAP镜像与OUT可执行文件可对照分析编译链接和存储分配压缩包仅582KB轻量易下载。目前已有214人学习下载。实验从CAN控制器寄存器配置入手覆盖帧结构、波特率设置、消息邮箱管理和错误处理再结合CCS调试器进行在线仿真能够帮助读者快速掌握在DSP28335平台上开发CAN应用的完整路径对工业控制、车载电子等场景极具参考价值。1. 一个导入就能跑通 eCAN 收发的 DSP28335 工程lab27-CAN_CCS_dsp28335 这套工程第一次打开时会觉得文件杂.pjt、.cdtproject、eCAN_back.c、DSP2833x_ECan.c、两个.cmd脚本和一堆.obj。但真正干过 CAN 开发的人会明白这正是 C2000 平台比较完整的工程形态。和串口点灯不同CAN 一旦跑起来意味着三四块板子能在一条双绞线上按优先级仲裁、实时交换数据而不是靠主从轮询撑着。这个实验工程把 DSP2833x 外设库、eCAN 驱动、链接脚本和 CCS 工程元数据都打包好了导入、编译、烧进 28335 就能看到邮箱收发适合正在从串口通信转向总线通信的嵌入式工程师也适合需要快速确认 DSP28335 eCAN 模块时序和寄存器行为的现场调试人员。它能直接回答三个问题eCAN 邮箱怎么映射、位时序怎么算、收发标志位怎么处理。2. eCAN 模块的邮箱机制与位时序设计2.1 邮箱映射32 个 Mailbox 怎么分工DSP28335 上的 eCAN 模块和很多 MCU 上只有一个发送缓冲、两三个接收缓冲的 CAN 控制器不一样它有 32 个完整的邮箱每个邮箱都带独立的 MSGID、控制字段、数据字段和状态位。这个设计的价值在于总线上的每一种报文类型都可以独占一个邮箱不需要在中断里做复杂的缓存管理。比如工业现场常见的做法是 Mailbox 1 固定发心跳帧Mailbox 2 发控制指令Mailbox 3 到 Mailbox 6 接收不同节点的状态帧每个邮箱只维护自己的 ID 和长度逻辑上干净很多。邮箱的方向由CANMD寄存器控制置 1 为发送邮箱置 0 为接收邮箱。发送侧用CANTRS发送请求置位发起发送发送完成后CANTA对应位置 1接收侧收到报文后CANRMP对应位置 1。这三个状态寄存器是 eCAN 编程的核心调试时用 CCS 的 Registers 窗口直接观察这几个值就能判断数据卡在哪一步。需要注意的是eCAN 的邮箱支持标准帧和扩展帧混用MSGID寄存器里 IDE 位决定帧格式AR 位决定是否为远程帧。实际项目中如果既有 11 位 ID 又有 29 位 ID配置邮箱时要逐个确认这些控制位不能只填 ID 数字。CAN 总线仲裁的原理是 ID 越小优先级越高多个节点同时发送时会按位仲裁隐性位让位于显性位。eCAN 的 32 个邮箱本质上就是 32 条并行的发送通道芯片内部会根据邮箱编号做优先级仲裁编号小的邮箱优先级更高。这意味着CANTRS同时置了多个位时不一定按置位顺序发送而是按邮箱号从小到大发。初学时容易忽略这一点导致两条报文的实际发送顺序和执行顺序不一致。2.2 位时序换算与采样点选择eCAN 的位时序由四段组成同步段固定 1 TQ、传播段加相位缓冲段 1合并为 TSEG1、相位缓冲段 2TSEG2、以及重同步跳转宽度 SJW。波特率计算公式为波特率 SYSCLKOUT / ((BRP 1) * (1 TSEG1 TSEG2)) 采样点 (1 TSEG1) / (1 TSEG1 TSEG2)DSP28335 的 SYSCLKOUT 典型值是 150 MHzeCAN 模块直接使用这个时钟不分频。要让波特率算得准关键是 BRP、TSEG1、TSEG2 的组合能让总时间份额整除。下面是三种常用波特率的配置参考TSEG1 和 TSEG2 的取值以位时间份额为单位波特率BRP实际预分频TSEG1TSEG2SJW位时间份额采样点1 Mbps91011311580%500 kbps181911311580%250 kbps383911311580%采样点选在 80% 是工程上比较保守的做法。CAN 总线信号在长距离传输后会有边沿变缓、相位偏移的问题采样点偏中后部可以让接收端在信号稳定后再采样。SJW 取 1 意味着每次重同步最多调整 1 TQ对于线束较短、节点不多的系统足够如果总线上有多个不同晶振精度的节点SJW 可以放宽到 2 到 3代价是抗干扰能力略微下降。实际配置时eCAN 头文件里提供位域结构可以直接对寄存器字段赋值避免手算子寄存器数值// 500 kbps 位时序配置SYSCLKOUT 150 MHz EALLOW; eCAN_Regs.CANBTC.bit.BRP 18; // 预分频 18 1 19 eCAN_Regs.CANBTC.bit.SJW 1; // 重同步跳转宽度 1 TQ eCAN_Regs.CANBTC.bit.TSEG1 11; // 相位段1 加传播段共 11 TQ eCAN_Regs.CANBTC.bit.TSEG2 3; // 相位段2 为 3 TQ EDIS;BRP 决定了时间份额的基础粒度BRP 越小每个 TQ 时间越短但 TSEG1 和 TSEG2 的可取值范围有限所以波特率越低BRP 必须相应增大。TSEG1 同时包含传播段和相位缓冲段 1总线长度越长、收发器延迟越大TSEG1 需要留的空间越多。这里给出的 80% 采样点对应 TSEG1 占比 73%对 5 米以内的 CAN 线足够。注意 eCAN 模块不支持 CAN FD它只能跑 Classic CAN 的 1 Mbps 上限做 CAN FD 方案时不能沿用这套寄存器配置。2.3 波特率误差对同步的影响CAN 总线没有独立的时钟线接收方靠每一位的跳变沿做同步。两个节点的实际波特率如果偏差超过一定范围连续多个相同电平的位就会让采样点逐渐偏移最终出现填充位错误或 CRC 错误。eCAN 的错误计数器会把这种持续错误累计到 Bus-Off然后节点自动脱离总线。判断波特率是否匹配最快的办法是用示波器抓 CAN_TX 引脚测量一帧报文的实际位宽再和理论位宽对比。误差在 0.5% 以内通常问题不大超过 1% 就要检查 BRP 计算是不是有进位问题或者 SYSCLKOUT 是不是被 PLL 配置改过。很多 CCS 工程里 PLL 配置代码被注释掉芯片跑在 30 MHz 内部振荡器上这时按 150 MHz 算出的 CAN 波特率会差 5 倍现象就是总线上完全收不到数据。3. CCS 工程骨架CMD 脚本与编译环境3.1 工程目录里的关键文件与导入流程这个 lab27 工程的根目录下有.project、.ccsproject、.cdtproject三个工程描述文件它们是 CCS 识别工程类型的依据。Debug目录里放着编译产物包括.obj目标文件、.out可执行文件、.map内存映射文件还有cc_build_Debug.log编译日志。.map文件很值得看它能告诉你每个段被放到了哪段内存、占了多少空间遇到链接溢出时先打开它查是哪个段超了。源文件分两类一类是 TI 提供的 DSP2833x 外设库文件如DSP2833x_GlobalVariableDefs.c、DSP2833x_PieVect.c、DSP2833x_SysCtrl.c、DSP2833x_ECan.c这些文件提供寄存器定义、中断向量表和外设驱动函数另一类是工程自身的应用代码核心是eCAN_back.c里面通常是 eCAN 初始化、消息收发测试的主流程。CCS环境中导入这套工程时先确认安装时勾选了 C2000 组件然后在菜单栏选 Project → Import CCS Projects指定工程所在目录CCS 会读取.ccsproject和.project自动还原工程配置。导入后要检查两个地方一是工程的 Include Options 里是否包含DSP2833x_headers/include和DSP2833x_common/include这两个头文件路径头文件找不到是这类工程最常见的导入报错二是预定义符号里有没有DSP28335或DEBUG这个符号会决定DSP2833x_Device.h里外设头文件的选择符号缺失时寄存器结构体可能是错误的。工程文件里SRC和INCLUDE两个文件夹是逻辑分组CCS 的 Virtual Folder 机制实际路径并不在工程目录下不要把工程文件直接复制走。3.2 两个 CMD 链接脚本的分工工程里有两个 CMD 文件28335_RAM_lnk.cmd和DSP2833x_Headers_nonBIOS.cmd。前者负责把代码段、数据段分配到 28335 片内 SARAM 和外部 Zone 内存后者把 eCAN、GPIO、ADC 等外设寄存器映射到对应的内存地址。很多初学者只注意前者忽略后者导致编译链接时报错说外设符号 undefined。28335_RAM_lnk.cmd的核心是 MEMORY 和 SECTIONS 两段。MEMORY 定义内存区域的起始地址和长度SECTIONS 把输出段如.text、.ebss、.stack映射到这些区域。下面是一段典型的 RAM 链接片段MEMORY { PAGE 0: RAML0 : origin 0x008000, length 0x001000 RAML1 : origin 0x009000, length 0x001000 PAGE 1: RAMM0 : origin 0x000000, length 0x000400 RAMM1 : origin 0x000400, length 0x000400 } SECTIONS { .text : RAML0, PAGE 0 .stack : RAMM1, PAGE 1 .ebss : RAML1, PAGE 1 }这个文件的名字已经说明了用途RAM 版本代码全部放在片内 RAM 里掉电即失适合调试阶段反复烧写。真正做产品时要换成 Flash 版本的链接脚本把.text放到 0x33FFF6 起始的 Flash 区域并在启动代码里做 RAM 拷贝。DSP2833x_Headers_nonBIOS.cmd里的 nonBIOS 表示不使用 TI 的 BIOS 实时操作系统外设寄存器直接以绝对地址映射方式访问。改外设寄存器映射时会同时改DSP2833x_GlobalVariableDefs.c里的结构体定义和这个 CMD 里的地址两边不一致会导致写寄存器写到了错误地址。3.3 编译期常见的三类报错这套工程用 CCS 自带构建系统编译时常见的报错集中在三处。第一类是找不到头文件报错形如#10008: could not open file DSP2833x_Device.h这是 Include Options 缺路径第二类是外设结构体成员不识别比如写了eCAN_Regs.CANBTC.bit.BRP但编译报错通常是预定义符号DSP28335没加导致编译器走了DSP2833x_Device.h里的默认分支寄存器结构体被替换成了空定义第三类是链接阶段报错program will not fit into available memory原因是.text或.ebss超出了对应 SRAM 段的容量需要回到.map文件里查具体段的占用。批量验证时可以在命令行用 headless 方式构建工程CCS 安装目录下的eclipse可执行文件支持-noSplash -application com.ti.ccstudio.apps.projectBuild -ccs.projects参数脚本化编译对 CI 场景比较友好。不过这类 DSP 工程一般还是用 IDE 交互调试命令行构建主要用于确认不同代码版本能不能编译通过。4. eCAN 初始化序列与收发驱动拆解4.1 初始化顺序从默认配置模式切到 eCAN 模式DSP28335 的 eCAN 模块上电后默认工作在 SCC标准 CAN 控制器模式只支持有限的邮箱必须先切到 eCAN 模式才能使用全部 32 个邮箱。切换过程是写CANMC寄存器的 SCB 位置 1然后立即设置 CCR 请求进入配置模式等待CANES的 CCE 位置 1。很多移植过来的代码会漏掉这个顺序直接配置邮箱和位时序结果寄存器写进去不生效。初始化序列不能随意调换顺序先切模式再请求配置等待 CCE 确认然后才能写位时序和邮箱配置。退出配置模式时同理清 CCR 后要等 CCE 清零表示模块已经回到正常运行模式之后才能收发报文。这个等待确认的过程必须做不能用固定延时代替因为 eCAN 模式切换需要等待当前总线活动结束在总线忙时可能要多等几个位时间。// eCAN_back.c 中 eCAN_init 的核心步骤 void eCAN_init(void) { EALLOW; // 切到 eCAN 模式否则只有 16 个 SCC 邮箱可用 eCAN_Regs.CANMC.bit.SCB 1; // 请求进入配置模式 eCAN_Regs.CANMC.bit.CCR 1; EDIS; // 等待 eCAN 进入配置模式 while (eCAN_Regs.CANES.bit.CCE ! 1) {} EALLOW; // 位时序配置500 kbps eCAN_Regs.CANBTC.bit.BRP 18; eCAN_Regs.CANBTC.bit.SJW 1; eCAN_Regs.CANBTC.bit.TSEG1 11; eCAN_Regs.CANBTC.bit.TSEG2 3; // 邮箱 0 发送邮箱 1 接收 eCAN_Regs.CANME.all 0x00000003; // 使能邮箱 0、1 eCAN_Regs.CANMD.all 0x00000001; // bit0 1 发送bit1 0 接收 eCAN_Regs.CANMSGID[0].all 0x12340000; // 标准帧ID 0x1234 eCAN_Regs.CANMSGID[1].all 0x12350000; // 标准帧ID 0x1235 // 退出配置模式 eCAN_Regs.CANMC.bit.CCR 0; EDIS; // 等待配置模式退出 while (eCAN_Regs.CANES.bit.CCE 1) {} }EALLOW和EDIS是 TI 对关键寄存器写的保护机制中间写的寄存器都属于 EALLOW 保护区域不加会触发非法访问。CCE 位是配置模式使能的确认位置 1 表示进入清 0 表示退出。CANME决定哪些邮箱被使能CANMD决定方向这两个寄存器会直接影响后续收发逻辑配置前最好先清掉旧值避免上电残留数据影响。MSGID 的低 18 位对应标准帧 ID标准帧 11 位 ID 存放在 bit17 到 bit6所以0x1234 6才对直接左移 18 位会错位。4.2 发送路径TRS 置位、TAA 确认与数据区写入发送一帧数据先把 ID、控制字和数据写入对应邮箱的CANMSGID、CANMCF、CANMDL、CANMDH寄存器然后在CANTRS里把该邮箱对应的位置 1硬件会自动发起发送。发送完成后硬件把CANTA对应位置 1软件读到这个标志后要写 1 清除否则下一帧数据进来时无法区分新旧状态。发送代码的核心逻辑// 通过邮箱 0 发送一帧标准帧 void can_send_message(Uint32 id, Uint32 dlc, Uint32 data_lo, Uint32 data_hi) { EALLOW; // 标准帧 ID 放在 MSGID 低 18 位的高 11 位 eCAN_Regs.CANMSGID[0].all (id 0x7FF) 6; // 数据长度寄存器低 4 位有效 eCAN_Regs.CANMCF[0].all dlc 0x0F; // 8 字节数据低 32 位和地 32 位各占一个寄存器 eCAN_Regs.CANMDL[0].all data_lo; eCAN_Regs.CANMDH[0].all data_hi; // 请求发送 eCAN_Regs.CANTRS.all 0x00000001; EDIS; // 等待发送完成TAA 位置 1 while (eCAN_Regs.CANTA.all ! 0x00000001) {} EALLOW; // 清发送确认标志写 1 清除 eCAN_Regs.CANTA.all 0x00000001; EDIS; }CANTRS是只写寄存器读回来的值可能全是 0不能依赖它判断发送状态。发送完成的判定要用CANTA。发送请求置位后硬件会先判断邮箱里有没有配置有效的 ID若 MSGID 全 0 且数据也为 0部分版本的 eCAN 会直接报发送错误所以发送前要确保 ID 和 DLC 都写正确。CANMCF的 bit4 是 RTR 远程帧请求位普通数据帧保持 0需要请求对方节点发数据时才置 1。4.3 接收路径RMP 标志与数据读取接收路径和发送路径对称。邮箱使能且配置为接收方向后硬件自动匹配总线上的 ID匹配成功就把数据锁存到邮箱数据寄存器同时置位CANRMP对应位。软件轮询到CANRMP位置 1就可以读取 ID、DLC 和数据读完必须写 1 清标志。// 轮询邮箱 1 的接收标志 void can_poll_receive(void) { if (eCAN_Regs.CANRMP.all 0x00000002) { // 读取标准帧 ID Uint32 rx_id (eCAN_Regs.CANMSGID[1].all 6) 0x7FF; // 读取数据长度 Uint32 rx_dlc eCAN_Regs.CANMCF[1].all 0x0F; // 读取 8 字节数据 Uint32 rx_lo eCAN_Regs.CANMDL[1].all; Uint32 rx_hi eCAN_Regs.CANMDH[1].all; // 清接收挂起标志允许下一次接收 EALLOW; eCAN_Regs.CANRMP.all 0x00000002; EDIS; } }CANRMP清标志的时序很关键如果不清后续相同 ID 的报文会一直留在邮箱里不更新数据看起来像卡住了。如果读取数据后清标志前又来了新报文硬件会把新报文丢弃此时CANRML相应位置 1表示有报文丢失。实际项目里要检查CANRML一旦置位说明接收处理速度跟不上总线速率需要在中断里尽快读取或增加缓冲区。轮询方式适合测试代码产品级代码建议把 RMP 接到 eCAN 中断设置好后PieCtrl里挂对应的中断服务函数。接收邮箱的 ID 配置相对灵活可以配置成只接收指定 ID也可以配合验收滤波接收一组 ID。如果多个接收邮箱配置了相同 IDeCAN 会按邮箱编号从低到高匹配编号小的邮箱优先接收。这个优先级规则在调试时会带来困惑建议每个接收邮箱只用一个 ID保持行为可预期。4.4 回环自测不看总线也能验证驱动eCAN 支持外部回环和内部回环两种测试方式。内部回环模式不用接 CAN 收发器发送数据直接在芯片内部回到接收邮箱适合在没有总线的条件下验证驱动代码和寄存器配置。配置方法是写CANMC的测试模式位让 eCAN 进入回环模式然后正常调用发送函数观察接收邮箱的 RMP 位是否置位。外部回环是把 CANH 和 CANL 短接或者通过收发器的测试模式实现数据会经过外部收发器回到本节点。外部回环能验证 28335 和收发器之间的硬件连接内部回环则完全绕开外部电路。常规步骤是先跑内部回环确认驱动逻辑正确再接上收发器跑外部回环最后挂到真实总线和其他节点联调。这三个阶段分别对应不同的问题域跳过任何一步出问题时的排查范围都会变大。CAN 总线两端需要 120 欧终端电阻测试时至少一端要接上否则波形反射会导致采样错误。5. 调 CAN 时值得留意的三个细节5.1 用采样点公式反推错误原因当总线上两个节点互通失败但每个节点单独收发都正常时问题往往出在采样点不一致。排查时先用示波器量出实际位宽再根据公式反推对方的采样点位置。采样点过大接近 90% 时对下降沿后的连续显性位容错差过小接近 60% 时容易采到上升沿附近的振荡。eCAN 的 TSEG1 和 TSEG2 分别调整相位段 1 和 2调采样点就是平移这两个值。同一网络里所有节点的采样点要尽量一致差值控制在 5% 以内比较安全。5.2 Bus-Off 状态下的恢复处理eCAN 的错误状态寄存器CANES里有一个 BO 位置 1 表示模块进入了 Bus-Off。进入 Bus-Off 后模块不会再参与总线通信直到完成协议规定的 128 个总线空闲位检测。恢复有两种方式硬件自动恢复在CANMC里设置 ABO 位为 1软件恢复则要在检测到 BO 后重新初始化模块。// Bus-Off 恢复处理清发送请求、重新初始化 if (eCAN_Regs.CANES.bit.BO 1) { EALLOW; // 停止当前发送请求和发送复位 eCAN_Regs.CANTRS.all 0; eCAN_Regs.CANTRR.all 0; // 请求进入配置模式并重配 eCAN_Regs.CANMC.bit.CCR 1; EDIS; while (eCAN_Regs.CANES.bit.CCE ! 1) {} eCAN_init(); // 重新走一遍初始化 // 配置模式退出 EALLOW; eCAN_Regs.CANMC.bit.CCR 0; EDIS; while (eCAN_Regs.CANES.bit.CCE 1) {} }Bus-Off 恢复期间应用层优先处理的是发送队列里的待发报文。eCAN 的发送请求寄存器在模块复位后会丢失恢复完成后需要重新置位所有未完成的报文。如果项目对数据连续性要求高应该在 Bus-Off 期间把待发数据缓存在 RAM 里恢复后按顺序补发。CANTRR是发送请求复位寄存器置 1 可以取消正在等待的发送请求恢复时统一清一下可以避免残存的发送请求在重配完成后自动发出旧数据。5.3 验收滤波与 AMR 屏蔽位接收邮箱的报文筛选由MSGID里的 AME 位和AMR寄存器共同控制。AME 置 1 启用验收滤波这时AMR逐位决定 ID 的每一位是否参与匹配AMR 对应位为 0 表示必须匹配为 1 表示屏蔽该位。比如只接收 ID 为 0x1234 的标准帧AMR 应该把 ID 区域之外的位置 1ID 位置 0写成十六进制就是0xFFFE003F这类形式。// 邮箱 2 只接收 ID 0x1234 的标准帧 EALLOW; eCAN_Regs.CANMSGID[2].all (0x1234 6) | (1 30); // AME 1 eCAN_Regs.CANAMR[2].all 0xFFFE003F; // 校验 bit17-6 EDIS;这里(1 30)置的就是 MSGID 寄存器里的 AME 位CANAMR的 bit17 到 bit6 对应标准帧 11 位 ID必须设 0 参与匹配其他位屏蔽。验收滤波配置错误时现象是永远收不到特定 ID 的报文但把 AMR 改成全 0xFFFFFFFF 后却能收到全部报文。这时基本可以确定是 AMR 的值计算有误逐位核对 ID 位置和屏蔽位即可。把验收滤波和回环模式做成调试宏挂线测试时先放开 ID 过滤确认链路通了再收紧滤波规则能省掉大半排查时间。本文还有配套的精品资源点击获取