基于STM32的CAN总线双向通信例程

一、硬件连接方案

STM32A (主设备)        STM32B (从设备)
---------------------  ---------------------
CAN1_TX (PA12)  →→→  CAN1_RX (PA11)
CAN1_RX (PA11)  ←←←  CAN1_TX (PA12)
GND --------------  GND
总线两端各接120Ω终端电阻

二、CubeMX配置

  1. 时钟配置

    • HSE: 8MHz外部晶振

    • PLL: 42MHz系统时钟

    • APB1: 21MHz (CAN时钟源)

  2. CAN参数配置

    // CAN1配置
    hcan1.Instance = CAN1;
    hcan1.Init.Prescaler = 6;        // 分频系数
    hcan1.Init.Mode = CAN_MODE_NORMAL; // 正常模式
    hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;
    hcan1.Init.TimeSeg1 = CAN_BS1_8TQ; // 8时间单元
    hcan1.Init.TimeSeg2 = CAN_BS2_3TQ; // 3时间单元
    hcan1.Init.TimeTriggeredMode = DISABLE;
    hcan1.Init.AutoBusOff = DISABLE;
    hcan1.Init.AutoWakeUp = DISABLE;
    hcan1.Init.AutoRetransmission = ENABLE; // 自动重传
    
  3. 过滤器配置

    // 接收所有标准帧
    CAN_FilterTypeDef filter;
    filter.FilterBank = 0;
    filter.FilterMode = CAN_FILTERMODE_IDMASK;
    filter.FilterScale = CAN_FILTERSCALE_32BIT;
    filter.FilterIdHigh = 0x0000;
    filter.FilterIdLow = 0x0000;
    filter.FilterMaskIdHigh = 0x0000;
    filter.FilterMaskIdLow = 0x0000;
    filter.FilterFIFOAssignment = CAN_RX_FIFO0;
    HAL_CAN_ConfigFilter(&hcan1, &filter);
    

三、核心代码实现

1. CAN初始化
void MX_CAN1_Init(void)
{
    CAN_HandleTypeDef hcan1;
    
    hcan1.Instance = CAN1;
    HAL_CAN_Init(&hcan1);
    
    // 启动CAN并激活接收中断
    HAL_CAN_Start(&hcan1);
    HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);
}
2. 数据发送函数
void CAN_Send_Cmd(uint8_t cmd, uint16_t value)
{
    CAN_TxHeaderTypeDef TxHeader;
    uint8_t TxData[8] = {0};
    
    TxHeader.StdId = 0x100 + cmd;    // 命令ID范围0x100-0x1FF
    TxHeader.IDE = CAN_ID_STD;
    TxHeader.RTR = CAN_RTR_DATA;
    TxHeader.DLC = 2;
    TxData[0] = (value >> 8) & 0xFF;
    TxData[1] = value & 0xFF;
    
    HAL_CAN_AddTxMessage(&hcan1, &TxHeader, TxData, NULL);
}
3. 接收中断处理
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
    CAN_RxHeaderTypeDef RxHeader;
    uint8_t RxData[8] = {0};
    
    HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxHeader, RxData);
    
    switch(RxHeader.StdId)
    {
        case 0x101: // 电机控制指令
            Motor_SetSpeed(RxData[0], RxData[1]);
            break;
        case 0x102: // 传感器数据请求
            Sensor_SendData();
            break;
    }
}

四、通信协议设计

字段 说明 长度
Start Byte 固定为0xAA 1 Byte
Command ID 功能标识(如0x01=启动电机) 1 Byte
Data Length 数据长度(1-8字节) 1 Byte
Payload 实际数据 1-8 Byte
CRC CRC-8校验 1 Byte

示例数据帧

0xAA 0x01 0x02 0x10 0x27 0x00 0x00 0x00 0x3C

五、多节点通信实现

// 节点地址映射表
const uint8_t node_addr[3] = {0x01, 0x02, 0x03};

// 广播消息发送
void CAN_Broadcast(uint8_t cmd, uint8_t* data, uint8_t len)
{
    CAN_TxHeaderTypeDef TxHeader;
    
    TxHeader.StdId = 0x00;    // 广播ID
    TxHeader.IDE = CAN_ID_STD;
    TxHeader.RTR = CAN_RTR_DATA;
    TxHeader.DLC = len + 2;   // 包含cmd和len
    
    TxData[0] = cmd;
    TxData[1] = len;
    memcpy(&TxData[2], data, len);
    
    HAL_CAN_AddTxMessage(&hcan1, &TxHeader, TxData, NULL);
}

// 定向消息发送
void CAN_Direct_Send(uint8_t target_addr, uint8_t cmd, uint8_t* data, uint8_t len)
{
    CAN_TxHeaderTypeDef TxHeader;
    
    TxHeader.StdId = (target_addr << 3) | 0x04; // 地址左移3位 + 类型标识
    TxHeader.IDE = CAN_ID_STD;
    TxHeader.RTR = CAN_RTR_DATA;
    TxHeader.DLC = len + 2;
    
    TxData[0] = cmd;
    TxData[1] = len;
    memcpy(&TxData[2], data, len);
    
    HAL_CAN_AddTxMessage(&hcan1, &TxHeader, TxData, NULL);
}

六、错误处理机制

void CAN_Error_Callback(void)
{
    uint32_t error = HAL_CAN_GetError(&hcan1);
    
    if(error & HAL_CAN_ERROR_BOF) {
        printf("总线关闭,尝试恢复...\r\n");
        HAL_CAN_ResetError(&hcan1);
        HAL_CAN_Start(&hcan1);
    }
    
    if(error & HAL_CAN_ERROR_PASSIVE) {
        printf("进入错误被动状态!\r\n");
        HAL_Delay(1000);
        HAL_CAN_Stop(&hcan1);
        MX_CAN1_Init(); // 重新初始化
    }
}

参考代码 基于STM32的CAN与CAN之间的通信例程 www.youwenfan.com/contentcnr/101724.html

七、测试流程

  1. 回环测试

    • 将CAN_TX/RX短接,发送测试数据验证硬件
    while(1) {
        CAN_Send_Cmd(0x01, 0xABCD);
        HAL_Delay(1000);
    }
    
  2. 双机通信测试

    • 主机发送指令:0xAA 0x10 0x02 0x34 0x12

    • 从机应答:0xAA 0x20 0x02 0x56 0x78

  3. 总线负载测试

    • 同时发送10个节点数据,监控总线利用率
    for(int i=0; i<10; i++) {
        CAN_Direct_Send(i+1, 0x55, test_data, 4);
        HAL_Delay(50);
    }
    

通过本方案可实现多节点CAN总线通信系统,支持标准帧/扩展帧、广播通信、错误恢复等完整功能。实际应用中建议根据具体需求调整过滤器配置和协议设计。

posted @ 2026-03-02 17:04  风一直那个吹  阅读(0)  评论(0)    收藏  举报