一、STM32 CAN控制器(bxCAN)概述

STM32系列微控制器内置的CAN控制器(bxCAN,Basic Extended CAN)支持CAN 2.0A/B协议,具备高效处理多节点通信的能力,广泛应用于汽车电子、工业控制等领域。其核心特性包括:

  1. 多邮箱管理:提供3个发送邮箱和2个接收FIFO(FIFO0/FIFO1),支持优先级发送与接收队列管理。
  2. 灵活过滤器:最多28个可配置的过滤器组,支持标识符掩码模式(ID+Mask)和列表模式(List),实现精准帧过滤。
  3. 高兼容性:支持标准帧(11位ID)和扩展帧(29位ID),并可通过配置自动处理不同格式的帧。
  4. 错误处理机制:支持自动重传、错误计数器管理及总线离线恢复功能,确保通信可靠性。
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;
}


四、调试与优化建议

  1. 环回模式测试:验证硬件连接前,先通过内部环回模式测试代码逻辑。
  2. 错误中断处理:启用错误中断(HAL_CAN_ActivateNotification),监控总线状态。
  3. 终端电阻匹配:确保总线两端接入120Ω电阻,减少信号反射。
  4. 时序优化:根据实际布线长度调整BS1/BS2,确保采样点位于位时间70%~80%处。

五、扩展应用:CAN FD支持

新一代STM32(如H7/G4系列)支持CAN FD协议,数据段速率与仲裁段分离,最大传输64字节数据,速率可达5Mbps。需注意硬件兼容性及协议栈适配。


参考资料
CAN入门(瑞萨科技)
良许嵌入式
微信交流群:
在这里插入图片描述

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐