简介
CAN(Controller Area Network 控造器局域收集)是一种普遍用于主动控造、嵌入式设备和汽车范畴的工业总线手艺,由德国BOSCH公司开发,并最末成为国际尺度(ISO 11898-1)。CAN总线是一种串行通信协议,能有效地撑持具有很高平安品级的散布实时控造,在汽车电子行业里,利用CAN 毗连策动机的控造单位、传感器、防刹车系统等,传输速度可达1Mbps。CAN总线的工做原理当CAN总线上的节点发送数据时,以报文形式播送给收集中的所有节点,总线上的所有节点都不利用节点地址等系统设置装备摆设信息,只按照每组报文开头的 11 位标识符(CAN 2.0A 标准)解释数据的含义来决定能否领受。那种数据收发体例称为面向内容的编址计划。
当某个节点要向其他节点发送数据时,那个节点的处置器将要发送的数据和本身的标识符传送给该节点的CAN总线接口控造器,并处于筹办形态;当收到总线分配时,转为发送报文形态。数据按照协议组织成必然的报文格局后发出,此时收集上的其他节点处于领受形态。处于领受形态的每个节点对领受到的报文停止检测,判断那些报文能否是发给本身的以确定能否领受。
因为CAN总线是一种面向内容的编址计划,因而很容易成立高水准的控造系统并灵敏地停止设置装备摆设我们能够很容易地在CAN总线上加进一些新节点而无须在硬件或软件长进行修改。
当供给的新节点是纯数据领受设备时,数据传输协议不要求独立的部门有物理目标地址。此时允许散布过程同步化。也就是说,当总线上的控造器需要丈量数据时,数据可由总线上间接获得,而无需每个控造器都有本身独立的传感器。
CAN收发器负责逻辑电安然平静物理信号之间的转换。
将逻辑信号转换成物理信号(差分电平),或者将物理信号转换成逻辑电平。
CAN尺度有两个,即IOS11898和IOS11519,两者差分电平特征差别。
好文保举:
嵌入式Rom:嵌入式软件是如许debug的
面试高频问答:怎么在Linux下编译C法式
一文搞懂Linux下内审定时器(Timer)
Linux 办事器性能优化,看了都说好!
玩转腾讯首发Linux内核源码《嵌入式开发条记》,也许能帮到你哦
嵌入式Rom:若何理解Linux内存与Kasan东西阐发15 附和 · 0 评论文章CAN总线的工做特点
CAN总线的有以下三方面特点:能够多主体例工做,收集上的肆意节点均能够在肆意时刻主动地向收集上的其他节点发送信息,而不分主从,通信体例灵敏。收集上的节点(信息)可分红差别的优先级,能够满足差别的实时要求。接纳非毁坏性位仲裁总线构造机造,当两个节点同时向收集上传送信息时,优先级低的节点主动停行数据发送,而优先级高的节点可不受影响地继续传输数据。
别的差别于传统的收集(好比USB或者以太网),CAN节点与节点之间不会传输大数据块,一帧CAN动静最多传输8字节用户数据,接纳短数据包也能够使得系统获得更好的不变性。CAN总线具有总线仲裁机造,能够组建多主系统。
CAN总线协议条理构造
CAN是一种复杂逻辑的总线构造。从条理上能够将 CAN 总线划分为三个差别条理:
(1) 物理层
在物理层中定义现实信号的传输办法,包罗位的编码息争码、位的按时和同步等内容,感化是定义差别节点之间按照电器属性若何停止位的现实传输。在物理毗连上,CAN总线构造供给两个引脚:CANH和CANL,总线通过CANH和CANL之间的差分电压完成信号的位传输。在差别系统中,CAN总线的位速度差别;在系统中,CAN总线的位速度是独一的,而且是固定,那需要对总线中的每个节点设置装备摆设同一的参数。
(2) 传输层
传输层是CAN总线协议的核心。传输层负责把领受到的报文供给给对象层,以及领受来自对象层的报文。传输层负责位的按时及同步、报文分帧、仲裁、应答、错误检测和标定、毛病界定。
(3) 对象层
在对象层中能够为长途数据恳求以及数据传输供给办事,确定由现实要利用的传输层领受哪一个报文,而且为恢复办理和过载通知供给手段。
can总线发送形式:can总线发送节点以播送的形式发送,领受节点以id来判断能否是该节点所需要的数据。
can总线特点:1,不变性高、实时性好。2,各个节点都能够收发信号。3,通信速度更高1mb/s。4,集成度较好,拥有屏障id、排优先级、完美的报错机造等特点。
CAN总线的报文构造
CAN 总线上的报文传输由以下 4 个差别的帧类型暗示和控造。
(1) 数据帧
数据帧照顾数据从发送器至领受器。总线上传输的大多是那种帧。从标识符长度上,又能够把数据帧分为尺度帧(11 位标识符)和扩展帧(29 位标识符)。
数据帧由 7 个差别的位场构成:帧起始、仲裁场、控造场、数据场、CRC场、应答场、帧完毕。此中,数据场的长度为0~8个字节。标识符位于仲裁场中,报文领受节点通过标识符停止报文滤波。
(2) 长途帧
由总线上的节点发出,用于恳求其他节点发送具有统一标识符的数据帧。当某个节点需要数据时,能够发送长途帧恳求另一节点发送响应数据帧。与数据帧比拟,长途帧没有数据场。
(3) 错误帧
任何单位,一旦检测到总线错误就发出错误帧。错误帧由两个差别的场构成,第一个场是由差别站供给的错误标记的叠加(错误标记),第二个场是错误界定符。
(4) 过载帧
过载帧用于在先行的和后续的数据帧(或长途帧)之间供给附加延时。过载帧包罗两个场:过载标记和过载界定符。
尺度CAN
尺度CAN只要11位标识符,每帧的数据长度为51+(0~64)=(51~117)位。注:不计位填充。如下图所示:尺度CAN---11位标识符
·SOF - 帧起始,显性(逻辑0)暗示报文的起头,并用于同步总线上的节点。
· 标识符 - 尺度CAN具有11位标识符,用来确定报文的优先级。此域的数值越小,优先级越高。
· RTR - 长途发送恳求位,当需要从另一个节点恳求信息时,此位为显性(逻辑0)。所有节点都能领受那个恳求,但是帧标识符确定被指定的节点。响应数据帧同样被所有节点领受,能够被有兴趣的节点利用。
· IDE - 标识符扩展位为显性时暗示那是一个尺度CAN格局,为隐形暗示那是扩展CAN格局。
· r0 - 保留位(可能未来尺度修订会利用)
· DLC - 4位数据长度代码暗示传输数据的字节数目,一帧CAN最多传输8字节用户数据
· 数据0~8 – 最多能够传输8字节用户数据
· CRC - 16位(包罗1位定界符)CRC校验码用来校验用户数据区之前的(包罗数据区)传输数据段。
· ACK - 2位,包罗应答位和应答界定符。发送节点的报文帧中,ACK两位是隐性位,当领受器准确地领受到有效的报文,领受器会在应答位期间向发送节点发送一个显性位,暗示应答。若是领受器发现那帧数据有错误,则不向发送节点发送ACK应答,发送节点会稍后重传那帧数据。
· EOF – 7位帧完毕标记位,全数为隐性位。若是那7位呈现显性位,则会引起填充错误。
· IFS – 7位帧间隔标记位,CAN控造器将领受到的帧准确的放入动静缓冲区是需要必然时间的,帧间隔能够供给那个时间。
扩展CAN
扩展CAN具有29位标识符,每帧数据长度为71+(0~64)=(71~135)位。注:不计位填充。
扩展CAN动静相关于尺度CAN动静增加的内容如下:
· SRR – 取代长途恳求位,为隐性。所以当尺度帧与扩展帧发送彼此抵触而且扩展帧的根本标识符与尺度帧的标识符不异时,尺度帧优先级高于扩展帧。
· IDE – 为隐性位暗示标记位扩展帧,18位扩展标识符紧跟着IDE位。
· r1 – 保留
CAN接口设置装备摆设Linux 操做系统对CAN总线供给优良撑持,它为大部门CAN控造器供给了不变的驱动,而那些驱动都接纳的是SocketCAN子系统编写的。
SocketCAN子系统
SocketCAN是Linux系统为CAN驱动定义的一种收集子系统。Linux下最早的CAN驱动是基于字符设备来实现的,而SocketCAN则是基于Linux收集协议栈来实现的,并利用了BSD Socket做为编程接口
。SocketCAN利用收集协议栈来实现CAN控造器驱动,因而CAN设备驱动胜利后会为CAN控造器生成一个收集设备,收集设备接纳“can+序号”的形式定名,第一个CAN设备的设备名为can0,那种定名体例与以太网接口类似。在Linux系统下利用ifconfig–a参看所有的收集设备,包罗以太网及CAN等设备。
Linux将CAN与以太网设备比量齐观,但因为CAN总线有本身的奇特性,有些字段并没有意义:如CAN总线没有MAC地址的概念,can设备的Hwaddr老是为00。好像以太网设备名一样,CAN的设备名也是后续所有操做的关键。
使能和封闭CAN设备Linux系统启动后,所有的CAN设备默认是封闭形态的,若是要使能CAN设备,利用ifconfig canX up即可:
#ifconfig can0 up若是要将已使能的CAN设备封闭,需要利用ifconfig canX down号令,如:
# ifconfig can0 down设置CAN波特率SocketCAN利用/sbin/ip号令来设置CAN总线的bitrate值来设置装备摆设波特率,设置装备摆设波特率之前,CAN设备必需处于封闭形态,已使能的设备如需修改波特率则需先封闭,设置波特率号令用法如下:
# /sbin/ip link set can0 type can bitrate 1000000设置CAN设备为只听形式SocketCAN利用/sbin/ip号令设置CAN总线listen-only选项来启用CAN的只听形式,CAN总线进入只听形式后,将不再对总线上其他设备CAN报文停止ACK应答,同时该CAN设备也不克不及发送CAN帧到总线上。其用法如下:
# /sbin/ip link set can0 type can listen-only on设置CAN设备为回环形式SocketCAN利用/sbin/ip号令设置CAN总线loopback选项来启用CAN的回环形式,进入回环形式后,经该设备发送的CAN帧会在该设备上回环。其用法如下
# /sbin/ip link set can0 type can loopback onSocketCAN编程SocketCAN帧构造
SocketCAN使构造体can_frame来暗示CAN帧,它的定义如下:
struct can_frame { canid_t can_id; // 32bit的无符号整形 __u8 can_dlc; // CAN帧的数据位长度,取值为0到8 __u8 data[8] __attribute__((aligned(8))); // 保留CAN帧的数据 }; #define CAN_EFF_FLAG 0x80000000U /* 尺度帧标识 */#define CAN_RTR_FLAG 0x40000000U /* 长途帧标识 */#define CAN_ERR_FLAG 0x20000000U /* 错误帧标识 */#define CAN_SFF_MASK 0x000007FFU /* 尺度帧ID掩码 */#define CAN_EFF_MASK 0x1FFFFFFFU /* 扩展帧ID掩码 */#define CAN_ERR_MASK 0x1FFFFFFFU /* 忽略错误、长途、尺度帧标识 */canid_t类型定义
Bit0 ~ B28帧ID,若是帧为尺度帧,帧ID有效为bit 0~bit10,若是为扩展帧,帧ID有效为Bit0~Bit28Bit29错误帧标识位,值为0时为数据帧,值为1时为错误帧Bit30长途帧标识位,值为1时为长途帧Bit31帧格局标识位,值为0时为尺度帧,值为1时为扩展帧eg:以下代码初始化一个帧ID为0x1ffffff含有8Byte数据的长途数据帧:
struct can_frame frame; frame.can_id = 0x1fffffff | CAN_RTR_FLAG; frame.can_dlc = 8; for (i = 0; i < frame.can_dlc; i++) { frame.data[i] = i; }翻开CAN设备套接字SocketCAN子系统利用socket套接字来操做CAN设备,根本操做流程与以太网很类似。利用设备前需要挪用socket系统挪用创建套接字:
#include <linux/can.h>#include <linux/can/raw.h>#include <sys/types.h>#include <sys/socket.h>int socket(int domain, int type, int protocol);当利用SocketCAN时socket函数参数domain需要设置为PF_CAN,参数type需要设置为SOCK_RAW,protocol能够取以下值,代表差别的传输协议。当不需要的传输协议时可取值为CAN_RAW。
#define CAN_RAW 1 /* RAW sockets */#define CAN_BCM 2 /* Broadcast Manager */#define CAN_TP16 3 /* VAG Transport Protocol v1.6 */#define CAN_TP20 4 /* VAG Transport Protocol v2.0 */#define CAN_MCNET 5 /* Bosch MCNet */#define CAN_ISOTP 6 /* ISO 15765-2 Transport Protocol */#define CAN_NPROTO 7socket套接字创建出来后,需要将套接字绑定到指定的can设备上,那个步调通过bind系统挪用来完成。
#include <sys/types.h>#include <sys/socket.h>int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);关于SocketCAN套接字,bind函数的第二个参数addr要求是struct sockaddr_can类型的变量,并在变量里绑定需要指定的设备,struct sockaddr_can类型定义如下:
struct sockaddr_can { __kernel_sa_family_t can_family; int can_ifindex; union { struct { canid_t rx_id, tx_id; } tp; } can_addr; };此中can_family值需要设为 AF_CAN,can_ifindex需要设置为CAN设备的索引,设备的索引能够通过ioctl 的SIOCGIFINDEX来获取,整个翻开CAN设备套接字的操做如下所示:
int sock; struct sockaddr_can addr; struct ifreq ifr; sock = socket(PF_CAN, SOCK_RAW, CAN_RAW); strcpy(ifr.ifr_name, "can0" ); ioctl(sock, SIOCGIFINDEX, &ifr); addr.can_family = AF_CAN; addr.can_ifindex = ifr.ifr_ifindex; bind(sock, (struct sockaddr *)&addr, sizeof(addr));bind函数施行后,CAN设备的套接字也翻开完成,接下来就能够领受发送CAN帧了。
发送与领受
SocketCAN的套接字翻开后就能够间接挪用read/write函数来收发CAN帧,read/wirte函数都通过一个struct can_frame类型的参数用来保留帧数据。发送can帧的代码如下:
struct can_frame frame; /* frame CAN帧初始化操做*/ write(sock, &frame, sizeof(struct can_frame));write函数发送胜利会返回一个CAN帧的长度值,暗示帧发送完成;若是发送失败会返回-1。
领受CAN帧的代码如下:
nbytes = read(s, &frame, sizeof(struct can_frame)); if (nbytes < 0) { perror("can raw socket read"); return 1; } if (nbytes < sizeof(struct can_frame)) { fprintf(stderr, "read: incomplete CAN frame\n"); return 1; }read函数领受胜利会返回一个CAN帧的长度值,暗示领受到一个完好的帧;若是领受失败会返回-1或者不完好数据包长度。
过滤器
SocketCAN套接字能够利用CAN_RAW_FILTER套接字选项指定的多个过滤规则来过滤领受帧数据。SocketCAN的领受过滤器定义在linux/can.h文件中:
struct can_filter { canid_t can_id; canid_t can_mask; };所有满足以下规则的CAN帧才被领受:
<领受帧ID>&can_mask == can_id &can_mask要利用领受过滤器前需要先定义好can_filter,然后利用setsockopt函数停止设置,过程如下所示:
struct can_filter rfilter[1]; rfilter[0].can_id = 0x123; rfilter[0].can_mask = CAN_SFF_MASK; setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));以上代码对套接字设置只领受帧ID为0x123的尺度帧。
代码示例
/*1.报文发送法式*/ #include<stdio.h>#include<stdlib.h>#include<string.h>#include<unistd.h>#include<net/if.h>#include<sys/ioctl.h>#include<sys/socket.h>#include<linux/can.h>#include<linux/can/raw.h>int main() { int s,nbytes; struct sockaddr_can addr; struct ifreq ifr; struct can_frame frame[2] = {{0}}; s = socket(PF_CAN, SOCK_RAW, CAN_RAW);//创建套接字 strcpy(ifr.ifr_name, "can0"); ioctl(s, SIOCGIFINDEX, &ifr);//指定can0设备 addr.can_family = AF_CAN; addr.can_ifindex = ifr.ifr_ifindex; bind(s, (structsockaddr*)&addr, sizeof(addr));//将套接字与can0绑定 //禁用过滤规则,本历程不领受报文,只负责发送 setsockopt(s, 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(s,&frame[0], sizeof(frame[0]));//发送frame[0] if(nbytes! = sizeof(frame[0])) { printf("Send Error frame[0]\n!"); break;//发送错误,退出 } sleep(1); nbytes = write(s,&frame[1], sizeof(frame[1]));//发送frame[1] if(nbytes != sizeof(frame[0])) { printf("SendErrorframe[1]\n!"); break; } sleep(1); } close(s); return0; } /*2.报文过滤领受法式*/ #include<stdio.h>#include<stdlib.h>#include<string.h>#include<unistd.h>#include<net/if.h>#include<sys/ioctl.h>#include<sys/socket.h>#include<linux/can.h>#include<linux/can/raw.h>int main(){ int s,nbytes; struct sockaddr_can addr; struct ifreq ifr; struct can_frame frame; struct can_filter rfilter[1]; s = socket(PF_CAN, SOCK_RAW, CAN_RAW);//创建套接字 strcpy(ifr.ifr_name, "can0"); ioctl(s, SIOCGIFINDEX, &ifr);//指定can0设备 addr.can_family = AF_CAN; addr.can_ifindex = ifr.ifr_ifindex; bind(s,(struct sockaddr*)&addr, sizeof(addr));//将套接字与can0绑定 //定义领受规则,只领受暗示符等于0x11的报文 rfilter[0].can_id=0x11; rfilter[0].can_mask = CAN_SFF_MASK; //设置过滤规则 setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter)); while(1) { nbytes = read(s, &frame, sizeof(frame));//领受报文 //显示报文 if(nbytes>0) { printf("ID=0x%XDLC=%ddata[0]=0x%X\n",frame.can_id, frame.can_dlc,frame.data[0]); } } close(s); return 0; } 【文章福利】小编保举本身的Linux内核源码交换群:【869634926】整理了一些小我觉得比力好的进修册本、视频材料共享在群文件里面,有需要的能够自行添加哦!!!前50名可进群领取,并额外赠送一份价值600的内核材料包(含视频教程、电子书、实战项目及代码)!点击下方链接即可免费领取内核相关进修材料哦进修曲通车:Linux内核源码/内存调优/文件系统/历程办理/设备驱动/收集协议栈
内核材料领取:Linux内核源码手艺进修道路+视频教程内核源码
原文参考:https://mp.weixin.电话.com/s/0awuebw7qc1_etveib7u0Q (侵权删除)