第09章 CAN通信基础
约 1635 字大约 5 分钟
2026-07-21
9.1 CAN通信介绍
CAN(Controller Area Network,控制器局域网) 是一种功能丰富的车用总线标准,被设计用于在不需要主机(Host)的情况下,允许网络上的单片机和仪器相互通信。
CAN通信是一种串行、异步、半双工的通信方式。它基于消息传递协议,拥有良好的弹性调整能力,可以在现有网络中增加节点而不用在软、硬件上做出调整。

物理层
网络节点组成
CAN网络节点由CAN控制器和CAN收发器组成:
- 控制器:由MCU提供,STM32内部提供了1个CAN控制器
- 收发器:需要专门芯片,本项目使用 TJA1050 收发器芯片
控制器与收发器之间通过CAN_Tx及CAN_Rx信号线相连;收发器与CAN总线之间使用CAN_High及CAN_Low信号线相连。

差分信号
CAN使用差分信号传输,CAN_High和CAN_Low两根线:
- 显性电平(逻辑0):CAN_H和CAN_L之间有压差(约2V)
- 隐性电平(逻辑1):CAN_H和CAN_L之间无压差
差分信号具有强抗干扰能力,适合工业和汽车环境。



协议层
CAN帧种类
CAN总线上有5种不同的报文类型:
| 帧类型 | 说明 |
|---|---|
| 数据帧 | 最常用,用于发送节点向接收节点发送数据 |
| 远程帧(遥控帧) | 用于请求特定ID节点发送数据,不携带数据 |
| 错误帧 | 检测出错误时向其他节点通知 |
| 过载帧 | 用于提供附加延迟 |
| 帧间隔 | 用于将数据帧及遥控帧与前面的帧分离 |
数据帧结构
数据帧包含:帧起始(SOF)、仲裁段(ID)、控制段(IDE/RTR/DLC)、数据段(0~8字节)、CRC段、ACK段、帧结束(EOF)。



CAN总线仲裁
- 总线空闲时,最先发送消息的单元获得发送权
- 多个单元同时发送时,从仲裁段(报文ID)的第一位开始仲裁,ID越小优先级越高
- 竞争失败会自动检测总线空闲,在第一时间重新尝试发送
CAN位时序
| 段名 | 缩写 | 时间份额(Tq) | 作用 |
|---|---|---|---|
| 同步段 | SS | 固定1 Tq | 实现硬同步 |
| 传播时间段 | PTS | 1-8 Tq | 补偿物理延迟 |
| 相位缓冲段1 | PBS1 | 1-8 Tq | 可被延长(重新同步) |
| 相位缓冲段2 | PBS2 | 2-8 Tq | 可被缩短(重新同步) |
采样点 位于PBS1末尾,一般设置在位时间的75%~90%。
波特率计算公式:
波特率 = 1 / Tbit = 1 / (Tss + Tpts + Tpbs1 + Tpbs2)9.2 STM32 CAN外设
STM32的bxCAN控制器支持CAN 2.0A和2.0B Active标准,最高通讯速率1Mb/s。
三种工作模式

| 模式 | 说明 |
|---|---|
| 初始化模式 | 用于配置寄存器 |
| 正常模式 | 正常通讯 |
| 睡眠模式 | 低功耗,上电默认 |
三种测试模式
| 模式 | 说明 | 影响总线 |
|---|---|---|
| 静默模式 | 只接收不发送 | 不影响 |
| 环回模式 | 自收自发 | 影响 |
| 环回静默模式 | 自收自发但不影响总线 | 不影响 |



功能描述

- 发送邮箱:3个,缓存待发送报文
- 接收FIFO:2个,每个可存3个完整报文
- 接收滤波器:14个过滤器组,支持标识符列表模式和掩码模式

STM32位时序特点
STM32将CAN标准时序中的传播时间段(PTS)和相位缓冲段1(PBS1)合并为 BS1。

9.3 CAN通信案例1:环回静默模式测试
需求
使用环回静默模式测试CAN能否正常工作,把接收到的报文数据发送到串口输出。

CubeMX设置
- 使用重定向引脚 PB8(RX) 和 PB9(TX)

- 配置关键参数:
- Automatic Bus-Off Management: Enable
- Automatic Wake-Up Mode: Enable
- Automatic Retransmission: Disable
- Receive Fifo Locked Mode: Disable
- Transmit Fifo Priority: Disable

HAL库相关函数
| 函数 | 功能 |
|---|---|
HAL_CAN_ConfigFilter() | 配置CAN接收过滤器 |
HAL_CAN_Start() | 启动CAN外设 |
HAL_CAN_AddTxMessage() | 将消息添加到发送邮箱并发起传输 |
HAL_CAN_GetRxFifoFillLevel() | 获取FIFO中暂存的消息数量 |
HAL_CAN_GetRxMessage() | 从FIFO中读取一条消息 |
HAL_CAN_GetTxMailboxesFreeLevel() | 获取空闲发送邮箱数量 |
代码实现
// 配置CAN过滤器(接收所有消息)
void Int_CAN_ConfigFilter(void)
{
CAN_FilterTypeDef sFilterConfig;
sFilterConfig.FilterBank = 0;
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
sFilterConfig.FilterIdHigh = 0x0000;
sFilterConfig.FilterIdLow = 0x0000;
sFilterConfig.FilterMaskIdHigh = 0x0000;
sFilterConfig.FilterMaskIdLow = 0x0000;
sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0;
sFilterConfig.FilterActivation = ENABLE;
HAL_CAN_ConfigFilter(&hcan, &sFilterConfig);
}
// 发送CAN消息
void Int_CAN_Send(uint32_t std_id, uint8_t *tx_data, uint32_t tx_len)
{
while (HAL_CAN_GetTxMailboxesFreeLevel(&hcan) == 0);
CAN_TxHeaderTypeDef sTxHeader;
sTxHeader.StdId = std_id;
sTxHeader.RTR = CAN_RTR_DATA;
sTxHeader.IDE = CAN_ID_STD;
sTxHeader.DLC = tx_len;
uint32_t tx_mailbox;
HAL_CAN_AddTxMessage(&hcan, &sTxHeader, tx_data, &tx_mailbox);
}
// 接收CAN消息
void Int_CAN_Receive(CAN_RxMsgTypeDef *rx_msg_arr, uint32_t *rx_len)
{
do {
*rx_len = HAL_CAN_GetRxFifoFillLevel(&hcan, CAN_RX_FIFO0);
} while (*rx_len == 0);
for (uint32_t i = 0; i < *rx_len; i++) {
CAN_RxHeaderTypeDef sRxHeader;
HAL_CAN_GetRxMessage(&hcan, CAN_RX_FIFO0, &sRxHeader, rx_msg_arr[i].Data);
rx_msg_arr[i].StdId = sRxHeader.StdId;
rx_msg_arr[i].ExtId = sRxHeader.ExtId;
rx_msg_arr[i].IDE = sRxHeader.IDE;
rx_msg_arr[i].DLC = sRxHeader.DLC;
}
}消息结构体定义:
typedef struct
{
uint32_t StdId; // 标准ID
uint32_t ExtId; // 扩展ID
uint8_t IDE; // 标识符类型
uint8_t DLC; // 数据长度代码
uint8_t Data[8]; // 数据字段
} CAN_RxMsgTypeDef;9.4 CAN通信案例2:双机测试(一发一收)
需求
使用2块开发板实现CAN消息的发送和接收。
硬件连接
将两块开发板的CAN_High和CAN_Low对应连接即可。
配置
将Test Mode改为Normal模式,其他不变。

发送方核心代码
// CAN初始化后发送消息
Int_CAN_ConfigFilter();
HAL_CAN_Start(&hcan);
uint32_t std_id = 0x0001;
uint8_t tx_data[] = "yuJJ";
Int_CAN_Send(std_id, tx_data, 4);接收方核心代码
// 轮询接收
while (1) {
Int_CAN_Receive(rx_msg_arr, &rx_msg_cnt);
for (uint32_t i = 0; i < rx_msg_cnt; i++) {
printf("ID: 0x%08X, DLC: %d, Data: %s\n",
rx_msg_arr[i].StdId, rx_msg_arr[i].DLC, rx_msg_arr[i].Data);
}
}文档生成于: 2026-07-21
