最近寫了個自認爲不錯的基於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
如果覺得好用,記得給個好評!