深入解析CAN协议3:STM32驱动CAN总线通信详解
STM32系列微控制器内置的CAN控制器(
一、STM32 CAN控制器(bxCAN)概述
STM32系列微控制器内置的CAN控制器(bxCAN,Basic Extended CAN)支持CAN 2.0A/B协议,具备高效处理多节点通信的能力,广泛应用于汽车电子、工业控制等领域。其核心特性包括:
- 多邮箱管理:提供3个发送邮箱和2个接收FIFO(FIFO0/FIFO1),支持优先级发送与接收队列管理。
- 灵活过滤器:最多28个可配置的过滤器组,支持标识符掩码模式(ID+Mask)和列表模式(List),实现精准帧过滤。
- 高兼容性:支持标准帧(11位ID)和扩展帧(29位ID),并可通过配置自动处理不同格式的帧。
- 错误处理机制:支持自动重传、错误计数器管理及总线离线恢复功能,确保通信可靠性。
1. CAN控制器框图
CAN控制内核:包含各种控制/状态/配置寄存器,用于配置CAN控制器的模式、波特率等参数。
发送邮箱(Transmit Mailbox):用来缓存待发送的CAN报文。STM32等微控制器通常具有多个发送邮箱(如3个),以支持同时缓存多个报文。
接收FIFO(First In First Out):缓存接收到的有效CAN报文。CAN控制器通常具有多个接收FIFO(如2个),以提高接收效率。
接收过滤器(Receive Filter):筛选接收到的CAN报文,只将符合特定条件的报文保存到接收FIFO中。这有助于减少CPU的处理负担,提高系统的响应速度
二、STM32 CAN控制器核心功能解析
1. 工作模式
CAN控制器的工作模式有三种:初始化模式、正常模式和睡眠模式。
睡眠模式:在睡眠模式下,CAN控制器的时钟停止,以降低功耗。但软件仍然可以访问邮箱寄存器。
初始化模式:在初始化模式下,禁止报文的接收和发送,并且CANTX引脚输出隐性位(高电平)。此时,可以对CAN控制器的相关寄存器进行配置,如位时间特性(CAN_BTR)和控制(CAN_MCR)等。
正常模式:作为总线的正常节点,可以向总线发送或接收数据。
2. 测试模式
CAN控制器的测试模式有三种:静默模式、环回模式和环回静默模式,主要用于特定的测试或调试目的,以确保CAN控制器的功能正常。
- 环回模式:内部自环测试,数据不发送至总线,用于调试。
- 静默模式:仅监听总线,不主动发送数据,适用于网络监控。
- 环回静默模式:环回静默模式结合了静默模式和环回模式的特点。

3. 发送处理过程

4. 接收处理过程:

5. 接收过滤器:
当总线上报文数据量很大时,总线上的设备会频繁获取报文,占用CPU。过滤器的存在,选择性接收有效报文,减轻系统负担。
选择模式可设置屏蔽位模式或标识符列表模式,寄存器内容的功能就有所区别。屏蔽位模式,可以选择出一组符合条件的报文。寄存器内容功能相当于是否符合条件。标识符列表模式,可以选择出几个特定ID的报文。寄存器内容功能就是标识符本身。
6. 波特率配置
CAN通信速率由以下参数决定:
- 预分频器(Prescaler):将APB1时钟分频为时间量子(Tq)基准。
- 位时序分段:
- BS1(Phase Segment 1):传播段,补偿物理延迟(1~16 Tq)。
- BS2(Phase Segment 2):相位缓冲段,用于时序同步(1~8 Tq)。
- SJW(同步跳转宽度):允许的最大时序调整量(1~4 Tq)。
计算公式:
例:APB1时钟42MHz,BS1=5 Tq,BS2=3 Tq,Prescaler=6,则波特率为:


设TS1=8、TS2=7、BRP=3,波特率 = 36000 / [( 9 + 8 + 1 ) * 4] = 500Kbps。
注意:通信双方波特率需要一致才能通信成功。
三、STM32 CAN通信实现步骤
1. 硬件配置
- 引脚复用:将CAN_TX(如PB9)和CAN_RX(如PB8)配置为复用功能(AF9)。
- 收发器连接:通过MCP2551等收发器将TTL电平转换为差分信号(CANH/CANL)。
2. 基本驱动流程

3. 数据收发示例
CAN初始化
void can_init(void)
{
can_handle.Instance = CAN1;
can_handle.Init.Mode = CAN_MODE_NORMAL;
can_handle.Init.Prescaler = 4;
can_handle.Init.TimeSeg1 = CAN_BS1_9TQ;
can_handle.Init.TimeSeg2 = CAN_BS2_8TQ;
can_handle.Init.SyncJumpWidth = CAN_SJW_1TQ;
can_handle.Init.AutoBusOff = DISABLE; /* 禁止自动离线管理 */
can_handle.Init.AutoRetransmission = DISABLE; /* 禁止自动重发 */
can_handle.Init.AutoWakeUp = DISABLE; /* 禁止自动唤醒 */
can_handle.Init.ReceiveFifoLocked = DISABLE; /* 禁止接收FIFO锁定 */
can_handle.Init.TimeTriggeredMode = DISABLE; /* 禁止时间触发通信模式 */
can_handle.Init.TransmitFifoPriority = DISABLE; /* 禁止发送FIFO优先级 */
HAL_CAN_Init(&can_handle);
CAN_FilterTypeDef can_filterconfig = {0};
can_filterconfig.FilterMode = CAN_FILTERMODE_IDMASK;
can_filterconfig.FilterScale = CAN_FILTERSCALE_32BIT;
can_filterconfig.FilterIdHigh = 0;
can_filterconfig.FilterIdLow = 0;
can_filterconfig.FilterMaskIdHigh = 0;
can_filterconfig.FilterMaskIdLow = 0;
can_filterconfig.FilterBank = 0;
can_filterconfig.FilterFIFOAssignment = CAN_FILTER_FIFO0;
can_filterconfig.FilterActivation = CAN_FILTER_ENABLE;
can_filterconfig.SlaveStartFilterBank = 14;
HAL_CAN_ConfigFilter(&can_handle, &can_filterconfig);
HAL_CAN_Start(&can_handle);
}
MSP函数
void HAL_CAN_MspInit(CAN_HandleTypeDef *hcan)
{
__HAL_RCC_CAN1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef gpio_initstruct;
gpio_initstruct.Pin = GPIO_PIN_12;
gpio_initstruct.Mode = GPIO_MODE_AF_PP;
gpio_initstruct.Pull = GPIO_PULLUP;
gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &gpio_initstruct);
gpio_initstruct.Pin = GPIO_PIN_11;
gpio_initstruct.Mode = GPIO_MODE_AF_INPUT;
HAL_GPIO_Init(GPIOA, &gpio_initstruct);
}
发送数据帧:
void can_send_data(uint32_t id, uint8_t *buf, uint8_t len)
{
CAN_TxHeaderTypeDef tx_header = {0};
uint32_t tx_mail = CAN_TX_MAILBOX0;
tx_header.ExtId = id;
tx_header.DLC = len;
tx_header.IDE = CAN_ID_EXT;
tx_header.RTR = CAN_RTR_DATA;
HAL_CAN_AddTxMessage(&can_handle, &tx_header, buf, &tx_mail);
while(HAL_CAN_GetTxMailboxesFreeLevel(&can_handle) != 3);
uint8_t i = 0;
printf("发送数据:\r\n");
for(i = 0; i < len; i++)
printf("%X ", buf[i]);
printf("\r\n");
}
接收数据帧(中断方式):
uint8_t can_receive_data(uint8_t *buf)
{
CAN_RxHeaderTypeDef rx_header = {0};
if(HAL_CAN_GetRxFifoFillLevel(&can_handle, CAN_RX_FIFO0) == 0)
return 0;
HAL_CAN_GetRxMessage(&can_handle, CAN_RX_FIFO0, &rx_header, buf);
uint8_t i = 0;
printf("接收数据:\r\n");
for(i = 0; i < rx_header.DLC; i++)
printf("%X ", buf[i]);
printf("\r\n");
return rx_header.DLC;
}
四、调试与优化建议
- 环回模式测试:验证硬件连接前,先通过内部环回模式测试代码逻辑。
- 错误中断处理:启用错误中断(HAL_CAN_ActivateNotification),监控总线状态。
- 终端电阻匹配:确保总线两端接入120Ω电阻,减少信号反射。
- 时序优化:根据实际布线长度调整BS1/BS2,确保采样点位于位时间70%~80%处。
五、扩展应用:CAN FD支持
新一代STM32(如H7/G4系列)支持CAN FD协议,数据段速率与仲裁段分离,最大传输64字节数据,速率可达5Mbps。需注意硬件兼容性及协议栈适配。
参考资料
CAN入门(瑞萨科技)
良许嵌入式
微信交流群:
更多推荐



所有评论(0)