STM32F407 CAN 总线快速上手:从硬件连接到代码实现
这里为了连接稳定,使用了24分频得到125K 的速度,最高1M的话可修改为3,后面的TQBS1 TQBS2 RSJW的值使用的经验证(直接抄现成的省事,原理可查文档)最后的操作模式如果是两个模块的真实互联,使用Normal 如果只是单机调试可使用Lookback 相当于自发自收。STM32F407 有两条CAN总线,用于连接CAN设备,但使用CAN总线必须要连接CAN收发器才能使用,笔者使用了一对
·
一、硬件连接说明
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配置完成,可以实现最简单的数据收发。
更多推荐



所有评论(0)