RT-Thread论坛
直播中

王艳

8年用户 1372经验值
私信 关注
[问答]

移植CANfestival,发现can无法接收数据,为什么?

最近在研究Canopen协议,在f407的板子上移植了canfestival协议栈
移植成功后遇到一些问题,总线上我在没有发数据的情况下还是可以进入can接收中断,我不清为什么会进入中断,后来我自己通过can的上位机来发送报文,发现如下的情况

无论发送任何数据,发现rt_device_read返回值是0,查了一下函数返回值发现读取失败
然后我在下面一行打印接收到的数据

发现接收到数据都是0

后来我单独写了一个can收发的demo,发现是ok的,可以正常发送,也可以正常读取。
请问有大佬知道这是为啥吗


回帖(1)

石玉兰

2025-9-15 17:49:21

你的问题描述非常清晰,结合你提供的现象(单独 CAN Demo 正常,但 CANfestival 移植后 rt_device_read 返回 0 且数据为 0,以及未发数据时进入中断),这指向了 CANfestival 协议栈内部的接收处理机制与你的底层 CAN 驱动/RT-Thread 的设备模型之间存在整合问题。核心原因是 CANfestival 的接收路径未被正确激活或数据未被正确传递给它。


以下是可能的原因和排查步骤:


?️ 1.  CANfestival 接收回调未正确注册或底层驱动未配置中断



  • 关键点: CANfestival 本身不直接提供硬件中断服务程序。它依赖你提供的回调函数 _CO_CANrx_callback 来接收数据。

  • 你需要实现:

    • 在初始化 CAN 控制器时,必须正确设置 CAN 控制器产生接收中断。

    • 在底层驱动的接收中断服务程序 (ISR) 中,必须调用 CANfestival 提供的 canDispatch 函数(通常你会传递一个代表CAN接口的对象指针,例如 &CanDevice)。

    • canDispatch 内部会根据你的设置(对象字典中的 Receive PDO 映射和 TPDOCOB-ID),检查收到的 CAN 报文 ID。如果匹配到一个已注册的接收函数(PDO 或 SDO 服务器),它会调用你实现的 _CO_CANrx_callback 函数


  • 检查:

    • 中断配置: 确认你的底层驱动在初始化 CAN 控制器时启用了接收中断 (CAN_IER_FMPIE0CAN_IER_FMPIE1 对于 FDCAN,通常是类似标志)。

    • ISR 调用 canDispatch: 在你的 CANx_RX0_IRQHandler(或对应的接收中断函数)中,检查是否调用了 canDispatch(&CanDevice)

    • _CO_CANrx_callback 注册:initNodessetNodeId 调用期间,CANfestival 会尝试配置 PDO 映射。这会隐式地注册 _CO_CANrx_callback

    • _CO_CANrx_callback 实现: 确认你的 CO_driver.c 文件里实现了 void _CO_CANrx_callback(CO_CANrx_t *rxHandle, void *object, const CO_CANrxMsg_t *message) 函数。这个函数是 CANfestival 处理接收数据的入口。



? 2.  CANfestival 的节点 ID 和 COB-ID 配置问题



  • 关键点: canDispatch 根据报文 ID 决定是否调用回调。如果配置不正确,报文会被忽略。

  • 检查:

    • 节点 ID: 确认你的 CANopen 节点 ID (Node_ID) 设置正确(通常在 initNodes 调用时设置)。

    • COB-ID: 检查你发送的测试报文 ID 是否匹配 CANfestival 节点预期的接收 COB-ID。

    • (可选) RPDO 映射: 检查对象字典中的接收 PDO (RPDO1 - RPDOx) 的 COB-ID 是否设置为你期望接收的 ID(特别是 0x180 + NodeID, 0x280 + NodeID 等)。确认 Enabled



? 3.  接收中断处理冲突或优先级问题



  • 现象解释 (未发数据进入中断): 这通常表明 CAN 控制器产生了错误中断状态变化中断(如唤醒、错误被动、总线关闭恢复),而不是接收中断。CANfestival 默认可能对这些中断不敏感,但你的底层驱动可能配置了这些中断,并且没有正确处理。

  • 检查:

    • 中断向量: 检查你的 CAN 中断服务程序是否 处理了接收中断 (CAN_RX0_INT, FDCAN_IT_RX_FIFO0_NEW_MESSAGE 等)?还是也处理了错误中断?在中断函数开头打印状态寄存器 (CAN_ESR, FDCAN_IR) 的值,看看是什么标志触发的中断。

    • 中断优先级: 确保接收中断的优先级设置合理,不会被长时间阻塞。

    • 错误中断处理: 如果检测到错误中断,需要读取错误计数器并进行处理(记录、尝试恢复),然后清除中断标志位。否则会频繁进入中断。



? 4.  RT-Thread 设备模型的使用冲突



  • 关键点: CANfestival 期望直接控制硬件中断和使用 canDispatch。RT-Thread 的设备模型 (rt_device_xxx) 提供了另一个抽象层。

  • 问题根源:

    • 你之前的 Demo 使用 rt_device_read,说明你的底层驱动是按照 RT-Thread 的设备模型实现的。

    • 如果你在 CANfestival 的移植中,同时又在某个线程里使用 rt_device_read 尝试读取 CAN 数据,就会导致数据被 RT-Thread 的驱动缓冲层拿走,而 CANfestival 的 canDispatch 在 ISR 里读不到数据(rt_device_read 返回 0 就是因为底层驱动没有数据,数据被 RT-Thread 的缓冲机制管理)。

    • CANfestival 需要直接访问硬件寄存器或底层的 DMA 缓冲区来读取数据,而不是通过 RT-Thread 的设备接口。


  • 解决:

    • 二选一: 必须统一数据接收路径。

    • 方案 A (推荐): 绕过 RT-Thread 设备模型的读接口 (用于 CANfestival)。

      • 在你的底层驱动实现中,如果这个驱动是专门给 CANfestival 用的,不要在应用中再使用 rt_device_find, rt_device_open, rt_device_read

      • 在底层驱动的 中断服务程序 (ISR) 中:

        • 直接读取 CAN 控制器的接收 FIFO 或邮箱寄存器,获取帧数据。

        • 将帧数据填充到 CO_CANrxMsg_t 结构体中。

        • 调用 canDispatch(&CanDevice, &rxMsg)


      • 这样,数据直接从硬件到 canDispatch,不会经过 RT-Thread 的设备缓冲队列。


    • 方案 B (复杂): 适配 RT-Thread 设备模型到 CANfestival。

      • 非常复杂且不推荐。

      • 需要实现一个适配层:在 RT-Thread 的接收回调中调用 canDispatch

      • 需要处理 RT-Thread 的缓冲机制与 CANfestival 实时性要求的潜在冲突。




? 5.  接收缓冲区设置



  • 关键点: 确保 CO_CANrxMsg_t 结构体或你用于暂存接收报文的内存区域被正确初始化。

  • 检查: 在你的 _CO_CANrx_callback 函数中添加调试打印,输出 message->ident (ID), message->DLC (长度), message->data (数据)。如果这里打印正常,说明 CANfestival 收到数据了,问题可能在 CANfestival 内部处理或应用层逻辑。如果这里打印也是 0,问题出在传递数据给 canDispatch 之前(中断处理、寄存器读取)。


? 6.  CAN 控制器过滤器配置



  • 关键点: 确保 CAN 控制器的硬件过滤器配置允许你测试的报文 ID 通过。

  • 检查:

    • 在单独 Demo 中,你可能使用了更宽松的过滤器(如接收所有 ID)。

    • 在 CANfestival 移植中,你是否在初始化时配置了硬件过滤器?CANfestival 通常会在 setNodeId 或其他初始化步骤中尝试配置过滤器来匹配其需要的 COB-ID。检查你的 CO_CANmodule_init 函数或相关的过滤器配置代码。

    • 在你的测试发送中,确保上位机发送的报文 ID 在你配置的过滤器允许范围内。

    • 临时测试: 尝试将过滤器配置为接收所有 ID (0x000, Mask 0x000),看是否能收到数据。如果可以,说明之前的过滤器配置过于严格。



? 调试建议:



  1. 定位中断源: 在 CAN 中断入口函数最开头,打印 CAN 状态寄存器 (CAN->ESR for bxCAN, FDCAN_ErrorStatusGet or FDCAN->IR for FDCAN) 的值。确定是什么标志触发了中断。重点关注 FMP, LEC, EP, BOFF 等标志位。

  2. 检查 canDispatch 调用: 在你的接收中断服务程序中,确保 canDispatch(&CanDevice) 被调用。在其前后加调试打印。

  3. 检查 _CO_CANrx_callback: 在这个函数内部开头添加打印,输出 message->ident, message->DLC, message->data[0-7]。观察是否有预期的数据到达这里。

  4. 验证节点 ID 和 COB-ID: 使用 CAN 分析仪(如 PCAN-View, ZLG USBCAN-II)监听总线。确认你上位机发送的报文 ID 是否正确(如预期发给 0x600 + NodeID0x7E0 等)。确认你的 CANopen 节点的 NodeID 设置正确。

  5. 简化测试: 尝试使用 CANfestival 自身的 masterslave 示例中的心跳或 NMT 消息进行测试(例如,发送 NMT 启动命令 0x000 数据 [0x01, NodeID]),而不是完全自定义的报文。这些是 CANfestival 内部默认处理的。

  6. 排查 RT-Thread 干扰: 最关键的步骤 - 在你的 CANfestival 移植代码中,注释掉或移除 所有使用 rt_device_find, rt_device_open, rt_device_read, rt_device_write 等操作 CAN 设备的代码片段。确保 CAN 的发送是通过调用 CANfestival 的 CO_CANsend 函数实现的,接收完全依赖你在底层驱动 ISR 中直接调用 canDispatch。✅


? 总结与最可能的点:


根据你的描述(单独 Demo 正常,rt_device_read 返回 0,未发数据进中断),问题 3 (错误中断)问题 4 (RT-Thread 设备模型使用冲突) 是最核心的原因:



  1. 未发数据进中断: 几乎可以肯定是 CAN 错误中断 (LEC, EP, BOFF)状态中断 未被正确处理。检查并打印状态寄存器。

  2. rt_device_read 返回 0 & 数据全 0:

    • 使用 rt_device_read 表明你在应用层尝试通过 RT-Thread 设备接口读取数据。

    • 但同时,CANfestival 需要在中断里直接读寄存器调用 canDispatch

    • 这导致数据要么被 RT-Thread 的驱动缓冲拿走(应用层 rt_device_read 读到),要么被 CANfestival 在中断里拿走(但你的应用层 rt_device_read 读不到)。

    • 更可能是,你在移植 CANfestival 后,底层驱动被修改为在 ISR 中直接为 CANfestival 服务了,没有填充 RT-Thread 的设备接收缓冲队列。因此应用层的 rt_device_read 总是读不到数据(返回 0),打印的数据自然也是无效的(可能是未初始化的内存或你定义的缓冲区的初始值)。

    • 解决方案:停止在应用层使用 rt_device_read 来读取 CAN 数据给 CANfestival。 CANfestival 有自己的接收机制 (canDispatch -> _CO_CANrx_callback)。数据的接收应完全通过中断服务程序调用 canDispatch 来驱动。应用层通过 CANopen 对象字典、PDO 映射、SDO 访问等机制来获取数据。



解决问题 4 是让你的 CANfestival 收到数据的关键。解决问题 3 能消除那些恼人的意外中断。 仔细排查这两个方向,尤其确保接收中断路径统一为:硬件中断 -> 读寄存器获取帧 -> 填充 CO_CANrxMsg_t -> 调用 canDispatch -> 触发 _CO_CANrx_callback,并且不再使用 rt_device_read。祝移植顺利!?

举报

更多回帖

发帖
×
20
完善资料,
赚取积分