—————————————————————————————————————
文章结尾可免费获取RK3576、RK3568、RK3588学习视频相关资料
—————————————————————————————————————
1、CAN是什么?

2、CAN的发展史
现在,几乎每一辆新车均装配有CAN 局域网;

3、CAN总线特点和优势
下图为奥迪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协议帧类型分类

4.3.1、数据帧
用于发送单元向接收单元传送数据的帧,由帧起始、仲裁段、控制段、数据段、CRC 循环冗余校验段、应答段和帧结束7个不同段组成
4.3.2、遥控帧
用于请求发送特定 ID 的数据帧,与数据帧的最大差别在于遥控帧没有数据段
4.3.3、错误帧
用于在传输出错时通知其他节点
4.3.4、超载帧
用于告知当前帧传输已超出 CAN 传输速率负载
4.3.5、帧间隔
用来隔离数据帧或者遥控帧
5、CAN驱动代码
下面使用S32K144配置一下CAN驱动
void SetCAN1Mode( uint8_t mode )
{
if(mode == CAN_MODE_NORMAL)
{
clear(PTA->PDOR,0);
}
else if(mode == CAN_MODE_STANDBY)
{
set(PTA->PDOR,0);
}
}
static void FLEXCAN1_init(uint8_t u8CANBaudType, can_id_infor_man_st* pstInforMan);
static void FLEXCAN1PortInit(void);
static void FLEXCAN1InitIRQs(void);
static void FLEXCAN1PortInit(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
}
static void FLEXCAN1_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; // clear
if(u8CANBaudType == CAN_500K)
{
CAN1->CTRL1 = s_u32CTRL1_500k;
}
else if(u8CANBaudType == CAN_250K)
{
CAN1->CTRL1 = s_u32CTRL1_250k;
}
else
{
// just for debug
u8CANBaudType = u8CANBaudType;
return;
}
#if 1
if((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) */
}
static void FLEXCAN1InitIRQs(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, const uint8_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;
}
else if(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;
}
return 0;
}
结束
1、微信扫码关注 擎天柱工坊

2、发送消息 RK3576硬件设计 或者 RK3568硬件设计 或者 RK3588硬件设计


