最近写了个自认为不错的基于linux socket can程序,主要功能:

  1. 程序具备全部CAN功能,包括CAN标准帧/扩展帧接收与发送、CAN总线错误判断、环回等功能
  2. 适用基于LINUX SOCKET机制实现的CAN接口,可用于嵌入式LINUX的CAN测试
  3. 程序采用标准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程序,主要功能:

  1. 程序具备全部CAN功能,包括CAN标准帧/扩展帧接收与发送、CAN总线错误判断、环回等功能
  2. 适用基于LINUX SOCKET机制实现的CAN接口,可用于嵌入式LINUX的CAN测试
  3. 程序采用标准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 组合键结束程序运行

 

部分源码




​001​

​int​​ ​​main(​​​​int​​ ​​argc, ​​​​char​​ ​​**argv)​


​002​

​{​


​003​

​S_CanFrame sendframe, recvframe;​


​004​

​byte *psendframe = (byte *)&sendframe;​


​005​

​byte *precvframe = (byte *)&recvframe;​


​006​

​u_canframe_data_t *psend_data = (u_canframe_data_t *)sendframe.data;​


​007​

​const​​ ​​int​​ ​​can_frame_len = ​​​​sizeof​​​​(S_CanFrame);​


​008​

 


​009​

​pid_t pid = -1;​


​010​

​int​​   ​​status;​


​011​

 


​012​

​int​​  ​​ret = 0;​


​013​

​char​​ ​​buf[128] = {0};​


​014​

​bool​​ ​​carry_bit = ​​​​false​​​​;​​​​// 进位标志​


​015​

 


​016​

​int​​ ​​segment_id;​​​​//id for shared memo​


​017​

 


​018​

 


​019​

​if​​ ​​(parse_options(argc, argv))​


​020​

​{​


​021​

​usage();    ​​​​return​​  ​​0;​


​022​

​}​


​023​

 


​024​

​if​​ ​​(!find_can(port))​


​025​

​{​


​026​

​sprintf​​​​(buf, ​​​​"\n\t错误:CAN%d设备不存在\n\n"​​​​, port + 1);​


​027​

​panic(buf);​


​028​

​return​​  ​​-1;​


​029​

​}​


​030​

 


​031​

​close_can(port);​​​​// 必须先关闭CAN,才能成功设置CAN波特率​


​032​

​set_bitrate(port, bitrate);​​​​// 操作CAN之前,先要设置波特率​


​033​

​open_can(port, bitrate);​


​034​

 


​035​

​send_socket_fd = socket_connect(port);​


​036​

​recv_socket_fd = socket_connect(port);​


​037​

​//printf("send_socket_fd = %d, recv_socket_fd = %d\n", send_socket_fd, recv_socket_fd);​


​038​

​if​​ ​​(send_socket_fd < 0 || send_socket_fd < 0)​


​039​

​{​


​040​

​disconnect(&send_socket_fd);​


​041​

​disconnect(&recv_socket_fd);​


​042​

​panic(​​​​"\n\t打开socket can错误\n\n"​​​​);​


​043​

​return​​  ​​-1;​


​044​

​}​


​045​

​set_can_filter();​


​046​

​set_can_loopback(send_socket_fd, lp);​


​047​

 


​048​

​printf_head();​


​049​

 


​050​

​memset​​​​(&sendframe, 0x00, ​​​​sizeof​​​​(sendframe));​


​051​

​memset​​​​(&recvframe, 0x00, ​​​​sizeof​​​​(recvframe));​


​052​

 


​053​

​if​​ ​​(extended_frame) ​​​​// 指定发送帧类型:扩展帧或标准帧​


​054​

​{​


​055​

​sendframe.can_id = (send_frame_id & CAN_EFF_MASK) | CAN_EFF_FLAG;​


​056​

​}​


​057​

​else​


​058​

​{​


​059​

​sendframe.can_id = (send_frame_id & CAN_SFF_MASK);​


​060​

​}​


​061​

​sendframe.can_dlc = dlc;​


​062​

​memcpy​​​​(sendframe.data, send_frame_data, dlc);​


​063​

 


​064​

 


​065​

​segment_id = shmget(IPC_PRIVATE, ​​​​sizeof​​​​(​​​​int​​​​), S_IRUSR | S_IWUSR);​​​​// allocate memo​


​066​

​pframeno = (​​​​int​​ ​​*)shmat(segment_id, NULL, 0);​​​​// attach the memo​


​067​

​if​​ ​​(pframeno == NULL)​


​068​

​{​


​069​

​panic(​​​​"\n\t创建共享内存失败\n\n"​​​​);​


​070​

​return​​  ​​-1;​


​071​

​}​


​072​

​*pframeno = 1;​


​073​

 


​074​

​run = ​​​​true​​​​;​


​075​

 


​076​

​pid = fork();​


​077​

​if​​​​(pid == -1)​


​078​

​{​


​079​

​panic(​​​​"\n\t创建进程失败\n\n"​​​​);​


​080​

​return​​  ​​-1;​


​081​

​}​


​082​

​else​​ ​​if​​​​(pid == 0) ​​​​// 子进程,用于发送CAN帧​


​083​

​{​


​084​

​while​​ ​​(run && (send_frame_times > 0))​


​085​

​{​


​086​

​ret = send_frame(send_socket_fd, (​​​​char​​ ​​*)&sendframe, ​​​​sizeof​​​​(sendframe));​


​087​

​printf_frame(sendframe.can_id & CAN_EFF_MASK, sendframe.data, sendframe.can_dlc,​


​088​

​((sendframe.can_id & CAN_EFF_FLAG) ? ​​​​true​​ ​​: ​​​​false​​​​),​


​089​

​ret > 0 ? ​​​​true​​ ​​: ​​​​false​​​​,​


​090​

​true​​​​);​


​091​

​delay_ms(send_frame_freq_ms);​


​092​

 


​093​

​if​​ ​​(send_frame_id_inc_en)​


​094​

​{​


​095​

​sendframe.can_id++;​


​096​

​if​​ ​​(extended_frame)​


​097​

​{​


​098​

​sendframe.can_id = (sendframe.can_id & CAN_EFF_MASK) | CAN_EFF_FLAG;​


​099​

​}​


​100​

​else​


​101​

​{​


​102​

​sendframe.can_id = (sendframe.can_id & CAN_SFF_MASK);​


​103​

​}​


​104​

​}​


​105​

 


​106​

​if​​ ​​(send_frame_data_inc_en && dlc > 0)​


​107​

​{​


​108​

​if​​ ​​(dlc > 4 && psend_data->s.dl == ((__u32)0xFFFFFFFF))​


​109​

​{​


​110​

​carry_bit = ​​​​true​​​​;​​​​// 发生进位​


​111​

​}​


​112​

​psend_data->s.dl++;​


​113​

 


​114​

​if​​ ​​(dlc <= 4)​


​115​

​{​


​116​

​if​​ ​​(psend_data->s.dl >= (1 << (dlc * 8)))​


​117​

​{​


​118​

​psend_data->s.dl = 0;​


​119​

​}​


​120​

​}​


​121​

​else​​ ​​if​​ ​​(dlc <= 8)​


​122​

​{​


​123​

​if​​ ​​(carry_bit)​


​124​

​{​


​125​

​psend_data->s.dh++;​


​126​

​if​​ ​​(psend_data->s.dh >= (1 << ((dlc - 4) * 8)))​


​127​

​{​


​128​

​psend_data->s.dh = 0;​


​129​

​}​


​130​

 


​131​

​carry_bit = ​​​​false​​​​;​


​132​

​}​


​133​

​}​


​134​

​}​


​135​

 


​136​

​send_frame_times--;​


​137​

​}​


​138​

 


​139​

​exit​​​​(0);​


​140​

​}​


​141​

​else​​ ​​// 父进程,接收CAN帧​


​142​

​{​


​143​

​install_sig();​


​144​

 


​145​

​while​​ ​​(run)​


​146​

​{​


​147​

​memset​​​​(precvframe, 0x00, can_frame_len);​


​148​

​ret = recv_frame(recv_socket_fd, precvframe, can_frame_len, 5 * 1000);​


​149​

​if​​ ​​(ret > 0)​


​150​

​{​


​151​

​printf_frame(recvframe.can_id & CAN_EFF_MASK, recvframe.data, recvframe.can_dlc,​


​152​

​((recvframe.can_id & CAN_EFF_FLAG) ? ​​​​true​​ ​​: ​​​​false​​​​),​


​153​

​true​​​​,​


​154​

​false​​​​);​


​155​

​}​


​156​

​}​


​157​

 


​158​

​while​​​​(((pid = wait(&status)) == -1) && (​​​​errno​​ ​​== EINTR))​


​159​

​{​


​160​

​delay_ms(10);​


​161​

​}​


​162​

​}​


​163​

 


​164​

​disconnect(&send_socket_fd);​


​165​

​disconnect(&recv_socket_fd);​


​166​

 


​167​

​shmdt(pframeno);​​​​// detach memo​


​168​

​shmctl(segment_id, IPC_RMID, NULL);​​​​// remove​


​169​

 


​170​

​return​​  ​​0;​


​171​

​}​


使用示例


linux socket can程序cantool_命令行参数

cantool使用示例