3. 标准数据帧发送与接收验证

标准数据帧发送与接收验证 学习卡片

任务说明

本案例使用两块 STM32F103(或任意带 bxCAN 的 MCU)通过 CAN 总线互联,发送端周期发送标准数据帧(11 位 ID + 8 字节数据),接收端在中断中解析并打印 ID 与数据。总线两端各接 120Ω 终端电阻,CANH-CANH、CANL-CANL 对接。

数据帧结构

SOF 1 bit Identifier (11 bit) 0x123 RTR/IDE 标准帧 DLC 8 Data (8 Byte) 0x11 0x22 ... 0x88 CRC/ACK EOF 标准数据帧格式 发送端填充 ID=0x123, DLC=8, Data[0..7] 接收端通过过滤器匹配 0x123 后进入 RX 中断

发送端代码(CAN1,PA11=RX, PA12=TX)

#include "stm32f1xx_hal.h"

CAN_HandleTypeDef hcan;

void CAN_Init(void) {
    __HAL_RCC_CAN1_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    GPIO_InitTypeDef gpio = {0};
    gpio.Pin = GPIO_PIN_11 | GPIO_PIN_12;
    gpio.Mode = GPIO_MODE_AF_PP;
    gpio.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOA, &gpio);

    hcan.Instance = CAN1;
    hcan.Init.Prescaler = 4;          // 36MHz/4 = 9MHz
    hcan.Init.Mode = CAN_MODE_NORMAL;
    hcan.Init.SyncJumpWidth = CAN_SJW_1TQ;
    hcan.Init.TimeSeg1 = CAN_BS1_5TQ;
    hcan.Init.TimeSeg2 = CAN_BS2_2TQ;
    hcan.Init.TimeTriggeredMode = DISABLE;
    hcan.Init.AutoBusOff = DISABLE;
    hcan.Init.AutoWakeUp = DISABLE;
    hcan.Init.AutoRetransmission = ENABLE;
    hcan.Init.ReceiveFifoLocked = DISABLE;
    hcan.Init.TransmitFifoPriority = DISABLE;
    HAL_CAN_Init(&hcan);              // 波特率 = 9MHz/(1+5+2) = 1Mbps
}

void CAN_SendStdFrame(void) {
    CAN_TxHeaderTypeDef tx = {0};
    uint8_t data[8] = {0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88};
    uint32_t mailbox;

    tx.StdId = 0x123;                 // 11 位标准标识符
    tx.IDE   = CAN_ID_STD;
    tx.RTR   = CAN_RTR_DATA;
    tx.DLC   = 8;
    tx.TransmitGlobalTime = DISABLE;

    if (HAL_CAN_AddTxMessage(&hcan, &tx, data, &mailbox) != HAL_OK) {
        printf("TX failed\r\n");
    }
}

int main(void) {
    HAL_Init();
    SystemClock_Config();             // 72MHz, APB1=36MHz
    CAN_Init();
    while (1) {
        CAN_SendStdFrame();
        HAL_Delay(500);
    }
}

接收端代码(CAN1,中断接收)

#include "stm32f1xx_hal.h"

CAN_HandleTypeDef hcan;

void CAN_Init(void) {
    // 与发送端相同的 GPIO 与 CAN 初始化配置
    // ...(略去重复部分,Prescaler/BS1/BS2 必须一致)
}

void CAN_Filter_Init(void) {
    CAN_FilterTypeDef f = {0};
    f.FilterBank = 0;
    f.FilterMode = CAN_FILTERMODE_IDMASK;
    f.FilterScale = CAN_FILTERSCALE_32BIT;
    f.FilterIdHigh = 0x123 << 5;      // 标准 ID 左移 5 位
    f.FilterIdLow = 0;
    f.FilterMaskIdHigh = 0x7FF << 5;  // 只匹配 11 位 ID
    f.FilterMaskIdLow = 0;
    f.FilterFIFOAssignment = CAN_RX_FIFO0;
    f.FilterActivation = ENABLE;
    HAL_CAN_ConfigFilter(&hcan, &f);
}

void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) {
    CAN_RxHeaderTypeDef rx;
    uint8_t data[8];
    if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &rx, data) != HAL_OK) return;

    if (rx.IDE == CAN_ID_STD && rx.DLC == 8) {
        printf("ID=0x%03X DLC=%d Data=", rx.StdId, rx.DLC);
        for (int i = 0; i < rx.DLC; i++) printf("%02X ", data[i]);
        printf("\r\n");
    }
}

int main(void) {
    HAL_Init();
    SystemClock_Config();
    CAN_Init();
    CAN_Filter_Init();
    HAL_CAN_Start(&hcan);
    HAL_CAN_ActivateNotification(&hcan, CAN_IT_RX_FIFO0_MSG_PENDING);
    while (1) { }
}

运行与验证

  1. 两块板子烧录对应固件,CANH/CANL 对接,各接 120Ω 终端电阻。
  2. 接收端串口接 PC,波特率 115200。
  3. 上电后接收端串口应每 500ms 打印一行:ID=0x123 DLC=8 Data=11 22 33 44 55 66 77 88。
  4. 若打印为空,检查:两端波特率是否一致、终端电阻是否接好、过滤器掩码是否匹配 0x123。

通过条件:接收端稳定输出上述格式行,ID 与数据与发送端一致,无丢帧或错误帧。