ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

TMS32F28P550调试实录:C2000 DSP开发避坑指南

TMS32F28P550调试实录:C2000 DSP开发避坑指南 说实话我第一次拿到 TMS32F28P550 这颗料的时候心里是有点底的。毕竟C2000系列用了不少年从F28035一路玩到F28379D觉得DSP调试也就那几下子。结果真把板子焊好、插上XDS110打开CCS准备点连接目标板的那一刻心态直接炸了连不上连来连去都是错误。后来总算连上了又挨个遇到烧不进Flash、PWM不出波形、ADC采出来是死数这些破事。这篇实录就是把我在 TMS32F28P550 调试过程中踩过的问题、排查思路和最终解法一条条记下来给同样在用F28P55x或者准备上C2000调试的兄弟们做个参考尤其适合从ARM平台转过来的朋友——两边的调试逻辑真的很不一样。1. 调试环境与最小系统先让板子“活着”很多调试问题其实不是软件问题而是最小系统没搭好。TMS32F28P550 的调试环境和传统MCU一样第一步永远是确认电源、时钟和复位。这一步偷懒后面全是玄学。1.1 电源和上电时序F28P55x系列虽然属于C2000家族但电源结构上比老前辈要复杂一点。我手头这块设计使用的是外部供电方案VDDIO接了3.3V内核VDD由电源芯片单独供1.2V。核心问题是上电时序VDDIO必须先稳定内核电压后才能拉起来。如果时序反了芯片可能进入异常状态最典型的症状就是仿真器能识别到设备但读不到CPU寄存器。我在调板时没有严格的电源时序控制直接用两个LDO手工上电结果CCS一直提示“Error connecting to the target: (Error -1135 0x0)”后来查资料才发现是内核电压和IO电压的时序冲突。解决方法是重新设计了上电逻辑用电源监控芯片控制使能脚确保3.3V先建立、再延迟几十毫秒打开1.2V。这里有个容易忽略的细节F28P55x的每个电源引脚旁边都要放足够容量的去耦电容推荐是0.1uF和1uF搭配靠近引脚放置。别省这点电容调试的时候波形毛刺特别难查不如一开始就摆好。1.2 时钟从内部振荡器到外部晶振C2000的老用户都知道芯片内部有个INTOSC可以先用但F28P55x的系统时钟配置比老芯片更敏感。我最初为了简单直接使用内部振荡器跑连接仿真器下载程序后倒是能运行但串口波特率偏差很大打印出来的数据全乱码。排查了很久才发现是内部振荡器精度不够尤其在上电早期温度变化时频率会漂。后来直接在GPIO18和GPIO19上挂了25MHz外部晶振通过SysConfig里把时钟源切换到外部晶振再把PLL倍频到系统目标频率。注意外部晶振的负载电容要根据晶振手册来我用的20pF负载电容实测起振很稳。还要检查一个很容易踩的坑如果外部晶振引脚和别的功能复用而你在初始化的时候先把引脚配置成了GPIO那晶振根本不会起振。应该在时钟初始化之前就确保OSC状态不要急着配GPIO。1.3 复位和DEBUG引脚检查调试连不上时很多人会忽略复位引脚。F28P55x的复位脚一般有内部上拉但如果外部电路把它拉低了仿真器怎么都连不上。我第一次画板子时复位脚上直接并联了一个0.1uF电容用来滤波结果上电后电容充电时间太长导致复位释放过慢CCS连接时总报“Target must be connected before loading program”。另外JTAG接口里TMS、TCK、TDI需要上拉TDO可以下拉这个上拉电阻值一般在10kΩ左右。有时候为了省事用杜邦线连接XDS110一旦线长超过10cm高频信号就很差连接变得时好时坏。强烈建议用排线或者直接板载XDS110调试体验会好非常多。2. 仿真器连接故障实录CCS认不到目标板如果是第一次接触C2000遇到连不上仿真器基本就懵了。这里把我几次典型的连接故障和排查方法写出来省得大家把时间浪费在乱试上。2.1 常见的错误信息与应对连接时最常看到的是下面几种Error -1135 0x0通常和电源有关系优先检查电压和时序。Error -1133 0x0目标板没有响应可能是复位脚拉死或者JTAG引脚配置不对。Device is lockedFlash安全区被锁或者DCSM密码区域被误配。我处理的第一个问题是-1135当时怀疑是仿真器坏了换个仿真器结果一样。后来用示波器量内核电压发现上电时有个很大的跌落原因是电源芯片的输出电容太小瞬间电流拉不起来。换大电容之后问题消失。如果报-1133先别去改软件。用万用表量NRST引脚的电平正常应该为高如果为低说明复位电路有问题或者外部有设备把复位拉低了。还有一个容易忽略的是JTAG接口的TCK信号如果TCK上没波形看看仿真器驱动是否正常XDS110驱动有时被Windows更新搞坏重装驱动能解决一大批古怪问题。2.2 程序跑飞后救不回来的处理用C2000调试最刺激的环节就是程序里开了看门狗然后又写了一个卡死的循环结果上电后CPU一直被看门狗复位仿真器根本没时间建立连接。最开始我不知道这个套路烧了一个带喂狗Bug的程序进Flash后CCS直接连不上目标板心情瞬间跌落谷底。后来学了一招连接目标板之前先把板上的Boot模式引脚设置到“Boot to RAM”或者“Boot to SARAM”然后在CCS的Target Configuration里把连接选项改成“Reset on Connect”不勾选或者勾选“Halt on Reset”再点连接。如果还是连不上就按住复位键点下Connect等脚本跑起来以后松开复位成功率很高。另外XDS110连接速度可以调低一点比如从默认的10MHz改成2.5MHz有些目标板在高频率下就是不稳定。这个玄学其实有理论依据线缆电容、板子布局都会影响信号完整性降速以后眼图质量明显变好。2.3 JTAG频率和连接选项的玄学有些板子用默认设置能连接但加载程序到一半就掉线十有八九是JTAG频率和负载问题。CCS的Target Configuration里有一个“JTAG TCLK Frequency”的设置我一般先降到5MHz试试。如果板子上有其他高频信号干扰还可以降到1MHz。另一个选项是“Enable Watchdog Window”有的版本默认开启会在调试和烧写时导致看门狗不断复位如果遇到烧到一半失败可以尝试关闭这个选项或者把看门狗在外设初始化之前就先关闭。C2000程序烧写阶段其实不吃看门狗但某些版本CCS会做超时检测挺坑的。3. 程序烧写与启动异常RAM能跑Flash不跑的根源仿真器连接解决后新的折磨来了程序在RAM里调试一点问题没有一烧进Flash断电重新上电要么没反应要么乱跑。这个问题几乎是所有C2000新手必遇的原因不复杂但排查起来很磨人。3.1 RAM调试和Flash烧写其实两套配置在CCS里点Debug时默认会把程序加载到RAM所有代码直接在RAM里运行速度不一定比Flash慢但是断电就丢。如果你只在Debug里跑通了没有正确生成烧写文件或者链接脚本里没有把代码段分配到Flash区域那断电后芯片里什么都不会留下。F28P55x的Flash烧写有两种常用方式一种是在线仿真器烧写另一种是量产烧写。在线烧写时在CCS的Target Configuration里会选择对应的Flash模式比如“Texas Instruments XDS110 USB Debug Probe”和“C28XX Flash Programmer”。注意Flash烧写需要额外的Flash API库这个库会在烧写时被加载到RAM。如果Flash API版本和芯片不匹配会出现烧写成功率低的问题。所以我的建议是从项目一开始就分别建RAM和Flash两个构建配置RAM配置用于日常调试Flash配置用于最终验证。这样能避免临到发布才发现Flash跑不起来。3.2 烧写后不运行的排查流程如果程序烧写成功但重新上电后不运行按下面顺序查检查Boot模式引脚TMS32F28P550上电后芯片内部的Boot ROM会检测一组GPIO引脚决定是Boot to Flash还是Boot to RAM。如果这几个引脚被外部电阻拉到了错误状态芯片根本不会去Flash执行程序。检查链接脚本确认Linker CMD文件里把.text、.cinit等段放到了Flash区域而不是RAM区域。有些老工程的CMD文件是从F2803x复制来的段地址跟F28P55x不匹配烧进去必挂。检查看门狗如果Flash程序里初始化看门狗太早而Flash上电到主函数入口之间的时间过长看门狗可能提前溢出并复位造成“一直在启动但永远进不了main”的假象。在启动代码最开始加一个GPIO翻转或者串口打印用示波器或者串口助手确认程序到底有没有跑起来。我还遇到过一个很刁钻的问题程序能跑但每次跑到初始化ADC的地方就卡死。后来发现是代码里把看门狗中断使能了却一直没喂狗初始化还没执行完就被看门狗打断。解决方法是把所有外设初始化完成后再开看门狗或者先关闭看门狗调试稳定后再打开。3.3 Flash安全区与免死金牌TMS32F28P550的Flash是有安全区保护的也就是DCSM模块。如果配置了Zone1和Zone2的密码而密码又不小心丢了那Flash就永久锁死仿真器也无法再写入。这个问题的破坏力非常大我在实验室听过太多“板子变砖”的故事。调试阶段我的经验是先不要设置安全区的密码等全部功能验证完成后再启用。如果必须要验证安全区功能也一定在烧写前把当前工程的备份存好尤其是DCSM配置区域的内容。万一锁死了有些芯片可以通过Boot ROM里的“unlock”功能恢复但过程很复杂可能需要写一个专门的解锁程序通过串口下载折腾半天不如预防为主。4. 串口打印与变量监视TMS32F28P550调试的实用技巧连接和烧写问题都搞定后真正进入代码调试环节。C2000的调试手段比ARM平台多一些但也需要一些技巧才能高效定位问题。4.1 串口助手的正确打开方式串口打印依然是最常用的调试手段。TMS32F28P550内部有SCI模块即串行通信接口配置比较简单。推荐用115200-8-N-1的常见配置方便各种串口助手直接读取。我自己平时用sscom或XCOM两个都支持收码和发码界面也直观。初始化SCI时有一个关键点C2000的引脚复用非常灵活同一个引脚可以复用成很多功能必须先通过SysConfig或者GPIO配置寄存器把引脚切到SCI模式再初始化SCI模块。否则串口完全没输出。曾经我就是因为忘记配置GPIO复用在排除时钟、波特率等问题上浪费了大半天。这里给一个示意性的初始化代码用C语言描述具体寄存器映射以你的项目为准#include driverlib.h #include sysctl.h #include sci.h void SCI_Init(void) { GPIO_setPinConfig(GPIO_28_SCIRX_A); GPIO_setPadConfig(28, GPIO_PIN_TYPE_STD); GPIO_setPinConfig(GPIO_29_SCITX_A); GPIO_setPadConfig(29, GPIO_PIN_TYPE_STD); SCI_setConfig(SCIA_BASE, DEVICE_SYSCLK_FREQ, 115200, (SCI_CONFIG_WLEN_8 | SCI_CONFIG_STOP_ONE | SCI_CONFIG_PAR_NONE)); SCI_resetChannels(SCIA_BASE); SCI_enableModule(SCIA_BASE); SCI_resetTxFIFO(SCIA_BASE); SCI_clearInterruptStatus(SCIA_BASE, SCI_INT_TXFF | SCI_INT_RXFF); }然后就可以用标准库的printf重定向到串口或者简单写一个字节发送函数。调试时建议把串口的TX引脚直接连到USB转串口的RX别搞反。4.2 Expressions与Graph比串口更快的实时观察如果代码已经跑到控制环里串口打印的实时性不够这时候CCS自带的Expressions和Graph工具就能派上大用场。在调试界面里打开Expressions窗口把要观察的变量填进去。如果变量被优化了可能显示cannot evaluate这时需要把变量定义成volatile或者在编译器优化等级上做调整。C2000的CCS优化默认是-O2调试阶段建议改成-O0虽然代码慢一点但变量基本都能看到。Graph工具适合观察波形比如ADC采样值数组、编码器位置等。在Tools里选择Graph选Single Time Series然后填写数组地址和长度就能像示波器一样看到数据变化。我用这个手段调试速度环比用串口一个个点打印高效太多。另外C2000支持实时仿真模式。在调试界面里把Real-Time Emulation打开同时开启Run Free这样即使程序在运行时也能刷新变量和Graph。不过这个模式对目标板的电源稳定性和JTAG信号质量要求比较高如果频繁掉线还是先用普通模式。4.3 调试CLA协处理器时的心态调整F28P55x除了主核C28x之外还有CLA协处理器可以独立执行控制算法。CLA的调试方式和主核不太一样因为它和主核并不是对称的调试接口。我第一次用CLA时一边在CLA任务里写代码一边在主核ISR里打断点结果发现CLA任务根本不进断点。后来才知道CLA在CCS里有独立的调试视图需要先连接主核再选择CLA Debug Session或者用多核调试器同时连接两个核心。如果没有多核调试器可以换一种思路在CLA任务执行的共享RAM里放一个标志位主核在主循环里轮询这个标志一旦发生变化就GPIO翻转。用示波器测量GPIO波形间接判断CLA任务是否执行、执行周期是多少。这个方法虽然土但非常可靠。还要注意CLA访问不了普通的Flash和RAM只能访问被配置为共享RAM或者消息RAM的存储区。如果把一个大型数组定义在非共享RAM里CLA访问时会触发异常程序就卡在奇怪的地方。因此要仔细检查链接脚本把CLA需要用到的数据区域放到共享RAM段。5. 核心外设调试中的坑PWM和ADC不按预期工作电机控制和数字电源项目里PWM和ADC是最核心的两个外设。这两个环节调试不好控制算法写得再漂亮也没用。这里记录几个我实际遇到的坑和排查方法。5.1 PWM输出全高全低先从寄存器查起PWM输出不正常时很多人的第一反应是代码逻辑问题。其实C2000的EPWM模块配置项非常细稍有不慎就会输出恒定电平。我记得有一次PWM输出一直是低查了半天逻辑最后发现问题在TBCTR根本没有计数——EPWM的时钟输入就没使能。C2000的外设时钟默认很多是关闭的必须在SysCtrl寄存器里打开对应外设的时钟。以EPWM1为例需要确保PCLKCR0里的EPWM1使能位被置1。如果在启动代码里没有开这一位后面怎么配置都没用。另外输出引脚复用也很关键。EPWM1A在F28P55x上可能有好几个引脚位置选择其中之一后必须在GPIO配置里做复用。如果引脚复用正确再用调试器观察EPwm1Regs.TBCTR是不是在跑如果不跑就是时钟没开如果跑但AQA/B比较寄存器没有正确触发就检查CAU/CAD或CMPA寄存器值。我经常用的一个检查方法是先把EPWM初始化里所有比较值设置成一个固定占空比比如50%然后用示波器看引脚。如果50%占空比都能出再把代码里动态调制的那部分逻辑打开。这样能快速区分是初始化问题还是控制逻辑问题。5.2 ADC采样值固定或跳变ADC采样出的数字如果一直是0或4095多半不是算法问题而是ADC配置问题。TMS32F28P550的ADC模块是12位的参考电压可以选择内部基准或者外部基准。如果用内部基准需要确认基准源已经使能并且稳定否则采样值会飘。还有SOC触发源的问题。有的人配置了ADC中断但从头到尾没有触发SOC就直接读ADCRESULT结果读出来全是初始值。常规做法是设置一个周期性触发源比如用ePWM的SOC信号去触发ADC或者在CPU定时器中断里配置软件触发。如果你用的是软件触发记得在触发后等待适当的延迟再读结果。对于采样值跳变的问题很大概率是采样窗口太短。F28P55x的ADC采样窗口由ACQPS寄存器控制值太小可能导致内部采样电容还没充好电就进入转换读出来的数自然不稳。我一般会尝试把ACQPS从默认值加大直到采样数据稳定再加一点余量。如果信号源输出阻抗高这个参数尤其重要。ADC还有个冷门但致命的问题校准。TI官方文档要求C2000系列ADC在启动时需要调用自校准函数否则转换结果会有较大偏移和增益误差。如果完全照抄某个早期例程漏掉校准某些通道可能会差几十个LSB。我自己就遇到过直到读官方勘误才知道。5.3 中断不响应的PIE坑C2000的中断系统是PIE和ARM的NVIC完全不同。很多从STM32转过来的人在调试时都会掉进同一个坑外设中断标志清了但中断就是不进。PIE有个PIEACK寄存器机制中断响应后必须向所在的组写PIEACK来解锁下一轮中断。如果在ISR里没写PIEACK那么改组的中断只会触发一次之后就再也不会响应了。我一开始不知道总想着怎么就进一次中断后来在ISR最后加了类似下面的代码才正常PieCtrlRegs.PIEACK.all PIEACK_GROUP6; // 以EPWM中断为例具体组看映射另外主核的IER和全局中断开关也要配好。老手建议写初始化时抄一遍官方例程的PIE初始化不要自己拍脑袋写因为PIE向量表不对齐的问题非常隐蔽。6. 常见问题与防坑经验速查最后把我在TMS32F28P550调试过程中遇到的典型问题整理成速查表方便大家遇到类似问题时快速定位。表格里有些内容是老生常谈但每次排查都能用上。6.1 调试问题速查表现象可能原因处理思路仿真器连不上报Error -1135电源时序错乱或电压跌落检查VDDIO与VDD上电顺序加大输出电容仿真器连不上报Error -1133复位引脚拉低或JTAG引脚配置错误量NRST电平检查上拉电阻连接后一加载程序就掉线JTAG频率过高或线缆过长降JTAG频率到2.5MHz换短排线程序烧写成功但上电不运行Boot引脚状态错误或Flash链接脚本问题检查Boot配置、CMD文件的段分配RAM运行正常Flash运行异常Flash Wait State配置不正确根据系统时钟配置FLASH的等待周期串口打印乱码时钟源选择错误导致波特率偏差改用外部晶振或调整分频器SCI完全没有输出引脚复用没配或SCITX连反检查GPIO复用确认TX接RX变量观察不到被编译器优化掉了加volatile或降低优化等级PWM输出恒定电平EPWM时钟未使能或引脚复用错误打开PCLKCR0对应位检查GPIO复用ADC读出来固定值没有SOC触发或参考源未准备好配置周期触发源检查参考电压中断只进一次PIEACK寄存器没写在ISR末尾写对应组的PIEACKFlash被锁DCSM密码配置错误且密码丢失很难复位只能预防或通过Boot ROM尝试解锁6.2 几条用“事故”换来的建议第一拿到新芯片后别急着焊自己的板子先买官方评估板跑通一个点灯例程。这一条我曾经觉得是废话但自从在一个多月的时间里反复怀疑自己的代码时才明白评估板“开箱即用”的价值。它能帮你彻底分离硬件问题和软件问题。第二调试控制环之前务必先开环验证驱动板。给PWM一个固定占空比确认母线电压和电流采样都正常再跑闭环。我自己就因为在开环没验证的情况下直接闭环导致电流尖峰烧过一次功率管教训极其深刻。第三每次改动Flash配置和DCSM之前务必导出当前工程的备份。C2000上Flash锁死的风险永远存在工程里的*.out文件、*.cmd文件和DCSM配置脚本都需要定期存档。不要觉得版本管理是软件团队的事硬件工程师同样需要。第四调试日志一定要有。哪怕只是用串口打印关键变量的变化也比用眼睛盯着屏幕强。遇到偶发问题时日志能帮你回放现场。我现在每个模块初始化完成都会打印一行短标志一旦程序卡死就知道卡在哪里。最后再分享一个我个人的小技巧写到这里再多说一句。TMS32F28P550这颗芯片本身的性能没问题C28x和CLA组合起来很能打但它的调试体系确实比ST、NXP那些ARM核要更“硬核”一些。如果你和我一样是从ARM平台转过来的前期最大的障碍不是功能库用不熟而是“调试思路没转换过来”。ARM平台几乎不关心连接时序、不关心启动模式引脚、不关心Flash等待周期但C2000上这些都得自己操心。解决办法也很简单每次都从官方例程的工程结构抄起别自己从头搭一遍底层初始化等跑通了再逐步替换成自己的模块。我在这个项目里前前后后折腾了将近两周后来换了官方例程做底子很多莫名其妙的问题都消失了。希望这份实录能让你少踩几个坑顺利把功能调通。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进