• STM32 CAN/CANFD软件快速配置(HAL库版本)


    STM32 CAN/CANFD软件快速配置(HAL库版本)

    前言

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

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

    1 软件编程

    1.1 建立工程

    要建立一个HAL库版本的工程可以直接使用其他项目的HAL工程,也可以通过移植HAL固件库或者用STM32CubeMX生成。

    我这里以STM32G0为例讲解一下如何用STM32CubeMX生成一个工程。
    特别说明:如果不使用STM32CubeMX工具,可以跳过以下步骤,直接从1.2开始,把CANFD相关代码加入其他HAL工程即可。

    1、配置时钟
    我这里使用外部晶振时钟(HSE),8M晶振倍频到64M时钟。

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

    2、配置引脚
    选择自己实际使用的引脚作为CAN_TX和CAN_RX。

    在这里插入图片描述

    3、配置CAN参数
    我这里用CAN1作为CANFD,CAN2作为普通CAN。

    CAN1配置参考如下:
    特别说明:以下数据仅供参考,请根据实际情况配置。
    在这里插入图片描述

    CAN2配置参考如下:
    特别说明:以下数据仅供参考,请根据实际情况配置。
    在这里插入图片描述

    最后使能中断(注:CAN1和CAN2可以共用一个中断接口)。
    在这里插入图片描述

    4、生成工程
    请添加图片描述

    1.2 初始化

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

    1.2.1 引脚设置

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

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

    static uint32_t HAL_RCC_FDCAN_CLK_ENABLED=0;
    
    void HAL_FDCAN_MspInit(FDCAN_HandleTypeDef* fdcanHandle)
    {
      GPIO_InitTypeDef GPIO_InitStruct = {0};
      RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
      if(fdcanHandle->Instance==FDCAN1)
      {
        PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_FDCAN;
        PeriphClkInit.FdcanClockSelection = RCC_FDCANCLKSOURCE_PCLK1;
    
        if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
        {
          Error_Handler();
        }
    
        /* FDCAN1 clock enable */
        HAL_RCC_FDCAN_CLK_ENABLED++;
        if(HAL_RCC_FDCAN_CLK_ENABLED==1){
          __HAL_RCC_FDCAN_CLK_ENABLE();
        }
    
        __HAL_RCC_GPIOB_CLK_ENABLE();
        /**FDCAN1 GPIO Configuration
        PB8     ------> FDCAN1_RX
        PB9     ------> FDCAN1_TX
        */
        GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        GPIO_InitStruct.Alternate = GPIO_AF3_FDCAN1;
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    
        /* FDCAN1 interrupt Init */
        HAL_NVIC_SetPriority(TIM16_FDCAN_IT0_IRQn, 0, 0);
        HAL_NVIC_EnableIRQ(TIM16_FDCAN_IT0_IRQn);
      }
      else if(fdcanHandle->Instance==FDCAN2)
      {
        PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_FDCAN;
        PeriphClkInit.FdcanClockSelection = RCC_FDCANCLKSOURCE_PCLK1;
    
        if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
        {
          Error_Handler();
        }
    
        /* FDCAN2 clock enable */
        HAL_RCC_FDCAN_CLK_ENABLED++;
        if(HAL_RCC_FDCAN_CLK_ENABLED==1){
          __HAL_RCC_FDCAN_CLK_ENABLE();
        }
    
        __HAL_RCC_GPIOB_CLK_ENABLE();
        /**FDCAN2 GPIO Configuration
        PB5     ------> FDCAN2_RX
        PB6     ------> FDCAN2_TX
        */
        GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        GPIO_InitStruct.Alternate = GPIO_AF3_FDCAN2;
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    
        /* FDCAN2 interrupt Init */
        HAL_NVIC_SetPriority(TIM16_FDCAN_IT0_IRQn, 0, 0);
        HAL_NVIC_EnableIRQ(TIM16_FDCAN_IT0_IRQn);
      }
    }
    
    • 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
    1.2.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的,因此,这个要根据实际情况来配置。

    本例用的是STM32G0,CAN时钟频率配置为64MHz。
    参考代码:
    注:该代码可以通过STM32CubeMX生成

    FDCAN_HandleTypeDef hfdcan1;
    FDCAN_HandleTypeDef hfdcan2;
    
    /* FDCAN1 init function */
    void MX_FDCAN1_Init(void)
    {
      /* USER CODE END FDCAN1_Init 1 */
      // CAN波特率 = 时钟频率 / 时钟分频  / 预分频系数 / (1 + TSG1 + TSG2) 
      // 仲裁段波特率 = 64M / 1 / 8 / (1 + 10 + 5) = 500k
      // 数据段波特率 = 64M / 1 / 2 / (1 + 10 + 5) = 2M
      hfdcan1.Instance = FDCAN1;                               // FDCAN1
      hfdcan1.Init.ClockDivider = FDCAN_CLOCK_DIV1;            // 时钟分频               
      hfdcan1.Init.FrameFormat = FDCAN_FRAME_FD_BRS;           // 配置为CANFD         
      hfdcan1.Init.Mode = FDCAN_MODE_NORMAL;                   // 正常工作状态       
      hfdcan1.Init.AutoRetransmission = DISABLE;               // 关闭自动重传, 传统模式下需关闭           
      hfdcan1.Init.TransmitPause = DISABLE;                    // 关闭传输暂停      
      hfdcan1.Init.ProtocolException = DISABLE;                // 关闭协议异常处理          
      hfdcan1.Init.NominalPrescaler = 8;                       // 仲裁段时钟分频,传统CAN模式时只设置这一段即可   
      hfdcan1.Init.NominalSyncJumpWidth = 1;                   // 仲裁段同步跳转段的宽度                      
      hfdcan1.Init.NominalTimeSeg1 = 10;                       // 仲裁段时间段1                      
      hfdcan1.Init.NominalTimeSeg2 = 5;                        // 仲裁段时间段2                     
      hfdcan1.Init.DataPrescaler = 2;                          // 数据段时钟分频,若工作于传统模式,不需要设置数据段参数
      hfdcan1.Init.DataSyncJumpWidth = 1;                      // 数据段同步跳转段的宽度   
      hfdcan1.Init.DataTimeSeg1 = 10;                          // 数据段时间段1           
      hfdcan1.Init.DataTimeSeg2 = 5;                           // 数据段时间段2           
      hfdcan1.Init.StdFiltersNbr = 28;                         // 标准帧滤波器数量 
      hfdcan1.Init.ExtFiltersNbr = 8;                          // 扩展帧滤波器数量 
      hfdcan1.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION;  // 发送模式:先入先出   
      if (HAL_FDCAN_Init(&hfdcan1) != HAL_OK)
      {
        Error_Handler();
      }
    }
    /* FDCAN2 init function */
    void MX_FDCAN2_Init(void)
    {
      /* USER CODE END FDCAN2_Init 1 */
      // CAN波特率 = 时钟频率 / 时钟分频 / 预分频系数 / (1 + TSG1 + TSG2) = 64M / 1 / 8 / (1 + 10 + 5) = 500k
      hfdcan2.Instance = FDCAN2;                               // FDCAN2
      hfdcan2.Init.ClockDivider = FDCAN_CLOCK_DIV1;            // 时钟分频 
      hfdcan2.Init.FrameFormat = FDCAN_FRAME_CLASSIC;          // 配置为传统模式    
      hfdcan2.Init.Mode = FDCAN_MODE_NORMAL;                   // 正常工作状态
      hfdcan2.Init.AutoRetransmission = DISABLE;               // 关闭自动重传, 传统模式下需关闭
      hfdcan2.Init.TransmitPause = DISABLE;                    // 关闭传输暂停
      hfdcan2.Init.ProtocolException = DISABLE;                // 关闭协议异常处理
      hfdcan2.Init.NominalPrescaler = 8;                       // 仲裁段时钟分频,传统CAN模式时只设置这一段即可
      hfdcan2.Init.NominalSyncJumpWidth = 1;                   // 仲裁段同步跳转段的宽度               
      hfdcan2.Init.NominalTimeSeg1 = 10;                       // 仲裁段时间段1                   
      hfdcan2.Init.NominalTimeSeg2 = 5;                        // 仲裁段时间段2                   
      hfdcan2.Init.DataPrescaler = 8;                          // 数据段时钟分频,若工作于传统模式,不需要设置数据段参数
      hfdcan2.Init.DataSyncJumpWidth = 1;                      // 数据段同步跳转段的宽度
      hfdcan2.Init.DataTimeSeg1 = 10;                          // 数据段时间段1           
      hfdcan2.Init.DataTimeSeg2 = 5;                           // 数据段时间段2           
      hfdcan2.Init.StdFiltersNbr = 28;                         // 标准帧滤波器数量
      hfdcan2.Init.ExtFiltersNbr = 8;                          // 扩展帧滤波器数量
      hfdcan2.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION;  // 发送模式:先入先出                  
      if (HAL_FDCAN_Init(&hfdcan2) != HAL_OK)
      {
        Error_Handler();
      }
    }
    
    • 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
    1.2.3 CAN收发初始化设置

    CAN接收需要设置CAN滤波器,它的主要作用是筛选CAN接收的数据,只有满足设定规则的数据才会被接收,否则会被过滤掉。
    而CAN发送则需要在发送一条数据前,先配置好这条数据的信息,如CANID,发送长度,数据类型(CAN/CANFD)等等。

    参考代码:

    FDCAN_TxHeaderTypeDef TxHeader1;
    FDCAN_RxHeaderTypeDef RxHeader1;
    FDCAN_TxHeaderTypeDef TxHeader2;
    FDCAN_RxHeaderTypeDef RxHeader2;
    void FDCAN1_Config(void)
    {
      FDCAN_FilterTypeDef sFilterConfig;
      /* Configure Rx filter */
      sFilterConfig.IdType = FDCAN_STANDARD_ID;
      sFilterConfig.FilterIndex = 1;
      sFilterConfig.FilterType = FDCAN_FILTER_RANGE;
      sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
      sFilterConfig.FilterID1 = 0x00000000;
      sFilterConfig.FilterID2 = 0x000007FF;
      if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK)
      {
        Error_Handler();
      }
    
      sFilterConfig.IdType = FDCAN_EXTENDED_ID;
      sFilterConfig.FilterIndex = 0;
      sFilterConfig.FilterType = FDCAN_FILTER_RANGE;
      sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
      sFilterConfig.FilterID1 = 0x00000000;
      sFilterConfig.FilterID2 = 0x1FFFFFFF;
      if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK)
      {
        Error_Handler();
      }
    
      /* Configure global filter:
         Filter all remote frames with STD and EXT ID
         Reject non matching frames with STD ID and EXT ID */
      if (HAL_FDCAN_ConfigGlobalFilter(&hfdcan1, FDCAN_REJECT, FDCAN_REJECT, FDCAN_FILTER_REMOTE, FDCAN_FILTER_REMOTE) != HAL_OK)
      {
        Error_Handler();
      }
    
      /* Activate Rx FIFO 0 new message notification on both FDCAN instances */
      if (HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0) != HAL_OK)
      {
        Error_Handler();
      }
    
      // if (HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_BUS_OFF, 0) != HAL_OK)
      // {
      //   Error_Handler();
      // }
    
      /* Tx Config*/
      TxHeader1.Identifier = 0x000000000;                 // CAN ID
      TxHeader1.IdType = FDCAN_STANDARD_ID;               // 标准ID
      TxHeader1.TxFrameType = FDCAN_DATA_FRAME;           
      TxHeader1.DataLength = FDCAN_DLC_BYTES_8;
      TxHeader1.ErrorStateIndicator = FDCAN_ESI_PASSIVE;
      TxHeader1.BitRateSwitch = FDCAN_BRS_OFF;
      TxHeader1.FDFormat = FDCAN_FD_CAN;                  // CANFD
      TxHeader1.TxEventFifoControl = FDCAN_NO_TX_EVENTS;  
      TxHeader1.MessageMarker = 0;
    
      /* Configure and enable Tx Delay Compensation, required for BRS mode.
            TdcOffset default recommended value: DataTimeSeg1 * DataPrescaler
            TdcFilter default recommended value: 0 */
      HAL_FDCAN_ConfigTxDelayCompensation(&hfdcan1, hfdcan1.Init.DataPrescaler * hfdcan1.Init.DataTimeSeg1, 0);
      HAL_FDCAN_EnableTxDelayCompensation(&hfdcan1);
      
      /* Start the FDCAN module */
      if (HAL_FDCAN_Start(&hfdcan1) != HAL_OK)
      {
        Error_Handler();
      }
    }
    
    void FDCAN2_Config(void)
    {
      FDCAN_FilterTypeDef sFilterConfig;
      /* Configure Rx filter */
      sFilterConfig.IdType = FDCAN_STANDARD_ID;
      sFilterConfig.FilterIndex = 1;
      sFilterConfig.FilterType = FDCAN_FILTER_RANGE;
      sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
      sFilterConfig.FilterID1 = 0x00000000;
      sFilterConfig.FilterID2 = 0x000007FF;
      if (HAL_FDCAN_ConfigFilter(&hfdcan2, &sFilterConfig) != HAL_OK)
      {
        Error_Handler();
      }
    
      sFilterConfig.IdType = FDCAN_EXTENDED_ID;
      sFilterConfig.FilterIndex = 0;
      sFilterConfig.FilterType = FDCAN_FILTER_RANGE;
      sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
      sFilterConfig.FilterID1 = 0x00000000;
      sFilterConfig.FilterID2 = 0x1FFFFFFF;
      if (HAL_FDCAN_ConfigFilter(&hfdcan2, &sFilterConfig) != HAL_OK)
      {
        Error_Handler();
      }
    
      /* Configure global filter:
         Filter all remote frames with STD and EXT ID
         Reject non matching frames with STD ID and EXT ID */
      if (HAL_FDCAN_ConfigGlobalFilter(&hfdcan2, FDCAN_REJECT, FDCAN_REJECT, FDCAN_FILTER_REMOTE, FDCAN_FILTER_REMOTE) != HAL_OK)
      {
        Error_Handler();
      }
    
      /* Activate Rx FIFO 0 new message notification on both FDCAN instances */
      if (HAL_FDCAN_ActivateNotification(&hfdcan2, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0) != HAL_OK)
      {
        Error_Handler();
      }
    
      // if (HAL_FDCAN_ActivateNotification(&hfdcan2, FDCAN_IT_BUS_OFF, 0) != HAL_OK)
      // {
      //   Error_Handler();
      // }
    
      /* Tx Config*/
      TxHeader2.Identifier = 0x000000000;                 // CAN ID
      TxHeader2.IdType = FDCAN_STANDARD_ID;               // 标准ID
      TxHeader2.TxFrameType = FDCAN_DATA_FRAME;           
      TxHeader2.DataLength = FDCAN_DLC_BYTES_8;
      TxHeader2.ErrorStateIndicator = FDCAN_ESI_PASSIVE;
      TxHeader2.BitRateSwitch = FDCAN_BRS_OFF;
      TxHeader2.FDFormat = FDCAN_CLASSIC_CAN;             // CAN
      TxHeader2.TxEventFifoControl = FDCAN_NO_TX_EVENTS;  
      TxHeader2.MessageMarker = 0;
    
      /* Configure and enable Tx Delay Compensation, required for BRS mode.
            TdcOffset default recommended value: DataTimeSeg1 * DataPrescaler
            TdcFilter default recommended value: 0 */
      HAL_FDCAN_ConfigTxDelayCompensation(&hfdcan2, hfdcan2.Init.DataPrescaler * hfdcan2.Init.DataTimeSeg1, 0);
      HAL_FDCAN_EnableTxDelayCompensation(&hfdcan2);
    
      /* Start the FDCAN module */
      if (HAL_FDCAN_Start(&hfdcan2) != HAL_OK)
      {
        Error_Handler();
      }
    }
    
    
    • 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
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    1.2.4 中断设置

    如果使用CAN接收,需要配置中断服务函数。

    参考代码:

    /**
      * @brief This function handles TIM16, FDCAN1_IT0 and FDCAN2_IT0 Interrupt.
      */
    void TIM16_FDCAN_IT0_IRQHandler(void)
    {
      HAL_FDCAN_IRQHandler(&hfdcan1);
      HAL_FDCAN_IRQHandler(&hfdcan2);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    1.3 CAN发送

    CAN发送需要先配置发送参数,我这里为了方便测试,直接固定发送标准帧,ID也是固定的。
    实际使用时可以把CANID,数据长度等参数作为函数入参,这样会更灵活一点。

    参考代码:

    void canfd_tx_test(void)
    {
      TxHeader1.Identifier = 0x123;                 // CAN ID
      TxHeader1.IdType = FDCAN_STANDARD_ID;         // 标准ID
      TxHeader1.TxFrameType = FDCAN_DATA_FRAME;           
      TxHeader1.DataLength = FDCAN_DLC_BYTES_8;     // 发送长度:8byte
      TxHeader1.ErrorStateIndicator = FDCAN_ESI_PASSIVE;
      TxHeader1.BitRateSwitch = FDCAN_BRS_OFF;
      TxHeader1.FDFormat = FDCAN_FD_CAN;            // CANFD
      TxHeader1.TxEventFifoControl = FDCAN_NO_TX_EVENTS;  
      TxHeader1.MessageMarker = 0;
    
      if(HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &TxHeader1, can1_txbuf) != HAL_OK)
      {
        Error_Handler();
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    1.4 CAN接收

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

    参考代码:

    void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs)
    {
      if((RxFifo0ITs & FDCAN_IT_RX_FIFO0_NEW_MESSAGE) != RESET)
      {
        if(hfdcan->Instance == FDCAN1)
        {
          /* Retrieve Rx messages from RX FIFO0 */
          if (HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, &RxHeader1, can1_rxbuf) != HAL_OK)
          {
            Error_Handler();
          }
          /* 把接收到的数据以CANFD发送回去 */
          TxHeader1.DataLength = RxHeader1.DataLength;
          TxHeader1.Identifier = RxHeader1.Identifier;
          if(HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &TxHeader1, can1_rxbuf) != HAL_OK)
          {
            Error_Handler();
          }
        }
    
        if(hfdcan->Instance == FDCAN2)
        {
          /* Retrieve Rx messages from RX FIFO0 */
          if (HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, &RxHeader2, can2_rxbuf) != HAL_OK)
          {
            Error_Handler();
          }
          /* 把接收到的数据以CAN发送回去 */
          TxHeader2.DataLength = RxHeader2.DataLength;
          TxHeader2.Identifier = RxHeader2.Identifier;
          if(HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan2, &TxHeader2, can2_rxbuf) != HAL_OK)
          {
            Error_Handler();
          }
        }
      }
    }
    
    • 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

    2 运行测试

    使用USB-CAN工具测试。

    CAN1使用CANFD(仲裁域波特率500k,数据域波特率2M),上位机每发送一条数据,STM32收到后同样回一条一样的数据,CANID、数据长度和数据都是一致,说明STM32收到的数据没有问题。
    在这里插入图片描述

    CAN2使用普通CAN(仲裁域和数据域波特率均为500k),上位机每发送一条数据,STM32收到后同样回一条一样的数据,CANID、数据长度和数据都是一致,说明STM32收到的数据没有问题。
    在这里插入图片描述

    还有一些其他测试,比如切换扩展帧,远程帧等等,这里就不展示了,感兴趣的同学可以自己改参数试试。

    结论:CAN收发正常。

    结束语

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

    需要源码工程的同学可以自行下载:点击跳转下载链接

  • 相关阅读:
    python Never是什么?
    vSphere之vCLS
    oracle 还原被覆盖的视图
    TypeScript入门笔记(四)
    JAVA系列之JVM内存调优
    Maven 生命周期clean default size含义
    HTML5期末大作业:游戏网站设计与实现——基于bootstrap响应式游戏资讯网站制作HTML+CSS+JavaScript
    MindSpore:一文带你入门虚拟遗憾最小化CFR算法
    ARIMA模型
    qcow2等格式镜像磁盘冷访问
  • 原文地址:https://blog.csdn.net/ShenZhen_zixian/article/details/132775214