<Linux开发> linux应用开发-之-can通信开发例程
一、简介
对于Can通信的相关介绍,读作不过多介绍了,网上其它网友的介绍有很多。
二、环境搭建
本次测试can通信的应用例程是运行在ubuntu pc上的;当然也是可以运行在linux开发板 或相关linux设备上的。
在ubuntu pc上安装虚拟can,命令如下:
1.sudo modprobe vcan
加载虚拟can模块
2.sudo ip link add dev can0 type vcan
添加can0网卡
3.ifconfig -a
查看can0
4.sudo ip link set dev can0 up
开启can0
5.sudo ip link set dev can0 down
关闭can0
6. sudo ip link del dev can0
删除can0
对于我们要使用虚拟Can,运行1~4步即可;当测试完Can通信,不使用时可使用 5~6删除。
启动后查看如下:
三、例程代码
本次代码会使用主进程发送数据,子进程接收数据;详细代码如下:
/***************************************************************
Copyright © OneFu Co., Ltd. 1998-2022. All rights reserved.
文件名 : can.c
作者 : waterfxw
版本 : V1.0
描述 : CAN 数据发送接收示例代码
其他 : 无
日志 : 初版 V1.0 2023/03/15 waterfxw创建
***************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <net/if.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
void procc(void)
{
struct ifreq ifr = {0};
struct sockaddr_can can_addr = {0};
struct can_frame frame = {0};
int sockfd = -1;
int i;
int ret;
/* 打开套接字 */
sockfd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
if(0 > sockfd) {
perror("socket error");
exit(EXIT_FAILURE);
}
/* 指定 can0 设备 */
strcpy(ifr.ifr_name, "can0");
ioctl(sockfd, SIOCGIFINDEX, &ifr);
can_addr.can_family = AF_CAN;
can_addr.can_ifindex = ifr.ifr_ifindex;
/* 将 can0 与套接字进行绑定 */
ret = bind(sockfd, (struct sockaddr *)&can_addr, sizeof(can_addr));
if (0 > ret) {
perror("bind error");
close(sockfd);
exit(EXIT_FAILURE);
}
/* 设置过滤规则 */
//setsockopt(sockfd, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);
/* 接收数据 */
for ( ; ; ) {
if (0 > read(sockfd, &frame, sizeof(struct can_frame))) {
perror("water read error");
break;
}
/* 校验是否接收到错误帧 */
if (frame.can_id & CAN_ERR_FLAG) {
printf("water Error frame!\n");
break;
}
/* 校验帧格式 */
if (frame.can_id & CAN_EFF_FLAG) //扩展帧
printf("扩展帧 <0x%08x> ", frame.can_id & CAN_EFF_MASK);
else //标准帧
printf("标准帧 <0x%03x> ", frame.can_id & CAN_SFF_MASK);
/* 校验帧类型:数据帧还是远程帧 */
if (frame.can_id & CAN_RTR_FLAG) {
printf(" remote request\n");
continue;
}
/* 打印数据长度 */
printf(" [%d] ", frame.can_dlc);
/* 打印数据 */
for (i = 0; i < frame.can_dlc; i++)
printf("%02x ", frame.data[i]);
printf(" [子进程 接收数据] \n");
}
/* 关闭套接字 */
close(sockfd);
exit(EXIT_SUCCESS);
//子进程测试使用
// while (1)
// {
// /* code */
// printf("子进程<%d>被创建---\n", getpid());
// sleep(2);
// }
// _exit(0);
}
int main(void) {
struct ifreq ifr = {0};
struct sockaddr_can can_addr = {0};
struct can_frame frame = {0};
int sockfd = -1;
int ret;
int status;
int i;
switch (fork()) {
case -1:
perror("fork error");
exit(-1);
case 0:
/* 子进程 */
procc();
sleep(1);
_exit(0);
default:
/* 父进程 */
break;
}
//父进程测试使用
// while (1)
// {
// /* code */
// printf("父进程<%d>被创建---\n", getpid());
// sleep(2);
// }
/* 打开套接字 */
sockfd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
if(0 > sockfd) {
perror("socket error");
exit(EXIT_FAILURE);
}
/* 指定 can0 设备 */
strcpy(ifr.ifr_name, "can0");
ioctl(sockfd, SIOCGIFINDEX, &ifr);
can_addr.can_family = AF_CAN;
can_addr.can_ifindex = ifr.ifr_ifindex;
/* 将 can0 与套接字进行绑定 */
ret = bind(sockfd, (struct sockaddr *)&can_addr, sizeof(can_addr));
if (0 > ret) {
perror("bind error");
close(sockfd);
exit(EXIT_FAILURE);
}
/* 设置过滤规则:不接受任何报文、仅发送数据 */
setsockopt(sockfd, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);
/* 发送数据 */
frame.data[0] = 0xA0;
frame.data[1] = 0xB0;
frame.data[2] = 0xC0;
frame.data[3] = 0xD0;
frame.data[4] = 0xE0;
frame.data[5] = 0x11;
frame.data[6] = 0x22;
frame.data[7] = 0x33;
frame.can_dlc = 8; //一次发送 6 个字节数据
frame.can_id = 0x321;//帧 ID 为 0x123,标准帧
for ( ; ; ) {
ret = write(sockfd, &frame, sizeof(frame)); //发送数据
if(sizeof(frame) != ret) { //如果 ret 不等于帧长度,就说明发送失败
perror("write error");
goto TT;
}
printf("[OnefU] 主进程发送数据成功!!!\n");
sleep(1); //一秒钟发送一次
}
ret = waitpid(-1, &status, 0);
if (-1 == ret) {
if (ECHILD == errno) {
printf("没有需要等待回收的子进程\n");
exit(0);
}
else {
perror("wait error");
exit(-1);
}
}
printf("回收子进程<%d>, 终止状态<%d>\n", ret, WEXITSTATUS(status));
TT:
/* 关闭套接字 */
close(sockfd);
exit(EXIT_SUCCESS);
}
四、编译运行
1、编译命令:
gcc can.c -o can
2、运行命令
./can
3、运行输出效果如下图:
linux下can通信应用例程测试完毕,上述例程只是一个简单的应用。实际开发过程可能会更加复杂。