【STM32F103 外设】CAN 数据收发标准库实现

CAN 初始化

#include "stm32f10x.h"

#define CAN_Rx 					GPIO_Pin_11
#define CAN_Tx 					GPIO_Pin_12

/** @brief CAN 初始化
  * @params None
  * @return None
  */
void MyCAN_Init(void)
{
	/* 开启时钟 */
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	/* GPIO 配置 */
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = CAN_Rx;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;						//复用推挽输出,将 IO 控制权交由 CAN 外设
	GPIO_InitStructure.GPIO_Pin = CAN_Tx;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	/* CAN 配置 */
	CAN_InitTypeDef CAN_InitStructure;
	CAN_InitStructure.CAN_Mode = CAN_Mode_LoopBack;						//回环模式,单个设备自收发
	CAN_InitStructure.CAN_ABOM = ENABLE;								//使能离线自动管理
	CAN_InitStructure.CAN_AWUM = ENABLE;								//使能自动唤醒
	CAN_InitStructure.CAN_NART = DISABLE;								//开启自动重传(一直传输,直至成功)
	CAN_InitStructure.CAN_RFLM = DISABLE;								//接收 FIFO 不锁定(溢出后覆盖旧数据)
	CAN_InitStructure.CAN_TTCM = DISABLE;								//不启用时间戳功能
	CAN_InitStructure.CAN_TXFP = DISABLE;								//按照报文 ID 发送(使能时,先请求先发送)
	/* 设置波特率 1e6 bit per second = 1 /(tq * (1 + BS1 + BS2))*/
	CAN_InitStructure.CAN_SJW = CAN_SJW_4tq;	
	CAN_InitStructure.CAN_BS1 = CAN_BS1_5tq;
	CAN_InitStructure.CAN_BS2 = CAN_BS2_3tq;							//(1+5+3)tq
	CAN_InitStructure.CAN_Prescaler = 8;								// tq = 8 / 71M =(1 / 9M)s
																		//发送一位数据时间为(1 / 1M)
	CAN_Init(CAN1, &CAN_InitStructure);
}

位时序图
波特率 = (1 / 正常的位时间)
= 1 / (tq + tBS1 + tBS2)
= 1 / (tq + 5tq + 3tq)
= 1 / 9tq
= 1 / 72tPCLK(对于STM32F103C8T6芯片,tPCLK = (1 / 72 000 000)s)
= 1 000 000 bps

CAN 标识符过滤器配置

  • STM32F103C8T6的CAN控制器为应用程序提供了14个位宽可变的、可配置的过滤器组(13~0)。
  • 每个过滤器组的位宽都可以独立配置,以满足应用程序的不同需求。根据位宽的不同,每个过滤器组可提供:
    • 1个32位过滤器,包括:STDID[10:0]、EXTID[17:0]、IDE和RTR位
    • 2个16位过滤器,包括:STDID[10:0]、IDE、RTR和EXTID[17:15]位
    • 过滤器可配置为,屏蔽位模式和标识符列表模式
    在标识符列表模式下,屏蔽寄存器也被当作标识符寄存器用

过滤器组位宽设置

#define CAN_STD_ID  			0x123
#define CAN_EXT_ID 				(uint32_t)0x1800f001
/** @brief CAN 标识符过滤器配置
  * @params None
  * @return None
  */
void CAN_Filter_Init(void)
{
	/* CAN 过滤器配置 */
	CAN_FilterInitTypeDef CAN_FilterInitStructure;
	CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; 				//启动过滤器
	CAN_FilterInitStructure.CAN_FilterNumber = 0;						//指定过滤器组编号(STM32F103 有 0~13 共 14 个过滤器组)
	CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0;//存放在 FIFO0 中
	/* 配置 CAN 过滤器为 32 位列表模式 */
	/* 16 为列表模式 */
	/* 16 为掩码模式 */
	/* 32 为列表模式 */
	/* 32 为掩码模式 */
	CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;						//过滤器位宽为 32 位
	CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdList;							//ID 列表模式
																							//收到报文的标识符必须与过滤器的值完全相等才能通过
																							//屏蔽位模式,可以指定标识符的某些位为指定值时能通过
	/* 过滤标准数据帧 ID:0x123 */
	CAN_FilterInitStructure.CAN_FilterIdHigh = (CAN_STD_ID << 5); 							//设置 STDID 
	CAN_FilterInitStructure.CAN_FilterIdLow = 0 | (CAN_ID_STD & 0xff);						//设置 IDE 位为 0 ,不使用扩展 ID 	 	
	CAN_FilterInitStructure.CAN_FilterMaskIdHigh = ((CAN_EXT_ID<<3)&0xffff)>>16;			
	CAN_FilterInitStructure.CAN_FilterMaskIdLow = ((CAN_EXT_ID<<3)&0xffff) | CAN_ID_EXT;	//设置 IDE 位为 1 ,使用扩展 ID
	CAN_FilterInit(&CAN_FilterInitStructure);
}

标识符配置说明:本例中蚕蛹32位标识符列表模式。
0x123 = 0b0000 0000 0000 0000 0000 0[001 0010 0011] 为标准标识符(未超过11位),方括号中表示STDID
0x1800 f001 = 0b000[1 1000 0000 00][00 1111 0000 0000 0001] 为扩展标识符(超过11位),第一个方括号中的11位为STDID,第一个方括号中的18位为EXTID
过滤器组位宽设置图可知,STDID存放在32位寄存器的21-31位,EXTID存放在32位寄存器的3-20位
从标识符过滤器配置结构体中可得,需要将整个32位的过滤器拆分为两个16位数据传入,而两个标识符均为32位,所以直接传入会默认强转为uint16_t,丢弃高16位。
标识符过滤器配置结构体

对于0x123,将变为0000 0[001 0010 0011],所以,只要左移5位即可将STDID存入高16位寄存器中,低16位寄存器中只需要将IDE置0,表示非扩展标识符即可。
对于0x1800 f001,若直接强转会导致高16位丢失,所以先左移3位取出扩展标识符的有效部分1 1000 0000 0000 1111 0000 0000 0001,随后右移16位使有效标识符的最高位对应强转后的最高位,从而保证强转数据不失真,而后存入高16位寄存器中,有效标识符的剩余部分直接存入低16位寄存器并将IDE置1,表示扩展标识符即可。

CAN 发送数据

/** @brief CAN 发送数据(实现一)
  * @params 
  * 	@arg ID 数据标识 ID
  * 	@arg Length 数据长度(取值范围为1~8)
  * 	@arg TxData 存放数据的数组名称
  * @return None
  */
void CAN_Send_Data(uint32_t ID, uint8_t Length, uint8_t *TxData)
{
	CanTxMsg TxMessage;
	TxMessage.StdId = ID;													//标准标识符
	TxMessage.ExtId = ID;													//扩展标识符
	TxMessage.IDE = CAN_Id_Standard;										//标准 ID 有效
	TxMessage.RTR = CAN_RTR_Data;											//数据帧标志位
	TxMessage.DLC = Length;													//数据字节数目
	for (uint8_t i = 0; i < Length; i++) {TxMessage.Data[i] = TxData[i];}	//获取发送内容
	CAN_Transmit(CAN1, &TxMessage);
}

/** @brief CAN 发送数据(实现二)
  * @params TxMessage CAN 发送数据结构体指针
  * @return None
  */
void CAN_Send_Data(CanTxMsg *TxMessage)
{
	CAN_Transmit(CAN1, TxMessage);
}

CAN 接收数据

/** @brief CAN 接收数据(实现一)
  * @params 
  * 	@arg ID 数据标识指针
  * 	@arg Length 数据长度指针
  * 	@arg TxData 存放接收数据的数组
  * @return None
  * @note C 语言中不支持函数多返回值,采用指针传参获取函数返回值
  */
void CAN_Receive_Data(uint32_t *ID, uint8_t *Length, uint8_t *RxData)
{
	CanRxMsg RxMessage;
	CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);
	if (RxMessage.IDE == CAN_Id_Standard)
	{
		*ID = RxMessage.StdId;
	}
	else
	{
		*ID = RxMessage.ExtId;
	}
	if (RxMessage.RTR == CAN_RTR_Data)
	{
		*Length = RxMessage.DLC;
		for (uint8_t i = 0; i < *Length; i++) {RxData[i] = RxMessage.Data[i];}	//获取发送内容
	}
	else
	{
		/* ... ... */
	}
}
/** @brief CAN 接收数据(实现二)
  * @params RxMessage CAN 接收数据结构体指针
  * @return None
  */
void CAN_Receive_Data(CanRxMsg *RxMessage)
{
	CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);
}

TODO:CAN中断发送

欢迎批评指正!!!

文中图片来源:STM32F10xxx参考手册
参考教程:CAN总线入门教程-全面细致 面包板教学 多机通信

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值