树莓派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
相关产品推荐
相关产品推荐

