能否在BCM SocketCAN上为CAN帧设置不同的循环发送周期?
技术建议:SocketCAN BCM 与原始套接字实现动态周期CAN帧发送
一、SocketCAN BCM模块的可行性分析
- BCM原生支持11位和29位CAN ID:只需在
struct bcm_msg_head的can_id字段中,为29位ID设置CAN_EFF_FLAG标志位(例如can_id = CAN_EFF_FLAG | 0x12345678)即可区分。 - 独立周期配置:BCM允许为每个CAN帧单独设置发送周期,但默认的周期发送机制是重复发送初始配置的固定数据。若要实现每次发送数据动态变化,可以通过以下方式适配:
在周期发送的间隙,发送MODIFY命令更新bcm_msg_head关联的can_frame数据,BCM会在下一个周期发送更新后的内容。但这种方式存在同步延迟风险,且需要严格控制数据更新的时机,避免和BCM的发送周期冲突。
二、原始套接字手动实现方案(更推荐动态数据场景)
核心思路
创建原始CAN套接字,为每个需要周期发送的帧维护独立的定时器,定时器触发时生成最新帧数据并发送。
具体实现步骤
- 创建并配置原始CAN套接字
int sock = socket(PF_CAN, SOCK_RAW, CAN_RAW); struct ifreq ifr; strcpy(ifr.ifr_name, "can0"); ioctl(sock, SIOCGIFINDEX, &ifr); struct sockaddr_can addr; addr.can_family = AF_CAN; addr.can_ifindex = ifr.ifr_ifindex; bind(sock, (struct sockaddr *)&addr, sizeof(addr)); - 区分11位/29位ID
- 11位ID:直接赋值
can_frame.can_id = 0x123; - 29位ID:设置
CAN_EFF_FLAG标志:can_frame.can_id = CAN_EFF_FLAG | 0x12345678;
- 11位ID:直接赋值
- 周期调度实现
- 推荐使用
timerfd结合epoll的单线程方案:
每个定时器对应一个帧的发送周期,将所有定时器文件描述符加入epoll监控,触发时生成最新数据、填充can_frame并调用write发送。这种方式无需多线程同步,资源占用低。 - 备选线程方案:每个帧对应一个独立线程,线程内循环执行「生成动态数据→发送CAN帧→调用
nanosleep等待周期」。注意若数据生成涉及共享资源,需加锁保证线程安全。
- 推荐使用
关键注意事项
- 控制总线负载:避免发送频率过高导致丢帧,可通过
setsockopt(sock, SOL_CAN_RAW, CAN_RAW_ERR_FILTER, &err_mask, sizeof(err_mask))过滤错误帧,同时检查write的返回值判断发送是否成功。 - 定时器精度:若需要微秒级精度,确保内核启用了
CONFIG_HIGH_RES_TIMERS选项,使用nanosleep或timerfd可满足大部分场景需求。
三、方案选择建议
- 若数据更新频率较低,且希望减少定时器维护工作量,可尝试优化BCM的
MODIFY命令用法; - 若数据需要实时动态生成、周期调度要求灵活,原始套接字方案更可靠,完全自主控制发送逻辑,适配性更强。
内容的提问来源于stack exchange,提问作者Andy Höfler
相关产品推荐
相关产品推荐

