• STM32 CAN使用记录:bxCAN基础通讯


    目的

    CAN是非常常用的一种数据总线,被广泛用在各种车辆系统中。这篇文章将对STM32中CAN的使用做个示例。

    CAN的一些基础介绍可以参考下面文章:
    《CAN基础概念》https://blog.csdn.net/Naisu_kun/article/details/132814079

    本文使用STM32F407作为主控芯片,CAN电路设计如下:
    在这里插入图片描述

    本文使用使用STM32CubeIDE进行开发。

    关键配置与代码

    轮询方式

    在这里插入图片描述
    在这里插入图片描述

    除了默认生成的代码只需在 main.c 中手动添加一些代码即可:

    int main(void)
    {
      HAL_Init();
      SystemClock_Config();
      MX_GPIO_Init();
      MX_CAN1_Init();
    
      /**************** 以下为过滤器设置 ****************/
      CAN_FilterTypeDef  sFilterConfig;
    
      sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; // 使用MASK模式
      sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; // 过滤器使用32bit模式
      sFilterConfig.FilterIdHigh = 0x0000;
      sFilterConfig.FilterIdLow = 0x0000;
      sFilterConfig.FilterMaskIdHigh = 0x0000;
      sFilterConfig.FilterMaskIdLow = 0x0000;
      sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; // 经过过滤的数据放到FIFO0
      sFilterConfig.FilterBank = 0; // 使用第0组过滤器
      sFilterConfig.SlaveStartFilterBank = 14; // 从CAN过滤器其实地址
      sFilterConfig.FilterActivation = ENABLE; // 使能过滤器
    
      if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)
      {
        // 过滤器设置失败
      }
    
      /**************** 以下为启动CAN外设 ****************/
      HAL_CAN_Start(&hcan1); // 启动CAN
    
      while (1)
      {
    
    	/**************** 以下为接收消息并回发处理 ****************/
    	if(HAL_CAN_GetRxFifoFillLevel(&hcan1, CAN_RX_FIFO0) != 0) // 接收队列不为0,有数据可读
    	{
    		CAN_RxHeaderTypeDef   RxHeader; // 用来保存接收到的数据帧头部信息
    		uint8_t               RxData[8]; // 用来保存接收数据端数据
    
    		if(HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &RxHeader, RxData) == HAL_OK) // 从接收队列中读取数据帧
    		{
    			CAN_TxHeaderTypeDef   TxHeader; // 用来保存发送数据帧头部信息
    			uint8_t               TxData[8]; // 用来保存发送数据帧数据
    			uint32_t              TxMailbox; // 用来保存发送数据帧所使用的邮箱号
    
    			if(RxHeader.IDE == CAN_ID_STD) // 标准数据帧
    			{
    				TxHeader.StdId = RxHeader.StdId; // 标准帧ID
    			}
    			if(RxHeader.IDE == CAN_ID_EXT) // 扩展数据帧
    			{
    				TxHeader.ExtId = RxHeader.ExtId; // 扩展帧ID
    			}
    			TxHeader.IDE = RxHeader.IDE; // ID类型
    
    			TxHeader.RTR = RxHeader.RTR; // CAN_RTR_DATA 数据帧;CAN_RTR_REMOTE 远程帧
    
    			TxHeader.DLC = RxHeader.DLC; // 数据段字节数
    
    			for(int i=0; i<TxHeader.DLC; i++)
    			{
    				TxData[i] = RxData[i];
    			}
    
    			while(HAL_CAN_GetTxMailboxesFreeLevel(&hcan1) == 0); // 等待有发送邮箱可用
    
    			HAL_CAN_AddTxMessage(&hcan1, &TxHeader, TxData, &TxMailbox); // 发送数据帧
    		}
    	}
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70

    上面代码中的过滤器是接收数据才需要的。过滤器可以使只有ID匹配上的报文才接收到接收FIFO中。

    过滤器最常使用MASK模式,该模式下与 FilterMaskId1 的bit位对应的 FilterId bit位的值必须完全匹配,比如下面示例:

    • FilterId = 0x00000233 FilterMaskId = 0xFFFFFFFF 只接收ID为 0x00000233 的报文;
    • FilterId = 0x00000233 FilterMaskId = 0xFFFFFF00 可以接收ID为 0x000002XX 的报文(X可以为任意值);
    • FilterId = 0x00000000 FilterMaskId = 0x00000000 可以接收ID为 0xXXXXXXXX 的报文(X可以为任意值);

    需要注意的是就算不想过滤任何报文也需要设置过滤器,因为需要通过过滤器来指定接收的报文存放到哪个FIFO中。

    中断方式

    中断方式基础配置与上面相同,唯一的变化是使能相关中断:
    在这里插入图片描述
    (下面代码中实际只用到RX0中断)

    除了默认生成的代码只需在 main.c 中手动添加一些代码即可:

    #include "main.h"
    
    CAN_HandleTypeDef hcan1;
    
    void SystemClock_Config(void);
    static void MX_GPIO_Init(void);
    static void MX_CAN1_Init(void);
    
    /**************** 以下为重写中断回调函数 ****************/
    // 注意下面的一些回调函数其实是在HAL库中已经若定义好的,我们只需要在保持函数类型不变的情况下重写实现其实际功能
    
    // Fifo0收到消息回调
    void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
    {
    	if(hcan->Instance == CAN1) // 如果是来自CAN1的数据
    	{
    		CAN_RxHeaderTypeDef   RxHeader; // 用来保存接收到的数据帧头部信息
    		uint8_t               RxData[8]; // 用来保存接收数据端数据
    
    		if(HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &RxHeader, RxData) == HAL_OK) // 从接收队列中读取数据帧
    		{
    			CAN_TxHeaderTypeDef   TxHeader; // 用来保存发送数据帧头部信息
    			uint8_t               TxData[8]; // 用来保存发送数据帧数据
    			uint32_t              TxMailbox; // 用来保存发送数据帧所使用的邮箱号
    
    			if(RxHeader.IDE == CAN_ID_STD) // 标准数据帧
    			{
    				TxHeader.StdId = RxHeader.StdId; // 标准帧ID
    			}
    			if(RxHeader.IDE == CAN_ID_EXT) // 扩展数据帧
    			{
    				TxHeader.ExtId = RxHeader.ExtId; // 扩展帧ID
    			}
    			TxHeader.IDE = RxHeader.IDE; // ID类型
    
    			TxHeader.RTR = RxHeader.RTR; // CAN_RTR_DATA 数据帧;CAN_RTR_REMOTE 远程帧
    
    			TxHeader.DLC = RxHeader.DLC; // 数据段字节数
    
    			for(int i=0; i<TxHeader.DLC; i++)
    			{
    				TxData[i] = RxData[i];
    			}
    
    			while(HAL_CAN_GetTxMailboxesFreeLevel(&hcan1) == 0); // 等待有发送邮箱可用
    
    			HAL_CAN_AddTxMessage(&hcan1, &TxHeader, TxData, &TxMailbox); // 发送数据帧
    		}
    	}
    
    }
    
    // void HAL_CAN_RxFifo0FullCallback(CAN_HandleTypeDef *hcan) { } // Fifo0队满回调
    
    // void HAL_CAN_TxMailbox0AbortCallback(CAN_HandleTypeDef *hcan) { } // TxMailbox0取消传输回调
    // void HAL_CAN_TxMailbox0CompleteCallback(CAN_HandleTypeDef *hcan) { } // TxMailbox0传输完成回调
    // void HAL_CAN_TxMailbox1AbortCallback(CAN_HandleTypeDef *hcan) { } // TxMailbox1取消传输回调
    // void HAL_CAN_TxMailbox1CompleteCallback(CAN_HandleTypeDef *hcan) { } // TxMailbox1传输完成回调
    // void HAL_CAN_TxMailbox2AbortCallback(CAN_HandleTypeDef *hcan) { } // TxMailbox2取消传输回调
    // void HAL_CAN_TxMailbox2CompleteCallback(CAN_HandleTypeDef *hcan) { } // TxMailbox2传输完成回调
    
    int main(void)
    {
      HAL_Init();
      SystemClock_Config();
      MX_GPIO_Init();
      MX_CAN1_Init();
    
      /**************** 以下为过滤器设置 ****************/
      CAN_FilterTypeDef  sFilterConfig;
    
      sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; // 使用MASK模式
      sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; // 过滤器使用32bit模式
      sFilterConfig.FilterIdHigh = 0x0000;
      sFilterConfig.FilterIdLow = 0x0000;
      sFilterConfig.FilterMaskIdHigh = 0x0000;
      sFilterConfig.FilterMaskIdLow = 0x0000;
      sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; // 经过过滤的数据放到FIFO0
      sFilterConfig.FilterBank = 0; // 使用第0组过滤器
      sFilterConfig.SlaveStartFilterBank = 14; // 从CAN过滤器其实地址
      sFilterConfig.FilterActivation = ENABLE; // 使能过滤器
    
      if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)
      {
        // 过滤器设置失败
      }
    
      /**************** 以下为启动中断 ****************/
      HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING); // 使能FIFO0数据接收中断
    
      /**************** 以下为启动CAN外设 ****************/
      HAL_CAN_Start(&hcan1); // 启动CAN
    
      while (1)
      {
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97

    收发测试

    本示例演示结果可以通过各种CAN工具配合上位机软件进行测试:
    在这里插入图片描述

    示例链接

    仓库地址: https://github.com/NaisuXu/STM32_MCU_Examples

    本文中的示例位于仓库中 CAN_RxTxPoll_F407CAN_RxTxIT_F407

    总结

    STM32中使用CAN非常方便,进行配置生成代码后只需要设置过滤器,然后就可以收发数据了。

  • 相关阅读:
    Linux卡死的解决方法 solution of server stuck or freezes
    二叉树路径问题+递归+有关题目
    云计算发展历史
    Spring Data JPA @Query注解
    vue3之echarts区域折线图
    汽车生产RFID智能制造设计解决方案与思路
    Sublime Text如何安装Package Control
    Java学习----线程池
    liunx下软链接和硬链接的用法
    Autosar AP的基本构成
  • 原文地址:https://blog.csdn.net/Naisu_kun/article/details/132830073