上一篇介绍了CAN通信的物理层,包括数据的传输方式,硬件的组成等,接着说下CAN的协议层。CAN有五种类型的帧,本篇我们主要说下数据帧,数据帧是我们比较常用的一种,我们一定要掌握住它的结构配置。
CAN数据帧分为:标准数据帧和扩展数据帧,区别主要在于 ID 信息的长度,标准格式的 ID 为 11 位,扩展格式的 ID 为 29 位,在标准 ID 的基础上多出 了18 位。 这个大家一定要搞清楚,特别注意后面在配置筛选器时。如下图红色方框中,定义了该帧是扩展数据帧,
CAN的波特率配置
波特率可以理解为每秒传输多少个bit;
CAN的外设是挂载在APB1总线上的,而位时序寄存器 CAN_BTR 中的 BRP[9:0] 寄存器位可以设置CAN 外设时钟的分频值,所以:
Tq = (BRP[9:0]+1) x Tpclk (这里一定要注意加1)
其中的 PCLK 指 APB1 时钟,默认值为 45MHz
例如表一种配置波特率为 1Mbps 的方式 说明了一种把波特率配置为 1Mbps 的方式
图中的4个时间段了解即可。这个波特率计算一定要掌握住
CAN模式:有4种模式,包括:正常模式、静默模式、回环模式、回环静默模式。我们可以用回环模式检验自己写的程序,重点掌握正常模式。
CAN有3个发送邮箱,每个邮箱可以发送8字节,最多可以缓存 3 个待发送的报文 。有 2 个接收 FIFO,每个 FIFO 中有 3 个邮箱,即最多可以缓存 6 个接收到的报文。
CAN验收筛选器,因为有验收筛选器,所以CAN总线上可以挂载多个设备。我们务必掌握住筛选器配置。共有 28 个筛选器组(基础型的有14个),每个筛选器组有 2 个寄存器 ,CAN1 和 CAN2 共用的筛选器。注意筛选器配置的位宽(16位或32位),配置的模式(掩码模式或列表模式),所以可以配置4种模式。
理解:列表模式,它把要接收报文的 ID 列成一个表,要求报文 ID 与列表中的某一个标识符
完全相同才可以接收,可以理解为白名单管理 。
掩码模式,它把可接收报文 ID 的某几位作为列表,这几位被称为掩码,可以把它理解 成关键字搜索,只要掩码 (关键字) 相同,就符合要求,报文就会被保存到接收 FIFO。
配置筛选器时注意标准格式与扩展格式区别,要移位,后面细讲
static void CAN_GPIO_Init(void )
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_11;//CAN_RX
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;
// GPIO_InitStruct.GPIO_Speed=
GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_12;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
// LED引脚配置
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_14;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_ResetBits(GPIOB,GPIO_Pin_14);
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_15;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_ResetBits(GPIOB,GPIO_Pin_15);
}
static void CAN_Config_Init(void)
{
CAN_InitTypeDef CAN_InitStruct;
CAN_FilterInitTypeDef C;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1,ENABLE);
CAN_DeInit(CAN1);
CAN_StructInit(&CAN_InitStruct);
CAN_InitStruct.CAN_ABOM =DISABLE;
CAN_InitStruct.CAN_AWUM =DISABLE;
CAN_InitStruct.CAN_NART =DISABLE;
CAN_InitStruct.CAN_RFLM =DISABLE;
CAN_InitStruct.CAN_TTCM =DISABLE;
CAN_InitStruct.CAN_TXFP =DISABLE;
CAN_InitStruct.CAN_Mode =CAN_Mode_LoopBack; //回环模式自发自收,测试CAN
CAN_InitStruct.CAN_SJW=CAN_SJW_1tq;
CAN_InitStruct.CAN_BS1=CAN_BS1_2tq;
CAN_InitStruct.CAN_BS2=CAN_BS2_3tq;
CAN_InitStruct.CAN_Prescaler=60; //波特率配置
CAN_Init(CAN1, &CAN_InitStruct);
C.CAN_FilterActivation=ENABLE;
C.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;
C.CAN_FilterIdHigh=0x0000;
C.CAN_FilterIdLow=0x0004;
C.CAN_FilterMaskIdHigh=0x0000;
C.CAN_FilterMaskIdLow=0x0000;
C.CAN_FilterMode=CAN_FilterMode_IdMask;
C.CAN_FilterNumber=0;
C.CAN_FilterScale=CAN_FilterScale_32bit;
CAN_FilterInit(&C);
}
void CAN1_Init(void)
{
CAN_GPIO_Init();
CAN_Config_Init();
}
/**发送函数**/
void CAN_Tx(uint8_t *buffer)
{
CanTxMsg TxMessage;
TxMessage.StdId=0x00;
TxMessage.ExtId=0x12;
TxMessage.IDE=CAN_Id_Extended;
TxMessage.RTR=CAN_RTR_Data;
TxMessage.Data[0]=buffer[0];
// buffer+=1;
TxMessage.Data[1]=buffer[0+1];
TxMessage.DLC=2;
CAN_Transmit(CAN1,&TxMessage);
}
/**接收函数**/
u8 CAN_Rx(void)
{
CanRxMsg RxMessage;
RxMessage.DLC=0;
RxMessage.StdId=0x0000;
RxMessage.ExtId=0x0000;
RxMessage.IDE=0;
RxMessage.RTR=0;
RxMessage.FMI=0;
RxMessage.Data[0]=0;
RxMessage.Data[1]=0;
if( CAN_MessagePending(CAN1,CAN_FIFO0)==0) return 0;
CAN_Receive(CAN1, CAN_FIFO0,&RxMessage);
if(RxMessage.Data[0]==0xaa&RxMessage.Data[1]==0xab)
{
return 1;
}
}上面是自己写的demo,供参考。
以上仅个人理解,不足之处多多指教
