一、硬件连接说明

STM32F407 内置 2 路 CAN 控制器,但 CAN 控制器的 TXD/RXD 为单端信号,无法直接驱动 CAN 总线,必须外接 CAN 收发器(本文使用野火小智 CAN 模块)。

1.1 模块引脚连接

野火小智 CAN 模块与 STM32F407 的连接引脚如下(共 5 路):

CAN 模块引脚 STM32 引脚 / 电源 说明
5V 5V 电源 模块供电(勿接 3.3V)
GND GND 接地
VIO 3.3V 电平匹配引脚(匹配 STM32 IO 电平)
TXD STM32 CAN_TX 发送信号(如 CAN1_TX 为 PA12)
RXD STM32 CAN_RX 接收信号(如 CAN1_RX 为 PA11)

1.2 总线终端电阻配置

  • 两台 STM32 通过 CAN 模块互联时,需将总线两端模块的终端电阻拨码开关设为 “ON”(接入 120Ω 匹配电阻);
  • CAN 模块间的 CAN_H/CAN_L 需一一对应连接,GND 建议强制连接以保证通信稳定性。

二、CubeMX 配置步骤

2.1 核心参数配置(CAN 波特率与模式)

本文为保证通信稳定性,配置 125Kbps 波特率(STM32F407 的 APB1 时钟为 42MHz):

  • 分频系数(Prescaler):24(若需 1Mbps 波特率,可改为 3);
  • 时间段配置(经验值,验证可用):
    • SJW(同步跳转宽度):CAN_SJW_1TQ;
    • BS1(时间段 1):CAN_BS1_9TQ;
    • BS2(时间段 2):CAN_BS2_4TQ;
  • 操作模式:
    • 真实多节点互联:Normal(正常模式);
    • 单机调试:LoopBack(回环模式,自发自收,无需外接模块)。

2.2 关键配置项(必做)

  •     中断开启:启用 CAN_RX_FIFO0_MSG_PENDING 中断(接收消息中断),否则无法触发接收回调;

  • GPIO 配置:CAN_TX/RX 引脚保持 CubeMX 默认的复用功能(如 CAN1 对应 PA11/PA12,AF9 复用),无需手动修改。

三、C 代码实现(HAL 库)

3.1 初始化流程(顺序不可变)

程序启动后,需按以下顺序初始化 CAN 总线,缺一不可:

    // 1. 配置CAN全通过滤器(核心:不配置会导致接收异常)
    CAN1_FilterAllPass();
    // 2. 启动CAN外设
    if (HAL_CAN_Start(&hcan1) != HAL_OK) {
        Error_Handler();
    }

    // 3. 启用接收FIFO0中断通知
    if (HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK) {
        Error_Handler();
    }

3.2 CAN 过滤器配置(全通策略)

配置全通过滤器,允许接收所有 ID 的 CAN 报文,核心是将掩码设为全 0:

/**
 * @brief  配置CAN1全通过滤器:允许所有ID的CAN帧被接收
 * @note   必须在 HAL_CAN_Start() 之前调用
 * @retval None
 */
void CAN1_FilterAllPass(void) {
    CAN_FilterTypeDef can_filter = {0};
 
    // 选择过滤器组(F407共28组,CAN1默认使用0~13组)
    can_filter.FilterBank = 0;
    // 掩码模式(全通核心:掩码全0则匹配所有ID)
    can_filter.FilterMode = CAN_FILTERMODE_IDMASK;
    // 32位过滤器宽度(可同时匹配标准/扩展ID)
    can_filter.FilterScale = CAN_FILTERSCALE_32BIT;
 
    // 全通配置:参考ID任意,掩码全0
    can_filter.FilterIdHigh = 0x0000;      // 参考ID高16位
    can_filter.FilterIdLow = 0x0000;       // 参考ID低16位
    can_filter.FilterMaskIdHigh = 0x0000;  // 掩码高16位(全0)
    can_filter.FilterMaskIdLow = 0x0000;   // 掩码低16位(全0)
 
    // 匹配报文存入FIFO0(与中断配置对应)
    can_filter.FilterFIFOAssignment = CAN_FILTER_FIFO0;
    // 启用过滤器
    can_filter.FilterActivation = ENABLE;
    // 多CAN配置(仅同时用CAN1/CAN2时需设,固定14即可)
    can_filter.SlaveStartFilterBank = 14;
 
    // 应用过滤器配置
    if (HAL_CAN_ConfigFilter(&hcan1, &can_filter) != HAL_OK) {
        Error_Handler();
    }
}

3.3 CAN 报文发送(每秒发送 1 帧示例)

void can_test() {
    // 每秒发送一帧 CAN 数据
    static uint32_t last_tick = 0;
    if (HAL_GetTick() - last_tick >= 1000) {
        last_tick = HAL_GetTick();

        CAN_TxHeaderTypeDef TxHeader;
        uint8_t TxData[8] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};  // 待发送的数据, 8字节
        uint32_t TxMailbox;

        TxHeader.StdId = 0x123;       // 标准帧 ID
        TxHeader.RTR = CAN_RTR_DATA;  // 数据帧
        TxHeader.IDE = CAN_ID_STD;    // 标准标识符
        TxHeader.DLC = 8;             // 数据长度
        TxHeader.TransmitGlobalTime = DISABLE;
        HAL_StatusTypeDef status = HAL_CAN_AddTxMessage(&hcan1, &TxHeader, TxData, &TxMailbox);
        if (status == HAL_OK) {
            printf("CAN Sent: ID=0x123, TxMailbox=%ld.\r\n", TxMailbox);
            HAL_GPIO_WritePin(LED_G, GPIO_PIN_RESET);
            osDelay(100);
            HAL_GPIO_WritePin(LED_G, GPIO_PIN_SET);
        } else if (status == HAL_ERROR) {
            printf("CAN Tx Failed Error! TxMailbox=%ld.\r\n", TxMailbox);
        } else if (status == HAL_BUSY) {
            printf("CAN Tx Failed Busy! TxMailbox=%ld.\r\n", TxMailbox);
        } else if (status == HAL_TIMEOUT) {
            printf("CAN Tx Failed Timeout! TxMailbox=%ld.\r\n", TxMailbox);
        }
        
    }
}

3.4 CAN 报文接收(中断回调方式)

重写 HAL 库接收回调函数,收到报文后自动打印(需提前配置串口 printf 输出):

// CAN接收FIFO0消息挂起回调函数
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef* hcan) {
    CAN_RxHeaderTypeDef RxHeader;
    uint8_t RxData[8];

    if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxHeader, RxData) == HAL_OK) {
        printf("CAN Recv: ID=0x%lX, DLC=%ld, Data=", RxHeader.StdId, RxHeader.DLC);
        for (int i = 0; i < RxHeader.DLC; i++) {
            printf("%02X ", RxData[i]);
        }
        printf("\r\n======================================================\r\n");
    }
}

至此CAN配置完成,可以实现最简单的数据收发。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐