1. 项目概述与核心价值
在工业自动化、汽车电子以及运动控制这些对实时性和可靠性要求极高的领域,CAN总线(Controller Area Network)几乎是嵌入式工程师绕不开的核心技术。它那套基于优先级的非破坏性仲裁机制,确保了即使在复杂的电磁环境下,多个节点也能有序、可靠地通信,不会因为总线冲突而导致数据丢失或系统瘫痪。然而,对于许多功能强大的通用微处理器(MCU)来说,CAN控制器并非标准片上外设,这就需要我们通过外部扩展来实现。
今天要深入探讨的,正是这样一个经典的“MCU + 独立CAN控制器”的解决方案:基于Freescale(现NXP)的MCF5272 ColdFire微处理器与Infineon的82C900 TwinCAN控制器。MCF5272是一款集成了以太网、USB等丰富外设的32位处理器,面向通信和工业市场,性价比很高,但它原生不带CAN。而82C900则是一个功能完备的双节点CAN控制器,支持CAN 2.0B协议,最高速率可达1Mbps,并内置了32个消息对象、FIFO和网关功能,能极大减轻CPU负担。
这个组合的巧妙之处在于接口选择:两者通过MCF5272的QSPI(Queued Serial Peripheral Interface)模块连接。相比并行总线,SPI接口引脚少,连接简单,无需额外的“胶合逻辑”,非常适合作为扩展低速、可靠外设的桥梁。整个项目的核心,就是打通这条从MCF5272的QSPI到82C900内部寄存器的“数据高速公路”,并在此基础上,构建一套稳定、高效的CAN通信驱动。这不仅仅是简单的寄存器读写,更涉及到精确的时序控制、复杂的中断管理以及符合CAN协议栈规范的消息对象配置,是嵌入式底层驱动开发的典型实战案例。
2. 硬件设计思路与关键考量
2.1 核心芯片选型逻辑
为什么是MCF5272和82C900?这个选择背后有清晰的工程逻辑。首先看应用场景,当时(项目文档提及的2000年代初)以及现在许多工业场景中,设备既需要CAN这样的实时现场总线与传感器、执行器通信,也需要以太网与上位机或云端进行大数据块、非实时性的交互。MCF5272恰好集成了快速以太网控制器(FEC),满足了“CAN+Ethernet”的双重需求,避免了使用两颗MCU或额外以太网芯片的复杂度和成本。
对于CAN控制器,市场上虽有BasicCAN和FullCAN之分。BasicCAN控制器只负责物理层和部分数据链路层,消息过滤、管理等高级功能全靠CPU软件实现,CPU负载高。而82C900属于FullCAN控制器,它内置了32个独立的消息缓冲区(Message Object),每个都可以单独配置为发送或接收,并带有复杂的标识符过滤和自动处理机制。这意味着一旦配置好,数据的收发、过滤甚至节点间路由(通过内置网关)都可以由82C900硬件自动完成,CPU仅在消息收发完成时被中断通知,极大地解放了CPU资源,保证了系统的实时性。此外,其双节点(CAN A和CAN B)设计可以连接两个独立的CAN网络,增加了系统设计的灵活性。
接口方面,82C900提供了复用总线接口和SPI接口。对于MCF5272这类引脚资源相对紧张、且追求精简设计的嵌入式处理器,SPI接口的吸引力是巨大的。它只需要4根线(时钟、片选、主机输出从机输入、主机输入从机输出),就能实现全双工通信,硬件连接极其简洁。MCF5272的QSPI更是SPI的增强版,支持命令队列,能进行一批数据传输而无需CPU频繁干预,进一步提升了效率。
2.2 接口与时序设计要点
硬件连接图的核心就是QSPI的四线制与82C900的SSC(同步串行通道)对接:
- MCF5272_QSPI_CLK->82C900_SCLK: 提供通信时钟。
- MCF5272_QSPI_CS0->82C900_/SLS: 片选信号,低电平有效。
- MCF5272_QSPI_DOUT->82C900_MTSR: 主发从收数据线。
- MCF5272_QSPI_DIN<-82C900_MRST: 主收从发数据线。
这里最关键的是时序匹配。82C900的SPI接口有严格的时序要求,例如片选有效到时钟有效的最小时间(tCSS)、连续数据字节传输之间的间隔时间等。文档中给出了具体参数,例如对于24MHz的CAN控制器时钟,读访问时数据字节间最小需要584ns,而写访问只需要209ns。如果时序不满足,轻则数据出错,重则通信完全失败。
MCF5272的QSPI模块提供了极高的灵活性来满足这些要求。其QSPI延迟寄存器(QDLYR)可以独立配置两个关键参数:
- QCD(QSPI Clock Delay): 控制片选有效到第一个时钟沿开始的延迟。计算公式为
SCLKDELAY = QCD / CLKIN。例如,系统时钟CLKIN为66MHz,设置QCD=6,则延迟约为6 / 66MHz ≈ 91ns,这满足了82C900对tCSS(最小84ns)的要求。 - DTL(Delay After Transfer): 控制每次传输(8/16位)结束后的延迟。计算公式为
TxRxDELAY = (32 * DTL) / CLKIN。设置DTL=2,则延迟为(32*2)/66MHz ≈ 970ns,这足以覆盖读写操作所需的最长间隔。
通过精确计算和配置这两个参数,我们无需使用82C900的可选RDY(就绪)握手信号,就能实现稳定可靠的SPI通信,简化了硬件设计和软件流程。
2.3 电源、时钟与复位设计
电源: M5272C3开发板只提供3.3V,而82C900和其配套的PCA82C250 CAN收发器、24MHz有源晶振需要5V供电。解决方案是在子卡上集成了一颗Maxim的MAX682电荷泵芯片,它能将3.3V升压至5V,并提供高达250mA的电流,足以驱动整个CAN子系统。这种设计让整个模块只需从主板取3.3V和地,非常简洁。
时钟: 82C900需要一个外部时钟源,这里选择了24MHz的有源晶振。这个频率的选择很有讲究:它要能支持CAN总线最高1Mbps的速率。CAN控制器的内部时钟经过分频后产生时间份额(Time Quanta),进而组成位时间。24MHz的时钟在合理配置分频器和位时间段参数后,可以稳定工作在1Mbps。文档中也提到,如果同时使用FIFO和网关等高级功能,总线速率可能需要降低至500kbps以保证数据处理能力。
复位: 82C900的复位引脚直接连接到MCF5272的复位输出(-RSTO)。这意味着任何导致MCF5272复位的事件(如上电、看门狗超时)都会同时复位CAN控制器,确保两者状态同步。这里有一个重要的软件注意事项:82C900复位释放后,需要等待至少1100个CAN时钟周期(约46μs @24MHz)才能对其进行第一次访问。因此,在系统初始化代码中,在配置QSPI和82C900之前,必须插入足够的延时或确保其他初始化流程耗时超过这个时间,否则首次寄存器访问可能会失败。
3. 软件架构与QSPI驱动实现
3.1 QSPI模块初始化与底层读写函数
软件驱动的基石是QSPI通信。MCF5272的QSPI强大之处在于其80字节的队列RAM,可以预装最多16个传输命令和数据,然后自动执行,期间CPU可以处理其他任务。但在我们这个相对简单的点对点通信中,我们采用更直观的“查询-等待”模式,每次传输都等待完成,代码更易于理解和调试。
首先看QSPI的初始化函数mcf5272_qspi_init()。它的核心任务是配置好通信的基本参数:
- 模式寄存器(QMR): 设置波特率(Baud Rate)、数据位宽(8位或16位)、时钟极性和相位。
- 延迟寄存器(QDLYR): 如前所述,配置QCD和DTL,以满足82C900的时序要求。
- 命令RAM: 初始化所有16个队列条目。虽然我们一次只传输2-3个字节,但预先配置好所有条目(例如,设置使用CS0、使能可编程延迟、8位传输等)是一种好习惯。代码中通过循环向QDR写入固定值
MCF5272_QSPI_QDR_CR_CONT来完成。
注意: 在向命令RAM或数据RAM写入时,必须先通过QSPI地址寄存器(QAR)指定要操作的RAM位置。写入QDR后,QAR会自动递增,指向下一个位置。这是QSPI队列操作的关键机制。
底层读写函数QSPI_SendByte和QSPI_ReadByte是访问82C900所有寄存器的门户。它们的流程高度相似:
- 设置页寄存器(PAGE): 调用
CAN_SetPageReg,将目标82C900寄存器地址的高4位写入其PAGE寄存器。这是82C900特有的11位地址寻址机制的一部分。 - 准备传输队列:
- 将QAR指向传输RAM(Tx RAM)顶部。
- 通过QDR写入第一个字节:对于写操作,是(寄存器地址低7位 | 0x80),其中最高位1表示写;对于读操作,是(寄存器地址低7位 & 0x7F),最高位0表示读。
- 对于写操作,再通过QDR写入要发送的数据字节;对于读操作,则写入一个哑元字节(Dummy Byte)以产生时钟来读取数据。
- 配置并启动传输:
- 设置QSPI环绕寄存器(QWR): 告诉QSPI传输的起始队列指针和结束队列指针。例如,发送两个字节(地址+数据),则结束指针设为1(从0开始)。
- 在QSPI延迟寄存器(QDLYR)中设置SPE位,使能传输。
- 等待完成: 循环查询QSPI中断寄存器(QIR)中的SPIF(QSPI完成)标志位。
- 读取数据(仅读操作): 传输完成后,将QAR指向接收RAM(Rx RAM)顶部,然后通过QDR读取。第一个读出的字节是之前发送地址时收到的哑元数据,第二个字节才是从82C900寄存器读回的真实数据。
// 示例:QSPI_SendByte 函数的核心流程(简化版) void QSPI_SendByte(uint16 CanRegAddr, uint8 Data) { MCF5272_IMM *imm = mcf5272_get_immp(); // 1. 设置页寄存器 CAN_SetPageReg((uint8)(CanRegAddr >> 7)); // 2. 准备传输队列 MCF5272_WR_QSPI_QAR(imm, MCF5272_QSPI_QAR_Tx); // 指向Tx RAM MCF5272_WR_QSPI_QDR(imm, (uint8)(CanRegAddr | 0x80)); // 写命令+地址低7位 MCF5272_WR_QSPI_QDR(imm, Data); // 写入数据 // 3. 配置并启动 MCF5272_WR_QSPI_QWR(imm, MCF5272_QSPI_QWR_SendByte); // 设置传输2字节 MCF5272_WR_QSPI_QDLYR(imm, MCF5272_QSPI_QDLYR_CanEnable); // 使能传输 // 4. 等待完成 while (!(MCF5272_RD_QSPI_QIR(imm) & MCF5272_QSPI_QIR_QSPIFinish)); }3.2 82C900寄存器映射与寻址机制
理解82C900的寄存器映射是软件设计的核心。其寄存器分为两大块:
- 独立外壳寄存器(Standalone Shell Registers): 位于内存映射的低端(0x0000 - 0x007F),负责全局控制,如节点控制、位定时、中断汇总、页寄存器(PAGE)等。
- TwinCAN寄存器: 从0x0200开始,包含节点控制/状态寄存器和32个消息对象(Message Object)的寄存器组。每个消息对象占用0x20字节,包含控制、配置、仲裁(标识符)和数据(8字节)寄存器。
82C900通过SPI访问时,使用11位地址。但SPI一次只能传输8位数据。解决方案是:高4位地址存放在PAGE寄存器中,低7位地址放在SPI传输的第一个字节的低7位,而该字节的最高位(A7)用作读/写标志位(1=写,0=读)。
这就是CAN_SetPageReg函数的作用:它先向固定的PAGE寄存器地址(0x7C或0xFC)写入一个字节,其低4位是目标寄存器地址的高4位,同时还可以设置自动地址递增位(AutoInc),方便连续读写。之后,再通过SPI发送包含低7位地址和R/W位的字节。这种分页寻址机制在地址空间大于8位的SPI外设中很常见。
实操心得: 在调试初期,最容易出错的地方就是地址计算。务必清楚地区分“11位的82C900内部寄存器地址”、“PAGE寄存器值”和“SPI传输的第一个字节”。建议将常用的寄存器地址(如节点控制寄存器ACR/BCR、位定时寄存器ABTR/BBTR、各消息对象寄存器组的基地址)用宏定义好,避免手动计算出错。例如:
#define CAN_ACR 0x0000 // Node A Control Register #define CAN_BCR 0x0004 // Node B Control Register #define CAN_ABTR 0x0010 // Node A Bit Timing Register #define CAN_MSG_CTRL 0x0300 // Base addr of Msg Obj 0 Control Register
4. CAN节点初始化与位定时配置
4.1 节点控制寄存器(ACR/BCR)详解
在让CAN节点上线之前,必须对其进行正确的初始化。这主要通过配置节点控制寄存器(对于节点A是ACR,节点B是BCR)来完成。该寄存器的关键位域包括:
- INIT(位0): 初始化位。置1将使节点进入“总线关闭”状态,脱离CAN总线,此时才能安全配置位定时等参数。配置完成后,需清零此位以使节点尝试与总线同步。
- CCE(位6): 配置使能位。只有当INIT=1时,此位才能被置1。CCE=1时,才允许写位定时寄存器(BTR)和错误计数器。配置完成后,在清零INIT前,应先清零CCE。
- CA(位7): CAN分析器模式。置1时,该节点不参与总线通信,仅监听总线流量,用于调试和监控。
- 中断使能位(LECIE, EIE, SIE): 分别控制“最后错误代码”、“错误状态改变”、“状态改变”中断的使能。
初始化一个节点的典型流程是:
- 向ACR/BCR写入0x41。这设置了INIT=1(进入初始化模式),CCE=1(允许配置),并禁用了所有中断。
- 配置位定时寄存器(ABTR/BBTR)。
- (可选)配置中断屏蔽寄存器,决定哪些中断源能触发节点全局中断。
- 向ACR/BCR写入0x00。这清除了INIT和CCE,节点退出初始化模式,开始尝试与总线同步(监听总线空闲状态,即连续11个隐性位)。
4.2 位定时寄存器(BTR)与波特率计算
CAN通信的可靠性极度依赖于精确的位定时。位时间被划分为几个段(Segment),每个段由整数个时间份额(Time Quanta, Tq)组成:
- 同步段(Sync_Seg): 固定为1个Tq,用于硬同步。
- 传播时间段(Prop_Seg): 用于补偿网络上的物理延迟。
- 相位缓冲段1(Phase_Seg1)和相位缓冲段2(Phase_Seg2): 用于重同步,补偿时钟误差。
在82C900的位定时寄存器中,我们配置以下参数:
- BRP(Baud Rate Prescaler, 位[5:0]): 波特率预分频器。
Tq = (BRP + 1) / fCAN,其中fCAN是CAN控制器的输入时钟频率(如24MHz)。 - SJW(Synchronization Jump Width, 位[7:6]): 同步跳转宽度,决定了在一次重同步中最多可以调整多少个Tq,必须小于等于Phase_Seg1。
- TSEG1(位[11:8]): 决定了Prop_Seg + Phase_Seg1的长度。
Prop_Seg + Phase_Seg1 = (TSEG1 + 1) * Tq。 - TSEG2(位[14:12]): 决定了Phase_Seg2的长度。
Phase_Seg2 = (TSEG2 + 1) * Tq。 - DIV8(位15): 选择时钟源为fCAN或fCAN/8。
因此,位时间(Bit Time) = (Sync_Seg + Prop_Seg + Phase_Seg1 + Phase_Seg2) * Tq = (1 + (TSEG1+1) + (TSEG2+1)) * Tq。波特率(Baud Rate) = 1 / 位时间。
文档中的例子:fCAN = 24MHz, BRP = 2, TSEG1 = 6, TSEG2 = 7, SJW = 0。
Tq = (2+1)/24MHz = 0.125μsBit Time = (1 + (6+1) + (7+1)) * 0.125μs = 16 * 0.125μs = 2μsBaud Rate = 1 / 2μs = 500 kbps
配置代码直观地体现了这一点:
/* 设置500kbps波特率: ((0+1)+(6+1)+(7+1)) * 0.125us = 2us */ QSPI_SendByte(CAN_BBTR, 0x02); // 写入BBTR低字节,包含BRP=2 QSPI_SendByte(CAN_BBTR+1, 0x67); // 写入BBTR高字节,TSEG1=6, TSEG2=7, SJW=0注意事项: 位定时参数的设置必须与总线上其他所有节点严格一致,否则无法通信。通常由系统架构师根据总线长度、传输速率和振荡器容差提前计算好。使用错误的TSEG值可能导致采样点位置不佳,在噪声环境下极易出错。建议使用芯片厂商或第三方提供的位定时计算工具进行验证。
5. 消息对象配置与数据收发实战
5.1 消息对象:CAN通信的邮箱
82C900的32个消息对象是它的核心资源,你可以把它们想象成32个独立的邮箱。每个邮箱(消息对象)都可以被配置为发送邮箱或接收邮箱,并绑定到一个特定的CAN节点(A或B)。每个邮箱有自己的:
- 标识符(ID): 存储在仲裁寄存器中,可以是11位标准ID或29位扩展ID。CAN总线根据ID进行仲裁和过滤。
- 控制寄存器: 包含有效位(MsgVal)、发送请求位(TxRqst)、新数据位(NewData)、中断使能位等状态和控制标志。
- 配置寄存器: 定义该消息对象属于哪个CAN节点、是发送还是接收、数据长度(0-8字节)、使用哪个中断节点等。
- 数据寄存器: 两个32位寄存器,共8字节,用于存放要发送或已接收的数据。
5.2 消息对象初始化流程
函数CAN_MsgObj_Init展示了配置一个消息对象的完整步骤,这是一个精细的“开关拨动”过程:
- 置为无效: 将控制寄存器的MsgVal位清零(写0x7F),确保在配置过程中,CAN控制器不会误操作该消息对象。
- 清除所有标志位: 依次清除中断挂起位、远程请求位、发送请求位。对于接收对象,还要清除数据丢失位;对于发送对象,则要设置“禁止自动发送”位。这相当于将邮箱清空并上锁。
- 配置邮箱属性: 向配置寄存器写入一个字节,这个字节融合了数据长度(左移4位)、节点选择(A/B)、标识符类型(标准/扩展)和方向(发送/接收)。
- 设置标识符: 将29位或11位的ID拆分,依次写入仲裁寄存器的4个字节。注意标准ID需要左移18位,以对齐到29位ID格式的特定位置。
- (后续使能): 初始化完成后,调用
CAN_MsgObjRx_Enable或CAN_MsgObjTx_Start来最终将MsgVal位置1,使邮箱生效。
// 配置消息对象的核心代码片段 void CAN_MsgObj_Init(uint8 Node, uint8 Msg, uint8 TxRx, uint8 NoBytes, uint8 ID, uint32 IDnum) { // 1. 置为无效 QSPI_SendByte(CAN_MSG_CTRL + (Msg*0x20), 0x7F); // 2. 清除各种标志位 QSPI_SendByte(CAN_MSG_CTRL + (Msg*0x20), 0xFD); // 清中断挂起 QSPI_SendByte(CAN_MSG_CTRL + (Msg*0x20), 0x7F); // 清远程请求 QSPI_SendByte(CAN_MSG_CTRL+1 + (Msg*0x20), 0xDF); // 清发送请求 if (TxRx == Tx) QSPI_SendByte(CAN_MSG_CTRL+1 + (Msg*0x20), 0xFD); // Tx: 禁止自动发送 else if (TxRx == Rx) QSPI_SendByte(CAN_MSG_CTRL+1 + (Msg*0x20), 0xF7); // Rx: 清数据丢失 QSPI_SendByte(CAN_MSG_CTRL+1 + (Msg*0x20), 0xFB); // 清NewData标志 // 3. 配置属性 QSPI_SendByte(CAN_MSG_CONFIG + (Msg*0x20), (uint8)(NoBytes<<4 | Node | ID | TxRx)); // 4. 设置ID if (ID == Stand) IDnum = IDnum << 18; // 标准ID对齐 QSPI_SendByte(CAN_MSG_ARB + (Msg*0x20), (uint8)(IDnum)); QSPI_SendByte(CAN_MSG_ARB+1 + (Msg*0x20), (uint8)(IDnum>>8)); QSPI_SendByte(CAN_MSG_ARB+2 + (Msg*0x20), (uint8)(IDnum>>16)); QSPI_SendByte(CAN_MSG_ARB+3 + (Msg*0x20), (uint8)(IDnum>>24)); }5.3 数据发送与接收流程
发送流程:
- 填充数据: 使用
CAN_MsgObj_TxData函数,将待发送的8字节数据(两个32位字)拆分并写入消息对象的数据寄存器。 - 启动发送: 调用
CAN_MsgObjTx_Start。这个函数依次执行:设置CPU更新完成标志(CPUUPD=1)、设置新数据标志(NewData=1)、设置消息有效(MsgVal=1)、最后置位发送请求标志(TxRqst=1)。一旦TxRqst被置位,82C900硬件就会在总线空闲时,自动将这个消息对象的数据按照CAN帧格式发送出去。 - 发送完成处理: 可以配置消息对象在发送成功时产生中断。在中断服务程序(ISR)中,需要清除中断挂起标志,并可以重置消息对象(调用
CAN_MsgObjTx_Reset)以准备下一次发送。
接收流程:
- 配置与使能: 通过
CAN_MsgObj_Init将消息对象配置为接收模式,并设置好过滤ID。然后调用CAN_MsgObjRx_Enable(本质上是置位MsgVal)使其开始监听总线。 - 中断等待: 配置接收中断使能(
CAN_MsgObj_IntEnable)。当总线上出现ID匹配的报文时,82C900会自动将其存入该消息对象的数据寄存器,并置位NewData标志和中断挂起标志,触发中断。 - 读取数据: 在中断服务程序(ISR)中,调用
CAN_MsgObj_RxData(函数在文档中未完整列出,但逻辑与TxData相反)从数据寄存器读取字节并组合成32位数据。读取后,需要清除中断挂起标志。
文档中的示例主函数和中断服务程序清晰地展示了这一互动:主程序初始化节点A的Msg0为发送,节点B的Msg1为接收(两者ID相同),并启动发送。当Msg0的数据通过CAN收发器发到总线上时,节点B的Msg1会收到数据并触发中断。在中断里,读取数据,重置Msg0,填入新数据,再次启动发送,形成一个简单的自发自收回环测试。
5.4 中断系统管理
82C900拥有多达72个中断源(32个消息对象 × 2 + 每个节点4个全局中断)。它通过一个灵活的“中断节点”机制进行汇总。每个中断源都可以被分配到一个中断节点(0-7)。然后,这些中断节点可以映射到两个物理中断输出引脚OUT0和OUT1上。例如,可以将所有接收中断分配到节点1,并配置OUT1输出节点1的中断。OUT1再连接到MCF5272的外部中断引脚(如IRQ1)。
在软件上,需要配置:
- 消息对象中断使能: 在消息对象控制寄存器中,置位TxIe或RxIe。
- 中断节点分配: 在消息对象配置寄存器的特定字段,设置该对象使用哪个中断节点(0-7)。
- 全局中断路由: 在82C900的全局控制寄存器中,配置哪些中断节点映射到OUT0,哪些映射到OUT1。
- 节点中断屏蔽: 在节点中断屏蔽寄存器(如BIMR0)中,使能特定消息对象作为该节点的中断源。
这种层级化的中断管理虽然略显复杂,但提供了极大的灵活性,允许工程师根据中断优先级和系统需求,精细地分配和管理中断。
6. 调试经验与常见问题排查
在实际调试MCF5272与82C900的CAN通信系统时,我踩过不少坑,也总结出一些关键排查点。
问题一:QSPI通信完全失败,读回数据全是0xFF或0x00。
- 检查硬件连接: 这是第一步也是最容易出错的一步。用示波器或逻辑分析仪检查QSPI_CLK、QSPI_CS0、QSPI_DOUT、QSPI_DIN四根线。确保片选信号在传输期间保持低电平,时钟信号正常,数据线有波形。特别注意电平是否匹配(3.3V)。
- 检查QSPI配置: 确认QSPI的时钟极性(CPOL)和相位(CPHA)与82C900的SSC模式匹配。文档指出82C900 SSC是“时钟空闲高,数据在领先沿变化,在跟随沿捕获”,这对应CPOL=0, CPHA=1。MCF5272的QMR寄存器必须据此设置。
- 检查延迟参数: 如果时序不满足82C900的要求,通信会不稳定。确保QDLYR寄存器中的QCD和DTL值是根据系统时钟(CLKIN)和82C900时序要求计算得出的。可以尝试增大延迟值进行测试。
- 检查PAGE寄存器设置: 在每次读写82C900寄存器前,是否正确地调用了
CAN_SetPageReg?发送的地址字节是否正确(高7位是地址低7位,最高位是R/W#)?
问题二:CAN节点无法进入“总线开启”状态,一直处于“总线关闭”或初始化模式。
- 检查位定时配置: 这是最常见的原因。用示波器测量CANH和CANL之间的差分信号,看是否有正常的显性/隐性位跳变。如果没有,首先确认ACR/BCR寄存器的INIT位是否已清零(退出初始化模式),CCE位是否已在清零INIT前被清零。然后,反复核对BRP、TSEG1、TSEG2、SJW的值是否计算正确,并与总线上其他节点一致。
- 检查物理层: CAN收发器(PCA82C250)是否正常工作?终端电阻(通常为120欧姆)是否在总线两端正确连接?总线是否有短路、断路?可以用一个已知正常的CAN节点(如USB-CAN适配器)接入总线,看是否能收到这个节点发出的报文,以隔离问题。
问题三:能发送,但接收不到数据,或收不到中断。
- 检查消息对象配置: 确认发送和接收消息对象的ID是否完全一致(包括标准/扩展格式)。确认接收消息对象是否已使能(MsgVal=1),并正确配置为接收模式(方向位)。确认接收中断是否已使能(RxIe=1),并分配了正确的中断节点。
- 检查中断路由与CPU配置: 确认82C900的OUT1(假设接收中断映射到此)引脚是否连接到MCF5272的正确外部中断引脚(如IRQ1)。确认MCF5272的SIM模块中,该外部中断的触发方式(边沿/电平)和优先级(IPL)是否已配置。确认全局中断是否已开启(MCF5272的SR寄存器中I位)。
- 使用轮询替代中断调试: 在中断服务程序无法触发时,可以暂时改为在主循环中轮询接收消息对象的NewData标志或中断挂起标志。如果能轮询到数据,说明CAN通信本身是通的,问题出在中断路径配置上。
问题四:通信不稳定,偶尔出现错误帧。
- 检查总线负载与终端电阻: 过高的总线负载或阻抗不匹配会引起信号反射。确保总线两端有且仅有两个120欧姆终端电阻。
- 检查采样点: 位定时参数,特别是TSEG1和TSEG2的比例,决定了采样点在位时间中的位置。不合理的采样点容易受到信号边沿抖动的影响。通常采样点应位于位时间的75%-80%处。可以尝试微调TSEG1和TSEG2。
- 检查82C900错误计数器: 82C900有发送错误计数器和接收错误计数器。通过读取错误计数器寄存器,可以了解错误积累情况。如果发送错误计数器达到255,节点会进入“总线关闭”状态。
问题五:连续读写数据出错。
- 注意地址自动递增: 82C900的PAGE寄存器有一个自动递增(AutoInc)位。如果使能了自动递增,在连续读写多个数据字节时,内部地址会在每次传输后加1。这适用于快速读写一个消息对象的所有寄存器(如连续写入8字节数据)。但如果你在单字节读写操作间不小心使能了它,地址就会错位。在
CAN_SetPageReg函数中,我们通常将AutoInc位与页地址一起设置。对于单次访问,这没问题。但如果后续操作没有重新设置PAGE寄存器,地址可能已经变了。稳妥的做法是,在每次独立的寄存器访问前,都调用一次CAN_SetPageReg来明确设置页地址和AutoInc状态。
最后,善用工具: 一个逻辑分析仪(带SPI和CAN解码功能)是无价之宝。它可以同时捕获QSPI总线上的命令/数据流和CAN总线上的报文,让你清晰地看到“CPU发出了什么命令”、“82C900收到了什么”、“CAN总线上实际传输了什么”,从而快速定位问题是出在SPI配置、寄存器访问、CAN控制器配置还是物理层。