1.CAN简介使用Socket CAN的主要目的就是为用户空间的应用程序提供基于Linux网络层的套接字接口。与广为人知的TCP/IP协议以及以太网不同CAN总线没有类似以太网的MAC层地址只能用于广播。CAN ID仅仅用来进行总线的仲裁。因此CAN ID在总线上必须是唯一的。当设计一个CAN-ECU(Electronic Control Unit 电子控制单元网络的时候CAN报文ID可以映射到具体的ECU。因此CAN报文ID可以当作发送源的地址来使用。1.1开发板的CAN资源1.2硬件连接通常由CPU出来的CAN信号是TTL信号并不是差分信号。因此需要一个CAN TTL信号转CAN差分信号的模块。其具体的接线图如下所示。2.快速上手2.1开发环境准备如果您初次阅读此文档请阅读《入门指南/开发环境准备/Easy-Eai编译环境准备与更新》并按照其相关的操作进行编译环境的部署。在PC端Ubuntu系统中执行run脚本进入EASY-EAI编译环境具体如下所示。cd ~/develop_environment ./run.sh 22042.2源码下载以及例程编译首先在虚拟机后台终端执行以下命令创建外设单例源码管理目录cd /opt mkdir -p EASY-EAI-PI2/demo首先到【百度网盘】上下载相关的单例程序链接https://pan.baidu.com/s/1ORJrMeW-bOJ6g_lPiNG6mw?pwd1234提取码1234比如把单例程序下载到此电脑\D:\BaiduNetdisk (无规定用户可自主选择)如下图所示。再将下载好的单例复制进入虚拟机的文件系统过程如下图所示。最后进入到对应的例程目录执行编译操作具体命令如下所示cd EASY-EAI-PI2/demo/11_CAN ./build.sh注* 由于依赖库部署在板卡上因此交叉编译过程中必须保持/mnt挂载。编译成功后会生成2个demo一个是发送端test-can_send另一个是接收端test-can_reception。并会自动部署到开发板的/userdata/目录中。* 此例程需要2个板卡做收发测试所以2个板卡都需要重复上述操作。2.3例程运行通过串口调试或ssh调试进入板卡后台定位到例程部署的位置如下所示cd /userdata首先在一台板卡上运行发送端运行命令如下sudo ./test-can-send然后【再在另外一台】板卡上运行接收端运行命令如下cd /userdata sudo ./test-can-reception【接收端】执行效果如下所示。API的详细说明以及API的调用本例程源码详细信息见下方说明。3.CAN操作API说明3.1创建socketcan套接字操作创建socketcan套接字操作函数原型如下所示。int socket(int domain, int type, int protocol);具体介绍如下所示。3.2指定本地网络接口地址操作指定本地网络接口地址函数原型如下所示。int ioctl(int fd, unsigned long request, ...);具体介绍如下所示。3.3绑定地址结构操作绑定地址结构函数原型如下所示。int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);具体介绍如下所示。3.4设置CAN过滤器操作设置CAN过滤器操作函数原型如下所示。int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);3.5CAN定义报文格式操作CAN定义报文格式如下所示。4.CAN通信例程【发送端】例程源码为11_CAN/test-can_send/main.c操作流程如下。【接收端】例程源码为11_CAN/test-can_send/main.c操作流程如下。参考例程如下所示。发送端例程/* 将CAN1波特率设置为500000 bps */ #define ip_cmd_set_can_params ip link set can1 type can bitrate 500000 triple-sampling on /* 打开CAN1 */ #define ip_cmd_open ifconfig can1 up /* 关闭CAN1 */ #define ip_cmd_close ifconfig can1 down int main() { int fd, nbytes; struct sockaddr_can addr; struct ifreq ifr; struct can_frame frame[2] {{0}}; system(ip_cmd_close); system(ip_cmd_set_can_params); system(ip_cmd_open); fd socket(PF_CAN, SOCK_RAW, CAN_RAW);//创建套接字 strcpy(ifr.ifr_name, can0 ); ioctl(fd, SIOCGIFINDEX, ifr); //指定 can1 设备 addr.can_family AF_CAN; addr.can_ifindex ifr.ifr_ifindex; bind(fd, (struct sockaddr *)addr, sizeof(addr));//将套接字与 can1 绑定 //禁用过滤规则本进程不接收报文只负责发送 setsockopt(fd, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0); //生成两个报文 frame[0].can_id 0x11; frame[0]. can_dlc 1; frame[0].data[0] Y; frame[1].can_id 0x22; frame[1]. can_dlc 1; frame[1].data[0] N; //循环发送两个报文 while(1) { nbytes write(fd, frame[0], sizeof(frame[0])); //发送 frame[0] printf(write ret:%d,nbytes); if(nbytes ! sizeof(frame[0])) { printf(Send Error frame[0]\n!); break; //发送错误退出 } sleep(1); nbytes write(fd, frame[1], sizeof(frame[1])); //发送 frame[1] if(nbytes ! sizeof(frame[1])) { printf(Send Error frame[1]\n!); break; } sleep(1); } close(fd); return 0; }接收端例程/* 将CAN1波特率设置为500000 bps */ #define ip_cmd_set_can_params ip link set can1 type can bitrate 500000 triple-sampling on /* 打开CAN1 */ #define ip_cmd_open ifconfig can1 up /* 关闭CAN01*/ #define ip_cmd_close ifconfig can1 down int main() { int fd, nbytes; struct sockaddr_can addr; struct ifreq ifr; struct can_frame frame; struct can_filter rfilter[1]; system(ip_cmd_close); system(ip_cmd_set_can_params); system(ip_cmd_open); fd socket(PF_CAN, SOCK_RAW, CAN_RAW); //创建套接字 strcpy(ifr.ifr_name, can1 ); ioctl(fd, SIOCGIFINDEX, ifr); //指定 can1 设备 addr.can_family AF_CAN; addr.can_ifindex ifr.ifr_ifindex; bind(fd, (struct sockaddr *)addr, sizeof(addr)); //将套接字与 can1 绑定 //定义接收规则只接收表示符等于 0x11 的报文 rfilter[0].can_id 0x11; rfilter[0].can_mask CAN_SFF_MASK; //设置过滤规则 setsockopt(fd, SOL_CAN_RAW, CAN_RAW_FILTER, rfilter, sizeof(rfilter)); while(1) { nbytes read(fd, frame, sizeof(frame)); //接收报文 //显示报文 if(nbytes 0) { printf(ID0x%X DLC%d data[0]0x%X\n, frame.can_id, frame.can_dlc, frame.data[0]); } } close(fd); return 0; }
瑞芯微RV1126B开发板(EASY-EAI-PI2) CAN
发布时间:2026/6/27 10:31:20
1.CAN简介使用Socket CAN的主要目的就是为用户空间的应用程序提供基于Linux网络层的套接字接口。与广为人知的TCP/IP协议以及以太网不同CAN总线没有类似以太网的MAC层地址只能用于广播。CAN ID仅仅用来进行总线的仲裁。因此CAN ID在总线上必须是唯一的。当设计一个CAN-ECU(Electronic Control Unit 电子控制单元网络的时候CAN报文ID可以映射到具体的ECU。因此CAN报文ID可以当作发送源的地址来使用。1.1开发板的CAN资源1.2硬件连接通常由CPU出来的CAN信号是TTL信号并不是差分信号。因此需要一个CAN TTL信号转CAN差分信号的模块。其具体的接线图如下所示。2.快速上手2.1开发环境准备如果您初次阅读此文档请阅读《入门指南/开发环境准备/Easy-Eai编译环境准备与更新》并按照其相关的操作进行编译环境的部署。在PC端Ubuntu系统中执行run脚本进入EASY-EAI编译环境具体如下所示。cd ~/develop_environment ./run.sh 22042.2源码下载以及例程编译首先在虚拟机后台终端执行以下命令创建外设单例源码管理目录cd /opt mkdir -p EASY-EAI-PI2/demo首先到【百度网盘】上下载相关的单例程序链接https://pan.baidu.com/s/1ORJrMeW-bOJ6g_lPiNG6mw?pwd1234提取码1234比如把单例程序下载到此电脑\D:\BaiduNetdisk (无规定用户可自主选择)如下图所示。再将下载好的单例复制进入虚拟机的文件系统过程如下图所示。最后进入到对应的例程目录执行编译操作具体命令如下所示cd EASY-EAI-PI2/demo/11_CAN ./build.sh注* 由于依赖库部署在板卡上因此交叉编译过程中必须保持/mnt挂载。编译成功后会生成2个demo一个是发送端test-can_send另一个是接收端test-can_reception。并会自动部署到开发板的/userdata/目录中。* 此例程需要2个板卡做收发测试所以2个板卡都需要重复上述操作。2.3例程运行通过串口调试或ssh调试进入板卡后台定位到例程部署的位置如下所示cd /userdata首先在一台板卡上运行发送端运行命令如下sudo ./test-can-send然后【再在另外一台】板卡上运行接收端运行命令如下cd /userdata sudo ./test-can-reception【接收端】执行效果如下所示。API的详细说明以及API的调用本例程源码详细信息见下方说明。3.CAN操作API说明3.1创建socketcan套接字操作创建socketcan套接字操作函数原型如下所示。int socket(int domain, int type, int protocol);具体介绍如下所示。3.2指定本地网络接口地址操作指定本地网络接口地址函数原型如下所示。int ioctl(int fd, unsigned long request, ...);具体介绍如下所示。3.3绑定地址结构操作绑定地址结构函数原型如下所示。int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);具体介绍如下所示。3.4设置CAN过滤器操作设置CAN过滤器操作函数原型如下所示。int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);3.5CAN定义报文格式操作CAN定义报文格式如下所示。4.CAN通信例程【发送端】例程源码为11_CAN/test-can_send/main.c操作流程如下。【接收端】例程源码为11_CAN/test-can_send/main.c操作流程如下。参考例程如下所示。发送端例程/* 将CAN1波特率设置为500000 bps */ #define ip_cmd_set_can_params ip link set can1 type can bitrate 500000 triple-sampling on /* 打开CAN1 */ #define ip_cmd_open ifconfig can1 up /* 关闭CAN1 */ #define ip_cmd_close ifconfig can1 down int main() { int fd, nbytes; struct sockaddr_can addr; struct ifreq ifr; struct can_frame frame[2] {{0}}; system(ip_cmd_close); system(ip_cmd_set_can_params); system(ip_cmd_open); fd socket(PF_CAN, SOCK_RAW, CAN_RAW);//创建套接字 strcpy(ifr.ifr_name, can0 ); ioctl(fd, SIOCGIFINDEX, ifr); //指定 can1 设备 addr.can_family AF_CAN; addr.can_ifindex ifr.ifr_ifindex; bind(fd, (struct sockaddr *)addr, sizeof(addr));//将套接字与 can1 绑定 //禁用过滤规则本进程不接收报文只负责发送 setsockopt(fd, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0); //生成两个报文 frame[0].can_id 0x11; frame[0]. can_dlc 1; frame[0].data[0] Y; frame[1].can_id 0x22; frame[1]. can_dlc 1; frame[1].data[0] N; //循环发送两个报文 while(1) { nbytes write(fd, frame[0], sizeof(frame[0])); //发送 frame[0] printf(write ret:%d,nbytes); if(nbytes ! sizeof(frame[0])) { printf(Send Error frame[0]\n!); break; //发送错误退出 } sleep(1); nbytes write(fd, frame[1], sizeof(frame[1])); //发送 frame[1] if(nbytes ! sizeof(frame[1])) { printf(Send Error frame[1]\n!); break; } sleep(1); } close(fd); return 0; }接收端例程/* 将CAN1波特率设置为500000 bps */ #define ip_cmd_set_can_params ip link set can1 type can bitrate 500000 triple-sampling on /* 打开CAN1 */ #define ip_cmd_open ifconfig can1 up /* 关闭CAN01*/ #define ip_cmd_close ifconfig can1 down int main() { int fd, nbytes; struct sockaddr_can addr; struct ifreq ifr; struct can_frame frame; struct can_filter rfilter[1]; system(ip_cmd_close); system(ip_cmd_set_can_params); system(ip_cmd_open); fd socket(PF_CAN, SOCK_RAW, CAN_RAW); //创建套接字 strcpy(ifr.ifr_name, can1 ); ioctl(fd, SIOCGIFINDEX, ifr); //指定 can1 设备 addr.can_family AF_CAN; addr.can_ifindex ifr.ifr_ifindex; bind(fd, (struct sockaddr *)addr, sizeof(addr)); //将套接字与 can1 绑定 //定义接收规则只接收表示符等于 0x11 的报文 rfilter[0].can_id 0x11; rfilter[0].can_mask CAN_SFF_MASK; //设置过滤规则 setsockopt(fd, SOL_CAN_RAW, CAN_RAW_FILTER, rfilter, sizeof(rfilter)); while(1) { nbytes read(fd, frame, sizeof(frame)); //接收报文 //显示报文 if(nbytes 0) { printf(ID0x%X DLC%d data[0]0x%X\n, frame.can_id, frame.can_dlc, frame.data[0]); } } close(fd); return 0; }