目录
CAN简介
CAN协议,全称为Controller Area Network(控制器局域网络),是一种广泛应用的串行通信协议,CAN协议是一种基于差分信号(两根线:抗干扰的能力好)的异步串行通信协议,采用双绞线作为传输介质,具有高性能、高可靠性和独特的设计特点。
IIC、SPI、串口都不是利用的差分信号的通信协议。传输距离都小于40米。
分类:(按传输速率和距离)
- 低速CAN(ISO11519)通信速率 10~125Kbps,总线长度可达 1000 米。
- 高速CAN(ISO11898)通信速率 125Kbps~1Mbps,总线长度 ≤40 米。
特点:(高可靠性)
- 多主控制:支持多主方式,即任何一个节点都可以在总线上发送数据,其他节点根据需要进行接收。当两个以上的节点同时开始发送数据时,会根据标识符(ID)决定优先级(仲裁)。
串口 | 1对1 |
IIC | 主要: 1主1从,1主多从; 多主多从(这里不好用) |
SPI | 1主1从;1主多从 |
- 系统柔软性:与总线相连的单元没有类似于“地址”的信息,因此在总线上增加单元时,连接在总线上的其他单元的软硬件及应用层都不需要改变。
- 通信速度快、距离远:数据传输速率较高,标准速率为125kbps,扩展速率可达1Mbps,且通信距离远,最远可达10KM(速率低于5Kbps)。
- 错误检测与恢复:具有错误检测、错误通知和错误恢复功能,能够确保数据传输的可靠性。
当节点接收到数据的时候,其他节点会进行检测,如果发现错误的数据,会进行销毁。
- 故障封闭功能:能够判断出错误的类型,并将引起故障的单元从总线上隔离出去。
应用领域:
CAN协议因其高效、可靠的特点,被广泛应用于多个领域:
汽车领域:用于实现车辆中各个控制单元(ECU)之间的通信,如发动机控制、车身控制、传动系统控制等。
工业自动化:用于机器人、物流设备、生产线等各种设备之间的通信与控制。
航空航天:用于飞机中的各种子系统之间的数据通信,如飞行控制系统、引擎控制系统、仪表盘、通信系统等。
船舶领域:用于实现船舶各种设备的可靠通信,包括远程控制、安全监测、航程分析、巡回检测等系统。
智能家居:连接各种智能设备,如照明设备、空调系统、安防系统等,实现设备的互联互通。
智能医疗:连接医疗设备,如监护仪、呼吸机等,实现设备间的快速通信。
CAN的 物理层 介绍
CAN网络通常由CAN控制器、CAN收发器、双绞线 和 终端电阻 组成。
- CAN控制器:负责处理数据的收发和协议转换。
- CAN收发器:负责将控制器的数字信号转换为差分信号进行传输,同时也负责将总线上的差分信号转换为数字信号供控制器处理。
- 终端电阻 :在高速CAN总线的两端分别连接一个电阻,称为终端电阻。终端电阻的主要作用:是匹配总线阻抗,提高信号质量,减少回波反射。一般来说,终端电阻的阻值为120Ω。
总线拓扑图
闭环CAN总线—高速CAN协议:
高速CAN拓扑图说明:
硬件接线:
- 所有的CAN节点都连接在CAN总线上,每个节点都有CAN控制器,通过CANRX,CANTX与CAN收发器进行连接,CAN收发器通过CANH和CANL连接到总线上。CAN总线有两根线,所有的CAN接到这2根线上。
开发板(CAN控制器)
CAN收发器 PA(CAN_TX) CAN_TX PA(CAN_RX) CAN_RX 5.5V VCC GND GND
CAN收发器 CAN总线 CANH CANH CANL CANL
开环CAN总线-低速CAN总线 :
电平标准
CAN使用差分信号进行数据传输,根据 CAN_H 和 CAN_L 上的 电位差 来判断 总线电平。
高速CAN:
- 显性电平表示逻辑 0 ,通常 CAN_H 和 CAN_L 有 2V 的压差;
- 隐性电平表示逻辑 1 ,通常 CAN_H 和 CAN_L 有 0V 的压差。
低速CAN:
- 显性电平表示逻辑 0 ,通常 CAN_H 和 CAN_L 有 3V 的压差;
- 隐性电平表示逻辑 1 ,通常 CAN_H 和 CAN_L 有-1.5 的压差。
- 显性电平在通信中具有优先权,能够覆盖隐性电平,确保数据的正确传输。
- 隐性电平则作为总线的空闲或监听状态存在,等待有节点发送数据。
电平图:
CAN控制器与收发器
CAN的控制器在后续会详细的讲:这节主要讲CAN的收发器
高速CAN的收发器:(本章主要讲这个)
![]() |
![]() |
原理图:
到这里可以完成:通过CAN总线发送010101的信号,组成各种数据帧,进行数据的传输,发送给其他的设备。
CAN 协议层 介绍
定义:CAN帧:前面介绍 通过CAN总线的差分信号 发送0和1,有一坨的010101组成的数据包,这就是数据帧。
CAN帧 种类介绍
- 数据帧(Data Frame):数据帧是CAN总线上用于传输用户数据的帧,包括必要的帧头、标识符、控制位、数据长度代码、数据域、CRC校验码 和 应答域等部分,是CAN通信中最基本和最重要的帧类型。
- 遥控帧(Remote Frame):遥控帧用于向总线上的其他节点请求发送具有相同标识符的数据帧,它没有数据域,仅通过标识符来指定所需的数据。遥控帧的帧结构与数据帧相似,但缺少数据部分。
- 错误帧(Error Frame):当CAN总线上的任何节点检测到通信错误时,会发送错误帧来通知其他节点。错误帧包含错误标志和错误界定符,用于指示错误的存在和类型。
- 过载帧(Overload Frame):过载帧用于在连续的数据帧或远程帧之间提供额外的延时,以指示接收节点尚未准备好接收下一个帧。当接收节点因内部条件限制而无法立即接收数据时,会发送过载帧来请求发送节点暂停发送。
- 帧间隔(Interframe Space):帧间隔用于隔离 数据帧 与前面的帧,确保它们之间的时间间隔足够长,以避免总线上的冲突和数据丢失。帧间隔包括连续三个隐性位(间隔段)和可能存在的空闲段,用于将数据帧或远程帧与前面的帧分隔开来。
*CAN数据帧介绍
数据帧由7段组成。数据帧又分为标准帧(CAN2.0A)和扩展帧(CAN2.0B),主要体现在仲裁段和控制段。
帧头(起始帧) |
功能
:表示数据帧的开始。
特点
:由一个显性位(0)(
Dominant Bit
)构成,此时
CAN_H
为高电平(如
3.5V
),
CAN_L
为低电平(如1.5V
),二者之间的电位差形成信号。
|
仲裁段(Arbitration Field)
|
功能
:确定发送优先级,并包含 标识符(
Identifier
)用于 唯一标识 发送者和接收者之间的通信关系。
组成:
|
控制段(Control Field)
| 组成:包含IDE位、r0 和 数据长度代码(DLC),用于定义数据帧中数据域的长度。 功能:
|
数据段(Data Field)
|
功能
:包含要传输的数据,是数据帧的主体部分。
特点
:数据域的长度可以根据
DLC
字段的值从
0 到 8
个字节不等,数据从最高位(
MSB
)开始传输。
|
CRC段(CRC Field
):
|
功能
:用于检测数据帧的传输错误。
特点
:
|
应答段(ACK Field)
|
功能
:
用来
确认数据帧的正常接收。
组成
:由
ACK槽(ACK Slot)
和
ACK界定符
两个位构成。当接收节点成功解析了数据帧并确认无误后,会在ACK
槽中发送一个显性位作为应答信号。
|
帧结束(Frame End)
|
功能
:表示数据帧的结束。
特点
:由
7
个连续的隐性位(1)构成,标志着数据帧的传输完成。
|
CAN 位时序
由于CAN总线不能像人眼一样可以直观的判断电位。只能通过固定的采样时间来获取此刻的电平。为实现这一操作:CAN总线以“位同步”机制,实现对电平的正确采样。
所谓采样点是读取总线电平,并将读到的电平作为位值的点。位置在 PBS1 结束处。
通常将每一位的数据分成四段:同步段(SS)、传播时间段(PTS)、 相位缓冲段1(PBS1)和相位缓冲段2(PBS2),每段又由多个位时序Tq(时间单位,1Tq = 5us,这个值可以自己设置)组成。
CAN总线通过 时钟同步机制 来确保 各个节点在通信过程中保持同步。时钟同步机制包括硬同步和再同步两种:
-
硬同步: 硬同步 只在帧的起始位(SOF)处进行 。 当接收节点检测到帧起始位的下降沿时,会将其与自身的位时间进行对齐,从而实现同步。
- 再同步
- 再同步在帧的后续数据位中进行。
- 如果接收节点检测到数据位的跳变沿不在自身的同步段内,则会通过延长或缩短相位缓冲段的时间来调整自身的位时间,以重新获得同步。
- 再同步时,PBS1和PBS2中增加或者减少的时间被称为“ 再同步补偿宽度(SJW)”,其范围:1~4Tq。
情况1:
情况2:
CAN仲裁机制
- CAN总线处于空闲状态,最先开始发送消息的单元获得发送权。
- 多个单元同时开始发送时,从仲裁段(报文ID)的第一位开始进行仲裁。仲裁原理如下:
标识符优先级: |
|
逐位仲裁: |
|
显性电平优先: | 在CAN总线上,显性电平(逻辑0)的优先级 高于隐性电平(逻辑1)。因此,在仲裁过程中,如果某个节点发送的是隐性电平,但检测到总线上存在显性电平,那么该节点就会知道有更高优先级的消息正在发送,并主动停止发送。 |
一旦仲裁结束,优先级最高的节点将获得总线控制权并开始传输数据。其他节点则成为接收节点并监听总线上的数据,并会自动检测总线空闲,在第一时间再次尝试发送。
STM32 CAN控制器 介绍
CAN控制器介绍
STM32的bxCAN(参考手册中22章详细介绍),即基本扩展CAN(Basic Extend CAN),是STM32微控制器系列中集成的CAN控制器模块。
协议支持
|
支持
CAN
协议
2.0A(只能识别标准的数据帧)
和
2.0B(既能识别标准数据帧又能识别扩展的数据帧)
的主动模式
|
高波特率
|
波特率(两个节点的波特率相同才能进行通信)最高可达
1
兆位
/
秒。
|
时间触发通信
| 支持时间触发通信功能,CAN的硬件内部定时器可以在TX/RX的帧起始位的采样点位置生成时间戳。 |
发送功能
|
|
接收功能
|
|
CAN控制器的模式
工作模式:
CAN控制器的工作模式有三种:初始化模式、正常模式和睡眠模式。
睡眠模式
|
在睡眠模式下,
CAN
控制器的时钟停止,以降低功耗。但
软件仍然可以访问邮箱寄存器
。
|
初始化模式
|
在初始化模式下,禁止 报文的接收和发送,并且
CANTX
引脚 输出隐性位(高电平)。此时,可以对CAN
控制器的相关
寄存器
进行配置,如位时间特性(
CAN_BTR
)和控制(
CAN_MCR
)等。
|
正常模式
|
作为总线的正常节点,可以向总线发送或接收数据。
|
测试模式:
CAN控制器的测试模式有三种:静默模式、环回模式和环回静默模式,主要用于特定的测试或调试目的,以确保CAN控制器的功能正常。
静默模式
|
特点
:
应用场景:
|
环回模式
|
特点
:
应用场景
:
|
环回静默模式
|
特点
:
应用场景
:
|
CAN控制器的框图
CAN
控制内核
|
包含各种控制
/
状态
/
配置寄存器,用于配置
CAN
控制器的模式、波特率等参数。
|
发送邮箱(
Transmit Mailbox
)
|
用来缓存待发送的
CAN
报文。
STM32
等微控制器通常具有多个发送邮箱
(如
3
个),以支持同时缓存多个报文。
|
接收
FIFO
(
First In First Out
)
|
缓存接收到的
有效CAN
报文。
CAN
控制器通常具有多个接收
FIFO
(如
2
个),以提高接收效率。
|
接收过滤器(
Receive Filter
)
|
筛选接收到的
CAN
报文,只将符合特定条件的报文保存到接收
FIFO
中。这
有助于减少
CPU
的处理负担,提高系统的响应速度。
|
- 发送处理过程
说明:邮箱不用时处于空置的状态,当缓存待发送的报文时,邮箱处于挂号状态,两个邮箱会进行优先级的排序,若此时邮箱处于最高优先级,进入预定状态。等待CAN总线处于空闲状态,进行发送报文。发送成功或失败,邮箱都会再次处于空置状态。
- 接收处理过程
说明:1个FIFO可以接收3个邮箱,收到1个有效报文时,进入挂号1的状态,在接收到1个时进入挂号2,再次接收到1个时进入挂号3状态,如果这是再有有效报文时,会溢出。当释放邮箱会先进来的最后释放,依次释放邮箱。
有效报文 指的是(数据帧直到 EOF 段的最后一位都没有错误),且通过过滤器组对标识符过滤。
- 接收过滤器
当总线上报文数据量很大时,总线上的设备会频繁获取报文,占用CPU。过滤器的存在,选择性接收有效报文,减轻系统负担。
STM32的CAN控制器支持配置过滤器组,每个过滤器组包含2个 32位 的寄存器CAN_FxR1和CAN_FxR2,用于存储要筛选的ID或掩码。对于STM32F103C8T6,如果只有一个CAN控制器,则可以配置14个过滤器组,对应的编号为0到13。
分类:
模式\位宽 | 16位(标准帧) | 32位(扩展帧) |
屏蔽位模式
|
| |
标识符列表模式
| | |
标识符列表模式:可以选择出几个特定ID的报文。寄存器内容功能就是标识符本身。
CAN控制器 位时序
举例:
- 设TS1=8、TS2=7、BRP=3,波特率 = 36000 / [( 9 + 8 + 1 ) * 4] = 500Kbps。
- 注意:通信双方波特率需要一致才能通信成功。
- 波特率的定义: 是通信系统中用于衡量信号传输速率的参数。单位时间内传输的符号(Symbol)数量,单位为波特(Baud,Bd)。例如,波特率为9600 Bd表示每秒传输9600个符号。
符号与比特的区别:
符号(Symbol):通信中可能代表一个或多个比特,具体取决于调制方式(如2-FSK、QPSK、16-QAM等)。
比特率(Bit Rate):每秒传输的比特数(bps),与波特率的关系为:
- 比特率=波特率×每符号携带的比特数比特率=波特率×每符号携带的比特数
CAN控制器 寄存器 的介绍
CAN控制和状态寄存器
- CAN主控制寄存器 (CAN_MCR)
- CAN发送状态寄存器 (CAN_TSR)
- CAN位时序寄存器 (CAN_BTR)
CAN邮箱寄存器
- 发送邮箱标识符寄存器 (CAN_TIxR) (x=0..2)
- 发送邮箱数据长度和时间戳寄存器 (CAN_TDTxR) (x=0..2)
- 发送邮箱低字节数据寄存器 (CAN_TDLxR) (x=0..2)
三个邮箱:报文包含0到8个字节数据,且从字节0开始。
- 发送邮箱高字节数据寄存器 (CAN_TDHxR) (x=0..2)
- 接收FIFO邮箱标识符寄存器 (CAN_RIxR) (x=0..1)
- 接收FIFO邮箱数据长度和时间戳寄存器 (CAN_RDTxR) (x=0..1)
- 接收FIFO邮箱低字节数据寄存器 (CAN_RDLxR) (x=0..1)
- 接收FIFO邮箱高字节数据寄存器 (CAN_RDHxR) (x=0..1)
CAN过滤器寄存器
- CAN 过滤器 主控寄存器 (CAN_FMR)
- CAN 过滤器模式寄存器 (CAN_FM1R)
- CAN 过滤器位宽寄存器 (CAN_FS1R)
- CAN 过滤器FIFO关联寄存器 (CAN_FFA1R)
- CAN 过滤器激活寄存器 (CAN_FA1R)
- CAN 过滤器组i的寄存器x (CAN_FiRx) (互联产品中i=0..27,其它产品中i=0..13;x=1..2)
- 在互联型产品中共有28组过滤器:i=0..27;在其它产品中共有14组过滤器:i=0..13。每组过滤器由2个32位的寄存器,CAN_FiR[2:1]组成。
- 只有在CAN_FAxR寄存器相应的FACTx位清’0’,或CAN_FMR寄存器的FINIT位为’1’时,才能修改相应的过滤器寄存器。
CAN 库函数 和 驱动流程
- 常用库函数
- 驱动流程
小实验:CAN收发实验
实验目的:
- 使用 回环模式 实现自发自收;
- 两个CAN设备实现收发。
硬件清单:TJA1050、开发板、ST-Link、USB转TTL
硬件接线
在stm32F103C8T6开发板上:CAN_TX和CAN_RX的GPIO口
文件代码
- can.c文件代码
#include "can.h"
#include "stdio.h"
CAN_HandleTypeDef can_handle = {0};
void can_init(void){
can_handle.Instance =CAN1;
//CAN的工作模式和位时序(波特率)的配置
can_handle.Init.Mode = CAN_MODE_LOOPBACK; //工作模式(静默、回环、静默回环):回环模式
can_handle.Init.Prescaler = 4; //配置波特率500bps 的 BRP[9:0] + 1
can_handle.Init.TimeSeg1 = CAN_BS1_9TQ; //配置波特率500bps 的 TS1[3:0] + 1
can_handle.Init.TimeSeg2 = CAN_BS2_8TQ; //配置波特率500bps 的 TS2[2:0] + 1
//上述 BRP[9:0] TS1[3:0] TS2[2:0] 在CAN_BTR寄存器中定义。tq = ([BRP[9:0]+1])*Tpclk(CAN时钟挂载在APB1总线上36MHz)
can_handle.Init.SyncJumpWidth = CAN_SJW_2TQ; //配置再同步补偿宽度 SJW
//其他工作模式的配置
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_configfilter = {0};
can_configfilter.FilterMode = CAN_FILTERMODE_IDMASK; //过滤器的模式:(掩码)屏蔽模式or列表模式。屏蔽模式
can_configfilter.FilterScale = CAN_FILTERSCALE_32BIT; //配置过滤器的位数:数据帧扩展格式,ID有28位
can_configfilter.FilterBank = 0; //非互联型开发板,只有14组过滤器,这里选择第一组过滤器
//每组过滤寄存器内有两个寄存器:CAN_FxR1和CAN_FxR2,这里不进行过滤,设置为0。
can_configfilter.FilterIdHigh = 0;
can_configfilter.FilterIdLow = 0;
can_configfilter.FilterMaskIdHigh = 0;
can_configfilter.FilterMaskIdLow = 0;
can_configfilter.FilterFIFOAssignment = CAN_FILTER_FIFO0; //选择接收过滤后有效报文的接收FIFO或FIFO1
can_configfilter.SlaveStartFilterBank = 14; //固定值,记住
can_configfilter.FilterActivation = CAN_FILTER_ENABLE; //使能过滤器
HAL_CAN_ConfigFilter(&can_handle,&can_configfilter);
//让CAN正常工作
HAL_CAN_Start(&can_handle);
}
//利用MSP初始化MCU外设,CLOCK、GPIO、NVIC
void HAL_CAN_MspInit(CAN_HandleTypeDef *hcan){
__HAL_RCC_CAN1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef gpio_initstruct;
//TXD端口 GPIOA GPIO_PIN_12
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);
//RXD端口的配置 GPIOA GPIO_PIN_11
gpio_initstruct.Pin = GPIO_PIN_11;
gpio_initstruct.Mode = GPIO_MODE_AF_INPUT; //复用输入
gpio_initstruct.Pull = GPIO_PULLUP;
gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
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; //拓展帧的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");
}
uint16_t can_receive_data(uint8_t *buf){
CAN_RxHeaderTypeDef rx_header = {0};
if(HAL_CAN_GetRxFifoFillLevel(&can_handle,CAN_FILTER_FIFO0) == 0) //FIFO中的没有空位
return 0 ;
HAL_CAN_GetRxMessage(&can_handle,CAN_FILTER_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;
}
- can.h文件代码
#ifndef __CAN_H__
#define __CAN_H__
#include "stm32f1xx.h"
void can_init(void);
void can_send_data(uint32_t id, uint8_t *buf ,uint8_t len);
uint16_t can_receive_data(uint8_t *buf);
#endif
- mian.c文件代码
#include "sys.h"
#include "led.h"
#include "delay.h"
#include "uart1.h"
#include "can.h"
#include "key.h"
uint8_t data_send[8] = {0,1,2,3,4,5,6,7};
uint8_t data_receive[8];
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
led_init(); /* LED初始化 */
uart1_init(115200);
printf("hello,world\n");
can_init();
key_init();
uint8_t key_num = 0;
while(1)
{
key_num = key_scan();
if(key_num == 1){
for(uint8_t i = 0;i< 8;i++)
data_send[i]++;
can_send_data(12345678,data_send,8);
}
can_receive_data(data_send);
}
}
注意:如何实现两个CAN设备间的通信?
- 上面函数中配置的CAN控制器的工作模式是回环模式,用于检测CAN控制器的接收和发送功能是否正常。
- 当检测CAN的回环模式下,代码能正常的接收和发送。进行下一步,将CAN控制器的工作模式配置成正常模式,实现两个CAN设备间的收发。
接线:
- CAN_TX要和收发器上的TX连接,CAN—RX和收发器上的RX连接。
- 两个收发器上的CANH接在一起,CANL接在一起。