CAN全称是Controller Area Network,控制器局域网络,是ISO国际标准化的串行通信协议。CAN是国际上应用最广泛的现场总线之一。CAN通信只有两根信号线,分别是CAN_H和CAN_L,CAN 控制器根据这两根线上的电位差来判断总线电平。总线申平分为显性电平和隐性申平,二者必居其一。发送方通过使总线电平发生变化,将消息发送给接收方。
CAN总线遵从“线与”机制,显性电平可以覆盖隐性电平。这就导致只有所有节点都发送隐形电平时总线才处于隐性状态。
CAN通信有5种帧类型
帧类型 | 帧用途 |
---|---|
数据帧 | 用于发送单元向接收单元传送数据的帧 |
遥控帧 | 用于接收单元向具有相同ID的发送单元请求数据的帧 |
错误帧 | 用于当检测出错误时向其它单元通知错误的帧 |
过载帧 | 用于接收单元通知其尚未做好接收准备的帧 |
帧间隔 | 用于将数据帧及遥控锁与前面的帧分离开来的帧 |
在上述的几种帧里,数据很和遥控帧有标准帧和扩展帧两种。标准帧有11位ID,扩展帧有29位ID。
CAN通信数据帧的构成如下
RTR是用来表示是否是远程帧(遥控帧)。RTR为0是数据帧,RTR为1是远程帧。扩展帧中的IDE是标识符的选择位,如果为0,使用标准标识符,如果为1,使用扩展标识符。扩展帧的SRR相当于标准帧中的RTR位。标准帧的ID禁止高七位是隐性电平。
由发送单元在非同步状态下每秒钟发送的位数称为位速率。一个位可以分成4段。
上面的这些段由称为Time Quantum(Tq)的最小时间单位组成。1个位分成4个段,一个段又分成若干个Tq,这成为位时序。
采样点是读取总线电平,并将读到的电平作为位值的点。
在总线空闲态,最先开始发送消息的单元获得发送权。当多个单元同时开始发送时,各发送单元从仲裁段的第一位开始进行仲裁。连续输出显性电平最多的单元可继续发送。
STM32F1芯片自带bxCAN 控制器 (Basic Extended CAN),即基本扩展CAN,可与 CAN 网络进行交互,它支持 2.0A 和 B 版本的CAN 协议。STM32F1的bxCAN有以下特点
bxCAN模块可以完全自动地接收和发送CAN报文,且完全支持标准标识符(11位)和扩展标识符(29位)。
bXCAN有3个主要的工作模式:初始化模式、正常模式和睡眠模式。除此之外,还有测试模式,静默模式,环回模式。
首先看一下CAN主控制寄存器 (CAN_MCR)的INRQ位。
通过介绍可以直到,想要进入初始化模式,软件先将CAN_MCR的INRQ位置1。然后等待硬件将CAN主状态寄存器(CAN_MSR)的INAK位置1。此时进入初始化模式。
当bxCAN处于初始化模式时,禁止报文的接收和发送,并且CANTX引脚输出隐性位(高电平)。
在初始化完成后,软件应该让硬件进入正常模式,以便正常接收和发送报文。继续看上面对于CAN主控制寄存器INRQ位的介绍。软件将INRQ位清0,可以使CAN从初始化模式进入正常模式。此时等待硬件将CAN主状态寄存器的INAK位清0即可。
bxCAN可工作在低功耗的睡眠模式。在该模式下,bxCAN的时钟停止了,但软件仍然可以访问邮箱寄存器。
可以看出,软件将CAN主控制寄存器的SLEEP置1,即可请求进入睡眠模式。清零该位,退出睡眠模式。另外,如果CAN_MCR寄存器的AWUM位为’1’,一旦检测到CAN总线的活动,硬件就自动对SLEEP位清’0’来唤醒bxCAN。
将CAN_BTR寄存器的SILM位置’1’,来选择静默模式。
在静默模式下,bxCAN可以正常地接收数据帧和远程帧,但只能发出隐性位,而不能真正发送报文。如果bxCAN需要发出显性位(确认位、过载标志、主动错误标志),那么这样的显性位在内部被接回来从而可以被CAN内核检测到,同时CAN总线不会受到影响而仍然维持在隐性位状态。因此,静默模式通常用于分析CAN总线的活动,而不会对总线造成影响-显性位(确认位、错误帧)不会真正发送到总线上。
将CAN_BTR寄存器的LBKM位置’1’,来选择环回模式。在环回模式下,bxCAN把发送的报文当作接收的报文并保存(如果可以通过接收过滤)在接收邮箱里。
环回模式可用于自测试。为了避免外部的影响,在环回模式下CAN内核忽略确认错误(在数据/远程帧的确认位时刻,不检测是否有显性位)。在环回模式下,bxCAN在内部把Tx输出回馈到Rx输入上,而完全忽略CANRX引脚的实际状态。发送的报文可以在CANTX引脚上检测到。
STM32将每一位分成三段
其中tpclk是APB1总线的时钟频率,默认为36MHz。
复用功能 | 没有重映射 | 部分重映射 | 完全重映射 |
---|---|---|---|
CAN_RX | PA11 | PB8 | PD0 |
CAN_TX | PA12 | PB9 | PD1 |
CAN_RX配置为上拉输入模式,CAN_TX配置为复用推挽输出。
uint8_t CAN_Init(CAN_TypeDef* CANx, CAN_InitTypeDef* CAN_InitStruct)
结构体成员如下
结构体成员 | 作用 |
---|---|
CAN_Prescaler | 设置Tq长度,范围为0~1023,实际为配置值加1 |
CAN_Mode | 设置CAN的工作模式 |
CAN_SJW | 指定CAN硬件允许延长或缩短位以执行重新同步的最大时间量,可以配置为1~4Tq |
CAN_BS1 | 设定BS1段的长度,范围是1~16Tq |
CAN_BS2 | 设定BS2段的长度,范围是1~8Tq |
CAN_TTCM | 是否使用时间触发功能 |
CAN_ABOM | 是否使用自动离线管理,使用的话可以在结点离线后,适时的自动恢复,不需要软件干预 |
CAN_AWUM | 是否使用自动唤醒 |
CAN_NART | 是否使用自动重传 |
CAN_RFLM | 是否使用锁存接收,接收FIFO溢出时,不使能该功能,则新的会覆盖旧的。使能该功能,会丢弃新的数据。 |
CAN_TXFP | 设置发送报文优先级判定方法,使能时以报文发送邮箱的先后顺序发送,不使能,按照ID优先级发送。 |
波特率 = Fpclk1 / ((CAN_BS1 + CAN_BS2 + 1)* CAN_Prescaler)
void CAN_FilterInit(CAN_FilterInitTypeDef* CAN_FilterInitStruct)
结构体内容如下
结构体成员 | 作用 |
---|---|
CAN_FilterIdHigh | 如果筛选器工作在32位模式,该成员存储ID的高16位。如果筛选器工作在16位模式,该成员存储的是完整的16位筛选ID。 |
CAN_FilterIdLow | 存储要筛选的ID,如果筛选器工作在32位模式,该成员存储ID的低16位。如果工作在16位模式,该成员存储第二个ID。 |
CAN_FilterMaskIdHigh | 存储要筛选的掩码。当筛选器工作在标识符模式,与上面的第一个成员功能相同。当筛选器工作在掩码模式时,改为存储的是掩码的高16位,或者是一个完整的16位掩码。 |
CAN_FilterMaskIdLow | 存储要筛选的掩码。当筛选器工作在标识符模式,与上面的第二个成员功能相同。当筛选器工作在掩码模式时,改为存储的是掩码的低16位,或者是一个完整的16位掩码。 |
CAN_FilterFIFOAssignment | 报文通过删选后,该报文存储到哪个FIFO,可选择FIFO0,FIFO1 |
CAN_FilterNumber | 选择要使用的筛选器编号,0~27 |
CAN_FilterMode | 设置筛选器的工作模式,可以设置为列表模式和掩码模式 |
CAN_FilterScale | 设置筛选器的位宽,32位或16位 |
CAN_FilterActivation | 是否激活筛选器 |
void CAN_ITConfig(CAN_TypeDef* CANx, uint32_t CAN_IT, FunctionalState NewState)
CAN的中断类型有很多,这里就不再一一介绍了。
#define IS_CAN_IT(IT) (((IT) == CAN_IT_TME) || ((IT) == CAN_IT_FMP0) ||\
((IT) == CAN_IT_FF0) || ((IT) == CAN_IT_FOV0) ||\
((IT) == CAN_IT_FMP1) || ((IT) == CAN_IT_FF1) ||\
((IT) == CAN_IT_FOV1) || ((IT) == CAN_IT_EWG) ||\
((IT) == CAN_IT_EPV) || ((IT) == CAN_IT_BOF) ||\
((IT) == CAN_IT_LEC) || ((IT) == CAN_IT_ERR) ||\
((IT) == CAN_IT_WKU) || ((IT) == CAN_IT_SLK))
uint8_t CAN_Transmit(CAN_TypeDef* CANx, CanTxMsg* TxMessage)
发送之前需要配置好消息的结构体,消息结构体成员如下
结构体成员 | 作用 |
---|---|
StdId | 存储报文的11位的标准标识符,范围是0x0~0x7FF |
ExtId | 存储报文的29位扩展标识符,范围是0x0~0x1FFFFFFF |
IDE | 配置使用哪个表示符,配置为STD,为标准帧。配置为EXT,为扩展帧。 |
RTR | 报文类型的标志,可以配置为CAN_RTR_Data,表示报文为数据帧。配置为CAN_RTR_Remote,表示报文为遥控帧。遥控帧没有数据段。 |
DLC | 存储数据段的长度,0~8。如果报文为遥控帧,该值配置为0。 |
Data[8] | 存储数据段数据 |
CAN接收函数为
void CAN_Receive(CAN_TypeDef* CANx, uint8_t FIFONumber, CanRxMsg* RxMessage)
CAN接收结构体与发送结构体基本相同,多了一个
结构体成员 | 作用 |
---|---|
FMI | 存储筛选器的编号,表示接收到的报文是从哪个筛选器进入FIFO的。 |
uint8_t CAN_TransmitStatus(CAN_TypeDef* CANx, uint8_t TransmitMailbox)
FlagStatus CAN_GetFlagStatus(CAN_TypeDef* CANx, uint32_t CAN_FLAG);
/*
*==============================================================================
*函数名称:Drv_Can_Init
*函数功能:初始化CAN
*输入参数:tsjw:重新同步跳跃宽度(Tsjw);tbs1:BS1长度;tbs2:BS2长度;
brp:Tq大小;mode:CAN工作模式
*返回值:无
*备 注:无
*==============================================================================
*/
void Drv_Can_Init (u8 tsjw,u8 tbs1,u8 tbs2,u16 brp,u8 mode)
{
// 结构体定义
GPIO_InitTypeDef GPIO_InitStructure;
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
// 开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE); // 打开CAN1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // PA端口时钟打开
// 初始化GPIO
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; // PA11 CAN_RX
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入模式
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; // PA12 CAN_TX
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // IO口速度为50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 初始化CAN
CAN_InitStructure.CAN_TTCM=DISABLE; // 非时间触发通信模式
CAN_InitStructure.CAN_ABOM=DISABLE; // 软件自动离线管理
CAN_InitStructure.CAN_AWUM=DISABLE; // 睡眠模式通过软件唤醒(清除CAN->MCR的SLEEP位)
CAN_InitStructure.CAN_NART=ENABLE; // 使用报文自动传送
CAN_InitStructure.CAN_RFLM=DISABLE; // 报文不锁定,新的覆盖旧的
CAN_InitStructure.CAN_TXFP=DISABLE; // 优先级由报文标识符决定
CAN_InitStructure.CAN_Mode= mode; //CAN工作模式设置
CAN_InitStructure.CAN_SJW=tsjw; // 重新同步跳跃宽度(Tsjw)为tsjw+1个时间单位 CAN_SJW_1tq~CAN_SJW_4tq
CAN_InitStructure.CAN_BS1=tbs1; // Tbs1范围CAN_BS1_1tq ~CAN_BS1_16tq
CAN_InitStructure.CAN_BS2=tbs2; // Tbs2范围CAN_BS2_1tq ~ CAN_BS2_8tq
CAN_InitStructure.CAN_Prescaler=brp; //分频系数(Fdiv)为brp+1
CAN_Init(CAN1, &CAN_InitStructure); // 初始化CAN1
// 初始化过滤器
CAN_FilterInitStructure.CAN_FilterNumber=0; // 过滤器0
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask; // 掩码模式
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; // 32位
CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000; // 32位ID
CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000; // 32位MASK
CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0; // 过滤器0关联到FIFO0
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; // 激活过滤器0
CAN_FilterInit(&CAN_FilterInitStructure); // 过滤器初始化
}