STM32 CAN快速配置(HAL库版本)

STM32 CAN快速配置(HAL库版本)

前言

控制器局域网总线(CAN,Controller Area Network)是一种用于实时应用的串行通讯协议总线,它可以使用双绞线来传输信号,是世界上应用最广泛的现场总线之一。CAN协议用于汽车中各种不同元件之间的通信,以此取代昂贵而笨重的配电线束。该协议的健壮性使其用途延伸到其他自动化和工业应用。CAN协议的特性包括完整性的串行数据通讯、提供实时支持、传输速率高达1Mb/s、同时具有11位的寻址以及检错能力。

特别说明:关于CAN总线协议和硬件电路等问题,这里不做介绍,网上的资料非常多,不懂的同学请自行查阅。

1 软件编程

1.1 初始化

初始化主要分成三部分:引脚设置,CAN参数设置和CAN滤波器设置。

1.1.1 引脚设置

把CAN_H和CAN_L两个引脚配置成复用功能即可。
注:如果CAN控制芯片的S引脚连接到STM32的话,还得初始化这个引脚,S引脚可以配置成高速模式或静音模式。

参考代码:
注:该代码可以通过STM32CubeMX生成

/**
* @brief CAN MSP Initialization
* This function configures the hardware resources used in this example
* @param hcan: CAN handle pointer
* @retval None
*/
void HAL_CAN_MspInit(CAN_HandleTypeDef* hcan)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(hcan->Instance==CAN1)
  {/* USER CODE END CAN1_MspInit 0 */
    /* Peripheral clock enable */
    HAL_RCC_CAN1_CLK_ENABLED++;
    if(HAL_RCC_CAN1_CLK_ENABLED==1){
      __HAL_RCC_CAN1_CLK_ENABLE();
    }

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**CAN1 GPIO Configuration
    PA11     ------> CAN1_RX
    PA12     ------> CAN1_TX
    */
    GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF9_CAN1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* CAN1 interrupt Init */
    HAL_NVIC_SetPriority(CAN1_RX0_IRQn, 0, 0);    // CAN接收中断
    HAL_NVIC_EnableIRQ(CAN1_RX0_IRQn);
  }
}
1.1.2 CAN参数设置

HAL库的CAN初始化有几个重要参数,都存放在几个结构体里面(如:CAN_HandleTypeDef,CAN_InitTypeDef),具体的结构体定义可以在HAL库查看。
说明:CAN参数需要根据自己实际的需求来配。

我这里着重讲解一下CAN波特率的配置。

CAN波特率 = CAN时钟频率 / 分频系数 / (TimeSeg1 + TimeSeg2 + 1)。

其中,CAN时钟频率不是固定不变的,它取决于CAN所挂载的总线时钟。
比如STM32F1,系统时钟最大72M,APB1的总线时钟最大36M,而CAN控制器的时钟是挂在APB1的,所以CAN的时钟频率也等于APB1的时钟。
如果换作其他型号的MCU,CAN外设不一定是挂载到APB1上面的,时钟也不一定是36M,比如F4系列,APB1的时钟是可以配成42M的,因此,这个要根据实际情况来配置。

参考代码:
注:该代码可以通过STM32CubeMX生成

/**
  * @brief CAN1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_CAN_Init(void)
{
  // CAN波特率 = CAN时钟频率 / Prescaler / (TimeSeg1 + TimeSeg2 + 1)
  // 例: 500kbps = 36MHz / 9 / (3 + 4 + 1)   36MHz为该例程APB1的总线时钟
  /* USER CODE END CAN1_Init 1 */

  hcan.Instance = CAN1;                     // 配置CAN1
  hcan.Init.Prescaler = 9;                  // 预分频系数
  hcan.Init.Mode = CAN_MODE_NORMAL;         // 正常CAN模式        
  hcan.Init.SyncJumpWidth = CAN_SJW_1TQ;    // 重同步跳跃宽度,CAN_SJW_1TQ~CAN_SJW_4TQ             
  hcan.Init.TimeSeg1 = CAN_BS1_3TQ;         // TimeSeg1        
  hcan.Init.TimeSeg2 = CAN_BS2_4TQ;         // TimeSeg2        
  hcan.Init.TimeTriggeredMode = DISABLE;    // 非时间触发通信模式             
  hcan.Init.AutoBusOff = DISABLE;           // 软件自动离线管理      
  hcan.Init.AutoWakeUp = DISABLE;           // 睡眠模式通过软件唤醒(清除CAN->MCR的SLEEP位)      
  hcan.Init.AutoRetransmission = DISABLE;   // 禁止报文自动重传               
  hcan.Init.ReceiveFifoLocked = DISABLE;    // FIFO报文不锁定,新的覆盖旧的              
  hcan.Init.TransmitFifoPriority = DISABLE; // 优先级由报文标识符决定                 
  if (HAL_CAN_Init(&hcan) != HAL_OK)
  {
    Error_Handler();
  }
}
1.1.3 CAN滤波器设置

CAN滤波器的主要作用是筛选CAN接收的数据,只有满足设定规则的数据才会被接收,否则会被过滤掉。

参考代码:

void CAN_Config(void)
{
  CAN_FilterTypeDef  sFilterConfig;

  /* Configure the CAN Filter */
  sFilterConfig.FilterBank = 0;                      // 过滤器编号,使用一个CAN,则可选0-13;使用两个CAN可选0-27
  sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;  // 过滤器模式,掩码模式或列表模式
  sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; // 过滤器位宽
  sFilterConfig.FilterIdHigh = 0x0000;               // 过滤器验证码ID高16位,0-0xFFFF
  sFilterConfig.FilterIdLow = 0x0000;                // 过滤器验证码ID低16位,0-0xFFFF
  sFilterConfig.FilterMaskIdHigh = 0x0000;           // 过滤器掩码ID高16位,0-0xFFFF
  sFilterConfig.FilterMaskIdLow = 0x0000;            // 过滤器掩码ID低16位,0-0xFFFF
  sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; // FIFOx,0或1
  sFilterConfig.FilterActivation = ENABLE;           // 使能过滤器
  sFilterConfig.SlaveStartFilterBank = 14;           // 从过滤器编号,0-27,对于单CAN实例该参数没有意义

  if (HAL_CAN_ConfigFilter(&hcan, &sFilterConfig) != HAL_OK)
  {
    /* Filter configuration Error */
  }

  /* Start the CAN peripheral */
  if (HAL_CAN_Start(&hcan) != HAL_OK)
  {
    /* Start Error */
  }

  /* Activate CAN RX notification */
  if (HAL_CAN_ActivateNotification(&hcan, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)
  {
    /* Notification Error */
  }
}

1.2 CAN发送

CAN发送需要先配置发送参数,我这里为了方便测试,直接固定发送标准帧,ID也是固定的。
实际使用时可以再增加一个ID的入参,这样会更灵活一点。

参考代码:

/****************************************************************************
* 名    称: uint8_t CAN_Send_Msg(uint8_t* msg, uint8_t len)
* 功    能:can发送一组数据(固定格式:ID为0X12,标准帧,数据帧)
* 入口参数:len:数据长度(最大为8)				     
           msg:数据指针,最大为8个字节.
* 返回参数:0,成功;
           其他,失败;
* 说    明:       
****************************************************************************/		
uint8_t CAN_Send_Msg(uint8_t* msg, uint8_t len)
{	
  uint8_t i=0;
  uint8_t message[8];
  uint32_t TxMailbox;
  CAN_TxHeaderTypeDef CAN_TxHeader;

  // 设置发送参数
  CAN_TxHeader.StdId = 0x12;                 // 标准标识符(12bit)
  CAN_TxHeader.ExtId = 0x12;                 // 扩展标识符(29bit)
  CAN_TxHeader.IDE = CAN_ID_STD;             // 使用标准帧
  CAN_TxHeader.RTR = CAN_RTR_DATA;           // 数据帧
  CAN_TxHeader.DLC = len;                    // 发送长度      
  CAN_TxHeader.TransmitGlobalTime = DISABLE;

  // 装载数据
  for(i = 0; i < len; i++)
  {
    message[i] = msg[i];
  }

  // 发送CAN消息
  if(HAL_CAN_AddTxMessage(&hcan, &CAN_TxHeader, message, &TxMailbox) != HAL_OK) 
  {
    return 1;
  }
  while(HAL_CAN_GetTxMailboxesFreeLevel(&hcan) != 3) 
  {

  }
  return 0;	
}

1.3 CAN接收

接收部分只要开启了Rx中断,在CAN控制器收到消息时会调用RxFifo的回调函数,此时我们在这里读取数据并根据实际情况做相应的处理即可。

参考代码:

/*******************************************************************************
* Function Name  : HAL_CAN_RxFifo0MsgPendingCallback
* Description    : 消息接收回调函数
* Input          : hcan
* Output         : None
* Return         : None
****************************************************************************** */
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{// 收到CAN数据会触发接收中断,进入该回调函数
  uint32_t i;
  uint8_t RxData[8];
  CAN_RxHeaderTypeDef CAN_RxHeader; 
  
  if(HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &CAN_RxHeader, RxData) == HAL_OK)
  {
    // 串口打印接收结果
    printf("GetRxMessage, CANID:0x%0X, Data:", CAN_RxHeader.StdId);
    for(i = 0;i < CAN_RxHeader.DLC; i++)
    {
      printf("%02x ", RxData[i]);
    }
    // 把接收的数据用CAN再发回去
    CAN_Send_Msg(RxData, CAN_RxHeader.DLC);
  }
}

2 运行测试

使用USB-CAN工具测试发送:
在这里插入图片描述

消息窗口如下:
在这里插入图片描述
可以看到,上位机发送了一条CAN数据,CANID为0x01,接着就收到了STM32回的一条数据,CANID为0x12(因为我代码固定写死了ID为0x12)。

同样的,通过串口也能看到STM32收到的CAN数据,如下图所示:
在这里插入图片描述
还有一些其他测试,比如收发不同长度,切换扩展帧等等,这里就不展示了,感兴趣的同学可以自己改参数试试。

结论:CAN收发正常。

结束语

好了,关于如何通过STM32如何配置和使用CAN就讲到这里,如果你有什么问题或者有更好的方法,欢迎在评论区留言。

### STM32 HALCAN通信接线实例 对于STM32单片机使用HAL进行CAN通信,具体的接线方式取决于所使用的开发板以及外部设备。通常情况下,STM32F103RCT6这样的型号具备多个USART接口,但针对CAN总线通信,则需特别注意其专用引脚配置[^2]。 #### 硬件准备 - **STM32开发板**:这里选用的是STM32F103RCT6作为主控制器。 - **CAN收发器模块**:例如TJA1050或MCP2551等常见的CAN收发器芯片。 #### 接线说明 为了使能CAN通信功能,需要按照如下方式进行接线: | STM32 Pin | 连接到 | |-----------|---------| | CAN_RX (PA11) | 收发器的RXD引脚 | | CAN_TX (PA12) | 收发器的TXD引脚 | | GND | 收发器的地线 | 此外,还需要确保CAN收发器与其他节点之间的物理层连接正常工作,这可能涉及到终端电阻(通常是120Ω)的设置来匹配传输线路阻抗,减少信号反射的影响。 #### 软件初始化代码示例 下面给出一段简单的初始化程序片段,利用STM32CubeMX生成的基础工程框架并结合HAL完成基本的CAN参数设定。 ```c /* 初始化CAN外设 */ static void MX_CAN_Init(void) { hcan.Instance = CAN1; hcan.Init.Prescaler = 9; // 设置波特率预分频系数 hcan.Init.Mode = CAN_MODE_NORMAL; hcan.Init.SyncJumpWidth = CAN_SJW_1TQ; hcan.Init.TimeSeg1 = CAN_BS1_13TQ; hcan.Init.TimeSeg2 = CAN_BS2_2TQ; hcan.Init.TimeTriggeredMode = DISABLE; hcan.Init.AutoBusOff = DISABLE; hcan.Init.AutoWakeUp = DISABLE; hcan.Init.GatewayOff = DISABLE; hcan.Init.NoAutoRetransmission = DISABLE; hcan.Init.ReceiveFifoLocked = DISABLE; hcan.Init.TransmitFifoPriority = DISABLE; if (HAL_CAN_Init(&hcan) != HAL_OK) { Error_Handler(); } } ``` 上述代码实现了对CAN控制器的基本配置,包括但不限于波特率调整、同步跳跃宽度及时段划分等内容。实际应用中还需进一步编写发送接收消息的具体逻辑函数。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值