目录
前言
一、socket can创建?
二、系统can节点设置
三、can过滤器设置
四、can数据发送
五、can数据接收
总结
CAN是ControllerArea Network(控制器局域网)的缩写。CAN通信协议在1986年由德国电气商博世公司所开发,主要面向汽车的通信系统。现已是ISO国际标准化的串行通信协议。根据不同的距离、不同的网络,可配置不同的速度,最高速度为1MBit/s。can总线协议的内容相对来说比较多,在此不做详细介绍,本文主要介绍socket can的使用方法。
Socket CAN采用的即是原始套接字,我们通过采用AF_CAN协议来创建一个socket can原始套接字,创建代码如下所示:
struct sockaddr_can tCanAddr;
struct ifreq ifr;
iCanFd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
strcpy(ifr.ifr_name, pcCanName);
/* 获取网络接口 */
iRet = ioctl(iCanFd, SIOCGIFINDEX, &ifr);
if (iRet < 0)
{
perror("can init:");
close(iCanFd);
return 0;
}
tCanAddr.can_family = AF_CAN;
tCanAddr.can_ifindex = ifr.ifr_ifindex;
bind(iCanFd, (struct sockaddr *)&tCanAddr, sizeof(tCanAddr));
使用ip link对linux can设备节点进行波特率参数设置,本文采用linux命令设置方式,分别对socket can 和 socket canfd进行参数设置,如下所示:
/* 关闭can设备节点 */
sprintf(acCmd, "ip link set %s down", pcCanName);
system(acCmd);
usleep(1000);
/* socket can/canfd波特率设置 */
//socket can设置
if(iCanType == E_CAN)
sprintf(acCmd, "ip link set %s type can bitrate %d", pcCanName, iSpeed);
else //socket canfd设置
sprintf(acCmd, "ip link set %s type can bitrate %d dbitrate %d fd on", pcCanName, iSpeed, iSpeed);
system(acCmd);
usleep(1000);
/* 开启can设备节点 */
sprintf(acCmd, "ip link set %s up", pcCanName);
system(acCmd);
usleep(1000);
socket can可以使用can_filter过滤器设置指定接收can id的数据报文,根据我们的需求读取can总线上指定id数据,具体设置如下函数接口所示:
int CAN_SetFilter(int iCanfd, int iCanId)
{
int iCanFdOn = 1;
struct can_filter rfilter;
if(iCanId > 0)
{
rfilter.can_id = iCanId;
rfilter.can_mask = CAN_SFF_MASK;
setsockopt(iCanfd, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));
}
else
{
/* To disable the reception of CAN frames on the selected CAN_RAW socket: */
setsockopt(iCanfd, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);
}
/* support canfd */
setsockopt(iCanfd, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &iCanFdOn, sizeof(iCanFdOn));
return 0;
}
socket can can_frame结构体是应用程序与内核驱动交互的结构体。内核驱动与CAN硬件交互之后,BUS上传输的通信协议由下文的通信协议指定。
/*
* 扩展格式识别符由 29 位组成。其格式包含两个部分:11 位基本 ID、18 位扩展 ID。
* Controller Area Network Identifier structure
*
* bit 0-28 : CAN识别符 (11/29 bit)
* bit 29 : 错误帧标志 (0 = data frame, 1 = error frame)
* bit 30 : 远程发送请求标志 (1 = rtr frame)
* bit 31 :帧格式标志 (0 = standard 11 bit, 1 = extended 29 bit)
*/
typedef __u32 canid_t;
struct can_frame {
canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
__u8 can_dlc; /* 数据长度: 0 .. 8 */
__u8 data[8] __attribute__((aligned(8)));
};
基于Linux Socket CAN的原始套接字,利用can_frame格式,一次最大发送8个字节数据。当然,也可以使用ISOTP格式,一次可以发送64字节。 下述函数接口可以发送标准帧、扩展帧,远程帧、数据帧。数据内容应用可以自行定义,发送函数接口如下所示:
int CAN_SendMsg(int iCanFd, T_CAN_MSG *ptCanMsg)
{
struct can_frame tFrame;
int iDataLen = 0;
int iRet = 0;
if ((iCanFd <= 0) || (NULL == ptCanMsg))
{
return 0;
}
memset(&tFrame, 0, sizeof(tFrame));
if (ptCanMsg->cLen < 0)
{
iDataLen = 0;
}
else if (ptCanMsg->cLen > 64)
{
iDataLen = 64;
}
else
{
iDataLen = ptCanMsg->cLen;
}
tFrame.can_id = ptCanMsg->uiCobId;
if (CAN_TYPE_EXTENDED == ptCanMsg->cIde)
{
tFrame.can_id &= CAN_EFF_MASK;
tFrame.can_id |= CAN_EFF_FLAG;
}
else
{
tFrame.can_id &= CAN_SFF_MASK;
}
/* attention:canfd has no remote frames */
if (CAN_FRAMETYPE_RTR == ptCanMsg->cRtr)
{
tFrame.can_id |= CAN_RTR_FLAG;
}
else
{
tFrame.can_id &= ~CAN_RTR_FLAG;
}
tFrame.can_dlc = iDataLen;
if (iDataLen > 0)
{
memcpy(tFrame.data, ptCanMsg->acData, iDataLen);
}
iRet = write(iCanFd, &tFrame, sizeof(tFrame));
//nbytes = sendto(sock_fd, &frame, sizeof(struct can_frame), 0, (struct sockaddr*)&addr, sizeof(addr));
return iRet;
}
根据CAN通信协议,可以收到3种类型数据:数据帧,远程请求帧,错误帧。数据帧,远程请求帧包括标准格式和扩展格式,错误帧没有区分。 所有收到的数据,都是struct can_frame结构体表示,接收API接口函数定义如下所示:
int CAN_RecvMsg(int iCanFd, T_CAN_MSG *ptCanMsg)
{
struct can_frame tFrame;
int iRet = 0;
if ((iCanFd <= 0) || (NULL == ptCanMsg))
{
return 0;
}
memset(&tFrame, 0, sizeof(tFrame));
//iLen = recvfrom(iCanFd, &tFrame, sizeof(struct can_frame), 0, (struct sockaddr *)&addr, &len);
iRet = read(iCanFd, &tFrame, sizeof(tFrame));
if (iRet < 0)
{
return iRet;
}
if (tFrame.can_id & CAN_EFF_FLAG)
{
ptCanMsg->cIde = CAN_TYPE_EXTENDED;
ptCanMsg->uiCobId = tFrame.can_id & CAN_EFF_MASK;
}
else
{
ptCanMsg->cIde = CAN_TYPE_STANDARD;
ptCanMsg->uiCobId = tFrame.can_id & CAN_SFF_MASK;
}
if (tFrame.can_id & CAN_RTR_FLAG)
{
ptCanMsg->cRtr = CAN_FRAMETYPE_RTR;
}
else
{
ptCanMsg->cRtr = CAN_FRAMETYPE_NORMAL;
}
ptCanMsg->cLen = tFrame.can_dlc;
memcpy(ptCanMsg->acData, tFrame.data, tFrame.can_dlc);
return iRet;
}
本文主要对socket can网络编程进行api接口封装,在linux操作系统下可以直接调用,完整api接口函数库在下面链接下载:
https://download.csdn.net/download/qq_18376583/86815135