ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MCP2515 CAN发送失败排查:邮箱占满与寄存器错误全解析

MCP2515 CAN发送失败排查:邮箱占满与寄存器错误全解析 做嵌入式Linux驱动或者单片机CAN通信MCP2515这块芯片十有八九会遇到。我前两年调试MCP2515驱动的时候遇到过一模一样的问题数据一直发不出去三个发送邮箱全被占满读寄存器还能看到总线错误相关的标志位。这个问题折磨了我快一周后来才发现根因根本不是简单一句“驱动没写好”能概括的而是多个配置和物理层的坑叠在一起。这篇文章就把我当时完整的排查思路、寄存器层面的判断技巧以及最终定位问题的过程整理出来重点讲清楚CAN接受和发送失败有哪些常见原因尤其是MCP2515这种SPI外挂CAN控制器特有的“邮箱占满”现象希望能帮你少走弯路。1. 先搞透MCP2515的发送与接收机制1.1 三个发送邮箱与RX缓冲器的工作方式MCP2515内部有3个发送邮箱分别叫TXB0、TXB1、TXB2每个邮箱都有一组独立寄存器包括TXBnCTRL、TXBnSIDH/SIDL、TXBnEID8/EID0、TXBnDLC、TXBnD0到TXBnD7。发送一帧的标准流程是把报文ID、DLC和数据长度写进邮箱对应的寄存器然后置位TXBnCTRL的TXREQ位芯片会自动把这一帧放到总线上参与仲裁并发送。同样接收端有2个带验收滤波器的接收缓冲器RXB0和RXB1。报文到达后如果通过了滤波器设置就会被存进接收缓冲器同时置位CANINTF寄存器里的RXB0IF或者RXB1IF。软件要做的就是在中断或者轮询里读走数据然后清标志位。如果数据没来得及读走新的报文来了之后旧数据就会被覆盖或者直接触发溢出标志表现出来就是“接收失败”。很多人一开始调试只盯着发忽略了接收侧的这些流程后来我在排障中发现接收失败和发送失败往往共享同一套底层原因比如波特率不对、物理层故障、芯片没有正常进入工作模式都可能让收发两边同时出问题。1.2 TXREQ标志位邮箱为什么卡住邮箱占满的直接表现在寄存器里面非常清晰读TXBnCTRL寄存器发现三个邮箱的TXREQ位全是1。TXREQ这个位的设计很有意思它一旦被置1芯片就开始尝试发送发送成功后硬件会自动把它清0同时置位对应的发送完成中断标志TXBnIF。如果这一帧始终没发出去TXREQ就一直是1。这时候如果驱动代码里还傻傻等待TXREQ清零再发送下一帧那就会一直卡死表现就是“三个发送邮箱都被占满数据发不出去”。所以问题的本质不是邮箱数量不够而是邮箱里的帧从来没被正常送走。发送卡住多少会有一些先兆。比如错误标志寄存器EFLG里的发送错误警告位TXWAR会提前置1发送错误被动位TXEP也会在错误累积到一定程度后置位严重时TXBO总线关闭位会直接置1。这几个位能帮你快速判断当前是不是已经处于异常状态而不只是干瞪眼看TXREQ。1.3 “寄存器-总线错误”到底是什么标题里面提到的“读寄存器-总线错误”我理解其实有两种含义一种是你通过SPI读MCP2515的寄存器时读出来的数据不对比如全是0xFF或者0x00这时候总线错误根本不在CAN协议层面而在你上层SPI通信这条链路上另一种是寄存器确实读出来了但发现EFLG寄存器里的总线错误标志置位了说明CAN控制器在协议层面已经出了状况。这两类问题解法完全不同。如果是SPI读出来全0xFF优先检查SPI引脚接线、主机SPI模式配置、MCP2515供电和复位引脚跟CAN总线关系不大。如果是EFLG寄存器里面的错误标志置位那就要重点查CAN总线物理层、波特率、节点数量这些问题。我见过很多人把这两件事混到一起结果排查方向从第一天就错了。2. 逐层排查发送失败六条根因链路2.1 波特率不匹配最隐蔽也最常见CAN通信不像UART那样只要两边按同一个波特率就能聊起来它的波特率必须同时配合位时序的采样点。MCP2515的波特率由CNF1、CNF2、CNF3三个寄存器决定核心计算方式是系统时钟经过BRP预分频后得到一个时间份额TQ然后位时间由同步段、传播段、相位缓冲段1、相位缓冲段2组成一个位由若干个TQ组成。如果你配置的波特率和总线上其他节点不一致发送节点发出的帧虽然波形在总线上有电平变化但其他节点根本无法正确采样自然也不会在ACK槽回一个显性位。发送节点一直等不到ACK就会不断产生ACK错误错误计数器持续累加。这里有个很重要的机制CAN节点发送完一帧之后必须在ACK槽里等到总线上任意一个节点确认。就算总线上只有一个节点在发没有对端回ACK也会报错。所以很多人在桌面上一块板子单独调试接个USB-CAN分析仪反而能通拔掉分析仪立刻就发不出去这多半就是没有节点回ACK导致的。错误计数器TEC的累加规律是每次错误加8到达127时节点进入错误被动状态到达255时直接进入总线关闭状态。一旦总线关闭MCP2515就彻底不参与总线通信了表现在应用层就是“三邮箱全占着怎么发都发不出去”。我建议遇到问题先读一下0x1C和0x1D这两个寄存器看看TEC和REC的数值如果TEC在不停涨波特率不匹配的嫌疑非常大。2.2 工作模式停在配置模式/监听模式MCP2515有正常模式、睡眠模式、监听模式、回环模式、配置模式等多种工作状态通过CANCTRL寄存器的REQOP位来切换。很多人写初始化的时候知道要进配置模式才能配置CNF1/CNF2/CNF3这些寄存器但配完之后忘了把REQOP改回0正常模式或者切换了之后没有回读CANSTAT确认。芯片如果停在配置模式接收和发送功能是受限的这时候你往邮箱里写数据、置TXREQ芯片不一定会真正把帧发出去。监听模式更坑它允许芯片接收总线上的数据但不能发送如果你不小心把工作模式设成了监听模式查驱动查半天都看不出发送为什么失败因为代码逻辑上每个步骤看起来都是对的。判断方法很简单读地址0x0E的CANSTAT寄存器看最低3位的OPMOD字段值。配置模式的OPMOD是0b100监听模式是0b011正常模式是0b000。调试的时候很多人只关注波特率寄存器忽略了这个模式状态字结果芯片一直没进入正常模式白折腾半天。2.3 物理层断链收发器、终端电阻与接线MCP2515本身只是CAN控制器不直接挂在总线上它需要外接一颗CAN收发器芯片比如TJA1050、MCP2551、SN65HVD230这些。收发器负责把控制器输出的显性/隐性逻辑电平转换成CANH和CANL总线上的差分电平。如果收发器供电不对、没有共地、CANH和CANL接反了或者收发器型号本身有问题控制器再正常也发不出去。终端电阻是另一个高频坑点。CAN总线规范要求在总线的两端各接一个120欧电阻这是为了匹配阻抗、消除反射。如果你只是把两个节点接在一起两边都没有120欧电阻总线上的波形反射会很严重通信时好时坏。如果用万用表量CANH和CANL之间的电阻规范情况下应该是60欧左右如果量出来是120欧说明只有一端接了电阻如果量出来很大或者无穷大说明两端都没接。收发器的第8脚通常有个模式控制有些收发器支持静音模式或待机模式如果这个引脚被拉到了错误电平收发器会处于只听或者关闭状态总线上的波形完全出不去。这个点在原理图设计阶段很容易被忽略我遇到过不止一次因为收发器STB引脚默认拉高导致整个CAN模块静悄悄的情况。2.4 晶振与时钟配置不对MCP2515需要外部时钟源最常见的是8MHz晶振也有不少板子用16MHz晶振。代码里配置波特率的时候依赖的是你对系统时钟频率的假设。如果板子上实际焊接的晶振是8MHz你按16MHz去算BRP和位时序最后出来的实际波特率和你想要的目标波特率会差整整一倍。这个坑最麻烦的地方在于程序编译不报错寄存器看起来也配置了甚至你也按照常见例程一字不差地写了但就是不通。排查方法也不复杂直接读CNF1/CNF2/CNF3的值反推一下当前波特率是多少再和代码里初始化时用的Fosc宏比对一下是否一致。另外晶振不起振也是常见故障示波器量一下晶振引脚如果没有振荡波形后面全免谈。2.5 SPI读写时序和上下层驱动bugMCP2515和主机之间走SPI通信这里出的问题往往看起来像是CAN问题其实是SPI问题。MCP2515对SPI模式要求一般是模式0CPOL0CPHA0片选拉低之后先发指令字节再发地址然后读数据或者写数据。如果你主控的SPI配置成了模式1或者模式2数据采样沿对不上读回来的寄存器值要么是固定的0xFF要么是看起来完全没规律的乱值。另一个容易出问题的是读状态寄存器。我建议调试初期先写一个简单的寄存器读函数直接读CANSTAT0x0E看看返回什么如果复位后能读到默认值0x80说明SPI链路基本是通的。如果读出来全0xFF先检查SPI时钟极性和相位再看MISO有没有和主控连对。MCP2515的SCK不建议一下子拉太高很多板子在10MHz以下稳定4MHz最稳尤其走杜邦线的时候。驱动代码本身的逻辑也要检查。最常见的写法是主循环里先检查邮箱是否空闲然后填充报文、置TXREQ最后轮询等待TXREQ清零。如果轮询条件写反了比如判断的是CANINTF里的TXBnIF而不是TXBnCTRL里的TXREQ或者中断服务函数里没有清标志位就会出现“明明发送已经完成代码却认为邮箱还在忙”的假象看起来也像邮箱占满。2.6 验收滤波器和接收中断接受失败的坑前面几节主要聊发送但标题问的是接受和发送失败一起排查接收侧的坑也有不少。MCP2515的接收缓冲器RXB0和RXB1都配有验收滤波器报文到了之后要匹配滤波条件才能被接收。很多人初始化的时候把验收滤波器全关了或者配置成了只接收特定ID结果总线上明明有数据驱动一个包都收不到读CANINTF又看不到接收标志。还有一类接收问题是溢出。如果RXB0里已经有数据没处理完又来了一帧新的如果RXB0的溢出保护位RXM1配置不对新帧会把旧帧覆盖掉或者直接丢弃同时置位EFLG里的RX0OVR溢出标志。所以排查接收问题的时候不能只看有没有收到包还要看EFLG寄存器里是不是已经挂了一堆溢出错误。中断标志位没有及时清除也会导致后续接收中断无法触发很多人中断服务函数进来之后直接读数据忘了写回清除标志结果下一次中断永远不来。3. 一次完整的寄存器级排障实录3.1 工具准备示波器、逻辑分析仪、USB-CAN卡我那次排障一开始也是瞎猜后来整理了一套固定流程效率高了很多。工具建议准备四样示波器至少双通道用来量CANH和CANL的差分波形逻辑分析仪用来抓SPI时序确认主机和MCP2515之间的通信到底正不正常USB-CAN分析仪作为总线上一个确定存在的对端节点万用表用来量供电、终端电阻和连线通断。USB-CAN分析仪非常重要。它不光是能发报文和收报文还能帮你确认“总线上有没有节点在回ACK”。当你怀疑发送失败是因为没有对端节点时挂一个USB-CAN卡上去就能验证。另外USB-CAN卡一般有终端电阻开关调试的时候把它的120欧终端打开也能帮助稳定波形。3.2 第一步验证SPI链路拿到板子先不跑完整应用单独写一个SPI读寄存器的测试函数固定读CANSTAT。MCP2515上电复位之后CANSTAT的默认值是0x80如果读出来是这个数说明SPI至少能正确让芯片回数据了。如果读出来是0xFF或者0x00我就不浪费时间往下查CAN了先解决SPI问题。这时候逻辑分析仪上场抓CS、SCLK、MOSI、MISO四根线。重点看CS是不是低电平有效SCLK上有没有时钟MISO在主机发完地址之后有没有拉出数据。SPI这边基本上要么是极性问题要么是引脚没对要么是芯片没复位。MCP2515的复位引脚RESET如果一直拉低芯片始终处于复位状态SPI读什么都会异常。3.3 第二步确认模式切换SPI没问题之后读CANSTAT看当前芯片工作在什么模式。如果OPMOD显示的是配置模式说明初始化代码里配置模式到正常模式这一步没执行成功。我那次就发现芯片停在配置模式原因是初始化函数里调用完配置寄存器的代码之后直接返回了根本没有任何切换到正常模式的语句。还有一种常见的半吊子写法写CANCTRL请求切换模式之后没有等待模式和请求一致再继续。REQOP置成0之后芯片不是瞬间切过去的需要等几个时钟周期。严谨的做法是循环读CANSTAT直到OPMOD等于0x00再往下走。这个等待步骤加上之后很多莫名其妙的初始化失败问题都能消掉。3.4 第三步盯住EFLG和错误计数器模式正常之后不要急着发数据先读EFLG寄存器0x2D和错误计数器TEC0x1C、REC0x1D。如果EFLG里面有总线关闭位TXBO置1先做一次复位或者用ABAT位手动退出总线关闭状态再继续试发送。我那次测得TEC一直在涨每发一次大约加8这说明芯片在反复尝试发送但都失败了。单纯看这个还不够我又读回EFLG发现TXWAR已经置位但是还没有到TXBO。这个状态说明物理层或者协议层还有问题不是代码层面的发送逻辑问题。把USB-CAN分析仪挂上去再发数据TEC不再涨了说明之前总线上根本没有对端节点在回应这让我确定了发送失败的链路在物理层和对端节点而不是SPI。3.5 第四步看总线波形用示波器直接测量CANH和CANL对GND的波形。正常信号在隐性位时两条线都在2.5V左右显性位时CANH拉高到大约3.5VCANL拉低到大约1.5V差分大概2V。如果示波器探到CANH和CANL完全没反应说明波形根本没从收发器出来问题在收发器或者控制器根本没有把发送请求送出来。如果波形能看到但是幅度不对比如CANH到不了3.5VCANL下不到1.5V或者波形明显有台阶、毛刺就要怀疑终端电阻匹配和线材质量。我还遇到过一种情况收发器供电正常但是收发器和MCP2515之间的TXD/RXD引脚接反了导致控制器输出逻辑到收发器之后完全错乱波形也是乱的。3.6 复盘与解决最终定位到我这台板子上是两个问题叠加第一初始化代码配置完波特率寄存器之后另一段启动代码又重新写了CANCTRL寄存器把芯片置成了监听模式第二我在调试时一直拿单板单独测总线上没有对端节点所以根本没有ACK响应。两个问题单独看都不算严重但叠在一起就表现为“邮箱全占满读寄存器始终有错误标志”。解决方式很简单把CANCTRL的写入顺序理顺保证任何代码路径最后都切回正常模式然后接上USB-CAN分析仪作为对端再发送就立刻成功了。这个问题让我意识到很多所谓“寄存器总线错误”很多时候只是“没有任何对端节点回ACK”而已属于调试环境搭建不完整而不是产品代码真的坏了。4. 常见问题速查表与独家经验4.1 常见问题速查表我把这次排障和之前一些项目中碰到的问题整理成了速查表排查的时候对着看效率高很多。现象直接原因快速检查方法解决方向三个TXBnCTRL的TXREQ全为1报文从未完成发送读EFLG看错误标志读TEC看错误计数检查波特率、工作模式、物理层、对端节点读寄存器全部返回0xFFSPI链路异常逻辑分析仪抓SPI时序检查MISO核对SPI模式0检查接线、片选、复位引脚CANSTAT的OPMOD不是0x00芯片未进入正常模式初始化后回读CANSTAT设置REQOP为0等待模式切换完成TEC数值持续增加发送过程中持续产生错误帧读0x1C寄存器观察变化检查总线是否有对端波特率是否一致终端电阻是否匹配EFLG中TXBO置1总线关闭读EFLG最高位手动退出总线关闭排查错误根源后再恢复发送CANINTF里RXB0IF一直不置位验收滤波器配置不当或没收到有效帧检查报文ID与滤波器掩码核对验收滤波寄存器配置用USB-CAN卡辅助发送测试帧EFLG中RX0OVR置1接收缓冲器溢出读EFLG溢出标志及时读走接收缓冲清溢出错标志这张表看起来简单但每一条背后都是真金白银的调试时间换来的。我自己踩坑的时候最痛苦的就是在几条链路之间反复横跳一会儿怀疑SPI一会儿怀疑波特率一会儿又怀疑驱动逻辑。但只要你把SPI链路、工作模式、错误计数器、物理层波形这四个点按顺序过一遍绝大多数问题都能在半小时内圈定范围。4.2 几条独家排查经验第一条经验所有寄存器操作都要写对应的读回函数。MCP2515驱动调起来寄存器读写函数就是你的眼睛。不要只写write函数不写read函数也不要只在初始化里操作寄存器调试阶段要随时能读。我习惯写一个mcp2515_read_reg(reg)和mcp2515_write_reg(reg, val)两个函数所有上层代码都基于这两个函数调试时改一行日志就能把所有寄存器状态打出来。第二条经验USB-CAN分析仪必须有。哪怕你只是验证驱动能不能发也建议挂一个USB-CAN分析仪在总线上。它一方面能回ACK另一方面能从工具侧看到有没有正确报文还能帮你确认波特率。就算你手头暂时没有分析仪用一个开发板外加一个CAN收发器做对端节点也行关键是总线上不能只有被测设备一个人。第三条经验SPI时序的稳定性会直接影响CAN寄存器读写的正常性。MCP2515的SPI时钟频率不是越高越好尤其在使用杜邦线连接的时候建议先用低速模式把功能调通再说。我之前遇到过主控SPI用18MHz调不通改到4MHz之后所有问题全消失的情况。等到PCB贴片打样、走线工艺固定之后再尝试提升SPI速率不迟。第四条经验关于晶振频率一定要把板子上的实际晶体和代码里的Fosc宏对清楚。很多MCP2515模块默认用8MHz晶振但也有不少板子用16MHz晶体。可以写一个小函数把当前CNF1/CNF2/CNF3组合反算成实际波特率打出来确认是否和目标值一致。这个函数写一次后面换板子换芯片都能用非常省事。第五条经验中断标志位能清就及时清能轮询就暂时不要用太复杂的中断逻辑。调试初期我建议用轮询模式跑通收发因为中断一旦没清标志位排查起来又多一层干扰。等轮询完全跑通再改成中断方式这样出错时能缩小范围。中断模式里也要记住读中断标志、处理数据、清标志、再退出顺序不能乱。最后再分享一点个人体会说实话MCP2515这个芯片本身并不复杂真正复杂的是CAN协议的状态机制和物理层条件。发送邮箱占满只是最终的现象真正的原因可能在波特率寄存器里、在收发器的使能引脚上、在总线的终端电阻上甚至在你压根没插对端节点这件事上。排障的时候不要一上来就怀疑驱动代码先按SPI通信、模式切换、错误标志、物理层波形这个顺序过一遍大多数问题都能快速定位。调试CAN这类通信协议最大的体会就是把每一步验证扎实寄存器能读回预期的值模式能正确切换错误计数不涨波形符合预期最后才有可能一次发送成功。如果你也正在被MCP2515的发送邮箱占满问题折磨希望这篇文章能让你少走一点我走过的弯路。
返回列表