全国服务热线 0755-27999972
您的当前所在位置:首页>>新闻中心>>技术资讯
STM32 CAN通信底层原理详解 | CAN帧结构、位时序、寄存器配置教程
2026-05-20 15:37:54/ 技术资讯/来源:特克电子

STM32 CAN通信底层原理详解 | CAN帧结构、位时序、寄存器配置教程

1、前言

在上一篇《STM32 CAN总线编程入门》中,我们讲解了CAN硬件接线、基础收发逻辑与TK1050硬件适配方案。本文进一步深入讲解CAN总线底层运行机制,包含收发器工作逻辑、CAN五大帧类型、标准数据帧详细结构、CAN位时序原理、波特率计算公式、STM32控制器寄存器机制,搭配完整库函数源码,帮助工程师吃透CAN底层原理,解决复杂总线调试、波特率不准、报文过滤异常等工程问题。

2、CAN收发器工作原理与主流芯片对比

2.1 收发器核心作用

单片机内部的CAN控制器只能处理逻辑电平,无法直接生成CAN总线差分物理电平。必须外置CAN收发器完成电平转换:将MCU的3.3V逻辑电平转换为CANH、CANL差分信号,同时将总线差分信号还原为单片机能识别的逻辑电平。除此之外,收发器集成浪涌防护、ESD静电防护、总线滤波、短路保护电路,保障工业环境长期稳定通信。

2.2 市面主流CAN收发器介绍

目前工业与车载最常用高速CAN收发器包含:TJA1050、TJA1042、SIST1050T、TK1050。四款芯片均遵循ISO11898-2高速CAN协议,引脚兼容、可相互替换。其中TK1050作为国产替代方案,具备±15KV ESD防护、优异EMC抗干扰能力、工业宽温设计,完美替代进口料,适合大批量工控、新能源、车载项目量产使用。

3、CAN总线五大帧类型详解

CAN协议一共定义五种帧类型,不同帧各司其职,共同保障总线稳定运行:

3.1 数据帧

最常用、用于用户数据传输的帧,分为标准帧CAN2.0A(11位ID)、扩展帧CAN2.0B(29位ID)。

3.2 遥控帧

用于请求远端节点发送数据,无数据段,仅携带ID标识。

3.3 错误帧

总线检测到校验错误、格式错误、应答错误时自动发送,用于通知全网节点总线异常。

3.4 过载帧

接收节点缓冲区满载,无法继续接收报文,发送过载帧延迟下一帧传输。

3.5 间隔帧

用于分隔前后报文,给硬件预留处理缓冲时间。

4、CAN标准数据帧结构(七段完整解析)

数据帧是工程开发最常用报文,由帧起始、仲裁段、控制段、数据段、CRC段、ACK段、帧结束七部分组成。

4.1 帧起始(SOF)

由1位显性电平构成,产生电平跳变,用于全网节点时间同步,标志一帧数据开始传输。

4.2 仲裁段

决定报文优先级,标准帧为11位ID。ID数值越小优先级越高;协议规定ID高7位禁止全部为隐性电平,避免总线逻辑异常。仲裁段是过滤器匹配报文的核心依据。

4.3 控制段

共6位,包含2位保留位+4位DLC数据长度码,DLC决定本帧有效数据字节,范围0~8字节。

4.4 数据段

有效载荷区域,支持0~8字节用户数据,工业设备、传感器、BMS电池数据全部存放于此。

4.5 CRC段

15位CRC校验码+1位界定符,用于校验传输过程是否出现数据畸变,硬件自动校验,无需程序干预。

4.6 ACK应答段

共2位,发送端默认发送隐性电平;接收端正确接收报文后,主动拉低为显性电平,告知发送方接收成功。无应答则判定为总线异常。

4.7 帧结束

连续7位隐性电平,标志当前报文传输结束。

5、CAN总线位时序原理与波特率计算

5.1 标准CAN位时序

CAN总线无独立时钟线,依靠位时序同步采样,一个数据位分为四段:同步段SS、传播时间段PTS、相位缓冲段PBS1、相位缓冲段PBS2。配合SJW同步补偿宽度,修正时钟偏差,保证多节点同步。

5.2 STM32位时序(三段式)

STM32 bxCAN控制器简化为三段结构:SYNC-SEG(固定1Tq)、BS1、BS2。采样点位于BS1与BS2交界处,可通过寄存器自由配置BS1、BS2时长。

5.3 位时序计算公式

单个时间片:Tq = (BRP+1) × Tpclk;
BS1时长:TS1 = Tq × (TS1[3:0]+1);
BS2时长:TS2 = Tq × (TS2[2:0]+1);
单bit总时长:T1bit = 1Tq + TS1 + TS2;
波特率公式:BaudRate = Fpclk1 / ((BS1+BS2+1) × BRP)。

5.4 举例说明(STM32F103)

APB1时钟36MHz,配置:SJW=1Tq、BS2=8Tq、BS1=9Tq、BRP=4;
波特率 = 36M / ((8+9+1)×4) = 500Kbps。

6、STM32 CAN控制器工作机制

6.1 报文寻址方式

CAN总线为内容寻址,节点无物理地址,依靠报文ID区分设备。总线所有节点都会接收全部报文,由过滤器自主筛选保留需要的报文,丢弃无关数据,降低CPU占用。

6.2 CAN协议版本

CAN2.0A:仅支持标准11位ID;CAN2.0B Passive:忽略扩展帧;CAN2.0B Active:兼容标准帧、扩展帧,工程最常用。

6.3 发送与接收资源

STM32F103内置3个发送邮箱,按ID优先级自动调度发送;配备2个接收FIFO,每个FIFO缓存3条报文,内置14组可配置过滤器,适配多节点组网。

6.4 控制器工作模式

初始化模式:用于寄存器配置,不可通信;正常模式:接入总线正常收发;睡眠模式:低功耗休眠。

6.5 硬件测试模式

静默模式:只收不发,用于总线监听;回环模式:内部自收发,无需外接收发器,适合代码自测;静默回环模式:内部自测且不干扰外部总线。

7、STM32标准库核心函数与初始化源码

7.1 常用库函数说明

本文整理STM32 CAN高频使用函数,覆盖初始化、发送、接收、中断、错误诊断,方便工程师快速查阅开发。
/* 初始化配置类 */
uint8_t CAN_Init(CAN_TypeDef* CANx, CAN_InitTypeDef* CAN_InitStruct);
void CAN_FilterInit(CAN_FilterInitTypeDef* CAN_FilterInitStruct);
void CAN_StructInit(CAN_InitTypeDef* CAN_InitStruct);

/* 发送接收类 */
uint8_t CAN_Transmit(CAN_TypeDef* CANx, CanTxMsg* TxMessage);
void CAN_Receive(CAN_TypeDef* CANx, uint8_t FIFONumber, CanRxMsg* RxMessage);

/* 模式与中断 */
uint8_t CAN_OperatingModeRequest(CAN_TypeDef* CANx, uint8_t CAN_OperatingMode);
void CAN_ITConfig(CAN_TypeDef* CANx, uint32_t CAN_IT, FunctionalState NewState);

/* 错误诊断 */
uint8_t CAN_GetLastErrorCode(CAN_TypeDef* CANx);

7.2 CAN基础初始化源码

CAN_InitTypeDef CAN_InitStruct;
CAN_InitStruct.CAN_TTCM=DISABLE;
CAN_InitStruct.CAN_ABOM =DISABLE;
CAN_InitStruct.CAN_AWUM=DISABLE;
CAN_InitStruct.CAN_NART=DISABLE;
CAN_InitStruct.CAN_RFLM=DISABLE;
CAN_InitStruct.CAN_TXFP=DISABLE;
CAN_InitStruct.CAN_Mode=CAN_Mode_LoopBack;  //回环测试模式
CAN_InitStruct.CAN_SJW=CAN_SJW_1tq;
CAN_InitStruct.CAN_BS1=CAN_BS1_9tq;
CAN_InitStruct.CAN_BS2=CAN_BS2_8tq;
CAN_InitStruct.CAN_Prescaler=4;
CAN_Init (CAN1,&CAN_InitStruct);

7.3 过滤器初始化源码

CAN_FilterInitTypeDef CAN_FilterInitStruct;
CAN_FilterInitStruct.CAN_FilterNumber=0;
CAN_FilterInitStruct.CAN_FilterActivation=ENABLE;
CAN_FilterInitStruct.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;
CAN_FilterInitStruct.CAN_FilterIdHigh=0x0000;
CAN_FilterInitStruct.CAN_FilterIdLow=0x0000;
CAN_FilterInitStruct.CAN_FilterMaskIdHigh=0x0000;
CAN_FilterInitStruct.CAN_FilterMaskIdLow=0x0000;
CAN_FilterInitStruct.CAN_FilterMode= CAN_FilterMode_IdMask;
CAN_FilterInitStruct.CAN_FilterScale=CAN_FilterScale_32bit;
CAN_FilterInit(&CAN_FilterInitStruct);

8、收发器匹配说明(TK1050适配建议)

在实际工程量产中,STM32控制器必须搭配外置高速CAN收发器使用。TK1050完全兼容TJA1050、TJA1042引脚定义,电气参数匹配STM32全部CAN时序标准。芯片内置完善保护电路,总线耐压范围宽广,抗干扰能力强,在工业工控、车载设备、储能BMS等项目中适配性极高,是目前性价比更高的国产高速CAN收发器之一。

9、总结

本文深度讲解CAN收发器作用、五大帧结构、位时序原理、波特率计算公式、STM32寄存器机制与标准库源码。掌握CAN底层原理,能够有效解决总线卡顿、丢包、波特率不匹配、报文错乱等工程难题。搭配TK1050高性能国产CAN收发器,可快速搭建稳定、低成本、高可靠的CAN通信系统,为工业自动化、车载电子项目提供成熟国产化解决方案。

这些电平是需要CAN的收发器实现的,目前CAN的收发器芯片有TAJ1050、TJA1042、SIST1050T。CAN收发器芯片能够将微控制器 的逻辑电平转换为CAN总线所使用的物理层电平,并负责将数据发送到总线上,同时接收总线上的数据并将其转换为适合微控制器处理的逻辑电平。收发器芯片还包含其他功能,如滤波、保护电路等,以确保可靠的通信。有了CAN的收发器实现差分信号到逻辑信号的转换之后,我们就可以基于CAN的控制器实现CAN的帧的通信。CAN的协议定义了五种类型的帧。 对于CAN的帧有数据帧、遥控帧、错误帧、过载帧、间隔帧。我们今天主要是针对标准的数据帧进行实现。 数据帧是由七段来组成的,当然数据帧又分为标准帧CAN2.0A、扩展帧CAN2.0B  帧起始:表示数据帧开始的段,是以显性信号表示,SOF发出后会产生一个跳变沿用于整个CAN网络的时间同步。 仲裁段:表示该帧优先级的段,该部分和CAN的过滤器相关,标准格式ID有11个位,从ID28到ID18被依次发,ID禁止设置高7位为隐性 控制段:表示数据的字节数和保留位的段,控制段是由6个位构成的,四个位为DLC,称数据长度码,表示后面要表示的数据的字节数,还有两个保留位 数据段:数据的内容,一帧可以发送0-8字节的数据 CRC段:检查帧的传输错误的段,由15个位的CRC顺序和一个位的CRC界定符构成 ACK段:表示确认正确接收的段,该段由两位组成,其中一位是界定符,对于发送单元而言在该段发两个隐性位,接收段在ACK Slot段发送显性位,通知发送单元是正常接收结束 帧结束:是以七个隐性电平表示数据帧的结束  对CAN的位时序介绍:在CAN总线上我们是读取电平来实现表征数据0、1。对于没有时钟线的CAN总线里,是以“位时序”的机制,实现对电平的正确采样,位数据是由四段组成的: 同步段SS:1Tq 传播时间段PTS:1~8Tq 相位缓冲段PBS1:1~8 Tq 相位缓冲段PBS2:2~8Tq 此外因时钟的频率偏差,传送延迟等,各单元有同步午餐,SJW为再补偿宽度,其实我们对位同步而言不需要太深入的了解其实现,该部分主要是由硬件自动完成,我们在程序设计中该部分主要是对我们的CAN波特率进行了设置,以STM32F103C8T6为例子 //CAN初始化 //tsjw:重新同步跳跃时间单元.范围:CAN_SJW_1tq~ CAN_SJW_4tq //tbs2:时间段2的时间单元.   范围:CAN_BS2_1tq~CAN_BS2_8tq; //tbs1:时间段1的时间单元.   范围:CAN_BS1_1tq ~CAN_BS1_16tq //brp :波特率分频器.范围:1~1024;  tq=(brp)*tpclk1 //波特率=Fpclk1/((tbs1+tbs2+1)*brp); //mode:CAN_Mode_Normal,普通模式;CAN_Mode_LoopBack,回环模式; //Fpclk1的时钟在初始化的时候设置为36M,如果设置CAN_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,CAN_Mode_LoopBack); //则波特率为:36M/((8+9+1)*4)=500Kbps AI写代码 在STM32 中的位时序和CAN的标准位时序有一点区别,在STM32中 包含三段,分别是同步段SYNC-SEG、位段BS1、位段BS2.采样的点是在BS1、BS2的交界处,同步段的固定长度是1Tq,对BS1、BS2可以在位时序寄存器上CAN——BTR设置他们的时间长度,它们可以在重新同步期间增加或者缩短。SJW也可以在位时序寄存器中配置。 BS1段时间: TS1=Tq x (TS1[3:0] + 1), BS2段时间: TS2= Tq x (TS2[2:0] + 1), 一个数据位的时间: T1bit =1Tq+TS1+TS2 =1+ (TS1[3:0] + 1)+ (TS2[2:0] + 1)= N Tq 其中单个时间片的长度Tq与CAN外设的所挂载的时钟总线及分频器配置有关,CAN1和CAN2外设都是挂载在APB1总线上的,而位时序寄存器CAN_BTR中的BRP[9:0]寄存器位可以设置CAN外设时钟的分频值,所以: Tq = (BRP[9:0]+1) x TPCLK 其中的PCLK指APB1时钟,默认值 为45MHz。 最终可以计算出CAN通讯的波特率: BaudRate = 1/N Tq 通过设置这些参数确定我们CAN总线的整体波特率。该部分也是我们在CAN的初始化过程中需要进行设置的。 关于报文寻址的问题 CAN网络中的通信基于与内容相关的寻址,因为CAN节点本身是没有ID的,但是可以通过ID区分CAN报文,所有CAN节点都会收到总线广播发送的所有CAN报文,每个接收方都根据自己的需求自行的选择CAN/报文。 STM32F103的CAN回环通信 stm32f103C8T6的最小系统板只有一个CAN的控制器,没有收发器,因此我们在实验中只能选择其回环模式进行测试。 CAN2.0A:只能处理标准数据帧且扩展帧的内容会识别错误 CAN2.0B  Active可以处理标准数据帧和扩展数据帧, CAN2.0B Passive 只能出来标准数据帧且扩展帧的内容会忽略 STM32的CAN控制器功能如下:  bxCAN模块可以完全自动的接收和发送CAN报文 我们可以通过控制、状态、配置寄存器实现 1.配置CAN参数,如波特率 2.请求发送报文 3.处理报文接收 4.管理中断 5.获取诊断信息 有三个发送邮箱来发送报文,发送调度器是根据优先级来决定那个邮箱的报文被发送。通过接收过滤器对广播的报文进行过滤,过滤器是和接收的邮箱进行连接的,在STM32F103中有14个位宽可变/可配置的标识符过滤器组,有两个接收的FIFO,每个FIFO都可以存放3个完整的报文。 对于CAN的控制器有三种工作模式: 初始化模式   该模式下对寄存器进行配置 正常模式       CAN总线同步,开始数据的接收和发送 睡眠模式        当我们复位后是进入睡眠模式,可降低功耗 同样的CAN的控制器有以下测试模式 静默模式:只向总线发送1不能发送0,可以从总线接收数据,可以实现对总线的统计。 环回模式:发送的数据直接到输入(总线可以监测数据),不能从总线接收数据,可用于自检 环回静默模式:发送的数据直接到输入(总线不可监测到数据),也不能从总线接收到数据 可用于自检并且不影响总线  发送处理的流程如下:   接收处理的流程如下: CAN的初始化结构体 CAN的外设功能非常的多,涉及的寄存器也是十分的丰富,我们可以通过标准库 里面的各种结构体函数及库函数对这些控制过程实现简化。 /* Initialization and Configuration functions *********************************/ uint8_t CAN_Init(CAN_TypeDef* CANx, CAN_InitTypeDef* CAN_InitStruct); void CAN_FilterInit(CAN_FilterInitTypeDef* CAN_FilterInitStruct);//初始化CAN的过滤器 void CAN_StructInit(CAN_InitTypeDef* CAN_InitStruct);//初始化CAN的控制器 void CAN_SlaveStartBank(uint8_t CAN_BankNumber); //启动CAN从机过滤器 void CAN_DBGFreeze(CAN_TypeDef* CANx, FunctionalState NewState);//控制CAN控制器的调试模式 void CAN_TTComModeCmd(CAN_TypeDef* CANx, FunctionalState NewState);//控制CAN控制器的TT-COM模式 /* Transmit functions *********************************************************/ uint8_t CAN_Transmit(CAN_TypeDef* CANx, CanTxMsg* TxMessage);//用于CAN消息的发送 uint8_t CAN_TransmitStatus(CAN_TypeDef* CANx, uint8_t TransmitMailbox);//用于获取CAN消息发送状态 void CAN_CancelTransmit(CAN_TypeDef* CANx, uint8_t Mailbox);//取消发送 /* Receive functions **********************************************************/ void CAN_Receive(CAN_TypeDef* CANx, uint8_t FIFONumber, CanRxMsg* RxMessage);//CAN消息接收 void CAN_FIFORelease(CAN_TypeDef* CANx, uint8_t FIFONumber);//清空缓冲区 uint8_t CAN_MessagePending(CAN_TypeDef* CANx, uint8_t FIFONumber);//获取CAN接收FIFO中待处理的消息数量 /* Operation modes functions **************************************************/ uint8_t CAN_OperatingModeRequest(CAN_TypeDef* CANx, uint8_t CAN_OperatingMode); uint8_t CAN_Sleep(CAN_TypeDef* CANx); uint8_t CAN_WakeUp(CAN_TypeDef* CANx); /* Error management functions *************************************************/ uint8_t CAN_GetLastErrorCode(CAN_TypeDef* CANx); uint8_t CAN_GetReceiveErrorCounter(CAN_TypeDef* CANx); uint8_t CAN_GetLSBTransmitErrorCounter(CAN_TypeDef* CANx); /* Interrupts and flags management functions **********************************/ void CAN_ITConfig(CAN_TypeDef* CANx, uint32_t CAN_IT, FunctionalState NewState); FlagStatus CAN_GetFlagStatus(CAN_TypeDef* CANx, uint32_t CAN_FLAG); void CAN_ClearFlag(CAN_TypeDef* CANx, uint32_t CAN_FLAG); ITStatus CAN_GetITStatus(CAN_TypeDef* CANx, uint32_t CAN_IT); void CAN_ClearITPendingBit(CAN_TypeDef* CANx, uint32_t CAN_IT); AI写代码 cpp 运行 CAN_InitTypeDef CAN_InitStruct; CAN_InitStruct.CAN_TTCM=DISABLE;//非时间触发通信模式 CAN_InitStruct.CAN_ABOM =DISABLE;//软件自动离线管理 CAN_InitStruct.CAN_AWUM=DISABLE;//睡眠模式通过软件自动唤醒 CAN_InitStruct.CAN_NART=DISABLE;//使用报文自动传送 CAN_InitStruct.CAN_RFLM=DISABLE;//报文不锁定,新的报文可以对旧报文覆盖 CAN_InitStruct.CAN_TXFP=DISABLE;//报文的优先级可以由标识符决定 CAN_InitStruct.CAN_Mode=mode;//回环模式 CAN_InitStruct.CAN_SJW=tsjw; CAN_InitStruct.CAN_BS1=tbs1; CAN_InitStruct.CAN_BS2=tbs2; CAN_InitStruct.CAN_Prescaler=brp; CAN_Init (CAN1,&CAN_InitStruct); AI写代码 cpp 运行 CAN_FilterInitTypeDef CAN_FilterInitStruct; CAN_FilterInitStruct.CAN_FilterNumber=0;//选择过滤器0    CAN_FilterInitStruct.CAN_FilterActivation=ENABLE;//激活过滤器0 CAN_FilterInitStruct.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;//过滤器0关联到FIFO0 CAN_FilterInitStruct.CAN_FilterIdHigh=0x0000; CAN_FilterInitStruct.CAN_FilterIdLow=0x0000; CAN_FilterInitStruct.CAN_FilterMaskIdHigh=0x0000; CAN_FilterInitStruct.CAN_FilterMaskIdLow=0x0000; CAN_FilterInitStruct.CAN_FilterMode= CAN_FilterMode_IdMask; CAN_FilterInitStruct.CAN_FilterScale=CAN_FilterScale_32bit; CAN_FilterInit(&CAN_FilterInitStruct); AI写代码 cpp 运行