CAN总线详解及STM32的CAN通信编程指南

对于CAN通信而言,本人之前也未接触了解过,由于实习的技术要求,因此也花费了一段时间对CAN通信进行学习,并且实现了基于STM32的CAN环回静默模式通信,因此写一遍比较详细的文章对该内容进行总结。本文的参考资料有STM32的中文参考手册、协议手册等。话不多说开始吧!

什么是CAN?

CAN是一种通信的方式,就是用来传递数据的,是属于串行通信的一种,是异步的半双工通讯,根据通信速率的不同可以分为高速CAN和低速CAN,主要的区别是在于对总线电平和数据传输速率(波特率)的定义,分别遵循ISO的不同标准。

低速CAN   通信速率 10-125Kbps,总线长度可达1000米。

高速CAN(ISO11898)通信速率125kbps~1Mbps,总线长度≤40米  经典CAN 。

CAN FD     通信速率可达5Mbps,并兼容经典CAN,遵循ISO 11898-1 做数据收发。

CAN的总线组成为:CAN控制器(芯片内的CAN外设),CAN收发器(CAN的收发器芯片),

CAN_High和CAN_Low两根线构成的总线结构

对于CAN_High、CAN_Low是共同组成一组差分的信号线,CAN网络中的物理信号是基于差分信号进行传输的,其终端的电阻是在高速CAN中防止信号反射和振铃。

低速的CAN是属于开环总线,高速的CAN是属于闭环总线。对于CAN总线有以下特点:多主控制,每个设备都是可以主动的发送数据,当然这一点就会涉及到优先级的问题,这个问题会在后面详细介绍。在CAN中是没有类似于地址的信息,数据是以广播的形式,所以这个特点也就涉及到CAN的ID相关信息。

写到现在,可以简单的回答一下几个问题:一个节点的MCU向总线上那么多的节点的某一个节点

发送消息的?一个节点又是如何知道某个信息是发送给自己或者不是发送给自己呢?这个就会涉及到CAN的ID 和CAN控制器的过滤器的内容啦。

CAN的简洁的物理层必须要配上一套更加复杂的协议

CAN的总线是使用差分信号,其物理媒介是一对双绞线,这样差分信号的优点可以实现对干扰信号的抵消,总线上是CAN_High、CAN_Low.通过电位差来判断总线的电平,当然在这个地方高速CAN和低速CAN是不一样的。

 在经典CAN及高速CAN中:

 显性电平:总线上逻辑0表示显性,遇0得0,显性最强  差分信号的电压差一般为2V

 隐形电平:总线上逻辑1表示隐形,隐形的攻击性不强 差分信号的电压差一般为0

在低速CAN中

显性电平:逻辑0表示显性 显性的电压差一般为3V

隐形电平:逻辑1表示隐性 隐性的电压差一般为-1.5V

刚刚说到,显性电平是逻辑0,并且是具有正的电压差的,因此显性电平是具有优先权的,只要有一个节点输出显性电平,总线上即为显性电平。但是隐性电平是需要所有的节点都输出隐性电平,总线上才是才是隐形电平。从逻辑上来说这是一种与逻辑,其物理的实现是通过集电极开路电路实现的。

说到这些电平是需要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

在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);
		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);

		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);

物联沃分享整理
物联沃-IOTWORD物联网 » CAN总线详解及STM32的CAN通信编程指南

发表评论