STM32和STM32CubeMX实现FDCAN通信, 保姆级教程

发布于:2025-08-09 ⋅ 阅读:(17) ⋅ 点赞:(0)

【背景】

各位,我现在要用树莓派和STM32的板子进行通信,一个是用串口,但是,考虑到串口的速度,通信距离和可靠性,还是用CAN比较合适。所以,我要做一个STM32的CAN通信接口。下面就是保姆级教程。

【保姆级教程】

先来看看CAN接口的硬件怎么设计。

【CAN接口硬件】

不多说,直接上电路图。

看到没有,CAN的接口电路就是这么简单。用的收发芯片是NXP的TJA1044GT/3Z, 为什么用这个芯片呢?因为它的VCC是5V,而IO口的电压可以用3.3V,STM32的IO的电压就是3.3V,那么就不需要电压转换电路,就可以直接接STM32的IO口了。

记得这个120欧的终端电阻一定要加上。

PD1,PD0, 在我的STM32G474VET6上,就是FDCAN_TX和FDCAN_RX。

【STM32CubeMX配置】

这个配置其实挺直观的,我的配置是这样的。

我的配置就是将FDCAN配置成经典的CAN2.0, 波特率配置成1Mbps。

(这里的配置有一个问题,后面讲软件的时候再讲)

中断配置成这样的。

我的时钟配置成170Mhz

其他没啥了。直接生成代码。

【软件代码】

cubeMX生成的代码是这样的。

要使用CAN,自己在main文件里还要加入下列变量。

我这里配置的CAN发送和接收的,都是8byte长度,这里变量,为了充足一点,都是声明的16bytes. 用8bytes应该也是没有问题的。

【fdcan.h】

这头文件是这样的。

【fdcan.c】

这C文件是这样的。

#include "fdcan.h"
#include "BSP_USB.h"
#include "gpio.h"

void my_FDCAN1_init(void)
{
    // 设置接收过滤器
    FDCAN_FilterTypeDef sFilterConfig;
    sFilterConfig.IdType = FDCAN_STANDARD_ID;
    sFilterConfig.FilterIndex = 0;
    sFilterConfig.FilterType = FDCAN_FILTER_MASK;
    sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
    sFilterConfig.FilterID1 = 0x002; // 设置过滤器ID1
    sFilterConfig.FilterID2 = 0x7ff; // 设置过滤器ID2
    if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK)
    {
        Error_Handler();
    }


    // 设置发送头
    fdcan1TxHeader.Identifier = 0x001; // 设置标识符
    fdcan1TxHeader.IdType = FDCAN_STANDARD_ID; // 设置ID类型
    fdcan1TxHeader.TxFrameType = FDCAN_DATA_FRAME; // 设置帧类型
    fdcan1TxHeader.DataLength = FDCAN_DLC_BYTES_8; // 设置数据长度
    fdcan1TxHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE; // 设置错误状态指示符
    fdcan1TxHeader.BitRateSwitch = FDCAN_BRS_OFF; // 设置比特率切换
    fdcan1TxHeader.FDFormat = FDCAN_CLASSIC_CAN; // 设置格式
    fdcan1TxHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS; // 设置事件FIFO控制
    fdcan1TxHeader.MessageMarker = 0; // 设置消息标记器

    // 启动 FDCAN1
    if (HAL_FDCAN_Start(&hfdcan1) != HAL_OK)
    {
        Error_Handler();
    }
    // 启动接收
    if (HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0) != HAL_OK)
    {
        Error_Handler();
    }

    CAN_STB_normal(); // 设置 FDCAN1 进入正常工作模式
    HAL_Delay(5);
    fdcan1_RXFlag = 0; // 初始化接收标志
    usb_printf("FDCAN1 initialized successfully\r\n");
}

void my_FDCAN1_Transmit(uint8_t *TxData)
{
    // 发送数据
    if (HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &fdcan1TxHeader, TxData) != HAL_OK)
    {
        usb_printf("HAL_FDCAN_AddMessageToTxFifoQ failed\r\n");
        Error_Handler();
    }
    
}

// FDCAN1 接收回调函数
void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs)
{
    if ((hfdcan->Instance == FDCAN1) && ((RxFifo0ITs & FDCAN_IT_RX_FIFO0_NEW_MESSAGE) != RESET))
    {
        // 从接收 FIFO 中读取数据
        if (HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, &fdcan1RxHeader, fdcan1RxData) != HAL_OK)
        {
            usb_printf("HAL_FDCAN_GetRxMessage failed\r\n");
            Error_Handler();
        }

        // 重启接收 FIFO
        if (HAL_FDCAN_ActivateNotification(hfdcan, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0) != HAL_OK)
        {
            usb_printf("HAL_FDCAN_ActivateNotification failed\r\n");
            Error_Handler();
        }

        // 设置接收标志
        fdcan1_RXFlag = 1;
        
    }
}

【main.c】

main函数里,CAN接收是这样处理的。

// fdcan1 test
    if(fdcan1_RXFlag == 1)
    {
      fdcan1_RXFlag = 0; // Clear the receive flag
      for(int i = 0; i < 8; i++)
      {
        fdcan1TxData[i] = fdcan1RxData[i]+1; // Copy received data to transmit buffer
      }

      my_FDCAN1_Transmit(fdcan1TxData); // Echo back the received data
      usb_printf("FDCAN1 Received data: ");
      HAL_Delay(5);
      for (int i = 0; i < 8; i++)
      {
        usb_printf("%02X ", fdcan1RxData[i]);
        HAL_Delay(1);
      }
      usb_printf("\r\n");
    }

这个很简单,就是如果CAN接收到数据了,那么将数据+1,给到发送buffer。然后,用CAN发出去。再把接收到的数据打印出来。

【结果检验】

那么,编译代码后,烧写到板子里去,将CAN用2根线接到CANH,CANL的对手CAN设备的端子上。(我是用的USB转CAN的canable2.0s)。 然后,用windows上的CAN上位机来测试。

我用的cangaroo-win32-ccdcb64.  结果如下。

已经可以发送和接收了。

但是,我想我的CAN设备只接收CANID 0x002的消息。也就是我的代码里设定的。

    sFilterConfig.FilterType = FDCAN_FILTER_MASK;
    sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
    sFilterConfig.FilterID1 = 0x002; // 设置过滤器ID1
    sFilterConfig.FilterID2 = 0x7ff; // 设置过滤器ID2

但是,我测试下来,是这个过滤好像没啥用,还是所有的CANID的消息都能收到。那么要实现CANID过滤功能怎么办呢?

这个过滤器的代码要改成这样的。

// 设置接收过滤器
    FDCAN_FilterTypeDef sFilterConfig;
    sFilterConfig.IdType = FDCAN_STANDARD_ID;
    sFilterConfig.FilterIndex = 0;
    sFilterConfig.FilterType = FDCAN_FILTER_MASK;
    sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
    sFilterConfig.FilterID1 = 0x002; // 设置过滤器ID1
    sFilterConfig.FilterID2 = 0x7ff; // 设置过滤器ID2
    if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK)
    {
        Error_Handler();
    }

    if(HAL_FDCAN_ConfigGlobalFilter(&hfdcan1, FDCAN_REJECT, FDCAN_REJECT, FDCAN_REJECT_REMOTE, FDCAN_REJECT_REMOTE) != HAL_OK)
    {
        Error_Handler();
    }

同时,main里的MX_FDCAN1_Init(void)也要改。这里是cubeMX生成的,有一行要改成这样。

 hfdcan1.Init.StdFiltersNbr = 1;

这就是我在cubeMX配置时提过的。

那么这样修改之后,就能够实现只有自己需要的CANID的消息才收到。

看看上面的cangaroo的图片,可以看到,只有CANID是0x002的消息才收到了。

【结果】

好了,大功告成,亲个嘴儿。


网站公告

今日签到

点亮在社区的每一天
去签到