文章目录[隐藏]
STM32F429 CAN通信
CAN 是控制器局域网络 (Controller Area Network) 的简称,它是由研发和生产汽车电子产品著称的德国 BOSCH 公司开发的,并最终成为国际标准(ISO11519),是国际上应用最广泛的现场总线之一。
1、全双工允许数据在两个方向上同时传输;
2、半双工允许数据在两个方向上传输,但是同一时间数据只能在一个方向上传输,实际上是切换的单工。
- 有人认为CAN是“全双工”,也有人看是“半双工”。其实我认为都对吧,大家都知道,CAN节点包括CAN控制器和CAN收发器。只是站的角度不同,站在CAN控制器看问题,很明显是全双工,CAN控制器发送接收信号就有点类似串口发送TTL信号(这里不严谨,只是举个例子),全双工通信;换个角度,站在收发器看,虽然是用了两根信号线,但是它们是一对差分线发送差模信号,用两根线代表一个信号(显性信号和隐形信号),在一个时刻只能表示一个信号 ,所以对通讯节点来说, CAN 通讯是半双工 的,收发数据需要分时进行。
目录
一、STM32中CAN的初始化代码实现
二、CAN的筛选器使用及理解
三、CAN的物理层
四、CAN的协议层
一、STM32中CAN的初始化代码实现
1、GPIO的引脚初始化
void CAN_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(CAN_TX_GPIO_CLK|CAN_RX_GPIO_CLK, ENABLE);
GPIO_PinAFConfig(CAN_TX_GPIO_PORT, CAN_RX_SOURCE, CAN_AF_PORT);
GPIO_PinAFConfig(CAN_RX_GPIO_PORT, CAN_TX_SOURCE, CAN_AF_PORT);
GPIO_InitStructure.GPIO_Pin = CAN_TX_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(CAN_TX_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = CAN_RX_PIN ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_Init(CAN_RX_GPIO_PORT, &GPIO_InitStructure);
}
2、CAN控制器的初始化配置
static void CAN_Mode_Config(void)
{
CAN_InitTypeDef CAN_InitStructure;
RCC_APB1PeriphClockCmd(CAN_CLK, ENABLE);
/*CAN1寄存器的初始化*/
CAN_DeInit(CAN1);
CAN_StructInit(&CAN_InitStructure);
/*CAN相关寄存器的配置*/
CAN_InitStructure.CAN_TTCM=DISABLE; //MCR-TTCM 关闭时间触发通信模式使能
CAN_InitStructure.CAN_ABOM=ENABLE; //MCR-ABOM 使能自动离线功能
CAN_InitStructure.CAN_AWUM=ENABLE; //MCR-AWUM 使能自动唤醒模式
CAN_InitStructure.CAN_NART=DISABLE; //MCR-NART 禁止报文自动重传,即使发送失败,不会再发
CAN_InitStructure.CAN_RFLM=DISABLE; //MCR-RFLM 接受FIFO不锁定,接受数据溢出时,新报文会覆盖原报文
CAN_InitStructure.CAN_TXFP=DISABLE; //MCR-TXFP FIFO优先级由报文的标识符决定,不是时间顺序
CAN_InitStructure.CAN_Mode = CAN_Mode_Normal; //正常模式
/*波特率配置,约定好双方收发一个数据位的速率*/
CAN_InitStructure.CAN_SJW=CAN_SJW_2tq; //BTR-SJW 重新同步跳跃宽度2个时间单元
/* ss=1 bs1=5 bs2=3 位时间宽度为(1+5+3) tq*(1+3+5) */
CAN_InitStructure.CAN_BS1=CAN_BS1_5tq; //BTR-TS1 时间段1 占用了5个时间单元 包括了PTS
CAN_InitStructure.CAN_BS2=CAN_BS2_3tq; //BTR-TS1 时间段 时间单元2 占用了5个时间单元
/* CAN Baudrate = 1 MBps (1MBps已是stm32中can的最高速速率) (CAN 的时钟总线 APB 1 = 45 MHz) */
CAN_InitStructure.CAN_Prescaler =5; //BTR-BRP 波特率分频器 波特率计算:45/(1+5+3)/5=1 Mbps
CAN_Init(CANx, &CAN_InitStructure);
}
3、CAN的筛选器配置
void CAN_Filter_Config(void)
{
CAN_FilterInitTypeDef CAN_FilterInitStructure;
/*CAN筛选器组0*/
CAN_FilterInitStructure.CAN_FilterNumber=0;
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask; //掩码模式
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //筛选器位宽 32位
/* 验证码配置 */
CAN_FilterInitStructure.CAN_FilterIdHigh= ((0x1314<<3)>>16)&0xffff//验证码32位中高16位
CAN_FilterInitStructure.CAN_FilterIdLow= ((0x1314<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF; //验证码32位中低16位
/*屏蔽码配置*/
CAN_FilterInitStructure.CAN_FilterMaskIdHigh= 0xFFFF; //屏蔽码高16位,
CAN_FilterInitStructure.CAN_FilterMaskIdLow= 0xFFFF; //屏蔽码低16位,只允许一个通过
CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0 ; //筛选器关联FIFO0
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; //使能筛选器
CAN_FilterInit(&CAN_FilterInitStructure);
/*使能筛选器 接收中断*/
CAN_ITConfig(CANx, CAN_IT_FMP0, ENABLE);
}
/*通信发送报文的设置*/
void CAN_SetMsg(CanTxMsg *TxMessage)
{
uint8_t ubCounter = 0;
TxMessage->ExtId=0x1314; //扩展id
TxMessage->IDE=CAN_ID_EXT; //扩展模式
TxMessage->RTR=CAN_RTR_DATA; //发送 数据 类型
TxMessage->DLC=8; //数据长度
/*数据载入*/
for (ubCounter = 0; ubCounter < 8; ubCounter++)
{
TxMessage->Data[ubCounter] = ubCounter;
}
}
4、中断服务程序
void CAN_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = CAN_RX_IRQ; //CAN RX中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void CAN1_RX0_IRQHandler(void)
{
/*从邮箱接收到报文*/
CAN_Receive(CANx, CAN_FIFO0, &RxMessage);
/* 验证id==0x1314 是否是扩展id 数据位是否8位*/
if((RxMessage.ExtId==0x1314) && (RxMessage.IDE==CAN_ID_EXT) && (RxMessage.DLC==8) )
{
flag = 1;
}
else
{
flag = 0;
}
}
二、CAN的筛选器使用及理解
1、CAN筛选器的作用
CAN总线中,往往会有很多收发器进行发送多条数据,但是这些数据中只有一部分数据是你想要的,CPU却要处理很多无关紧要的数据,这无疑是给运行的效率带来的极大负担。所以筛选器起到了很大的作用,它可以为CPU过滤掉不要的数据,筛选出想要的数据,并把得到的数据存入到接收邮箱中。stm32通过识别过滤报文中的id,过滤方法有两种,列表模式和掩码模式。
每组筛选器包含 2 个 32 位的寄存器,分别为 CAN_FxR1 和 CAN_FxR2,它们用来存储要筛选的ID 或掩码。由可将一个32位寄存器分为两个16位寄存器,每组筛选器包含了4个16位寄存器。
//不管是32位还是16位宽的筛选器
/*CAN_FxR1[31:16]*/
CAN_FilterInitStructure.CAN_FilterIdHigh
/*CAN_FxR1[15:0]*/
CAN_FilterInitStructure.CAN_FilterIdLow
/*CAN_FxR2[31:16]*/
CAN_FilterInitStructure.CAN_FilterMaskIdHigh
/*CAN_FxR2[15:0]*/
CAN_FilterInitStructure.CAN_FilterMaskIdLow
2、列表模式
筛选器会比较列表中固定的id,要求报文 ID 与列表中的某一个标识符完全相同才可以接收。这种筛选方式有个很大的缺陷,stm32资源很有限,如果选择32位宽的筛选器,只能筛选两个报文id,如果是16位,可以筛选4个报文id。
- 32位宽的筛选器 列表模式
void CAN_Filter_Config(void)
{
uint32_t StdId[2] = {0x1234,0x1314};
uint32_t ExdId = 0x80908890;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
/*CAN筛选器组0*/
CAN_FilterInitStructure.CAN_FilterNumber=0;
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdList; //列表模式
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //筛选器位宽 32位
/* 验证码配置 */
/*32筛选器寄存器 包括 11位标准id 18位扩展id 1位IDE 1位RTR 1位0*/
/*报文的标准id是11位,需要向左移5位传入16位的寄存器 STID[10:0]*/
CAN_FilterInitStructure.CAN_FilterIdHigh=StdId[0]<<5;//验证码32位中高16位
CAN_FilterInitStructure.CAN_FilterIdLow= 0|CAN_ID_STD|CAN_RTR_DATA; //验证码32位中低16位
/*32筛选器寄存器 包括 11位标准id 18位扩展id 1位IDE 1位RTR 1位0*/
/*扩展id左移3位,腾出IDE、RTR和0,再向右移得出高16位*/
CAN_FilterInitStructure.CAN_FilterMaskIdHigh= (ExdId<<3)>>16&0xFFFF;//屏蔽码高16位,
/*扩展id左移3位,腾出IDE、RTR和0,标记扩展和数据类型*/
CAN_FilterInitStructure.CAN_FilterMaskIdLow= (ExdId<<3)&0xFFFF|CAN_ID_EXT|CAN_RTR_DATA; //屏蔽码低16位,只允许一个通过
CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0 ; //筛选器关联FIFO0
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; //使能筛选器
CAN_FilterInit(&CAN_FilterInitStructure);
}
- 16位宽的筛选器 列表模式
由参考手册可知,配置16宽的筛选器,CAN_FilterIdHigh、CAN_FilterIdLow、CAN_FilterMaskIdHigh、CAN_FilterMaskIdLow这四个变量可以存放4个标准id,不用存放扩展id,扩展只能用32位的筛选器。
void CAN_Filter_Config(void)
{
uint32_t StdId[2] = {0x1234,0x1314,0x1212,0x1313};
CAN_FilterInitTypeDef CAN_FilterInitStructure;
/*CAN筛选器组0*/
CAN_FilterInitStructure.CAN_FilterNumber=0;
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdList; //列表模式
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //筛选器位宽 32位
/* 验证码配置 */
/*32筛选器寄存器 包括 11位标准id 18位扩展id 1位IDE 1位RTR 1位0*/
/*报文的标准id是11位,需要向左移5位传入16位的寄存器 STID[10:0]*/
CAN_FilterInitStructure.CAN_FilterIdHigh=StdId[0]<<5;//验证码32位中高16位
CAN_FilterInitStructure.CAN_FilterIdLow= StdId[1]<<5; //验证码32位中低16位
/*32筛选器寄存器 包括 11位标准id 18位扩展id 1位IDE 1位RTR 1位0*/
/*扩展id左移3位,腾出IDE、RTR和0,再向右移得出高16位*/
CAN_FilterInitStructure.CAN_FilterMaskIdHigh= StdId[2]<<5;//屏蔽码高16位,
/*扩展id左移3位,腾出IDE、RTR和0,标记扩展和数据类型*/
CAN_FilterInitStructure.CAN_FilterMaskIdLow= StdId[3]<<5;//屏蔽码低16位,只允许一个通过
CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0 ; //筛选器关联FIFO0
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;//使能筛选器
CAN_FilterInit(&CAN_FilterInitStructure);
}
3、掩码模式
为了解决列表模式下的,列表的容量资源有限的问题。出现了第二种方案,可以替代列表模式。相比列表模式,掩码模式可以筛选出更多的报文id。筛选过程中,需要经过屏蔽和对比两个操作(屏蔽码和验证码,验证码不唯一,看需求)。
为了能更好的理解,举个简单的例子,比如要筛查id出生年份为2000年的青年人(44200078):
0x04 0x04 0x02 0x00 0x00 0x00 0x07 0x08 -->经过屏蔽码(只需匹配第2个~第5个字节,进行相与操作)
0x00 0x00 0xff 0xff 0xff 0xff 0x00 0x00 -->得到
0x00 0x00 0x02 0x00 0x00 0x00 0x00 0x00 -->对比验证码
0x00 0x00 0x02 0x00 0x00 0x00 0x00 0x00
如果是筛查00后的年轻人(只需匹配第2个~第4个字节):
将屏蔽码改为0x00 0x00 0xff 0xff 0xff 0x00 0x00 0x00
可以识别通过的数值2000~2009,可以看出只要通过数值的多少,关键看屏蔽码。设得宽,则可以通过的多(所有位为0,则不过任何过滤操作,则谁都可以通过),设得窄,则通过的少(所有位设为1,则只有一个能通过)。
- 32位宽的筛选器 掩码模式
void CANFilterConfig_Scale32_IdMask_StandardIdOnly(void)
{
CAN_FilterConfTypeDef sFilterConfig;
uint16_t StdIdArray[10] ={0x7e0,0x7e1,0x7e2,0x7e3,0x7e4,
0x7e5,0x7e6,0x7e7,0x7e8,0x7e9}; //定义一组标准CAN ID
uint16_t mask,num,tmp,i;
sFilterConfig.FilterNumber = 2; //使用过滤器2
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; //配置为掩码模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; //设置为32位宽
sFilterConfig.FilterIdHigh =(StdIdArray[0]<<5); //验证码可以设置为StdIdArray[]数组中任意一个,这里使用StdIdArray[0]作为验证码
sFilterConfig.FilterIdLow =0;
mask =0x7ff; //下面开始计算屏蔽码
num =sizeof(StdIdArray)/sizeof(StdIdArray[0]);
for(i =0; i<num; i++) //屏蔽码位StdIdArray[]数组中所有成员的同或结果
{
tmp =StdIdArray[i] ^ (~StdIdArray[0]); //所有数组成员与第0个成员进行同或操作
mask &=tmp;
}
sFilterConfig.FilterMaskIdHigh =(mask<<5);
sFilterConfig.FilterMaskIdLow =0|0x02; //只接收数据帧
sFilterConfig.FilterFIFOAssignment = 0; //设置通过的数据帧进入到FIFO0中
sFilterConfig.FilterActivation = ENABLE;
sFilterConfig.BankNumber = 14;
}
void CANFilterConfig_Scale32_IdMask_ExtendIdOnly(void)
{
CAN_FilterConfTypeDef sFilterConfig;
//定义一组扩展CAN ID用来测试
uint32_t ExtIdArray[10] ={0x1839f101,0x1835f102,0x1835f113,0x1835f124,0x1835f105,
0x1835f106,0x1835f107,0x1835f108,0x1835f109,0x1835f10A};
uint32_t mask,num,tmp,i;
sFilterConfig.FilterNumber = 3; //使用过滤器3
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; //配置为掩码模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; //设为32位宽
sFilterConfig.FilterIdHigh =((ExtIdArray[0]<<3) >>16) &0xffff;//数组任意一个成员都可以作为验证码
sFilterConfig.FilterIdLow =((ExtIdArray[0]<<3)&0xffff) | CAN_ID_EXT;
mask =0x1fffffff;
num =sizeof(ExtIdArray)/sizeof(ExtIdArray[0]);
for(i =0; i<num; i++) //屏蔽码位数组各成员相互同或的结果
{
tmp =ExtIdArray[i] ^ (~ExtIdArray[0]); //都与第一个数据成员进行同或操作
mask &=tmp;
}
mask <<=3; //对齐寄存器
sFilterConfig.FilterMaskIdHigh = (mask>>16)&0xffff;
sFilterConfig.FilterMaskIdLow = (mask&0xffff)|0x02; //只接收数据帧
sFilterConfig.FilterFIFOAssignment = 0;
sFilterConfig.FilterActivation = ENABLE;
sFilterConfig.BankNumber = 14;
}
void CANFilterConfig_Scale32_IdMask_StandardId_ExtendId_Mix(void)
{
CAN_FilterConfTypeDef sFilterConfig;
//定义一组标准CAN ID
uint32_t StdIdArray[10] ={0x711,0x712,0x713,0x714,0x715,
0x716,0x717,0x718,0x719,0x71a};
//定义另外一组扩展CAN ID
uint32_t ExtIdArray[10] ={0x1900fAB1,0x1900fAB2,0x1900fAB3,0x1900fAB4,0x1900fAB5,
0x1900fAB6,0x1900fAB7,0x1900fAB8,0x1900fAB9,0x1900fABA};
uint32_t mask,num,tmp,i,standard_mask,extend_mask,mix_mask;
sFilterConfig.FilterNumber = 4; //使用过滤器4
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; //配置为掩码模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; //设为32位宽
sFilterConfig.FilterIdHigh =((ExtIdArray[0]<<3) >>16) &0xffff; //使用第一个扩展CAN ID作为验证码
sFilterConfig.FilterIdLow =((ExtIdArray[0]<<3)&0xffff);
standard_mask =0x7ff; //下面是计算屏蔽码
num =sizeof(StdIdArray)/sizeof(StdIdArray[0]);
for(i =0; i<num; i++) //首先计算出所有标准CAN ID的屏蔽码
{
tmp =StdIdArray[i] ^ (~StdIdArray[0]);
standard_mask &=tmp;
}
extend_mask =0x1fffffff;
num =sizeof(ExtIdArray)/sizeof(ExtIdArray[0]);
for(i =0; i<num; i++) //接着计算出所有扩展CAN ID的屏蔽码
{
tmp =ExtIdArray[i] ^ (~ExtIdArray[0]);
extend_mask &=tmp;
}
mix_mask =(StdIdArray[0]<<18)^ (~ExtIdArray[0]); //再计算标准CAN ID与扩展CAN ID混合的屏蔽码
mask =(standard_mask<<18)& extend_mask &mix_mask; //最后计算最终的屏蔽码
mask <<=3; //对齐寄存器
sFilterConfig.FilterMaskIdHigh = (mask>>16)&0xffff;
sFilterConfig.FilterMaskIdLow = (mask&0xffff);
sFilterConfig.FilterFIFOAssignment = 0;
sFilterConfig.FilterActivation = ENABLE;
sFilterConfig.BankNumber = 14;
}
- 16位宽的筛选器 掩码模式
void CANFilterConfig_Scale16_IdMask(void)
{
CAN_FilterConfTypeDef sFilterConfig;
uint16_t StdIdArray1[10] ={0x7D1,0x7D2,0x7D3,0x7D4,0x7D5, //定义第一组标准CAN ID
0x7D6,0x7D7,0x7D8,0x7D9,0x7DA};
uint16_t StdIdArray2[10] ={0x751,0x752,0x753,0x754,0x755, //定义第二组标准CAN ID
0x756,0x757,0x758,0x759,0x75A};
uint16_t mask,tmp,i,num;
sFilterConfig.FilterNumber = 5; //使用过滤器5
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; //配置为掩码模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_16BIT; //设为16位宽
//配置第一个过滤对
sFilterConfig.FilterIdLow =StdIdArray1[0]<<5; //设置第一个验证码
mask =0x7ff;
num =sizeof(StdIdArray1)/sizeof(StdIdArray1[0]);
for(i =0; i<num; i++) //计算第一个屏蔽码
{
tmp =StdIdArray1[i] ^ (~StdIdArray1[0]);
mask &=tmp;
}
sFilterConfig.FilterMaskIdLow =(mask<<5)|0x10; //只接收数据帧
//配置第二个过滤对
sFilterConfig.FilterIdHigh = StdIdArray2[0]<<5; //设置第二个验证码
mask =0x7ff;
num =sizeof(StdIdArray2)/sizeof(StdIdArray2[0]);
for(i =0; i<num; i++) //计算第二个屏蔽码
{
tmp =StdIdArray2[i] ^ (~StdIdArray2[0]);
mask &=tmp;
}
sFilterConfig.FilterMaskIdHigh = (mask<<5)|0x10; //只接收数据帧
sFilterConfig.FilterFIFOAssignment = 0; //通过的CAN 消息放入到FIFO0中
sFilterConfig.FilterActivation = ENABLE;
sFilterConfig.BankNumber = 14;
}
三、CAN的物理层
与SPI、I2C等具有时钟信号的同步通讯方式不同,CAN通信并不是以时钟信号进行同步,CAN是一种 异步 通讯。只具有CAN_High和CAN_Low两条信号线 ,共同构成 一对差分信号线 的形式进行通讯。
CAN的物理层主要有两种。
闭环总线网络
闭环总线通讯网络 中的 CAN 通讯网络是一种遵循ISO11898 标准的高速、短距离“闭环网络”,它的总线最大长度为** 40m** ,通信速度最高为 1Mbps,总线的两端各要求有一个“120 欧”的电阻(并联) .
开环总线网络
开环总线通讯网络 中的是遵循 ISO11519-2 标准的低速、远距离“开环网络”,它的最大传输距离为** 1km** ,最高通讯速率为 125kbps,两根总线是独立的、不形成闭环,要求每根总线上各串联有一个“2.2 千欧”的电阻 。
通讯节点
CAN总线上可以挂载多个通讯节点,CAN通讯协议不对节点进行地址编码 ,而是对数据内容进行编码。CAN通讯节点由一个控制器及CAN收发器组成。当 CAN 节点需要发送数据时,控制器把要发送的二进制编码通过 CAN_Tx 线发送到收发器(类似TTL信号),然后由收发器把这个普通的逻辑电平信号转化成差分信号,通过差分线 CAN_High 和 CAN_Low 线输出到 CAN 总线网络。
差分信号
差分信号又称差模信号,这两个信号线的振幅相等,相位相反,通过两根信号线的电压差值来表示逻辑0和逻辑1.
-
相对单信号线传输方式,具有的优点:抗干扰能力,有效抑制它对外部的电磁干扰。时许定位准确。
-
由于差分信号线具有这些优点,所以在 USB 协议、 485 协议、以太网协议及 CAN 协议的物理层中,都使用了差分信号传输。
CAN协议中的差分信号
-
当逻辑"0"(显性电平),CAN_High和CAN_Low的压差2.5v;当逻辑"1"(隐性电平),CAN_High和CAN_Low的压差0v。
-
在CAN总线中,在同一时间,只要由输出显性电平,总线处于显性电平状态。类似I2c总线的“线与”特性。
-
由于 CAN 总线协议的物理层只有 1 对差分线,在一个时刻只能表示一个信号 ,所以对通讯节点来说, CAN 通讯是半双工 的,收发数据需要分时进行。在 CAN 的通讯网络中,因为共用总线,在整个网络中同一时刻只能有一个通讯节点发送信号,其余的节点在该时刻都只能接收。
四、CAN的协议层
CAN 的波特率及位同步
CAN 属于异步通讯,节点间使用 约定好的波特率 进行通讯,特别地, CAN 还会使用“位同步”的方式来抗 干扰、吸收误差,实现对总线电平信号进行正确的采样,确保通讯正常。
位时序分解
为了实现位同步,CAN 协议把每一个数据位的时序分解成SS 段、PTS 段、 PBS1 段、 PBS2 段,这四段的长度加起来即为一个 CAN 数据位的长度。分解后最小的时间单位是 Tq,而一个完整的位由 8~25 个 Tq 组成.
该图中表示的 CAN 通讯信号每一个数据位的长度为 19Tq,其中 SS 段占 1Tq, PTS 段占 6Tq, PBS1 段占 5Tq, PBS2 段占 7Tq。信号的采样点位于 PBS1 段与 PBS2 段之间,通过控制各段的长度 ,可 以对采样点的位置进行偏移,以便准确地采样。
• SS 段 (SYNC SEG)
若通讯节点检测到总线上信号的跳变沿被包含在 SS 段的范围之内,则表示节点与总线的时序是同步的,当节点与总线同步时,采样点采集到的总线电平即可被确定为该位的电平。 SS 段的大小固定为 1Tq 。
• PTS 段 (PROP SEG)
PTS 译为传播时间段,这个时间段是用于补偿网络的物理延时时间。是总线上输入比较器延时和输出驱动器延时总和的两倍。PTS 段的大小可以为 1~8Tq。
• PBS1 段 (PHASE SEG1)
PBS1 译为 相位缓冲段 ,主要用来补偿边沿阶段的误差,它的时间长度在重新同步的 时候可以加长。 PBS1 段的初始大小可以为1~8Tq。
• PBS2 段 (PHASE SEG2)
PBS2 这是另一个相位缓冲段,也是用来补偿边沿阶段误差的,它的时间长度在重新同步时可以缩短。 PBS2 段的初始大小可以为 2~8Tq。
通讯的波特率
总线上的各个通讯节点只要约定好 1 个 Tq 的时间长度以及每一个数据位占据多少个 Tq,就可以 确定 CAN 通讯的波特率。
例如,假设上图中的 1Tq=1us,而每个数据位由 19 个 Tq 组成,则传输一位数据需要时间 T1bit =19us,从而每秒可以传输的数据位个数为:
1x106/19 = 52631.6 (bps)
这个每秒可传输的数据位的个数即为通讯中的波特率。
同步过程分析
波特率只是约定了每个数据位的长度 ,数据同步还涉及到相位的细节,这个时候就需要用到数据 位内的 SS、 PTS、 PBS1 及 PBS2 段了。
CAN的数据同步分为硬同步和重新同步 。其中硬同步只是当存在“帧起始信号”时起作用,无法确保后续一连串的位时序都是同步的,而重新同步方式可解决该问题。
硬同步
若某个 CAN 节点通过总线发送数据时,它会发送一个表示通讯起始的信号 ,该信号是一个由高变低的下降沿 。而挂载到 CAN 总线上的通讯节点在不发送数据 时,会时刻检测总线上的信号。
节点以硬同步的方式调整,把自己的位时序中的 SS 段平移至总线出现下降沿的部分,获得同步,同步后采样点就可以采集得正确数据了。
重新同步
如果在一帧很长的数据内,节点信号与总线信号相位有偏移时,这种同步方式就无能为力了。重新同步的方式分为超前和滞后两种情况。
第一种
相位超前的情况如图相位超前时的重新同步 ,节点从总线的边沿跳变中,检测到它内部的时序比总线的时序相对超前 2Tq,这时控制器在下一个位时序中的 PBS1 段增加 2Tq 的时间长度,使得节点与总线时序重新同步。
第二种相位滞后的情况如图相位滞后时的重新同步 ,节点从总线的边沿跳变中,检测到它的时序比总线的时序相对滞后 2Tq,这时控制器在前一个位时序中的 PBS2 段减少 2Tq 的时间长度,获得同步。
BS1 和 PBS2 中增加或减少的这段时间长度被定义为“重新同步补偿宽度 SJW (reSynchronization Jump Width)”。一般来说 CAN 控制器会限定 SJW 的最大值,如限定了最大 SJW=3Tq 时,单次同步调整的时候不能增加或减少超过 3Tq 的时间长度,若有需要,控制器会通过多次小幅度调整来实现同步。当控制器设置的 SJW 极限值较大 时,可以吸收的误差加大 ,但通讯的速度会下降 。
CAN 的报文种类及结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K7ZCJk5v-1629516487177)(./image_15.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YOlPOS6q-1629516487178)(./image_16.png)]
• 帧起始
SOF 段 (Start Of Frame),译为帧起始,帧起始信号只有一个数据位,是一个显性电平,它用于通 知各个节点将有数据传输,其它节点通过帧起始信号的电平跳变沿来进行硬同步 。
• 仲裁段
当同时有两个报文被发送时,总线会根据仲裁段的内容决定哪个数据包能被传输,这也是它名称的由来。 仲裁段的内容主要为本数据帧的 ID 信息 (标识符),数据帧具有标准格式和扩展格式两种,区别 就在于 ID 信息的长度,标准格式的 ID 为 11 位,扩展格式的 ID 为 29 位,它在标准 ID 的基础上多出 18 位。在 CAN 协议中, ID 起着重要的作用,它决定着数据帧发送的优先级 ,也决定着其它 节点是否会接收这个数据帧。 CAN 协议不对挂载在它之上的节点分配优先级和地址,对总线的占有权是由信息的重要性决定的,即对于重要的信息,我们会给它打包上一个优先级高的 ID,使 它能够及时地发送出去。也正因为它这样的优先级分配原则,使得 CAN 的扩展性大大加强,在总线上增加或减少节点并不影响其它设备。报文的优先级,是通过对 ID 的仲裁来确定的 。
RTR 位 (Remote Transmission Request Bit)
译作远程传输请求位,它是用于区分数据帧和遥控 帧的 ,当它为显性电平时表示数据帧,隐性电平时表示遥控帧。
IDE 位 (Identifier Extension Bit)
译作标识符扩展位,它是用于区分标准格式与扩展格式 ,当它 为显性电平时表示标准格式,隐性电平时表示扩展格式。
SRR 位 (Substitute Remote Request Bit)
只存在于扩展格式,它用于替代标准格式中的 RTR 位。由于扩展帧中的 SRR 位为隐性位, RTR 在数据帧为显性位,所以在两个 ID 相同的标准格式报文 与扩展格式报文中,标准格式的优先级较高。
• 控制段
在控制段中的 r1 和 r0 为保留位,默认设置为显性位。它最主要的是 DLC 段 (Data Length Code),译为数据长度码,它由 4 个数据位组成,用于表示本报文中的数据段含有多少个字节, DLC 段表示的数字为 0~8。
• 数据段
数据段为数据帧的核心内容,它是节点要发送的原始信息,由 0~8 个字节组成,MSB 先行。
• CRC 段
为了保证报文的正确传输, CAN 的报文包含了一段 15 位的 CRC 校验码,一旦接收节点算出的 CRC 码跟接收到的 CRC 码不同,则它会向发送节点反馈出错信息,利用错误帧请求它重新发送。CRC 部分的计算一般由 CAN 控制器硬件完成,出错时的处理则由软件控制最大重发数。在 CRC 校验码之后,有一个 CRC 界定符,它为隐性位,主要作用是把 CRC 校验码与后面的 ACK 段间隔起来。
• ACK 段
ACK 段包括一个 ACK 槽位,和 ACK 界定符位。类似 I2C 总线,在 ACK 槽位中,发送节点发送 的是隐性位,而接收节点则在这一位中发送显性位以示应答。在 ACK 槽和帧结束之间由 ACK 界 定符间隔开。
• 帧结束
EOF 段 (End Of Frame),译为帧结束,帧结束段由发送节点发送的 7 个隐性位表示结束。
以上是些学习笔记,如有错误,望批评指正!
起初筛选器部分,在下也是似懂非懂,通过参考以下博主的文章,很快就理解,好文,敬佩!
CAN筛选器 详解
版权声明:本文为CSDN博主「小昭dedug」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_43746325/article/details/119837354
STM32F429 CAN通信
CAN 是控制器局域网络 (Controller Area Network) 的简称,它是由研发和生产汽车电子产品著称的德国 BOSCH 公司开发的,并最终成为国际标准(ISO11519),是国际上应用最广泛的现场总线之一。
1、全双工允许数据在两个方向上同时传输;
2、半双工允许数据在两个方向上传输,但是同一时间数据只能在一个方向上传输,实际上是切换的单工。
- 有人认为CAN是“全双工”,也有人看是“半双工”。其实我认为都对吧,大家都知道,CAN节点包括CAN控制器和CAN收发器。只是站的角度不同,站在CAN控制器看问题,很明显是全双工,CAN控制器发送接收信号就有点类似串口发送TTL信号(这里不严谨,只是举个例子),全双工通信;换个角度,站在收发器看,虽然是用了两根信号线,但是它们是一对差分线发送差模信号,用两根线代表一个信号(显性信号和隐形信号),在一个时刻只能表示一个信号 ,所以对通讯节点来说, CAN 通讯是半双工 的,收发数据需要分时进行。
目录
一、STM32中CAN的初始化代码实现
二、CAN的筛选器使用及理解
三、CAN的物理层
四、CAN的协议层
一、STM32中CAN的初始化代码实现
1、GPIO的引脚初始化
void CAN_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(CAN_TX_GPIO_CLK|CAN_RX_GPIO_CLK, ENABLE);
GPIO_PinAFConfig(CAN_TX_GPIO_PORT, CAN_RX_SOURCE, CAN_AF_PORT);
GPIO_PinAFConfig(CAN_RX_GPIO_PORT, CAN_TX_SOURCE, CAN_AF_PORT);
GPIO_InitStructure.GPIO_Pin = CAN_TX_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(CAN_TX_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = CAN_RX_PIN ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_Init(CAN_RX_GPIO_PORT, &GPIO_InitStructure);
}
2、CAN控制器的初始化配置
static void CAN_Mode_Config(void)
{
CAN_InitTypeDef CAN_InitStructure;
RCC_APB1PeriphClockCmd(CAN_CLK, ENABLE);
/*CAN1寄存器的初始化*/
CAN_DeInit(CAN1);
CAN_StructInit(&CAN_InitStructure);
/*CAN相关寄存器的配置*/
CAN_InitStructure.CAN_TTCM=DISABLE; //MCR-TTCM 关闭时间触发通信模式使能
CAN_InitStructure.CAN_ABOM=ENABLE; //MCR-ABOM 使能自动离线功能
CAN_InitStructure.CAN_AWUM=ENABLE; //MCR-AWUM 使能自动唤醒模式
CAN_InitStructure.CAN_NART=DISABLE; //MCR-NART 禁止报文自动重传,即使发送失败,不会再发
CAN_InitStructure.CAN_RFLM=DISABLE; //MCR-RFLM 接受FIFO不锁定,接受数据溢出时,新报文会覆盖原报文
CAN_InitStructure.CAN_TXFP=DISABLE; //MCR-TXFP FIFO优先级由报文的标识符决定,不是时间顺序
CAN_InitStructure.CAN_Mode = CAN_Mode_Normal; //正常模式
/*波特率配置,约定好双方收发一个数据位的速率*/
CAN_InitStructure.CAN_SJW=CAN_SJW_2tq; //BTR-SJW 重新同步跳跃宽度2个时间单元
/* ss=1 bs1=5 bs2=3 位时间宽度为(1+5+3) tq*(1+3+5) */
CAN_InitStructure.CAN_BS1=CAN_BS1_5tq; //BTR-TS1 时间段1 占用了5个时间单元 包括了PTS
CAN_InitStructure.CAN_BS2=CAN_BS2_3tq; //BTR-TS1 时间段 时间单元2 占用了5个时间单元
/* CAN Baudrate = 1 MBps (1MBps已是stm32中can的最高速速率) (CAN 的时钟总线 APB 1 = 45 MHz) */
CAN_InitStructure.CAN_Prescaler =5; //BTR-BRP 波特率分频器 波特率计算:45/(1+5+3)/5=1 Mbps
CAN_Init(CANx, &CAN_InitStructure);
}
3、CAN的筛选器配置
void CAN_Filter_Config(void)
{
CAN_FilterInitTypeDef CAN_FilterInitStructure;
/*CAN筛选器组0*/
CAN_FilterInitStructure.CAN_FilterNumber=0;
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask; //掩码模式
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //筛选器位宽 32位
/* 验证码配置 */
CAN_FilterInitStructure.CAN_FilterIdHigh= ((0x1314<<3)>>16)&0xffff//验证码32位中高16位
CAN_FilterInitStructure.CAN_FilterIdLow= ((0x1314<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF; //验证码32位中低16位
/*屏蔽码配置*/
CAN_FilterInitStructure.CAN_FilterMaskIdHigh= 0xFFFF; //屏蔽码高16位,
CAN_FilterInitStructure.CAN_FilterMaskIdLow= 0xFFFF; //屏蔽码低16位,只允许一个通过
CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0 ; //筛选器关联FIFO0
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; //使能筛选器
CAN_FilterInit(&CAN_FilterInitStructure);
/*使能筛选器 接收中断*/
CAN_ITConfig(CANx, CAN_IT_FMP0, ENABLE);
}
/*通信发送报文的设置*/
void CAN_SetMsg(CanTxMsg *TxMessage)
{
uint8_t ubCounter = 0;
TxMessage->ExtId=0x1314; //扩展id
TxMessage->IDE=CAN_ID_EXT; //扩展模式
TxMessage->RTR=CAN_RTR_DATA; //发送 数据 类型
TxMessage->DLC=8; //数据长度
/*数据载入*/
for (ubCounter = 0; ubCounter < 8; ubCounter++)
{
TxMessage->Data[ubCounter] = ubCounter;
}
}
4、中断服务程序
void CAN_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = CAN_RX_IRQ; //CAN RX中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void CAN1_RX0_IRQHandler(void)
{
/*从邮箱接收到报文*/
CAN_Receive(CANx, CAN_FIFO0, &RxMessage);
/* 验证id==0x1314 是否是扩展id 数据位是否8位*/
if((RxMessage.ExtId==0x1314) && (RxMessage.IDE==CAN_ID_EXT) && (RxMessage.DLC==8) )
{
flag = 1;
}
else
{
flag = 0;
}
}
二、CAN的筛选器使用及理解
1、CAN筛选器的作用
CAN总线中,往往会有很多收发器进行发送多条数据,但是这些数据中只有一部分数据是你想要的,CPU却要处理很多无关紧要的数据,这无疑是给运行的效率带来的极大负担。所以筛选器起到了很大的作用,它可以为CPU过滤掉不要的数据,筛选出想要的数据,并把得到的数据存入到接收邮箱中。stm32通过识别过滤报文中的id,过滤方法有两种,列表模式和掩码模式。
每组筛选器包含 2 个 32 位的寄存器,分别为 CAN_FxR1 和 CAN_FxR2,它们用来存储要筛选的ID 或掩码。由可将一个32位寄存器分为两个16位寄存器,每组筛选器包含了4个16位寄存器。
//不管是32位还是16位宽的筛选器
/*CAN_FxR1[31:16]*/
CAN_FilterInitStructure.CAN_FilterIdHigh
/*CAN_FxR1[15:0]*/
CAN_FilterInitStructure.CAN_FilterIdLow
/*CAN_FxR2[31:16]*/
CAN_FilterInitStructure.CAN_FilterMaskIdHigh
/*CAN_FxR2[15:0]*/
CAN_FilterInitStructure.CAN_FilterMaskIdLow
2、列表模式
筛选器会比较列表中固定的id,要求报文 ID 与列表中的某一个标识符完全相同才可以接收。这种筛选方式有个很大的缺陷,stm32资源很有限,如果选择32位宽的筛选器,只能筛选两个报文id,如果是16位,可以筛选4个报文id。
- 32位宽的筛选器 列表模式
void CAN_Filter_Config(void)
{
uint32_t StdId[2] = {0x1234,0x1314};
uint32_t ExdId = 0x80908890;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
/*CAN筛选器组0*/
CAN_FilterInitStructure.CAN_FilterNumber=0;
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdList; //列表模式
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //筛选器位宽 32位
/* 验证码配置 */
/*32筛选器寄存器 包括 11位标准id 18位扩展id 1位IDE 1位RTR 1位0*/
/*报文的标准id是11位,需要向左移5位传入16位的寄存器 STID[10:0]*/
CAN_FilterInitStructure.CAN_FilterIdHigh=StdId[0]<<5;//验证码32位中高16位
CAN_FilterInitStructure.CAN_FilterIdLow= 0|CAN_ID_STD|CAN_RTR_DATA; //验证码32位中低16位
/*32筛选器寄存器 包括 11位标准id 18位扩展id 1位IDE 1位RTR 1位0*/
/*扩展id左移3位,腾出IDE、RTR和0,再向右移得出高16位*/
CAN_FilterInitStructure.CAN_FilterMaskIdHigh= (ExdId<<3)>>16&0xFFFF;//屏蔽码高16位,
/*扩展id左移3位,腾出IDE、RTR和0,标记扩展和数据类型*/
CAN_FilterInitStructure.CAN_FilterMaskIdLow= (ExdId<<3)&0xFFFF|CAN_ID_EXT|CAN_RTR_DATA; //屏蔽码低16位,只允许一个通过
CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0 ; //筛选器关联FIFO0
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; //使能筛选器
CAN_FilterInit(&CAN_FilterInitStructure);
}
- 16位宽的筛选器 列表模式
由参考手册可知,配置16宽的筛选器,CAN_FilterIdHigh、CAN_FilterIdLow、CAN_FilterMaskIdHigh、CAN_FilterMaskIdLow这四个变量可以存放4个标准id,不用存放扩展id,扩展只能用32位的筛选器。
void CAN_Filter_Config(void)
{
uint32_t StdId[2] = {0x1234,0x1314,0x1212,0x1313};
CAN_FilterInitTypeDef CAN_FilterInitStructure;
/*CAN筛选器组0*/
CAN_FilterInitStructure.CAN_FilterNumber=0;
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdList; //列表模式
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //筛选器位宽 32位
/* 验证码配置 */
/*32筛选器寄存器 包括 11位标准id 18位扩展id 1位IDE 1位RTR 1位0*/
/*报文的标准id是11位,需要向左移5位传入16位的寄存器 STID[10:0]*/
CAN_FilterInitStructure.CAN_FilterIdHigh=StdId[0]<<5;//验证码32位中高16位
CAN_FilterInitStructure.CAN_FilterIdLow= StdId[1]<<5; //验证码32位中低16位
/*32筛选器寄存器 包括 11位标准id 18位扩展id 1位IDE 1位RTR 1位0*/
/*扩展id左移3位,腾出IDE、RTR和0,再向右移得出高16位*/
CAN_FilterInitStructure.CAN_FilterMaskIdHigh= StdId[2]<<5;//屏蔽码高16位,
/*扩展id左移3位,腾出IDE、RTR和0,标记扩展和数据类型*/
CAN_FilterInitStructure.CAN_FilterMaskIdLow= StdId[3]<<5;//屏蔽码低16位,只允许一个通过
CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0 ; //筛选器关联FIFO0
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;//使能筛选器
CAN_FilterInit(&CAN_FilterInitStructure);
}
3、掩码模式
为了解决列表模式下的,列表的容量资源有限的问题。出现了第二种方案,可以替代列表模式。相比列表模式,掩码模式可以筛选出更多的报文id。筛选过程中,需要经过屏蔽和对比两个操作(屏蔽码和验证码,验证码不唯一,看需求)。
为了能更好的理解,举个简单的例子,比如要筛查id出生年份为2000年的青年人(44200078):
0x04 0x04 0x02 0x00 0x00 0x00 0x07 0x08 -->经过屏蔽码(只需匹配第2个~第5个字节,进行相与操作)
0x00 0x00 0xff 0xff 0xff 0xff 0x00 0x00 -->得到
0x00 0x00 0x02 0x00 0x00 0x00 0x00 0x00 -->对比验证码
0x00 0x00 0x02 0x00 0x00 0x00 0x00 0x00
如果是筛查00后的年轻人(只需匹配第2个~第4个字节):
将屏蔽码改为0x00 0x00 0xff 0xff 0xff 0x00 0x00 0x00
可以识别通过的数值2000~2009,可以看出只要通过数值的多少,关键看屏蔽码。设得宽,则可以通过的多(所有位为0,则不过任何过滤操作,则谁都可以通过),设得窄,则通过的少(所有位设为1,则只有一个能通过)。
- 32位宽的筛选器 掩码模式
void CANFilterConfig_Scale32_IdMask_StandardIdOnly(void)
{
CAN_FilterConfTypeDef sFilterConfig;
uint16_t StdIdArray[10] ={0x7e0,0x7e1,0x7e2,0x7e3,0x7e4,
0x7e5,0x7e6,0x7e7,0x7e8,0x7e9}; //定义一组标准CAN ID
uint16_t mask,num,tmp,i;
sFilterConfig.FilterNumber = 2; //使用过滤器2
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; //配置为掩码模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; //设置为32位宽
sFilterConfig.FilterIdHigh =(StdIdArray[0]<<5); //验证码可以设置为StdIdArray[]数组中任意一个,这里使用StdIdArray[0]作为验证码
sFilterConfig.FilterIdLow =0;
mask =0x7ff; //下面开始计算屏蔽码
num =sizeof(StdIdArray)/sizeof(StdIdArray[0]);
for(i =0; i<num; i++) //屏蔽码位StdIdArray[]数组中所有成员的同或结果
{
tmp =StdIdArray[i] ^ (~StdIdArray[0]); //所有数组成员与第0个成员进行同或操作
mask &=tmp;
}
sFilterConfig.FilterMaskIdHigh =(mask<<5);
sFilterConfig.FilterMaskIdLow =0|0x02; //只接收数据帧
sFilterConfig.FilterFIFOAssignment = 0; //设置通过的数据帧进入到FIFO0中
sFilterConfig.FilterActivation = ENABLE;
sFilterConfig.BankNumber = 14;
}
void CANFilterConfig_Scale32_IdMask_ExtendIdOnly(void)
{
CAN_FilterConfTypeDef sFilterConfig;
//定义一组扩展CAN ID用来测试
uint32_t ExtIdArray[10] ={0x1839f101,0x1835f102,0x1835f113,0x1835f124,0x1835f105,
0x1835f106,0x1835f107,0x1835f108,0x1835f109,0x1835f10A};
uint32_t mask,num,tmp,i;
sFilterConfig.FilterNumber = 3; //使用过滤器3
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; //配置为掩码模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; //设为32位宽
sFilterConfig.FilterIdHigh =((ExtIdArray[0]<<3) >>16) &0xffff;//数组任意一个成员都可以作为验证码
sFilterConfig.FilterIdLow =((ExtIdArray[0]<<3)&0xffff) | CAN_ID_EXT;
mask =0x1fffffff;
num =sizeof(ExtIdArray)/sizeof(ExtIdArray[0]);
for(i =0; i<num; i++) //屏蔽码位数组各成员相互同或的结果
{
tmp =ExtIdArray[i] ^ (~ExtIdArray[0]); //都与第一个数据成员进行同或操作
mask &=tmp;
}
mask <<=3; //对齐寄存器
sFilterConfig.FilterMaskIdHigh = (mask>>16)&0xffff;
sFilterConfig.FilterMaskIdLow = (mask&0xffff)|0x02; //只接收数据帧
sFilterConfig.FilterFIFOAssignment = 0;
sFilterConfig.FilterActivation = ENABLE;
sFilterConfig.BankNumber = 14;
}
void CANFilterConfig_Scale32_IdMask_StandardId_ExtendId_Mix(void)
{
CAN_FilterConfTypeDef sFilterConfig;
//定义一组标准CAN ID
uint32_t StdIdArray[10] ={0x711,0x712,0x713,0x714,0x715,
0x716,0x717,0x718,0x719,0x71a};
//定义另外一组扩展CAN ID
uint32_t ExtIdArray[10] ={0x1900fAB1,0x1900fAB2,0x1900fAB3,0x1900fAB4,0x1900fAB5,
0x1900fAB6,0x1900fAB7,0x1900fAB8,0x1900fAB9,0x1900fABA};
uint32_t mask,num,tmp,i,standard_mask,extend_mask,mix_mask;
sFilterConfig.FilterNumber = 4; //使用过滤器4
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; //配置为掩码模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; //设为32位宽
sFilterConfig.FilterIdHigh =((ExtIdArray[0]<<3) >>16) &0xffff; //使用第一个扩展CAN ID作为验证码
sFilterConfig.FilterIdLow =((ExtIdArray[0]<<3)&0xffff);
standard_mask =0x7ff; //下面是计算屏蔽码
num =sizeof(StdIdArray)/sizeof(StdIdArray[0]);
for(i =0; i<num; i++) //首先计算出所有标准CAN ID的屏蔽码
{
tmp =StdIdArray[i] ^ (~StdIdArray[0]);
standard_mask &=tmp;
}
extend_mask =0x1fffffff;
num =sizeof(ExtIdArray)/sizeof(ExtIdArray[0]);
for(i =0; i<num; i++) //接着计算出所有扩展CAN ID的屏蔽码
{
tmp =ExtIdArray[i] ^ (~ExtIdArray[0]);
extend_mask &=tmp;
}
mix_mask =(StdIdArray[0]<<18)^ (~ExtIdArray[0]); //再计算标准CAN ID与扩展CAN ID混合的屏蔽码
mask =(standard_mask<<18)& extend_mask &mix_mask; //最后计算最终的屏蔽码
mask <<=3; //对齐寄存器
sFilterConfig.FilterMaskIdHigh = (mask>>16)&0xffff;
sFilterConfig.FilterMaskIdLow = (mask&0xffff);
sFilterConfig.FilterFIFOAssignment = 0;
sFilterConfig.FilterActivation = ENABLE;
sFilterConfig.BankNumber = 14;
}
- 16位宽的筛选器 掩码模式
void CANFilterConfig_Scale16_IdMask(void)
{
CAN_FilterConfTypeDef sFilterConfig;
uint16_t StdIdArray1[10] ={0x7D1,0x7D2,0x7D3,0x7D4,0x7D5, //定义第一组标准CAN ID
0x7D6,0x7D7,0x7D8,0x7D9,0x7DA};
uint16_t StdIdArray2[10] ={0x751,0x752,0x753,0x754,0x755, //定义第二组标准CAN ID
0x756,0x757,0x758,0x759,0x75A};
uint16_t mask,tmp,i,num;
sFilterConfig.FilterNumber = 5; //使用过滤器5
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; //配置为掩码模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_16BIT; //设为16位宽
//配置第一个过滤对
sFilterConfig.FilterIdLow =StdIdArray1[0]<<5; //设置第一个验证码
mask =0x7ff;
num =sizeof(StdIdArray1)/sizeof(StdIdArray1[0]);
for(i =0; i<num; i++) //计算第一个屏蔽码
{
tmp =StdIdArray1[i] ^ (~StdIdArray1[0]);
mask &=tmp;
}
sFilterConfig.FilterMaskIdLow =(mask<<5)|0x10; //只接收数据帧
//配置第二个过滤对
sFilterConfig.FilterIdHigh = StdIdArray2[0]<<5; //设置第二个验证码
mask =0x7ff;
num =sizeof(StdIdArray2)/sizeof(StdIdArray2[0]);
for(i =0; i<num; i++) //计算第二个屏蔽码
{
tmp =StdIdArray2[i] ^ (~StdIdArray2[0]);
mask &=tmp;
}
sFilterConfig.FilterMaskIdHigh = (mask<<5)|0x10; //只接收数据帧
sFilterConfig.FilterFIFOAssignment = 0; //通过的CAN 消息放入到FIFO0中
sFilterConfig.FilterActivation = ENABLE;
sFilterConfig.BankNumber = 14;
}
三、CAN的物理层
与SPI、I2C等具有时钟信号的同步通讯方式不同,CAN通信并不是以时钟信号进行同步,CAN是一种 异步 通讯。只具有CAN_High和CAN_Low两条信号线 ,共同构成 一对差分信号线 的形式进行通讯。
CAN的物理层主要有两种。
闭环总线网络
闭环总线通讯网络 中的 CAN 通讯网络是一种遵循ISO11898 标准的高速、短距离“闭环网络”,它的总线最大长度为** 40m** ,通信速度最高为 1Mbps,总线的两端各要求有一个“120 欧”的电阻(并联) .
开环总线网络
开环总线通讯网络 中的是遵循 ISO11519-2 标准的低速、远距离“开环网络”,它的最大传输距离为** 1km** ,最高通讯速率为 125kbps,两根总线是独立的、不形成闭环,要求每根总线上各串联有一个“2.2 千欧”的电阻 。
通讯节点
CAN总线上可以挂载多个通讯节点,CAN通讯协议不对节点进行地址编码 ,而是对数据内容进行编码。CAN通讯节点由一个控制器及CAN收发器组成。当 CAN 节点需要发送数据时,控制器把要发送的二进制编码通过 CAN_Tx 线发送到收发器(类似TTL信号),然后由收发器把这个普通的逻辑电平信号转化成差分信号,通过差分线 CAN_High 和 CAN_Low 线输出到 CAN 总线网络。
差分信号
差分信号又称差模信号,这两个信号线的振幅相等,相位相反,通过两根信号线的电压差值来表示逻辑0和逻辑1.
-
相对单信号线传输方式,具有的优点:抗干扰能力,有效抑制它对外部的电磁干扰。时许定位准确。
-
由于差分信号线具有这些优点,所以在 USB 协议、 485 协议、以太网协议及 CAN 协议的物理层中,都使用了差分信号传输。
CAN协议中的差分信号
-
当逻辑"0"(显性电平),CAN_High和CAN_Low的压差2.5v;当逻辑"1"(隐性电平),CAN_High和CAN_Low的压差0v。
-
在CAN总线中,在同一时间,只要由输出显性电平,总线处于显性电平状态。类似I2c总线的“线与”特性。
-
由于 CAN 总线协议的物理层只有 1 对差分线,在一个时刻只能表示一个信号 ,所以对通讯节点来说, CAN 通讯是半双工 的,收发数据需要分时进行。在 CAN 的通讯网络中,因为共用总线,在整个网络中同一时刻只能有一个通讯节点发送信号,其余的节点在该时刻都只能接收。
四、CAN的协议层
CAN 的波特率及位同步
CAN 属于异步通讯,节点间使用 约定好的波特率 进行通讯,特别地, CAN 还会使用“位同步”的方式来抗 干扰、吸收误差,实现对总线电平信号进行正确的采样,确保通讯正常。
位时序分解
为了实现位同步,CAN 协议把每一个数据位的时序分解成SS 段、PTS 段、 PBS1 段、 PBS2 段,这四段的长度加起来即为一个 CAN 数据位的长度。分解后最小的时间单位是 Tq,而一个完整的位由 8~25 个 Tq 组成.
该图中表示的 CAN 通讯信号每一个数据位的长度为 19Tq,其中 SS 段占 1Tq, PTS 段占 6Tq, PBS1 段占 5Tq, PBS2 段占 7Tq。信号的采样点位于 PBS1 段与 PBS2 段之间,通过控制各段的长度 ,可 以对采样点的位置进行偏移,以便准确地采样。
• SS 段 (SYNC SEG)
若通讯节点检测到总线上信号的跳变沿被包含在 SS 段的范围之内,则表示节点与总线的时序是同步的,当节点与总线同步时,采样点采集到的总线电平即可被确定为该位的电平。 SS 段的大小固定为 1Tq 。
• PTS 段 (PROP SEG)
PTS 译为传播时间段,这个时间段是用于补偿网络的物理延时时间。是总线上输入比较器延时和输出驱动器延时总和的两倍。PTS 段的大小可以为 1~8Tq。
• PBS1 段 (PHASE SEG1)
PBS1 译为 相位缓冲段 ,主要用来补偿边沿阶段的误差,它的时间长度在重新同步的 时候可以加长。 PBS1 段的初始大小可以为1~8Tq。
• PBS2 段 (PHASE SEG2)
PBS2 这是另一个相位缓冲段,也是用来补偿边沿阶段误差的,它的时间长度在重新同步时可以缩短。 PBS2 段的初始大小可以为 2~8Tq。
通讯的波特率
总线上的各个通讯节点只要约定好 1 个 Tq 的时间长度以及每一个数据位占据多少个 Tq,就可以 确定 CAN 通讯的波特率。
例如,假设上图中的 1Tq=1us,而每个数据位由 19 个 Tq 组成,则传输一位数据需要时间 T1bit =19us,从而每秒可以传输的数据位个数为:
1x106/19 = 52631.6 (bps)
这个每秒可传输的数据位的个数即为通讯中的波特率。
同步过程分析
波特率只是约定了每个数据位的长度 ,数据同步还涉及到相位的细节,这个时候就需要用到数据 位内的 SS、 PTS、 PBS1 及 PBS2 段了。
CAN的数据同步分为硬同步和重新同步 。其中硬同步只是当存在“帧起始信号”时起作用,无法确保后续一连串的位时序都是同步的,而重新同步方式可解决该问题。
硬同步
若某个 CAN 节点通过总线发送数据时,它会发送一个表示通讯起始的信号 ,该信号是一个由高变低的下降沿 。而挂载到 CAN 总线上的通讯节点在不发送数据 时,会时刻检测总线上的信号。
节点以硬同步的方式调整,把自己的位时序中的 SS 段平移至总线出现下降沿的部分,获得同步,同步后采样点就可以采集得正确数据了。
重新同步
如果在一帧很长的数据内,节点信号与总线信号相位有偏移时,这种同步方式就无能为力了。重新同步的方式分为超前和滞后两种情况。
第一种
相位超前的情况如图相位超前时的重新同步 ,节点从总线的边沿跳变中,检测到它内部的时序比总线的时序相对超前 2Tq,这时控制器在下一个位时序中的 PBS1 段增加 2Tq 的时间长度,使得节点与总线时序重新同步。
第二种相位滞后的情况如图相位滞后时的重新同步 ,节点从总线的边沿跳变中,检测到它的时序比总线的时序相对滞后 2Tq,这时控制器在前一个位时序中的 PBS2 段减少 2Tq 的时间长度,获得同步。
BS1 和 PBS2 中增加或减少的这段时间长度被定义为“重新同步补偿宽度 SJW (reSynchronization Jump Width)”。一般来说 CAN 控制器会限定 SJW 的最大值,如限定了最大 SJW=3Tq 时,单次同步调整的时候不能增加或减少超过 3Tq 的时间长度,若有需要,控制器会通过多次小幅度调整来实现同步。当控制器设置的 SJW 极限值较大 时,可以吸收的误差加大 ,但通讯的速度会下降 。
CAN 的报文种类及结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K7ZCJk5v-1629516487177)(./image_15.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YOlPOS6q-1629516487178)(./image_16.png)]
• 帧起始
SOF 段 (Start Of Frame),译为帧起始,帧起始信号只有一个数据位,是一个显性电平,它用于通 知各个节点将有数据传输,其它节点通过帧起始信号的电平跳变沿来进行硬同步 。
• 仲裁段
当同时有两个报文被发送时,总线会根据仲裁段的内容决定哪个数据包能被传输,这也是它名称的由来。 仲裁段的内容主要为本数据帧的 ID 信息 (标识符),数据帧具有标准格式和扩展格式两种,区别 就在于 ID 信息的长度,标准格式的 ID 为 11 位,扩展格式的 ID 为 29 位,它在标准 ID 的基础上多出 18 位。在 CAN 协议中, ID 起着重要的作用,它决定着数据帧发送的优先级 ,也决定着其它 节点是否会接收这个数据帧。 CAN 协议不对挂载在它之上的节点分配优先级和地址,对总线的占有权是由信息的重要性决定的,即对于重要的信息,我们会给它打包上一个优先级高的 ID,使 它能够及时地发送出去。也正因为它这样的优先级分配原则,使得 CAN 的扩展性大大加强,在总线上增加或减少节点并不影响其它设备。报文的优先级,是通过对 ID 的仲裁来确定的 。
RTR 位 (Remote Transmission Request Bit)
译作远程传输请求位,它是用于区分数据帧和遥控 帧的 ,当它为显性电平时表示数据帧,隐性电平时表示遥控帧。
IDE 位 (Identifier Extension Bit)
译作标识符扩展位,它是用于区分标准格式与扩展格式 ,当它 为显性电平时表示标准格式,隐性电平时表示扩展格式。
SRR 位 (Substitute Remote Request Bit)
只存在于扩展格式,它用于替代标准格式中的 RTR 位。由于扩展帧中的 SRR 位为隐性位, RTR 在数据帧为显性位,所以在两个 ID 相同的标准格式报文 与扩展格式报文中,标准格式的优先级较高。
• 控制段
在控制段中的 r1 和 r0 为保留位,默认设置为显性位。它最主要的是 DLC 段 (Data Length Code),译为数据长度码,它由 4 个数据位组成,用于表示本报文中的数据段含有多少个字节, DLC 段表示的数字为 0~8。
• 数据段
数据段为数据帧的核心内容,它是节点要发送的原始信息,由 0~8 个字节组成,MSB 先行。
• CRC 段
为了保证报文的正确传输, CAN 的报文包含了一段 15 位的 CRC 校验码,一旦接收节点算出的 CRC 码跟接收到的 CRC 码不同,则它会向发送节点反馈出错信息,利用错误帧请求它重新发送。CRC 部分的计算一般由 CAN 控制器硬件完成,出错时的处理则由软件控制最大重发数。在 CRC 校验码之后,有一个 CRC 界定符,它为隐性位,主要作用是把 CRC 校验码与后面的 ACK 段间隔起来。
• ACK 段
ACK 段包括一个 ACK 槽位,和 ACK 界定符位。类似 I2C 总线,在 ACK 槽位中,发送节点发送 的是隐性位,而接收节点则在这一位中发送显性位以示应答。在 ACK 槽和帧结束之间由 ACK 界 定符间隔开。
• 帧结束
EOF 段 (End Of Frame),译为帧结束,帧结束段由发送节点发送的 7 个隐性位表示结束。
以上是些学习笔记,如有错误,望批评指正!
起初筛选器部分,在下也是似懂非懂,通过参考以下博主的文章,很快就理解,好文,敬佩!
CAN筛选器 详解
版权声明:本文为CSDN博主「小昭dedug」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_43746325/article/details/119837354
暂无评论