3. 标准数据帧发送与接收验证
任务说明
本案例使用两块 STM32F103(或任意带 bxCAN 的 MCU)通过 CAN 总线互联,发送端周期发送标准数据帧(11 位 ID + 8 字节数据),接收端在中断中解析并打印 ID 与数据。总线两端各接 120Ω 终端电阻,CANH-CANH、CANL-CANL 对接。
数据帧结构
发送端代码(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) { }
}
运行与验证
- 两块板子烧录对应固件,CANH/CANL 对接,各接 120Ω 终端电阻。
- 接收端串口接 PC,波特率 115200。
- 上电后接收端串口应每 500ms 打印一行:
ID=0x123 DLC=8 Data=11 22 33 44 55 66 77 88。 - 若打印为空,检查:两端波特率是否一致、终端电阻是否接好、过滤器掩码是否匹配 0x123。
通过条件:接收端稳定输出上述格式行,ID 与数据与发送端一致,无丢帧或错误帧。