You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

树莓派SPI转CAN:代码发送CANBUS帧失败求助

排查C代码发送CAN帧失败的问题

先明确你的问题:用cansend can0 01f801f2#1212121223232323发送CAN帧时控制器完全正常响应,但在pthread线程里的C代码发送相同帧却失败,我帮你梳理几个关键问题:

首先对比你的代码和cansend命令,能发现几个明显的错误点:

1. 数据字段完全不匹配

你的cansend命令里数据部分是1212121223232323,也就是前4字节为0x12,后4字节为0x23,但你的代码里frame.data[4]到frame.data[7]写的是0x34,这直接导致发送的数据和预期完全不一致,控制器自然无法执行正确动作。

2. 扩展帧标志缺失

你的CAN ID 0x01f801f2是29位的扩展帧ID,但在Linux SocketCAN规范中,struct can_frame的can_id字段需要通过CAN_EFF_FLAG宏来标记这是一个扩展帧。如果不设置这个标志,内核会默认把它当成11位的标准帧处理,ID会被截断,帧格式完全错误,控制器根本识别不了。

3. 缺乏错误检查

你调用write发送帧后完全没有判断返回值,也没打印错误信息,就算发送失败你也不知道具体是哪里出问题(比如权限不足、格式错误、总线异常等)。

修正后的代码

我把这些问题都修复了,还补充了必要的错误处理逻辑:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <pthread.h>

// 假设你的stopServer和LogEntry是已定义的全局变量/函数
int stopServer = 0;
void LogEntry(int level, const char* msg, ...);

void *CANUpdate(void *userParam) {
    struct sockaddr_can addr;
    struct ifreq ifr;
    const char *ifname = "can0";
    int can_s;

    // 创建CAN套接字
    if ((can_s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
        LogEntry(LOG_ERR, "Error while opening socket: %s\n", strerror(errno));
        return NULL;
    }

    // 获取CAN接口索引
    strcpy(ifr.ifr_name, ifname);
    if (ioctl(can_s, SIOCGIFINDEX, &ifr) < 0) {
        LogEntry(LOG_ERR, "Error getting CAN interface index: %s\n", strerror(errno));
        close(can_s);
        return NULL;
    }

    // 绑定套接字到CAN接口
    addr.can_family = AF_CAN;
    addr.can_ifindex = ifr.ifr_ifindex;
    if (bind(can_s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        LogEntry(LOG_ERR, "Error binding CAN socket: %s\n", strerror(errno));
        close(can_s);
        return NULL;
    }

    while (!stopServer) {
        if (can_s > 0) {
            struct can_frame frame;
            // 设置扩展帧ID,必须加上CAN_EFF_FLAG标记
            frame.can_id = 0x01f801f2 | CAN_EFF_FLAG;
            frame.can_dlc = 8;
            // 严格匹配cansend的数据内容
            frame.data[0] = 0x12;
            frame.data[1] = 0x12;
            frame.data[2] = 0x12;
            frame.data[3] = 0x12;
            frame.data[4] = 0x23;
            frame.data[5] = 0x23;
            frame.data[6] = 0x23;
            frame.data[7] = 0x23;

            int res = write(can_s, &frame, sizeof(struct can_frame));
            if (res != sizeof(struct can_frame)) {
                LogEntry(LOG_ERR, "Failed to send CAN frame: %s, sent %d bytes\n", 
                         strerror(errno), res);
            } else {
                LogEntry(LOG_ERR, "CAN frame sent successfully\n"); // 可根据日志级别改成LOG_INFO
            }
        }
        msleep(800);
    }

    // 退出线程前关闭套接字
    if (can_s > 0) {
        close(can_s);
    }
    return NULL;
}

额外排查建议

  • 发送前可以用candump can0抓包,对比cansend发送的帧和你的代码发送的帧,查看ID、数据、帧格式(标准/扩展)是否一致,这是排查CAN总线问题的常用方法。
  • 确保你的程序有足够权限访问CAN设备,必要时用sudo运行,或者给程序设置capabilities(sudo setcap cap_net_raw+ep your_program)。

内容的提问来源于stack exchange,提问作者Jaques

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:55:12