STM32 CAN外设(STM32F103C8T6)

       一、基本介绍

         STM32内置bxCAN外设(CAN控制器),支持CAN2.0A和2.0B,可以自动发送CAN报文和按照过滤器自动接收指定CAN报文,程序只需处理报文数据而无需关注总线的电平细节
        波特率最高可达1兆位/秒3个可配置优先级的发送邮箱2个3级深度的接收FIFO
14个过滤器组(互联型28个)时间触发通信、自动离线恢复、自动唤醒、禁止自动重传、接收FIFO溢出处理方式可配置、发送优先级可配置、双CAN模式。

        CAN通信的两个引脚是复用在了PA11和PA12上,只能使用这两个作为CAN通信引脚。(该芯片的USB和CAN无法同时使用的)

        下图为CAN收发器电路原理图以及实物图

        CAN报文流程

        CANTX是输出。引脚控制权在CAN外设,配置为复用推挽输出模式,输入引脚可以配置为上拉输入,当控制器接收到报文后会自动和配置的过滤器进行比对,符合条件的报文会自动保存在FIFO队列中,CPU直接读取FIFO即可。

        发送基本流程

        其中RQCP(Request completed)请求完成,TXOK(Transmission OK)发送成功,TME(Transmit mailbox empty是发送邮箱空,TME=1说明当前邮箱是空闲状态的。

接受基本流程

        二、标识符过滤器

        过滤器由两个32位寄存器组成,R1[31:0]和R2[31:0]

        其中,FSCx:位宽设置置0,16位;置1,32位

        FBMx:模式设置置0,屏蔽模式;置1,列表模式

        FFAx:关联设置置0,FIFO0;置1,FIFO 1

        FACTx:激活设置置0,禁用,置1,启用

其中,x表示的是过滤器x

        FSCx和FBMx两种设置组合起来可以设置四种工作模式,分别是00 16位屏蔽模式,01 16位列表模式,10 32位屏蔽模式,11 32位列表模式。

这里以32位列表模式进行说明:

        其中这个32位寄存器的高11位,需要存入的是标准格格式的ID号,STID(Standard ID)。跟着的后18位需要存入的是扩展格式的ID号,EXID(Extended ID),写入的为标准ID号的话则扩展部分的ID全部为0,标准ID的IDE写0,反之为1。RTR为遥控帧标志位,如果需要过滤数据帧,RTR写0,过滤遥控帧RTR写1。

        该模式下R1和R2可以各写入一个ID号,即最多写入两个ID号。

接下来看一下16位列表模式:

              同理,高11位写入的为标准ID,后面跟的RTR(同上),IDE写0。

屏蔽模式

        屏蔽模式下的R1我们需要写入的是ID号,R2写入的为屏蔽位(Mask掩码),即,只有屏蔽位对应位给1,对应ID位才能通过,屏蔽位给0的01均可通过(相与得到的ID),屏蔽位IDE给1,即匹配数据帧(R1的RTR),如果屏蔽位IDE给0,即遥控帧和数据帧都无所谓。

        配置方法:过滤ID为一组的使用屏蔽模式,无规律使用列表模式。

三、测试模式(主要用于调试)

静默模式:用于分析CAN总线的活动,不会对总线造成影响

环回模式:用于自测试,同时发送的报文可以在CAN_TX引脚上检测到

环回静默模式:用于热自测试,自测的同时不会影响CAN总线

四、工作模式

初始化模式:用于配置CAN外设,禁止报文的接收和发送

正常模式:配置CAN外设后进入正常模式, 以便正常接收和发送报文

睡眠模式:低功耗,CAN外设时钟停止,可使用软件唤醒或者硬件自动唤醒

AWUM:置1,自动唤醒,一旦检测到CAN总线活动,硬件就自动清零SLEEP,唤醒CAN外设;置0,手动唤醒,软件清零SLEEP,唤醒CAN外设

下图为工作模式示意,SLAK为睡眠确认状态位,=1表示进行睡眠模式,INAK为初始化确认模式,=0即为硬件目前没有确认进入初始化模式,另外反之。模式之间的相互转换依靠置位来进行请求转换,一般不会立即回应,在等待总线空闲后才会进行反应。

波特率=

APB1时钟频率/分频系数/一位的Tq数量=

36MHz/(BRP[9:01+1)/(1 +(TS1[3:0]+1)+(TS2[2:0]+1))

在C8T6中,CAN外设挂载在APB1总线上,PCLK1时钟默认36MHz

五、中断处理

CAN外设占用4个专用的中断向量
发送中断:发送邮箱空时产生
FIFO0中断:收到一个报文/FIFO 0满/FIFO 0溢出时产生
FIFO1中断:收到一个报文/FIFO 1满/FIFO 1溢出时产生
状态改变错误中断:出错/唤醒/进入睡眠时产生

六、代码编写

CAN基本初始化

void MyCAN_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//can挂载在APB1总线上的
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//CAN_TX引脚初始化,复用推挽输出模式,控制权交给can外设
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入,初始化接收模式
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	CAN_InitTypeDef CAN_InitStructure;//can结构体初始化
	CAN_InitStructure.CAN_Prescaler = 48;
	CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;//选择测试模式(正常模式)
	CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;//预分频设置 波特率=36M/48/(1+2+3)=125k  不用手动减1
	CAN_InitStructure.CAN_BS1 = CAN_BS1_2tq;
	CAN_InitStructure.CAN_BS2 = CAN_BS2_3tq;
	CAN_InitStructure.CAN_TTCM = DISABLE;//时间触发通信
	CAN_InitStructure.CAN_ABOM = DISABLE;//离线自动恢复
	CAN_InitStructure.CAN_AWUM = DISABLE;
	CAN_InitStructure.CAN_NART = DISABLE;//使能失能不自动重传模式
	CAN_InitStructure.CAN_RFLM = DISABLE;//FIFO锁定
	CAN_InitStructure.CAN_TXFP = DISABLE;//发送邮箱优先级(ENABLE先请求先发送)
	CAN_Init(CAN1, &CAN_InitStructure);
	
	CAN_FilterInitTypeDef CAN_FilterInitStructure;//过滤器的初始化配置
	CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;//这里是全通模式,16位列表模式(直接写同样的即可)
	CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
	CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;
	CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
	CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0;//配置过滤器关联
	CAN_FilterInitStructure.CAN_FilterNumber = 0;//选择初始化过滤器
	CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;//选择过滤器模式
	CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;//选择过滤器位宽
	CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;//过滤器激活
	CAN_FilterInit(&CAN_FilterInitStructure);
}

发送报文

void MyCAN_Transmit(uint32_t ID, uint8_t Length, uint8_t *Data)
{
	CanTxMsg TxMessage;
	TxMessage.StdId = ID;//标准ID
	TxMessage.ExtId = ID;//拓展ID
	TxMessage.IDE = CAN_Id_Standard;//决定是否有效
	TxMessage.RTR = CAN_RTR_Data;//遥控标志位
	TxMessage.DLC = Length;//数据段长度
	for (uint8_t i = 0; i < Length; i ++)
	{
		TxMessage.Data[i] = Data[i];//数据段内容
	}
	uint8_t TransmitMailbox = CAN_Transmit(CAN1, &TxMessage);
	while (CAN_TransmitStatus(CAN1, TransmitMailbox) != CAN_TxStatus_Ok);
}

查询接收

void MyCAN_Receive(uint32_t *ID, uint8_t *Length, uint8_t *Data)
{
	CanRxMsg RxMessage;
	
	CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);
	
	if (RxMessage.IDE == CAN_Id_Standard)
	{
		*ID = RxMessage.StdId;
	}
	else
	{
		//...
	}
	
	if (RxMessage.RTR == CAN_RTR_Data)
	{
		*Length = RxMessage.DLC;
		for (uint8_t i = 0; i < *Length; i ++)
		{
			Data[i] = RxMessage.Data[i];
		}
	}
	else
	{
		//...
	}
}

作者:真寻460

物联沃分享整理
物联沃-IOTWORD物联网 » STM32 CAN外设(STM32F103C8T6)

发表回复