1、CAN是什么?
- CAN ( Controller Area Network )即控制器局域网络。CAN最初是由德国的BOSCH公司为汽车监测、控制系统而设计的。
- CAN总线是一种基于广播机制的串行通信协议,采用差分信号传输方式,通过CAN_H和CAN_L两条线路实现数据同步传输,支持多节点并行通信。
- CAN总线是一种串行数据通信协议,其通信接口中集成了CAN协议的物理层和数据链路层功能,可完成对通信数据的成帧处理,包括位填充、数据块编码、循环冗余检验、优先级判别等项工作。
CAN总线网络
2、CAN的发展史
- 1983年,Bosch开始研究车上网络技术;
- 1986年,Bosch在SAE大会(汽车工程协会)公布CAN协议;
- 1987年,Intel和Philips先后推出CAN控制器芯片;
- 1991年,Bosch颁布CAN 2.0技术规范,CAN2.0包括A和B两个部分;
- 1991年,CAN总线最先在Benz S系列轿车上实现;
- 1993年,ISO颁布CAN国际标准ISO-11898;
- 1994年,SAE颁布基于CAN的J1939标准;
- 2003年,Maybach发布带76个ECU的新车型 (CAN,LIN,MOST);
- 2003年,VW发布带35个ECU的新型Golf;现在,几乎每一辆新车均装配有CAN 局域网;
CAN类型
3、CAN总线特点和优势
可以多主方式工作,网络上任意一个节点均可以在任意时刻主动地向网络上的其他节点发送信息,而不分主从,通信方式灵活
网络上的节点(信息)可分成不同的优先级,可以满足不同的实时要求
采用非破坏性位仲裁总线结构机制,当两个节点同时向网络上传送信息时,优先级低的节点主动停止数据发送,而优先级高的节点可不受影响地继续传输数据
可以点对点、一点对多点(成组)及全局广播几种传送方式接收数据
直接通信距离最远可达10km(速率5Kbps以下)
通信速率最高可达1MB/s(此时距离最长40m)
节点数实际可达110个
采用短帧结构,每一帧的有效字节数为8个
每帧信息都有CRC校验及其他检错措施,数据出错率极低
通信介质可采用双绞线,同轴电缆和光导纤维,一般采用廉价的双绞线即可,无特殊要求
节点在错误严重的情况下,具有自动关闭总线的功能,切断它与总线的联系,以使总线上的其他操作不受影响
下图为奥迪A6轿车总线拓扑图(根据网络搜索而来,如有侵权,可联系删除)。
车载网络总线系统拓扑图
4、CAN总线协议架构
4.1 物理层规范
4.1.1、双绞线传输介质
CAN总线通常采用双绞线(如ISO 11898-2定义的屏蔽/非屏蔽双绞线)作为物理传输介质,通过差分信号(CAN_H和CAN_L)传输数据,抗电磁干扰能力强,适合汽车等恶劣环境
双绞线
双绞线4.1.2、终端电阻匹配
总线两端需配置120Ω终端电阻以消除信号反射,确保信号完整性。当总线长度超过100米或速率高于125kbps时,必须严格遵循终端匹配规则
4.1.3、电气特性与速率关系
•传输速率与总线长度成反比,最高1Mbps(40米内),5kbps时可达10公里。电平标准为显性(逻辑0,压差≥1.5V)和隐性(逻辑1,压差≈0V)
电气特性4.1.4、编码方式
采用NRZ和位填充 的位编码方式
4.2 数据链路层功能
数据链路层4.2.1、多主竞争与仲裁机制
采用非破坏性逐位仲裁(CSMA/CR),优先级高的报文(标识符值小)继续发送,失败节点自动退出发送并重试,确保高优先级数据实时性
4.2.2、错误检测与处理
内置CRC校验、帧格式检查、应答错误等5种错误检测机制,故障节点会被自动隔离(进入被动错误状态),避免总线瘫痪
4.2.3、数据同步与位填充
通过硬同步和重同步机制解决时钟漂移问题,每5个连续相同位后插入1个补码位(位填充),保证信号边沿密度以维持同步
4.3 CAN协议帧类型分类
CAN协议帧4.3.1、数据帧
用于发送单元向接收单元传送数据的帧,由帧起始、仲裁段、控制段、数据段、CRC 循环冗余校验段、应答段和帧结束7个不同段组成
- 标准数据帧
- 扩展数据帧
4.3.2、遥控帧
用于请求发送特定 ID 的数据帧,与数据帧的最大差别在于遥控帧没有数据段
- 标准遥控帧
- 扩展遥控帧
4.3.3、错误帧
用于在传输出错时通知其他节点
4.3.4、超载帧
用于告知当前帧传输已超出 CAN 传输速率负载
4.3.5、帧间隔
用来隔离数据帧或者遥控帧
5、CAN驱动代码
下面使用S32K144配置一下CAN驱动
voidSetCAN1Mode( uint8_t mode ){if(mode == CAN_MODE_NORMAL) { clear(PTA->PDOR,0); }elseif(mode == CAN_MODE_STANDBY) {set(PTA->PDOR,0); }}staticvoidFLEXCAN1_init(uint8_t u8CANBaudType, can_id_infor_man_st* pstInforMan);staticvoidFLEXCAN1PortInit(void);staticvoidFLEXCAN1InitIRQs(void);staticvoidFLEXCAN1PortInit(void){ PCC->PCCn[PCC_PORTC_INDEX] |= PCC_PCCn_CGC_MASK; // enable clock for port PORTC->PCR[6] |= PORT_PCR_MUX(3); // PORTC6 can1 rx PORTC->PCR[7] |= PORT_PCR_MUX(3); // portC7 can1 tx }staticvoidFLEXCAN1_init(uint8_t u8CANBaudType, can_id_infor_man_st* pstInforMan){uint8_t i = 0;uint32_t data = 0; PCC->PCCn[PCC_FlexCAN1_INDEX] |= PCC_PCCn_CGC_MASK; /* CGC=1: enable clock to FlexCAN1 */ CAN1->MCR |= CAN_MCR_MDIS_MASK; /* MDIS=1: Disable module before selecting clock */ CAN1->CTRL1 &= ~CAN_CTRL1_CLKSRC_MASK; /* CLKSRC=0: Clock Source = oscillator (8 MHz) */ CAN1->MCR &= ~CAN_MCR_MDIS_MASK; /* MDIS=0; Enable module config. (Sets FRZ, HALT)*/// enter freeze mode CAN1->MCR = (CAN1->MCR & ~CAN_MCR_FRZ_MASK) | CAN_MCR_FRZ(1U); CAN1->MCR = (CAN1->MCR & ~CAN_MCR_HALT_MASK) | CAN_MCR_HALT(1U);/* Wait for entering the freeze mode */while (((CAN1->MCR & CAN_MCR_FRZACK_MASK) >> CAN_MCR_FRZACK_SHIFT) == 0U) {} CAN1->MCR |= CAN_MCR_RFEN(1) | /* RXFIFO enabled *///CAN_MCR_SRXDIS_MASK | /* Disable self reception */ CAN_MCR_IRMQ_MASK | /* Individual mask register used */// CAN_MCR_DMA_MASK | /* DMA enabled */// CAN_MCR_AEN_MASK | /* AEN=1: Abort enabled */ CAN_MCR_IDAM(0) | /* ID acceptance mode; 0..Format A */ CAN_MCR_MAXMB(32); /* 32 MB used */ CAN1->CTRL1 = 0; // clearif(u8CANBaudType == CAN_500K) { CAN1->CTRL1 = s_u32CTRL1_500k; }elseif(u8CANBaudType == CAN_250K) { CAN1->CTRL1 = s_u32CTRL1_250k; }else {// just for debug u8CANBaudType = u8CANBaudType;return; }#if 1if((CAN1->MCR & (1 << 29)) != 0) // RX FIFO enable ? {// CAN1->CTRL2 |= (0xC << 24); CAN1->CTRL2 = (CAN1->CTRL2 & ~CAN_CTRL2_RFFN_MASK) | ((11 << CAN_CTRL2_RFFN_SHIFT) & CAN_CTRL2_RFFN_MASK);// CAN1->RXFGMASK = 0x1FFC3FFF;// CAN1->RXFGMASK = 0xFFFFFFFF; }#endif// CAN1->CTRL1 = 0X00DB0006;for(i=24; i<128; i++ ) { /* CAN1: clear 24 msg bufs x 4 words/msg buf = 96 words*/ CAN1->RAMn[i] = 0x00; /* Clear msg buf word */ }for(i=0; i<32; i++ ) { /* In FRZ mode, init CAN1 32 msg buf filters */ CAN1->RXIMR[i] = 0xFFFFFFFF; /* Check all ID bits for incoming messages */ } CAN1->RXMGMASK = 0x1FFFFFFF; /* Global acceptance mask: check all ID bits */ CAN1->IMASK1 = 0X00000020;for(i = 0; i < pstInforMan->u8Sum; i++) { data = 0;if(pstInforMan->pstCANIDInfor[i].u8CANIDType == CAN_ID_STD) { data = (pstInforMan->pstCANIDInfor[i].u32CANID << 19); }else { data = ((1 << 30) | ((pstInforMan->pstCANIDInfor[i].u32CANID << 1))); } CAN1->RAMn[ 6*MSG_BUF_SIZE + i] = data; // at FIFO mode , MB0~5 use for fifo buffer }// exit freeze mode CAN1->MCR = (CAN1->MCR & ~CAN_MCR_HALT_MASK) | CAN_MCR_HALT(0U); CAN1->MCR = (CAN1->MCR & ~CAN_MCR_FRZ_MASK) | CAN_MCR_FRZ(0U);/* Wait till exit freeze mode */while ((CAN1->MCR & CAN_MCR_FRZACK_MASK) != 0U) {}while ((CAN1->MCR & CAN_MCR_NOTRDY_MASK) != 0U) {}/* Good practice: wait for NOTRDY to clear (module ready) */}staticvoidFLEXCAN1InitIRQs(void){ S32_NVIC->ICPR[1] = 1 << (CAN1_ORed_0_15_MB_IRQn % 32); /* clr any pending IRQ*/ S32_NVIC->ISER[(uint32_t)(CAN1_ORed_0_15_MB_IRQn) >> 5U] = (uint32_t)(1UL << ((uint32_t)(CAN1_ORed_0_15_MB_IRQn) & (uint32_t)0x1FU)); S32_NVIC->IP[CAN1_ORed_0_15_MB_IRQn] = 0x07;}// can1 use mb31 as general send buffer, mb30 as gateway send buffer.int16_t CAN1SendData(uint8_t u8CANIDType, uint32_t u32CANID, constuint8_t* pu8Data){ UNNData unndata;if(((CAN1->RAMn[ 31*MSG_BUF_SIZE + 0]>>24)&0x0F)!=0x08) {return-1; } CAN1->IFLAG1 |= ((uint32_t)1 << 31); /* Clear CAN 0 MB 8 flag without clearing others*/ unndata.u8Data[3] = pu8Data[0]; unndata.u8Data[2] = pu8Data[1]; unndata.u8Data[1] = pu8Data[2]; unndata.u8Data[0] = pu8Data[3]; CAN1->RAMn[ 31*MSG_BUF_SIZE + 2] = unndata.u32Data; unndata.u8Data[3] = pu8Data[4]; unndata.u8Data[2] = pu8Data[5]; unndata.u8Data[1] = pu8Data[6]; unndata.u8Data[0] = pu8Data[7]; CAN1->RAMn[ 31*MSG_BUF_SIZE + 3] = unndata.u32Data;if(CAN_ID_STD == u8CANIDType) { CAN1->RAMn[ 31*MSG_BUF_SIZE + 1] = (uint32_t)(u32CANID << 18); CAN1->RAMn[ 31*MSG_BUF_SIZE + 0] = 0X0C480000; }elseif(CAN_ID_EXT == u8CANIDType) { CAN1->RAMn[ 31*MSG_BUF_SIZE + 1] = u32CANID; CAN1->RAMn[ 31*MSG_BUF_SIZE + 0] = 0X0C680000; }else {// just for debug u8CANIDType = u8CANIDType;return-1; } return0;}