入坑嵌入式STM32之CAN通信-协议层

上一篇介绍了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,供参考。

以上仅个人理解,不足之处多多指教




原文链接:,转发请注明来源!