ARM40-A5应用程序——CAN总线的发送和接收
版权声明:本文为博主原创文章,允许转载。
ARM40-A5系列板卡共有2路隔离CAN总线,CAN总线的引脚定义见《ARM40-A5指南——CAN总线接口与测试》。
一、shell中CAN总线的接收与发送
1.1、硬件接线与配置
将CAN0的TX与CAN1的TX通过100R电阻连接,CAN0的RX和CAN1的RX通过100R电阻连接(直接相连亦可),然后:
ifconfig can0 down
ip link set can0 type can bitrate 250000 //配置can0的波特率为250kbps
ifconfig can0 up
ifconfig can1 down
ip link set can1 type can bitrate 250000 //配置can0的波特率为250Kbps
ifconfig can1 up
2.2、shell中接收与发送
root@ARM40:~# candump can1 & //将CAN1设置为接收
root@ARM40:~# cansend can0 5A1#11.2233.44556677.88 // CAN0发数据
can1 5A1 [8] 11 22 33 44 55 66 77 88
root@ARM40:~# candump can0 & //将CAN0设置为接收
root@ARM40:~# cansend can1 5A1#11.2233.44556677.88 // CAN1发数据
can0 5A1 [8] 11 22 33 44 55 66 77 88
二、CAN总线发送和接收的C源码
2.1、CAN总线发送
这个测试程序要用USB CAN II调试盒或者睿芯CAN转串口(USB串口)做接收,传送到PC机上。
/*
* test_can_recv.c
*ARM40发送,PC接收
*/
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <linux/socket.h>
#include <linux/net.h>
#include <linux/sockios.h>
#include <linux/if.h>
int main( int argc,char* argv[] )
{
int i, ret;
int nbytes;
int family = PF_CAN, type = SOCK_RAW, proto = CAN_RAW;
char *interface = "can0";
int can_fd = -1;
struct sockaddr_can addr;
struct ifreq ifr;
struct can_frame frame_rev;
printf("SocketCAN Test V1.0\n");
can_fd = socket(family, type, proto); // 创建SocketCAN 套接字
printf("SOCK_RAW can sockfd:%d\n", can_fd);
if( can_fd < 0 )
{
perror("socket");
return can_fd;
}
int loopback = 0; // 0 = disabled, 1 = enabled (default)
setsockopt(can_fd, SOL_CAN_RAW, CAN_RAW_LOOPBACK, &loopback, sizeof(loopback));
strcpy(ifr.ifr_name, interface); // 指定 can0 设备 strcpy(ifr.ifr_name, "can0" );
ret = ioctl(can_fd, SIOCGIFINDEX, &ifr); // 指定 can0 设备
if(ret) {
perror("ioctl:interface");
goto abort;
}
addr.can_family = family;
addr.can_ifindex = ifr.ifr_ifindex;
ret = bind(can_fd, (struct sockaddr *)&addr, sizeof(addr)); // 将套接字与 can0 绑定
if (ret) {
perror("bind\n");
goto abort;
}
// 设置过滤规则,只接收表示符等于 0x123 的报文,如果没有这段,则接受所有报文
struct can_filter rfilter;
rfilter.can_id = 0x123;
rfilter.can_mask = CAN_SFF_MASK; // 0x000007FFU
setsockopt(can_fd, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter)); //设置过滤规则
/*
* 如果应用程序不需要接收报文,可以禁用过滤规则。这样的话,原始套接字就会忽略所有接收到的报文。
* 在这种仅仅发送数据的应用中,可以在内核中省略接收队列,以此减少 CPU 资源的消耗。
* 禁用方法如下:
* setsockopt(can_fd, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0); //禁用过滤规则
*/
memset(&frame_rev,0x0,sizeof(frame_rev));
while(1) {
nbytes = read(can_fd, &frame_rev, sizeof(frame_rev)); //接收报文
if(nbytes < 0){
perror("read");
goto abort;
}
if(nbytes < sizeof(struct can_frame)){
printf("read:incomplete can frame\n");
goto abort;
}
printf("ID=0x%X,DLC=%d",frame_rev.can_id,frame_rev.can_dlc); //显示报文
for(i=0; i<frame_rev.can_dlc; i++)
printf(",data[%d]=%x", i,frame_rev.data[i]);
printf("\n");
memset(&frame_rev,0x0,sizeof(frame_rev));
}
close(can_fd);
return 0;
abort:
close(can_fd);
return ret;
}
测试结果为:
root@ARM40:~# ./test_can0_recv
SocketCAN Test V1.0
SOCK_RAW can sockfd:3 // PC上发送数据,ARM40上可以接收到
ID=0x123,DLC=8,data[0]=0,data[1]=1,data[2]=2,data[3]=3,data[4]=4,data[5]=5,data[6]=6,data[7]=7
ID=0x123,DLC=8,data[0]=0,data[1]=1,data[2]=2,data[3]=3,data[4]=4,data[5]=5,data[6]=6,data[7]=7
2.2、CAN总线接收
这个测试程序要用USB CAN II调试盒或者睿芯CAN转串口(USB串口)接到PC机上,PC发送,ARM40接收。
/*
*test_can_send.c
*ARM40接收,PC发送
*/
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <linux/socket.h>
#include <linux/net.h>
#include <linux/sockios.h>
#include <linux/if.h>
int main( int argc,char* argv[] )
{
int i, ret;
int nbytes;
int family = PF_CAN, type = SOCK_RAW, proto = CAN_RAW;
char *interface = "can1";
int can_fd = -1;
struct sockaddr_can addr;
struct ifreq ifr;
struct can_frame frame_send;
printf("SocketCAN Test V1.0\n");
can_fd = socket(family, type, proto); // 创建SocketCAN 套接字
printf("SOCK_RAW can sockfd:%d\n", can_fd);
if( can_fd < 0 )
{
perror("socket");
return can_fd;
}
int loopback = 0; // 0 = disabled, 1 = enabled (default)
setsockopt(can_fd, SOL_CAN_RAW, CAN_RAW_LOOPBACK, &loopback, sizeof(loopback));
strcpy(ifr.ifr_name, interface); // 指定 can0 设备 strcpy(ifr.ifr_name, "can0" );
ret = ioctl(can_fd, SIOCGIFINDEX, &ifr); // 指定 can0 设备
if(ret)
{
perror("ioctl:SIOCGIFINDEX");
goto abort;
}
addr.can_family = family;
addr.can_ifindex = ifr.ifr_ifindex;
ret = bind(can_fd, (struct sockaddr *)&addr, sizeof(addr)); // 将套接字与 can0 绑定
if(ret) {
perror("bind\n");
goto abort;
}
/* 发送一个字节的情况
frame_send.can_id = 0x00;
frame_send.can_dlc = 1;
frame_send.data[0] = 'Y';
*/
frame_send.can_id = 0x123;
frame_send.can_dlc = 8; // 发送8个字节,一次最多发8个字节
for(i=0; i<8; ++i) {
frame_send.data[i] = i;
}
//strncpy(frame_send.data,"12345678",8);
nbytes = write(can_fd, &frame_send, sizeof(frame_send)); //发送
if(nbytes <0 ){
perror("write");
goto abort;
}
// 打印发送的字节
printf("write %d bytes frame:can_id=0x%x,can_dlc=%d\n", i,frame_send.can_id,frame_send.can_dlc);
for(i=0; i<frame_send.can_dlc; i++)
printf(",data[%d]=%x", i,frame_send.data[i]);
printf("\n");
close(can_fd);
return 0;
abort:
close(can_fd);
return ret;
}
测试结果为:
root@ARM40:~# ./test_can1_send
SocketCAN Test V1.0
SOCK_RAW can sockfd:3 // PC上收到的数据
write 8 bytes frame:can_id=0x123,can_dlc=8
,data[0]=0,data[1]=1,data[2]=2,data[3]=3,data[4]=4,data[5]=5,data[6]=6,data[7]=7
root@ARM40:~#
参考文章:
Linux内核 Documentation/networking/can.txt
Low Level CAN Framework Application Programmers Interface
Linux内核Socket CAN中文文档
https://blog.youkuaiyun.com/yuanlulu/article/details/7220060
Linux socket CAN编程示例
https://blog.youkuaiyun.com/jirryzhang/article/details/79417986
Linux CAN编程详解
https://blog.youkuaiyun.com/reille/article/details/49980469
linux can 总线socket接口测试使用
http://blog.chinaunix.net/uid-13889805-id-3072479.html
CAN接口测试方法
http://www.embedsky.com/index.php?g=home&m=news&a=show&id=62
https://github.com/linux-can/can-utils
荟聚计划:共商 共建 共享 Grant