最近写了个自认为不错的基于linux socket can程序,主要功能:
- 程序具备全部CAN功能,包括CAN标准帧/扩展帧接收与发送、CAN总线错误判断、环回等功能
- 适用基于LINUX SOCKET机制实现的CAN接口,可用于嵌入式LINUX的CAN测试
- 程序采用标准LINUX命令行参数选项形式,接受用户参数
现把源码进行分享
功能介绍
SOCKET CAN工具程序 – Ver1.0 Build Nov 20 2015, COPYRIGHT (C) 2015 reille @ http://velep.com/
介绍:
本SOCKET CAN程序具备全部CAN功能,包括CAN标准帧/扩展帧接收与发送、CAN总线错误判断、环回等功能
适用基于LINUX SOCKET机制实现的CAN接口,可用于嵌入式LINUX中的CAN测试程序
程序采用标准LINUX命令行参数选项形式,接受用户参数
用法: ./cantool [选项]…
选项:
-p, –port=CAN接口号 指定CAN接口号,从1开始, 默认为 1(即CAN1接口)
-b, –baud=波特率 指定CAN通讯波特率,单位Kbps,默认为 250 Kbps
可用波特率:5,10,20,40,50,80,100,125,200,250,400,500,666,800,1000
-i, –frame-id=帧ID 指定CAN发送帧ID(Hex格式), 默认为1801F456
-d, –data=数据 指定CAN发送帧数据, 默认为:00 01 FF FF FF FF FF FF,字节数据间以空格隔开
-f, –freq=间隔 指定CAN帧发送间隔,单位ms, 默认为250ms, 最小值为1ms
-t, –times=次数 指定CAN帧发送次数, 默认为0次
-s, 指定CAN发送帧为标准帧, 默认为发送扩展帧
-I, 帧ID每发送一帧递增, 默认不递增
-g, 发送数据每发送一帧递增, 默认不递增
-l, 发送数据时本地环回, 默认不环回
–help 显示此帮助信息并退出
注意,以下CAN帧ID作为系统使用:
0x00000001 – TX timeout (by netdevice driver)
0x00000002 – lost arbitration / data[0]
0x00000004 – controller problems / data[1]
0x00000008 – protocol violations / data[2..3]
0x00000010 – transceiver status / data[4]
0x00000020 – received no ACK on transmission
0x00000040 – bus off
0x00000080 – bus error (may flood!)
0x00000100 – controller restarted
使用 Ctrl^C 组合键结束程序运行
部分源码
int main(int argc, char **argv) { S_CanFrame sendframe, recvframe; byte *psendframe = (byte *)&sendframe; byte *precvframe = (byte *)&recvframe; u_canframe_data_t *psend_data = (u_canframe_data_t *)sendframe.data; const int can_frame_len = sizeof(S_CanFrame); pid_t pid = -1; int status; int ret = 0; char buf[128] = {0}; bool carry_bit = false;// 进位标志 int segment_id;//id for shared memo if (parse_options(argc, argv)) { usage(); return 0; } if (!find_can(port)) { sprintf(buf, "\n\t错误:CAN%d设备不存在\n\n", port + 1); panic(buf); return -1; } close_can(port);// 必须先关闭CAN,才能成功设置CAN波特率 set_bitrate(port, bitrate);// 操作CAN之前,先要设置波特率 open_can(port, bitrate); send_socket_fd = socket_connect(port); recv_socket_fd = socket_connect(port); //printf("send_socket_fd = %d, recv_socket_fd = %d\n", send_socket_fd, recv_socket_fd); if (send_socket_fd < 0 || send_socket_fd < 0) { disconnect(&send_socket_fd); disconnect(&recv_socket_fd); panic("\n\t打开socket can错误\n\n"); return -1; } set_can_filter(); set_can_loopback(send_socket_fd, lp); printf_head(); memset(&sendframe, 0x00, sizeof(sendframe)); memset(&recvframe, 0x00, sizeof(recvframe)); if (extended_frame) // 指定发送帧类型:扩展帧或标准帧 { sendframe.can_id = (send_frame_id & CAN_EFF_MASK) | CAN_EFF_FLAG; } else { sendframe.can_id = (send_frame_id & CAN_SFF_MASK); } sendframe.can_dlc = dlc; memcpy(sendframe.data, send_frame_data, dlc); segment_id = shmget(IPC_PRIVATE, sizeof(int), S_IRUSR | S_IWUSR);// allocate memo pframeno = (int *)shmat(segment_id, NULL, 0);// attach the memo if (pframeno == NULL) { panic("\n\t创建共享内存失败\n\n"); return -1; } *pframeno = 1; run = true; pid = fork(); if(pid == -1) { panic("\n\t创建进程失败\n\n"); return -1; } else if(pid == 0) // 子进程,用于发送CAN帧 { while (run && (send_frame_times > 0)) { ret = send_frame(send_socket_fd, (char *)&sendframe, sizeof(sendframe)); printf_frame(sendframe.can_id & CAN_EFF_MASK, sendframe.data, sendframe.can_dlc, ((sendframe.can_id & CAN_EFF_FLAG) ? true : false), ret > 0 ? true : false, true); delay_ms(send_frame_freq_ms); if (send_frame_id_inc_en) { sendframe.can_id++; if (extended_frame) { sendframe.can_id = (sendframe.can_id & CAN_EFF_MASK) | CAN_EFF_FLAG; } else { sendframe.can_id = (sendframe.can_id & CAN_SFF_MASK); } } if (send_frame_data_inc_en && dlc > 0) { if (dlc > 4 && psend_data->s.dl == ((__u32)0xFFFFFFFF)) { carry_bit = true;// 发生进位 } psend_data->s.dl++; if (dlc <= 4) { if (psend_data->s.dl >= (1 << (dlc * 8))) { psend_data->s.dl = 0; } } else if (dlc <= 8) { if (carry_bit) { psend_data->s.dh++; if (psend_data->s.dh >= (1 << ((dlc - 4) * 8))) { psend_data->s.dh = 0; } carry_bit = false; } } } send_frame_times--; } exit(0); } else // 父进程,接收CAN帧 { install_sig(); while (run) { memset(precvframe, 0x00, can_frame_len); ret = recv_frame(recv_socket_fd, precvframe, can_frame_len, 5 * 1000); if (ret > 0) { printf_frame(recvframe.can_id & CAN_EFF_MASK, recvframe.data, recvframe.can_dlc, ((recvframe.can_id & CAN_EFF_FLAG) ? true : false), true, false); } } while(((pid = wait(&status)) == -1) && (errno == EINTR)) { delay_ms(10); } } disconnect(&send_socket_fd); disconnect(&recv_socket_fd); shmdt(pframeno);// detach memo shmctl(segment_id, IPC_RMID, NULL);// remove return 0; }
使用示例
程序源码
下载地址:linux socket can程序cantool
如果觉得好用,记得给个好评!
[…] linux socket can程序cantool […]
很不错很不错
感谢
多谢分享!!!
刚看了下载量,近1300次,评论寥寥无几, >.<
再次感谢博主无私分享
不用客气,希望对你有所帮助
谢谢分享,好评
程序下载了,ubuntu下编译之前需要把源文件打开另存为一下,不然输出的中文会是乱码。另外,压缩包里那个编译好的cantool无法运行,说无法识别。
另外,求问此程序如何能修改成对多个can口进行同时监听?
1. cantool测试程序,因为包含中文输出,编码时采用VS2010工具,所以在ubuntu下可能会现如你所说的问题
2. 压缩包里那个编译好的cantool是在我的交叉平台上编译的,所以在你的平台上,肯定是无法运行的
3. 对多个can口监听,我建议采用多线程实现,每个线程监听一个CAN口。当然,在一个线程上同样可以实现,就像在一个线程上同时监听多个socket一样