STM32CUBEMX-CAN通讯

本文围绕STM32的CAN通讯展开,先介绍CAN通讯原理,包括协议特点、帧类型、总线仲裁等;接着阐述STM32 CAN控制器,如控制器特点、标识符筛选器等;然后说明STM32 CUBEMX的配置步骤;最后进行代码讲解,涵盖CAN开启、发送、筛选器函数及main函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、CAN通讯原理

1.1 基本概述

  CAN(Controller Area Network)控制器区域网络是ISO国际标准化的串行通信协议,具有很高的可靠性,广泛应用于:汽车电子、工业自动化、船舶、医疗设备、工业设备等方面。
  CAN 控制器根据两根线上的电位差来判断总线电平。总线电平分为显性电平和隐性电平,二者必居其一。发送方通过使总线电平发生变化,将消息发送给接收方。

2.2 CAN协议特点

  • 多主控制;
  • 系统柔软性;
  • 速度快,距离远;
  • 具有错误检测、错误通知和错误恢复功能;
  • 故障封闭功能;
  • 连接节点多;

2.3 ISO11898标准下的物理层特征

  CAN 控制器根据CAN_L和CAN_H上的电位差来判断总线电平。总线电平分为显性电平和隐性电平,二者必居其一。发送方通过使总线电平发生变化,将消息发送给接收方。
   显性电平对应逻辑:0,CAN_H和CAN_L之差为2.5V左右。
   显性电平具有优先权,只要有一个单元输出显性电平,总线上即为显性电平。而隐形电平则具有包容的意味,只有所有的单元都输出隐性电平,总线上才为隐性电平(显性电平比隐性电平更强)。另外,在CAN总线的起止端都有一个120Ω的终端电阻,来做阻抗匹配,以减少回波反射。
在这里插入图片描述

2.4 CAN协议帧类型

  为了更有效地控制通信,CAN共规定了5种类型的帧,帧也称为报文,它们的类型及用途如下:

帧类型帧用途
数据帧用于发送单元向接收单元传送数据的帧
遥控帧用于接收单元向具有相同ID的发送单元请求的数据帧
错误帧用于当检测出错误时向其它单元通知错误的帧
过载帧用于接收单元通知其尚未做好接收准备的帧
间隔帧用于将数据帧及遥控帧与前面的帧分离开来的帧

上面五种帧类型中,数据帧和遥控帧有标准格式和扩展格式两种格式:

  • 标准格式有11 个位的标识符(ID),扩展格式有29 个位的ID 。

其中,最常用,也是最复杂的是数据帧,接着就看看数据帧,数据帧一般由以下 7 个段构成:

  • (1) 帧起始。表示数据帧开始的段。
  • (2) 仲裁段。表示该帧优先级的段。
  • (3) 控制段。表示数据的字节数及保留位的段。
  • (4) 数据段。数据的内容,一帧可发送 0~8 个字节的数据。
  • (5) CRC 段。检查帧的传输错误的段。
  • (6) ACK 段。表示确认正常接收的段。
  • (7) 帧结束。表示数据帧结束的段。
      数据帧的具体结构,如下图所示(D表示显性电平,R表示隐形电平):
    数据帧结构图

2.5 总线仲裁介绍

同时多个单元发送数据时,总线仲裁过程:
总线仲裁图片
  上图中,单元 1 和单元 2 同时开始向总线发送数据,开始部分他们的数据格式是一样的,故无法区分优先级,直到 T 时刻,单元 1 输出隐性电平,而单元 2 输出显性电平,此时单元 1仲裁失利,立刻转入接收状态工作,不再与单元 2 竞争,而单元 2 则顺利获得总线使用权,继续发送自己的数据。这就实现了仲裁,让连续发送显性电平多的单元获得总线使用权。

  遵循以下规律:

  • (1) 总线空闲时,最先发送的单元获得发送优先权,一但发送,其他单元无法抢占。
  • (2) 如果有多个单元同时发送,则连续输出显性电平多的单元,具有较高优先级。

2.6 位时序

  CAN总线通讯协议的每一个数据帧可以看作一连串的电平信号,每一个电平信号代表一位(一个字节8位的位),所以一帧中包含了很多个位,由发送单元在非同步的情况下发送的每秒钟的位数称为位速率。 一位又分为4段, 同步段(SS)、传播时间段(PTS)、相位缓冲段 1(PBS1)、相位缓冲段 2(PBS2)。分解后最小的时间单位是 Tq,而一个完整的位由 8~25 个 Tq 组成。
  位速率:由发送单元在非同步的情况下发送的每秒钟的位数称为位速率。一个位一般可以分为如下四段:

  • (1) 同步段(SS)
  • (2) 传播时间段(PTS)
  • (3) 相位缓冲段 1(PBS1)
  • (4) 相位缓冲段 2(PBS2)

  这些段又由可称为 Time Quantum(以下称为 Tq)的最小时间单位构成,1 位分为 4 个段,每个段又由若干个 Tq 构成,这称为位时序。
  位时间=1/波特率,因此,知道位时间,我们就可以知道波特率1 位由多少个 Tq 构成、每个段又由多少个 Tq 构成等,可以任意设定位时序。通过设定位时序,多个单元可同时采样,也可任意设定采样点。各段的作用和 Tq 数如表所示:
位时序图片CAN通讯1 个位的构成如下图所示:
位时序采样点  上图的采样点,是指读取总线电平,并将读到的电平作为位值的点。位置在 PBS1 结束处。根据这个位时序,我们就可以计算 CAN 通信的波特率了。图中采样时间加大或减少量的最大值就是SJW。

2、STM32 CAN控制器

2.1 STM32 CAN控制器简介

  STM32F1的bxCAN是基本扩展CAN(Basic Extended CAN)的缩写,它支持CAN协议2.0A和2.0B。它的设计目标是,以最小的CPU负荷来高效处理大量收到的报文。它也支持报文发送的优先级要求(优先级特性可软件配置)。
  对于安全紧要的应用,bxCAN提供所有支持时间触发通信模式所需的硬件功能。

STM32F1的bxCAN主要特点有:
(1) 支持CAN协议2.0A和2.0B主动模式
(2) 波特率最高可达1兆位/秒
(3) 支持时间触发通信功能
发送
3个发送邮箱
(1) 发送报文的优先级特性可软件配置
(2) 记录发送SOF时刻的时间戳
接收
(1) 3级深度的2个接收FIFO
(2) 可变的过滤器组:
    ─ 在互联型产品中,CAN1和CAN2分享28个过滤器组
    ─ 其它STM32F103xx系列产品中有14个过滤器组
(3) 标识符列表
(4) FIFO溢出处理方式可配置
(5) 记录接收SOF时刻的时间戳
时间触发通信模式
(1) 禁止自动重传模式
(2) 16位自由运行定时器
(3) 可在最后2个数据字节发送时间戳
管理
(1) 中断可屏蔽
(2) 邮箱占用单独1块地址空间,便于提高软件效率
双CAN
(1) CAN1:是主bxCAN,它负责管理在从bxCAN和SRAM存储器之间的通信
(2) CAN2:是从bxCAN,它不能直接访问SRAM存储器
(3) 这2个bxCAN模块共享SRAM存储器控制器局域网(bxCAN)

2.2 标识符筛选器

  CAN的标识符不表示目的地址而是表示发送优先级。接收节点根据标识符的值,来决定是否接收对应消息。
  STM32 CAN控制器,提供了28个可配置的筛选器组(F1仅互联型才有28个,其他的只有14个),可降低CPU处理CAN通信的开销。
  STM32 CAN控制器每个筛选器组由2个32位寄存器组成(CAN_FxR1和CAN_FxR2,x=0~27)。根据位宽不同,每个筛选器组可提供:

  • 1个32位筛选器,包括:STDID[10:0]、EXTID[17:0]、IDE和RTR位
  • 2个16位筛选器,包括:STDID[10:0]、IDE、RTR和EXTID[17:15]位

筛选器可配置为:屏蔽位模式和标识符列表模式。
屏蔽位模式: 在屏蔽位模式下,标识符寄存器和屏蔽寄存器一起,指定报文标识符的任何一位,应该按照“必须匹配”或“不用关心”处理。
标识符列表模式: 在标识符列表模式下,屏蔽寄存器也被当作标识符寄存器用。因此,不是采用一个标识符加一个屏蔽位的方式,而是使用2个标识符寄存器。接收报文标识符的每一位都必须跟过滤器标识符相同。
标识符寄存器的标识符列表模式或屏蔽位模式。
(1) 为了过滤出一组标识符,应该设置过滤器组工作在屏蔽位模式。
(2) 为了过滤出一个标识符,应该设置过滤器组工作在标识符列表模式。
(3) 应用程序不用的过滤器组,应该保持在禁用状态。
(4) 过滤器组中的每个过滤器,都被编号为(叫做过滤器号)从0开始,到某个最大数值-取决于过滤
器组的模式和位宽的设置。
关于过滤器配置,参见下图:
STM32 CAN过滤器寄存器

2.3 STM32 CAN模式

(1)工作模式
1.初始化模式(INRQ=1,SLEEP=0)
2.正常模式(INRQ=0,SLEEP=0)
3.睡眠模式(SLEEP=1)
(2)测试模式
1.静默模式( LBKM=0,SILM=1 )
静默模式
2.环回模式( LBKM=1,SILM=0 )
环回模式
3.环回静默模式
环回静默模式
4.调试模式

2.4 STM32 CAN发送流程

  CAN发送流程为:
  程序选择1个空置的邮箱(TME=1)—>设置标识符(ID),数据长度和发送数据—>设置CAN_TIxR的TXRQ位为1,请求发送—>邮箱挂号(等待成为最高优先级)—>预定发送(等待总线空闲)—>发送—>邮箱空置。
  还包含了很多其他处理,终止发送(ABRQ=1)和发送失败处理等。
CAN发送流程

2.5 STM32 CAN接收流程

  CAN接收流程为:
  FIFO空—>收到有效报文—>挂号_1(存入FIFO的一个邮箱,这个由硬件控制,从而节省了 CPU 的处理负荷,简化了软件并保证了数据的一致性)---->收到有效报文---->挂号_2---->收到有效报文---->挂号_3---->收到有效报文à溢出。
  CAN收到的有效报文,存储在3级邮箱深度的FIFO中。FIFO接收到的报文数,我们可以通过查询CAN_RFxR的FMP寄存器来得到,只要FMP不为0,我们就可以从FIFO读出收到的报文。
  报文FIFO具有锁定功能(由CAN_MCR,RFLM位控制),锁定后,新数据将丢弃,不锁定则新数据将替代老数据。
CAN接收流程

2.6 STM32 CAN位时序

  STM32把传播时间段和相位缓冲段 1(STM32称之为时间段1)合并了,所以 STM32的 CAN 一个位只有 3 段:同步段(SYNC_SEG)、时间段 1(BS1)和时间段 2(BS2)。 STM32的 BS1 段可以设置为 1~16 个时间单元,刚好等于CAN的传播时间段和相位缓冲段 1 之和。
STM32位时序  图中还给出了 CAN 波特率的计算公式,我们只需要知道 BS1 和 BS2 的设置,以及 STM32F1 APB1的时钟频率(一般为 36Mhz),就可以方便的计算出波特率。
  比如:
    STM32F103,设TS1=8、TS2=7、BRP=3,波特率=36000/[(9+8+1)*4]=500Kbps。
    STM32F407,设TS1=6、TS2=5、BRP=5,波特率=42000/[(7+6+1)*6]=500Kbps。

3、STM32 CUBEMX配置

  本工程使用的是STM32F103C8T6芯片,STM32CUBEMX配置也是采用这个型号的芯片进行配置。

3.1 时钟源配置

时钟源配置

3.2 调试接口配置

调试接口配置

3.3 工程配置

工程配置

3.4 工程文件配置

工程文件配置

3.5 时钟配置

时钟配置

3.6 STM32 CAN外设配置

STM32CAN外设配置

3.7 STM32 CAN中断配置

STM32 CAN中断配置

4、代码讲解

4.1 添加CAN开启函数

void HAL_CAN_MspDeInit(CAN_HandleTypeDef* canHandle)
{

  if(canHandle->Instance==CAN1)
  {
  /* USER CODE BEGIN CAN1_MspDeInit 0 */

  /* USER CODE END CAN1_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_CAN1_CLK_DISABLE();

    /**CAN GPIO Configuration
    PA11     ------> CAN_RX
    PA12     ------> CAN_TX
    */
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_11|GPIO_PIN_12);

    /* CAN1 interrupt Deinit */
    HAL_NVIC_DisableIRQ(USB_LP_CAN1_RX0_IRQn);
    HAL_NVIC_DisableIRQ(CAN1_RX1_IRQn);
  /* USER CODE BEGIN CAN1_MspDeInit 1 */
		
		/*需要添加CAN外设开启函数,否则CAN无法正常工作*/
		HAL_CAN_Start(&hcan);//开启CAN

  /* USER CODE END CAN1_MspDeInit 1 */
  }
}

4.2 CAN发送和CAN筛选器函数

  以下为CAN的发送、CAN筛选器接收和CAN中断回调函数。

/* USER CODE BEGIN 1 */

/*
* @brief:CAN过滤器配置,接收模式采用的是列表模式
* @param:can_id1(筛选第一个ID号)
* @param:can_id2(筛选第二个ID号)
*
* @retval:none
*/
void CAN_Fliter_Config_IDLIST(uint32_t can_id1,uint32_t can_id2)
{
	CAN_FilterTypeDef hcan_filterconfig;
	
	hcan_filterconfig.FilterActivation = CAN_FILTER_ENABLE;//过滤器使能
	hcan_filterconfig.FilterBank = 0;  //使用过滤器0
	hcan_filterconfig.FilterMode = CAN_FILTERMODE_IDLIST;//采用列表模式
	hcan_filterconfig.FilterScale = CAN_FILTERSCALE_32BIT;//采用32位掩码模式
	hcan_filterconfig.FilterFIFOAssignment = CAN_FilterFIFO0;//使用FIFO0
	
	hcan_filterconfig.FilterIdHigh =     (((((uint32_t)can_id1)<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xffff0000)>>16;//过滤器ID高16位,CAN_ID_EXT(扩展ID)、CAN_RTR_DATA(帧类型为数据帧)
	hcan_filterconfig.FilterIdLow =      (((((uint32_t)can_id1)<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0x0000ffff);    //过滤器ID低16位,CAN_ID_EXT(扩展ID)、CAN_RTR_DATA(帧类型为数据帧)
	hcan_filterconfig.FilterMaskIdHigh = (((((uint32_t)can_id2)<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xffff0000)>>16;//过滤器掩码高16位,CAN_ID_EXT(扩展ID)、CAN_RTR_DATA(帧类型为数据帧)
	hcan_filterconfig.FilterMaskIdLow =  (((((uint32_t)can_id2)<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0x0000ffff);    //过滤器掩码低16位,CAN_ID_EXT(扩展ID)、CAN_RTR_DATA(帧类型为数据帧)
	
	hcan_filterconfig.SlaveStartFilterBank = 14; //过滤器数量14
	
	HAL_CAN_ConfigFilter(&hcan,&hcan_filterconfig); //初始化过滤器
	
	HAL_CAN_Start(&hcan);//开启CAN
	
	HAL_CAN_ActivateNotification(&hcan,CAN_IT_RX_FIFO0_MSG_PENDING);//开启CAN-FIFO0中断
}

/*
* @brief:CAN过滤器配置,接收模式采用的是掩码模式
* @param:id(筛选的ID号)
* @param:mask_if(掩码)
*
* @retval:none
*/
void CAN_Fliter_Config_IDMASK(uint32_t id,uint32_t mask_id)
{
	CAN_FilterTypeDef hcan_filterconfig;
	
	hcan_filterconfig.FilterActivation = CAN_FILTER_ENABLE;//过滤器使能
	hcan_filterconfig.FilterBank = 0;  //使用过滤器0
	hcan_filterconfig.FilterMode = CAN_FILTERMODE_IDMASK;//采用列表模式
	hcan_filterconfig.FilterScale = CAN_FILTERSCALE_32BIT;//采用32位掩码模式
	hcan_filterconfig.FilterFIFOAssignment = CAN_FilterFIFO0;//使用FIFO0
	
	hcan_filterconfig.FilterIdHigh =     (((((uint32_t)id)<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xffff0000)>>16;//过滤器ID高16位,CAN_ID_EXT(扩展ID)、CAN_RTR_DATA(帧类型为数据帧)
	hcan_filterconfig.FilterIdLow =      (((((uint32_t)id)<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0x0000ffff);    //过滤器ID低16位,CAN_ID_EXT(扩展ID)、CAN_RTR_DATA(帧类型为数据帧)
	hcan_filterconfig.FilterMaskIdHigh = (mask_id & 0xffff0000)>>16;//过滤器掩码高16位
	hcan_filterconfig.FilterMaskIdLow =  mask_id & 0x0000ffff;      //过滤器掩码低16位
	
	hcan_filterconfig.SlaveStartFilterBank = 14; //过滤器数量14
	
	HAL_CAN_ConfigFilter(&hcan,&hcan_filterconfig); //初始化过滤器
	
	HAL_CAN_Start(&hcan);//开启CAN
	
	HAL_CAN_ActivateNotification(&hcan,CAN_IT_RX_FIFO0_MSG_PENDING);//开启CAN-FIFO0中断
}


/*
* @brief:CAN发送函数
* @param:id(CAN发送的ID号)
* @param:ide(帧ID号类型选择)
          CAN_ID_STD:标准ID号
          CAN_ID_EXT:扩展ID号
* @param:*pMsg(要发送的数据)
* @param:len(发送的数据长度)
*
* @retval:返回0:正常
           返回1:异常
*/
uint8_t CAN_SendMsg(uint32_t id,uint8_t ide,uint8_t *pMsg,uint8_t len)
{
	CAN_TxHeaderTypeDef pHeader;
	uint32_t pTxmailbox;
	uint8_t result = 0;
	
	HAL_StatusTypeDef CanStatus;
	
	if(ide == CAN_ID_STD)
	{
		pHeader.StdId = id;   //标准ID号
		pHeader.IDE = CAN_ID_STD;//标准ID
	}
	else
	{
		pHeader.ExtId = id;//扩展ID号
		pHeader.IDE = CAN_ID_EXT;//扩展ID
	}
	
	pHeader.RTR = CAN_RTR_DATA;//传输数据的帧类型为数据帧
	pHeader.DLC = len>8?8:len;//发送数据的字节长度,最大为8个字节
	pHeader.TransmitGlobalTime = DISABLE;//是否要发送时间戳
	
	CanStatus = HAL_CAN_AddTxMessage(&hcan, &pHeader, pMsg, &pTxmailbox);//CAN发送函数
	
	if(CanStatus != HAL_OK)
	{
		result = 1;
	}
	
	return result;
}

/*CAN-FIFO0中断回调函数*/
uint8_t CAN_Receive_Data[8];
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
	CAN_RxHeaderTypeDef Header;
	
	if(hcan->Instance == CAN1)
	{
		if(Header.ExtId == 0x012345678) //如果CAN接收的扩展ID为x012345678,则执行以下
		{
		    /*测试发现STM32F405单片机没有中断没有HAL_CAN_GetRxMessage函数,接收到第一条数据之后会一直卡死在中断里面*/
		    HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &Header, CAN_Receive_Data);//接收的数据存储在CAN_Receive_Data
			printf("接收到扩展ID为:0x01234567");
		}
		
		if(Header.StdId == 0x087654321) //如果CAN接收的扩展ID为0x07654321,则执行以下
		{
		    /*测试发现STM32F405单片机没有中断没有HAL_CAN_GetRxMessage函数,接收到第一条数据之后会一直卡死在中断里面*/
		    HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &Header, CAN_Receive_Data);//接收的数据存储在CAN_Receive_Data
			printf("接收到扩展ID为:0x07654321");
		}
	}
}

/* USER CODE END 1 */

4.3 main()函数

int main(void)
{
  /* USER CODE BEGIN 1 */
  char CAN_Send_Data[8] = {1,2,3,4,5,6,7,8};
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_CAN_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
	CAN_Fliter_Config_IDLIST(0x012345678,0x07654321);//CAN过滤器配置

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
//		printf("123456\r\n");
		
		CAN_SendMsg(0x01234567,CAN_ID_EXT,CAN_Send_Data,8);//CAN发送函数
		
		
		HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_13);
		HAL_Delay(500);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值