这块TMS32F28P550的板子到我手上的第三天我差点因为一个老掉牙的细节把它扔回箱子里。插上XDS110、打开CCSTest Connection直接报Error -1135好容易连上了程序又是无限复位好不容易跑起来烧进Flash断电再上电又变成一块砖。如果你最近也在调TI C2000家族的料尤其是TMS32F28P550或者隔壁同系列的C28x芯片这份调试实录应该能帮你省下几天的排查时间。它不是教程就是我把这几天踩过的坑、排查链路、最后真正的原因原样写出来顺手带一点CCS和XDS110使用上的经验。因为我用到的都是C2000里非常典型的机制所以哪怕你手上的具体型号不是P550大概率也能对号入座。1. 第一回合XDS110根本连不上1.1 报错现象与最初的判断插上仿真器打开CCS的Target Configuration双击设备跑一遍Test Connection屏幕上直接出现Error connecting to the target: (Error -1135 0x0) The debug probe reported an error. Confirm debug probe configuration and connections, reset the debug probe, and retry the operation.偶尔还会蹦出Error -1142提示无法访问目标。这种报错对用ST、使用Keil习惯的人来说特别容易上头因为Keil里SWD没连上一般就是检查杜邦线和供电压但C2000这套CCSXDS110的链路报错来源经常隔了好几层。我第一次的反应是先怀疑仿真器坏了换了一个XDS110还是同样问题。接着怀疑板子没上电确认3.3V正常、1.2V内核电源正常芯片表面也不烫电源没问题。1.2 真正的排查链路从线缆到TCK频率后面我按这个顺序一步步查才把问题逼出来确认XDS110固件。CCS提示需要升级固件时最好先升级否则连接不稳定。升级工具在CCS安装目录的xdsdfu也可以用TI官方提供的XDS110固件升级工具。如果电脑设备管理器里看不到XDS110的端口那问题在USB驱动不在目标板。换USB线。这个看起来低级但XDS110的USB线如果只有充电能力没有数据线能力电脑会识别到设备但连接行为非常诡异。我手里确实有几根只能充电的线换线前后完全是两个世界。查JTAG信号接线。TMS、TCK、TDI、TDO四条线必须核对清楚尤其如果板子上有多个调试接口或者复用到了其它连接器。杜邦线不要拉得太长我这次大概接了20厘米左右的杜邦线去连一个扩展接口信号完整性已经被拖垮了。降低TCK时钟。在Target Configuration里选中XDS110进入Advanced面板把JTAG时钟从默认的2.5MHz改到1MHz甚至500kHz。这一步对飞线环境几乎是救命配置。杜邦线长、板子电源纹波大、地线接触不良时高频TCK非常容易丢数据。试一下目标板复位状态。C2000启动后会跑Flash里的代码如果用户程序里把JTAG相关引脚配置成了别的功能或者上电瞬间程序就做了低功耗/Sleep操作仿真器很难抓住芯片。常见做法是按住复位键在CCS里点连接等连接上再松复位键。最后定位到的是两个因素叠加一是杜邦线从仿真器到板子确实走得太长二是仿真器默认2.5MHz的TCK频率在这种飞线条件下误码严重。降到1MHz以后Test Connection稳过后面烧录、调试再没出现Error -1135。这件事给我后面的调试定了基调先把调试环境弄得极度保守再去碰复杂的代码别一上来就追求高频率。2. 连上了程序却在无限复位时钟和Flash等待状态的锅2.1 现象描述点运行前一切正常点运行后立刻上天连接稳定以后我加载编译好的.out文件程序停在入口点单步没问题。但一按Continue观测窗口里刚跑了几下PC就跳到不知道哪里去了或者整个程序像被按了复位键一样重新开始。因为LED在主循环里有翻转逻辑我看到的直观现象是灯亮一下、灭一下然后就再也不动了。用示波器看复位引脚能看到周期性的低脉冲这基本说明看门狗或者芯片内部复位逻辑在反复触发。我这段时间第一个动作是把看门狗初始化放在main第一行前面明确关闭看门狗或者后续周期喂狗。C2000器件里看门狗的默认状态并不统一最稳的做法是参照官方例程在板上如果确定不依赖看门狗先把WDCR写成关断状态。这一步能排除掉程序跑太久没喂狗这种低级因素。2.2 InitSysCtrlPLL、SYSCLK、Flash等待状态三者的关系重启复位不一定是看门狗更常见的是时钟树没配好。C2000这套芯片外部一般接一个无源晶振比如20MHz内部PLL倍频到系统主频。你如果只把PLL倍频拉高却忘了让Flash控制逻辑匹配这个频率芯片从Flash取指就会随机失败。打个比方Flash读取就像一个人走路PLL把跑步机速度调快了但Flash的等待状态告诉它一步只用1拍就能迈出去结果步子跟不上直接摔倒。摔倒的PC就是乱跳程序自然表现成随机复位、随机跑飞。我们需要在时钟初始化完成之后马上按当前SYSCLK配置Flash控制器的等待状态。TI例程里通常有一行类似FlashRegs.FBT_REG或者Flash_Config的寄存器设置必须与PLL输出频率严格对应。我以前调STM32没有这个概念因为人家Flash等待状态很多都自动管理了但C2000不是它要求你按频率显式配等待状态。这次的根因就是我把PLL配置从80MHz改到了更高频率能跑起来了但Flash等待状态没有同步更新。程序在全速跑的时候取指偶尔错一个行为就变成随机的。改完等待状态之后程序连续跑了一整天没有复位。2.3 顺带排查栈溢出和PIE中断向量表在确认Flash等待状态之前我还怀疑过两个点虽然最后不是它们但对你排查同类问题有参考价值。第一个是栈溢出。C28x里默认的栈大小在cmd文件里用.stack段定义通常是0x400也就是1KB左右。如果中断函数里塞了很大的局部数组或者调用层级特别深1KB栈非常容易爆。爆栈后数据会写到相邻的内存区域轻则变量被莫名修改重则把返回地址清空直接跑飞。排查栈溢出最直接的办法是看编译生成的.map文件里.stack段放在哪再开一个内存窗口盯着栈顶地址全速跑一会儿看地址有没有穿越边界。CCS也提供了HWI/栈检测但直接在内存窗口里盯最低地址比较直观。第二个是PIE中断向量表。C2000的中断机制是PIE外设中断扩展模块上电复位后PIE向量表默认指向ROM里的默认向量如果你代码里使能了外设中断、写了中断服务函数但没有把PIE向量表初始化好中断一来芯片就会跳到未定义的处理分支。表现起来也可能像程序突然跑飞。正确流程大致是关闭全局中断INTM位→ 把片内RAM中的PieVectTable初始化→ 把各个中断服务函数地址写进PieVectTable对应位置 → 设置PIE控制寄存器使能对应组 → 再打开IER和INTM。很多人只写了某个外设的中断使能位忘了最上面的PIE模块使能于是所有中断都是空指针一进中断就飞。这里尤其要注意PIE模块配置是需要EALLOW保护的后面我会专门说这个坑。3. 烧到Flash就失忆Boot模式和DCSM锁3.1 烧录成功断电再上电却不运行程序在RAM调试没问题之后自然要往Flash里烧。CCS里用Flash Programmer或者UniFlash把.out烧进去提示成功。我看Flash内容校验也通过。但断开仿真器、断电、重新上电LED完全不亮串口也什么都没打。这种事在C2000上有一个非常经典的排查方向启动模式。C2000芯片内部有Boot ROM上电后Boot ROM会根据特定的引脚状态或者OTP里配置的选择决定从Flash启动、从SCI启动、从SPI启动、还是进入等待仿真器的模式。很多开发板为了灵活性在板子上留了启动模式选择跳线/拨码或者用0欧电阻把相关引脚拉到固定电平。如果某个引脚恰好在硬件上被拉到了SCI Boot的方向那芯片上电后是进入串口引导模式它安安静静地等着外部通过SCIA给它发数据压根不会执行你Flash里的程序。我这次的问题就出在启动模式引脚上。上一块板子上因为调试方便在GPIO上挂了别的用途这次没注意导致芯片每次上电都进了SCI Boot。用串口调试助手接上去能收到Boot ROM发过来的引导握手数据但是程序不跑。把Boot模式改回Flash Boot之后断电上电程序正常启动。这个排查过程强烈建议一上来就做查数据手册里Boot Mode Selection那几页确认引脚电平组合。3.2 DCSM安全锁差点以为芯片报废比启动配置更恐怖的是DCSM。C2000家族为了防代码被读引入了DCSM安全机制。DCSM里可以配置密钥、JTAG锁定、启动流程校验等。如果只是改普通配置不一定出问题但在某次测试里我为了模拟客户环境在DCSM区域写入了启用JTAG锁保护某块Flash的配置又故意填了一个测试密钥。结果程序烧写后整个Flash区域被安全锁住CCS连上也读不了Flash内容读出来全是0x0000。更麻烦的是一旦JTAG锁定被开启仿真器的探测会受到限制某些情况下Debug Server会提示目标处于安全状态只有通过UniFlash或者CCS的DebugServer执行解锁操作并且提供正确的密钥才能恢复访问。如果你把密钥都忘掉了而芯片又不支持后门恢复那基本就是换片的节奏。我们当时距离这个结局只差一个回车。最后是用UniFlash的Unlock功能输入之前记下的密钥把DCSM里Flash擦除到默认状态才把芯片救回来。这一步需要注意的是不同型号密钥长度和区域大小不一样C2000的DCSM文档里说得非常清楚去TI官网搜DCSM用户指南比任何博客都有用。3.3 如何防止自己把自己锁死经历过这次以后我的原则是开发阶段不要轻易开启JTAG锁定尤其不要为了模拟保密而提前在样板上试代价太大。如果一定要试先在不需要保密的板子上跑一遍完整流程并且把密钥同时存到至少两个地方。确认当前芯片支持哪种解锁方式是backdoor key还是只能erase all这决定了你还有没有退路。DCSM配置是带EALLOW保护的配置代码一定要放在EALLOW和EDIS之间不然你看着写进寄存器了实际上没写成功更容易产生部分配置写进去、密钥不完整的诡异状态。对于普通项目来说DCSM不是必须立即使用的东西。把它看成给芯片加的一道防盗门门锁一旦设好别说是别人你自己进不了门的时候一样被困在门外。4. 外设各种怪象ADC、PWM、CLA的调试心得4.1 ADC采样离谱校准函数和参考电压主程序能跑了外设问题开始冒头。首先是ADC我用内部ADC采集一个1.5V直流电压结果读出来不是偏高就是偏低而且每次重新上电数值还不一样波动能到几十个LSB。先排除了硬件外部信号很干净万用表测的非常稳定。那就只剩ADC配置问题。C2000的ADC在启动时有一项校准动作是TI在出厂时把校准值放在特定位置Boot ROM在启动过程会把它拷到ADC寄存器。但如果你在调试时直接通过仿真器把程序加载进RAM而且不是从头执行Boot ROM流程这部分校准可能没有生效ADC结果自然不靠谱。解决办法是确保ADC校准逻辑被正确执行不同型号调用方式略有不同TI例程里一般会把这部分放在初始化中用一段RAM函数运行你需要确认自己的工程里有没有漏掉它。第二个容易出问题的是参考电压。P550这类芯片的ADC参考源可以选择内部参考还是外部参考如果你的板子上没贴外部参考芯片或者参考电压的引脚没接对而代码又配成了外部参考ADC采出来必然是天马行空。我记得很清楚后来把参考源改回内部参考再跑ADC读数立刻稳了。第三个要留意采样窗口。C2000的ADC采样保持时间如果配置得太短信号还没充满采样电容数值就会偏低尤其前面的源阻抗比较大时更明显。一般例程会给出一个保守的采样窗口配置可以先用例程值遇到数值怪再拉大采样窗口看趋势。4.2 PWM没波形外设时钟使能和TBCLKSYNC另一个让我印象深刻的坑是PWM。初始化代码写得规规矩矩TBPRD设置周期、CMPA设置占空比、AQCTLA设置输出动作怎么看都对。但示波器一抓引脚上纹丝不动。很多人第一反应是GPIO复用配错了。C2000的PWM输出引脚确实需要把GPIO MUX切换到EPWM功能这个我也检查过没错。那问题在哪答案是外设时钟门控没有打开。C2000的很多外设系统复位后默认时钟是关闭的必须通过系统控制寄存器里的PCLKCR系列寄存器把对应的EPWM模块时钟使能模块才能工作。这是和很多通用MCU很不一样的地方不是写了外设寄存器它就通电模块的时钟闸门必须先抬起来。代码应该是类似这样EALLOW; SysCtrlRegs.PCLKCR0.bit.EPWM1CLK 1; EDIS;如果你漏掉这一句后面所有EPwm1Regs的操作都是写入一个没有时钟的模块寄存器看起来有值实际输出没有任何反应。还有一个容易忽略的位是TBCLKSYNC。多个EPWM模块为了同步启动都有一个时基时钟同步的总开关默认是关闭的。如果你只使能了EPWM1的模块时钟没管TBCLKSYNC计数器可能一直不跑自然也看不到波形。正确写法一般是先配置所有EPWM参数最后置位TBCLKSYNC让所有时基同时开始。4.3 CLA假死共享内存和单步调试的误区如果P550这颗料带CLA协处理器这里再分享一个CLA相关的调试经验。CLA是独立于CPU的协处理器有自己的一套指令集和寄存器组。它和CPU共享很大一块RAM用来交换数据。我第一次调CLA时遇到的现象是CPU端看到变量值不对数组某个元素突然被改写但改写的位置在代码里完全找不到。排查到最后才发现是CPU和CLA都在访问同一段共享RAM而仲裁逻辑配置或者内存分区的归属不对导致两边拿到的是互相打架的数据。更麻烦的是调试行为。单步走CPU时你以为CLA也被暂停了其实不一定。在某些配置下CLA有自己的执行流程CPU停在断点CLA可能已经跑完一轮甚至好几轮所以你看到的变量是过去的值而不是断点瞬间的值。这种情况下不要去怀疑代码逻辑先确认CLA是不是也在跑最好把CLA的断点也一起设上或者在CLA代码里加专门的调试标志位。5. 关于CCS和XDS110一些零碎但实用的调试习惯5.1 变量被优化看不到先别急着怀疑仿真器调试过程中最容易让人产生仿真器坏了错觉的其实是Watch窗口里的变量。有时候明明全局变量已经在代码里赋了值但Watch里永远显示0或者提示identifier not found。这通常不是仿真器的锅而是编译器优化等级太高。Release模式下编译器可能把变量直接优化成了寄存器操作或者把一个全局变量彻底优化没了。解决办法有三个一是把工程编译优化等级调低比如-O0二是在变量声明前加volatile告诉编译器不要乱优化三是用CCS的Live Expressions窗口它更适合观察运行时实时刷新的全局变量而普通Watch窗口在芯片暂停时才会刷新。5.2 EALLOW保护写寄存器没反应先查这个C2000老手常说的写寄存器写不进去九成和EALLOW有关。EALLOW和EDIS是C2000里的一对开关很多系统级寄存器时钟控制、看门狗、Flash等待状态、PIE向量表、DCSM配置都受到写保护。你要修改它们必须先执行EALLOW;改完后执行EDIS;否则写操作会被静默忽略不会报错但你看到的现象就是配置了半天一点效果都没有。这个坑最阴险的地方在于很多寄存器你读是能读的甚至能看到默认值但写就是写不进去。如果哪天你改了某个外设配置程序行为完全没变化先想想这个寄存器是不是被EALLOW保护了。5.3 冷静下来之后我养成的几个调试习惯这次TMS32F28P550调试经历里踩的每个坑都对应着一个坏习惯不看例程、不核实启动模式、不等稳定就连高频仿真。事后我给自己定了几条纪律拿到新板子先跑一遍官方例程而不是直接跑自己工程。官方例程的时钟、Flash等待状态、PIE初始化都是经过验证的能排除一大半环境问题。每个外设初始化后先在寄存器窗口读回关键配置确认真的写进去了。C2000的寄存器窗口能帮你验证EALLOW有没有生效、外设时钟有没有打开。.map文件和cmd文件不是摆设。栈溢出、段重叠、变量地址异常很多诡异跑飞靠盯.map文件比打断点管用得多。烧Flash前先检查启动模式烧完Flash后再做一次芯片复位不要带着仿真器跑得欢就以为万事大吉。这套习惯说起来简单但以前我经常嫌麻烦跳过结果时间都花在了一遍遍接仿真器、一遍遍重新擦除上。经过这块P550板子的折腾之后我是真的把先读例程、再查时钟和看门狗、最后才看业务代码刻进了肌肉记忆。调试C2000和调试普通单片机完全是两种风格前者更像是在和一个有脾气的硬件工程师打交道你必须按它的规矩来EALLOW、Flash等待状态、外设时钟门控、Boot模式、安全锁每一层都可能程序没问题但就是跑不对。这也是为什么很多从STM32转型过来的朋友一到CCS就抓狂因为Keil那种上电就跑、写寄存器就生效的体验在这里并不完全成立。好在C2000的这份脾气也有迹可循。把这篇实录里的几个关键检查点记下来下次遇到TMS32F28P550或者同系列的调试问题至少不会像我一样白白熬三天。