当前位置: 首页 > news >正文

细说STM32F407单片机CAN基础知识

目录

一、CAN总线结构和传输协议

1、 CAN总线结构

(1)闭环结构的CAN总线网络

(2)开环结构的CAN总线网络

(3)隐性电平和显性电平

2、CAN总线传输协议

(1)CAN总线传输特点

(2)位时序和波特率

1)同步段(SYNC_SEG)

2)位段1(Bit Segment 1,BS1)

3)位段2(Bit Segment 2,BS2)

3.帧的种类

4.标准格式数据帧和遥控帧

5.扩展格式数据帧和遥控帧

6.优先级法则

二、CAN外设工作原理和HAL驱动程序

1、片上CAN外设的功能概述

2、 CAN模块的基本控制

3、 CAN模块的测试模式

(1)静默模式(silent mode)

(2)回环模式(loop back mode)

(3)回环与静默组合模式(loop back combined with silent mode)

4、消息发送

5、消息接收

6、标识符筛选

(1)标识符筛选原理

1)1个32位筛选器——标识符掩码模式

2)2个32位筛选器——标识符列表模式

3)2个16位筛选器——标识符掩码模式

4)个16位筛选器——标识符列表模式

(2)函数HAL_CAN_ConfigFilter()

7、中断及处理

(1)中断和中断事件

(2)发送中断的事件源和回调函数

(3)FIFO0的中断事件源和回调函数

(4)FIFO1的中断事件源和回调函数

(5)状态改变或错误的中断事件源和回调函数


        CAN是控制器区域网络(Controller Area Network)的缩写。CAN总线是一种适用于工业设备的高性能总线网络。STM32F407有2个CAN控制器,开发板上有2个CAN收发器,可以进行CAN总线网络的通信试验。本文使用旺宝红龙STM32F407ZGT6 KIT V1.0开发板。

        CAN的高性能和可靠性已被普遍认可,并被广泛应用于船舶、医疗设备、工业设备等方面,特别是在汽车的控制方面,已经成为汽车网络的标准协议。 

一、CAN总线结构和传输协议

        作为一种串行通信总线,如同I2C总线协议一样,CAN总线也有物理层定义和传输协议定义。

1、 CAN总线结构

        CAN总线网络的结构有闭环和开环两种形式

(1)闭环结构的CAN总线网络

        总线两端各连接一个120Ω的电阻,两根信号线形成回路。这种CAN总线网络由ISO 11898标准定义,是高速、短距离的CAN网络,通信速率为125kbit/s到1Mbit/s。在1Mbit/s通信速率时,总线最长达40m。

 

(2)开环结构的CAN总线网络

        两根信号线独立,各自串联一个2.2k的电阻。这种CAN总线网路由ISO11519-2标准定义,是低速、远距离的CAN网络,通信速率最高为125kbit/s。

 

(3)隐性电平和显性电平

        CAN总线只有两根信号线CANH和CANL,没有时钟同步信号。所以CAN是一种异步通信方式,与UART的异步通信方式类似,而SPI、I2C是以时钟信号同步的同步通信方式。

       CAN总线的两根信号线通常采用双绞线,传输的是差分信号,通过两根信号线的电压差CANH-CANL来表示总线电平。以差分信号传输信息具有抗干扰能力强,能有效抑制外部电磁干扰等优点,这也是CAN总线在工业上应用广泛的一个原因。使用差分信号表示总线电平的还有RS485网络,也是一种常用的工业现场总线。

        两根信号线的电压差CANH-CANL表示CAN总线的电平,与传输的逻辑信号1或0对应。对应于逻辑1的称为隐性(Recessive)电平,对应于逻辑0的称为显性(Dominant)电平。对应于逻辑1和逻辑0,开环结构和闭环结构CAN网络的CANH和CANL的电压值不一样,隐性电平和显性电平的电压值也不一样。

典型电压

C A N  

闭环(高速)

开环(低速)

隐性(逻辑1)

显性(逻辑0 )

隐性(逻辑1)

显性(逻辑0)

CANH/V

2.5

3.5

1.75

4.0

CANL/V

2.5

1.5

3.25

1.0

CANH-CANL/V

0

2.0

-1.5

3.0

        在CAN总线网络中,CAN总线上的一个终端设备称为一个节点(Node),在CAN网络中,没有主设备和从设备的区别。一个CAN节点的硬件部分一般由CAN控制器和CAN收发器两个部分组成。CAN控制器负责CAN总线的逻辑控制,实现CAN传输协议;CAN收发器主要负责MCU逻辑电平与CAN总线电平之间的转换。

        CAN控制器一般是MCU的片上外设,例如,STM32F407有两个CAN控制器。CAN收发器一般是单独的芯片,并且根据CAN总线的结构不同,需要使用不同的CAN收发器芯片,例如,旺宝红龙STM32F407ZGT6 KIT V1.0开发板上使用的CAN收发器芯片是SN65HVD230,只能构成闭环网络结构

2、CAN总线传输协议

(1)CAN总线传输特点

        CAN总线的数据传输有其自身的特点,主要有以下几点。

  • ● CAN总线上的节点既可以发送数据又可以接收数据,没有主从之分。但是在同一个时刻,只能有一个节点发送数据,其他节点只能接收数据。
  • ● CAN总线上的节点没有地址的概念。CAN总线上的数据是以帧为单位传输的,帧又分为数据帧、遥控帧等多种帧类型,帧包含需要传输的数据或控制信息。
  • ● CAN总线具有"线与"的特性,也就是当有两个节点同时向总线发送信号时,一个发送显性电平(逻辑0),另一个发送隐性电平(逻辑1),则总线呈现为显性电平。这个特性被用于总线仲裁,也就是哪个节点优先占用总线进行发送操作。
  • 每个帧有一个标识符(Identifier,以下简称ID)。ID不是地址,它表示传输数据的类型,也可以用于总线仲裁时确定优先级。例如,在汽车的CAN总线上,假设用于碰撞检测的节点输出数据帧的ID为01,车内温度检测节点发送数据帧的ID为05等。
  • 每个CAN节点都接收数据,但是可以对接收的帧根据ID进行过滤。只有节点需要的数据才会被接收并进一步处理,不需要的数据会被自动舍弃。例如,假设安全气囊控制器只接受碰撞检测节点发出的ID为01的帧,这种ID的过滤是由硬件完成的,以便安全气囊控制器在发生碰撞时能及时响应。
  • ● CAN总线通信是半双工的,即总线不能同时发送和接收。在多个节点竞争总线进行发送时,通过ID的优先级进行仲裁,竞争胜出的节点继续发送,竞争失败的节点立刻转入接收状态。
  • ● CAN总线没有用于同步的时钟信号,所以需要规定CAN总线通信的波特率,所有节点都使用相同的波特率进行通信。

(2)位时序和波特率

        一个CAN网络需要规定一个通信的波特率,各节点都以相同的波特率进行数据通信。位时序指的是一个节点采集CAN总线上的一个位数据的时序,通过位时序的控制,CAN总线可以进行位同步,以吸收节点时钟差异产生的波特率误差,保证接收数据的准确性。 

        图中的标称位时间(Nominal Bit Time,NBT)指的是传输一个位数据的时间,用于确定CAN总线的波特率。这个时间被分成了3段。

1)同步段(SYNC_SEG)

        在这个时间段内,总线上应该发生一次位信号的跳变。如果节点在同步段检测到总线上的一个跳变沿,就表示节点与总线是同步的。同步段长度固定为1个tq。

        tq(time quantum [ˈkwɑ:ntəm])被称为时间片,tq由CAN控制器的时钟频率f_{CAN}决定。在STM32F407中,两个CAN控制器在APB1总线上,CAN控制器有预分频器,APB1总线的时钟信号PCLK1经分频后得到f_{CAN}

2)位段1(Bit Segment 1,BS1)

        定义了采样点的位置。在BS1结束的时间点对总线采样,得到的电平就是这个位的电平。BS1的初始长度是1到16个tq,但它的长度可以在再同步(resynchronization)的时候被自动加长,以补偿各节点频率差异导致的正相位漂移。

3)位段2(Bit Segment 2,BS2)

        定义了发送点的位置。BS2的初始长度是1到8个tq,再同步时可以被自动缩短,以补偿负相位漂移。

        CAN控制器可以自动对位时序进行再同步,再同步时自动调整BS1和BS2的长度,位段加长或缩短的上限称为再同步跳转宽度(Resynchronization Jump Width,SJW),SJW的取值是1到4个tq。

CAN总线的波特率就由标称位时间长度NBT决定,而NBT是位时序3个段的时间长度和,即

NBT=(1+m+n)×tq。
Baudrate=1/NBT

3.帧的种类

        CAN网络通信是通过5种类型的帧(frame)进行的,这5种帧及其用途如表:

帧类型

帧用途

数据帧(Data frame)

节点发送的包含ID和数据的帧

遥控帧(Remote frame)

节点向网络上的其他节点发出的某个ID的数据请求,发送节点收
到遥控帧后就可以发送相应ID的数据帧

错误帧(Error frame)

节点检测出错误时,向其他节点发送的通知错误的帧

过载帧(Overload frame)

接收单元未做好接收数据的准备时发送的帧,发送节点收到过载帧
后可以暂缓发送数据帧

帧间空间(Inter-frame space)

用于将数据帧、遥控帧与前后的帧分隔开的帧

        其中,数据帧和遥控帧有ID,并且有标准格式和扩展格式两种格式,标准格式的ID是11位,扩展格式的ID是29位。

4.标准格式数据帧和遥控帧

        标准格式数据帧和遥控帧的结构如下图所示,它们都有11位的ID。数据帧传输带有ID的0到8字节的数据;遥控帧只有ID,没有数据,用于请求数据。

数据帧可以分为以下几段。

  • 帧起始(Start Of Frame,SOF)。帧起始只有一个位,是一个显性电平(逻辑0),表示一个帧的开始。
  • 仲裁段(Arbitration Field)。仲裁段包括11位的ID和RTR位,共12位。多个节点竞争总线发送数据时,根据仲裁段的数据决定哪个节点优先占用总线。哪个ID先出现显性电平(逻辑0),对应的节点就占用总线。所以,ID数值小的优先级更高。如果两个节点发送数据帧的ID相同,再根据仲裁段最后的RTR位裁决。RTR(Remote Transmit Request)是远程传输请求,RTR位用于区分数据帧和遥控帧。数据帧的RTR位是显性电平(逻辑0),遥控帧的RTR位是隐性电平(逻辑1)。所以,具有相同ID的数据帧和遥控帧竞争总线时,数据帧优先级更高。 
  • 控制段。控制段包括IDE位、RB0位和4位的DLC,共6位。IDE是标识符扩展位(Identifier Extension Bit),用于表示帧是标准格式,还是扩展格式。标准格式帧的IDE是显性电平(逻辑0),扩展格式帧的IDE是隐性电平(逻辑1)。RB0是保留位,默认为显性电平。DLC是4个位的数据长度编码(Data Length Code),编码数值为0到8,表示后面数据段的字节数。遥控帧的DLC编码数值总是0,因为遥控帧不传输数据。
  • 数据段。数据段里是数据帧需要传输的数据,可以是0到8字节,数据的字节个数由DLC编码确定。遥控帧没有数据段。
  • CRC段。CRC段共16位,其中前15位是CRC校验码,最后一位总是隐性电平,是CRC段的界定符(Delimiter)。
  • ACK段。ACK段包括一个ACK位(Acknowledge Bit)和一个ACK段界定符。发送节点发送的ACK位是隐性电平,接收节点接收的ACK位是显性电平。
  • 帧结束(End Of Frame,EOF)。帧结束是帧结束段,由7个隐性位表示EOF。数据帧或遥控帧结束后,后面一般是帧间空间或过载帧,用于分隔开数据帧或遥控帧。

5.扩展格式数据帧和遥控帧

        扩展格式数据帧和遥控帧的结构如下图。扩展格式的ID总共是29位,扩展格式帧与标准格式帧的差异在于仲裁段和控制段。

  • 仲裁段。扩展格式数据帧的仲裁段总共32位,包括11位标准ID、SRR位、IDE位、18位扩展ID、RTR位。SRR位(Substitute Remote Request Bit)只存在于扩展格式帧中,用于替代标准格式帧中的RTR位。SRR位总是隐性电平,相当于是一个占位符,真正的RTR位在仲裁段的最后一位。RTR位还是用于区分数据帧和遥控帧。扩展格式帧中的IDE位总是隐性电平,表示这是扩展格式的帧。
  • 控制段。控制段由RB1位、RB0位和4位DLC组成。RB1位和RB0位是保留位,总是显性电平。4位的DLC编码表示数据的长度,从0到8字节。

6.优先级法则

        数据帧和遥控帧的仲裁段用于多个节点竞争总线时进行仲裁,优先级高的帧获得在总线上发送数据的权利。优先级的确认总结为以下几条法则。

  • 在总线空闲时,最先开始发送消息的节点获得发送权。
  • 多个节点同时开始发送时,从仲裁段的第一位开始进行仲裁,第一次出现各节点的位电平互异时,输出显性电平的节点获得发送权。
  • 相同ID和格式的数据帧和遥控帧,数据帧具有更高优先级,因为数据帧的RTR位是显性电平,而遥控帧的RTR位是隐性电平。
  • 对于11位标准ID相同的标准数据帧和扩展数据帧,标准数据帧具有更高的优先级,因为标准数据帧的IDE位是显性电平,而扩展数据帧的IDE位是隐性电平。

二、CAN外设工作原理和HAL驱动程序

1、片上CAN外设的功能概述

        STM32F4系列器件上有两个基本扩展CAN,支持2.0A和2.0B的CAN协议。两个CAN外设是CAN1和CAN2,称它们为CAN模块。

        STM32F4系列器件的两个CAN模块的结构如图所示。CAN1是带有512字节SRAM的主CAN控制器,CAN2无法直接访问SRAM存储器,是从CAN控制器。两个CAN控制器共享512字节SRAM

        STM32F4的CAN外设的主要特点如下:

  • 波特率最高为1Mbit/s。
  • 每个CAN模块有3个发送邮箱,可自动重发。
  • 具有16位自由运行的定时器,可以定时触发通信,可以在最后两个数据字节发送时间戳。
  • 每个CAN模块有两个FIFO单元,每个FIFO有3个接收邮箱,每个FIFO有独立的中断地址。
  • 两个CAN模块共用28个筛选器组,筛选器用于配置可接收ID列表或掩码。数据帧和遥控帧根据ID被筛选,只有通过筛选的帧才进入接收邮箱。帧的筛选完全由硬件完成,减少处理器的负担。

        STM32F4系列MCU上的CAN模块只是CAN控制器,要构成一个CAN节点,MCU还需要外接一个CAN收发器芯片,实现MCU逻辑电平到CAN总线物理层的电平转换和控制。

2、 CAN模块的基本控制

        CAN模块有3种主要的工作模式:初始化、正常和睡眠。硬件复位后,CAN模块处于睡眠模式;在初始化模式下,可以对CAN模块进行初始化设置;在正常模式下,可以进行数据的接收与发送。通过配置CAN主控制寄存器CAN_MCR的SLEEP、INRQ等位,用户可以实现在3种工作模式之间的转换。

        HAL驱动程序中用于CAN模块初始化、工作模式转换、启动和停止的函数如下表:

函数名

功能描述

HAL_CAN_Init()

CAN模块初始化,主要是配置CAN总线通信参数

HAL_CAN_MspInit()

CAN模块初始化MSP弱函数,在HAL_CAN_Init()里被调用。需要用户
程序重新实现,用于引脚GPIO配置,中断优先级配置

HAL_CAN_Start()

启动CAN模块

HAL_CAN_Stop()

停止CAN模块,允许重新访问配置寄存器

HAL_CAN_RequestSleep()

使CAN模块在完成当前操作后进入睡眠模式

HAL_CAN_WakeUp()

将CAN模块从睡眠模式唤醒

HAL_CAN_IsSleepActive()

查询CAN模块是否处于睡眠模式,返回值为1表示模块处于睡眠模式

        CAN模块的初始化函数是HAL_CAN_Init(),其原型定义如下:

HAL_StatusTypeDef HAL_CAN_Init(CAN_HandleTypeDef *hcan);

        其中,hcan是CAN_HandleTypeDef结构体类型指针,是CAN模块对象指针。       

        CAN_HandleTypeDef的成员变量Init是结构体类型CAN_InitTypeDef,用于存储CAN通信参数。在CubeMX生成的代码中,会为启用的CAN模块定义外设对象变量,例如:

CAN_HandleTypeDef hcan1;	//表示CAN1的外设对象变量

         其他函数的原型定义如下:

void HAL_CAN_MspInit(CAN_HandleTypeDef *hcan);				//MSP初始化函数
HAL_StatusTypeDef HAL_CAN_Start(CAN_HandleTypeDef *hcan);	//启动CAN模块
HAL_StatusTypeDef HAL_CAN_Stop(CAN_HandleTypeDef *hcan);	//停止CAN模块
HAL_StatusTypeDef HAL_CAN_Requestsleep(CAN_HandleTypeDef *hcan);	//进入睡眠模式
HAL_StatusTypeDef HAL_CAN_WakeUp(CAN_HandleTypeDef *hcan);	//从睡眠模式唤醒
uint32_t HAL_CAN_IsSleepActive(CAN_HandleTypeDef *hcan);	//返回1表示模块处于睡眠模式

        一个CAN模块需要先用函数HAL_CAN_Init()进行外设初始化,模块处于初始化模式,可以进行筛选器组的配置。执行函数HAL_CAN_Start()启动CAN模块进入正常模式,模块可以在正常模式和睡眠模式之间切换。执行HAL_CAN_Stop()将停止CAN模块。

3、 CAN模块的测试模式

        在对CAN模块进行初始化设置时,通过设置位时序寄存器CAN_BTR的SILM和LBKM位,可以使CAN模块进入测试模式。在测试模式下,我们将主控制寄存器CAN_MCR中的INRQ位复位,可以进入正常模式。要进入测试模式,必须在CAN模块初始化时进行设置。在测试模式下,CAN模块可以自发自收,以测试CAN模块的功能是否正常。CAN模块的3种测试模式如图:

(1)静默模式(silent mode)

        在静默模式下,CAN模块可以接收有效的数据帧和遥控帧,但是只能向总线发送隐性位,发送的显性位都被自己接收,所以在静默模式下,CAN模块无法启动发送操作。这种模式一般用于监测总线流量。 

(2)回环模式(loop back mode)

        在回环模式下,CAN模块可以正常地向总线发送数据,但不能接收总线上的数据,只能接收自己发送的数据(需要通过筛选规则)。这种模式可用于自检测试。为了不受外部事件的影响,CAN内核在此模式下不会对数据帧或遥控帧的ACK段采样,这样可以忽略ACK错误。

(3)回环与静默组合模式(loop back combined with silent mode)

        这是回环与静默模式的组合,可用于“热自检”。在这种模式下,CAN模块不能接收总线上的数据,只能接收自己发送的数据;只能向总线上发送隐性位,因而不会影响CAN总线。

        使CAN模块进入某种测试模式是在初始化函数HAL_CAN_Init()中,通过设置CAN模块的属性实现的,在示例代码里会具体介绍。

4、消息发送

        一个CAN模块有3个发送邮箱。发送数据时,用户需要选择一个空闲的发送邮箱,将标识符ID、数据长度和数据(最多8字节)写入邮箱,然后CAN模块会自动控制将邮箱内的数据发送出去。

        用户可以设置自动重发,也就是在出现错误后自动重发,直到成功发送出去。如果禁止自动重发,则发送失败后不再重发,会通过发送状态寄存器CAN_TSR相应的位指示错误原因,如仲裁丢失或发送错误。

        用户可以终止邮箱数据的发送,终止发送后邮箱会变成空闲状态。

        用户可以设置时间触发通信模式(time triggered communication mode)。在此模式下,会激活CAN模块内部的一个硬件计数器,CAN总线每收发一个位数据,计数器都会递增。在发送或接收时,在帧的起始位时刻捕获计数值,作为发送或接收数据帧的时间戳数据。在CAN的HAL驱动程序中,与发送消息相关的函数如表:

函数名

功能描述

HAL_CAN_GetTxMailboxesFreeLevel()

查询空闲的发送邮箱个数,空闲邮箱个数大于0时就可以发送

HAL_CAN_AddTxMessage()

向一个邮箱写入一条消息,由CAN模块自动控制邮箱内消息
的发送

HAL_CAN_AbortTxRequest()

中止发送一个被挂起(等待发送)的消息

HAL_CAN_IsTxMessagePending()

判断一个消息是否在等待发送

HAL_CAN_GetTxTimestamp()

如果使用了时间触发通信模式,此函数读取发送消息的时间戳

        函数HAL_CAN_GetTxMailboxesFreeLevel()用于查询一个CAN模块空闲的发送邮箱个数,如果有空闲的发送邮箱,就可以使用函数HAL_CAN_AddTxMessage()向发送邮箱写入一条消息,然后由CAN模块启动发送过程。这个函数只能发送数据帧或遥控帧,其函数原型定义如下:

HAL_StatusTypeDef HAL_CAN_AddTxMessage(CAN_HandleTypeDef *hcan,CAN_TxHeaderTypeDef*pHeader,uint8_t aData[],uint32_t *pTxMailbox)

        其中,参数hcan是CAN模块外设对象指针;参数pHeader是CAN_TxHeaderTypeDef结构体类型指针,定义了消息的一些参数;aData是发送数据的数组,最多8字节的数据;参数pTxMailbox用于返回实际使用的发送邮箱号。

        结构体CAN_TxHeaderTypeDef用于定义消息的一些参数,用于CAN模块组装成数据帧,该结构体完整定义如下:

typedef struct
{uint32_t StdId;	//11位的标准标识符,设置范围是0~0x7FFuint32_t ExtId;	//29位的扩展标识符,设置范围是0~0x1FFFFFFFuint32_t IDE;	//帧格式类型,标准ID(CAN_ID_STD)或扩展ID(CAN_ID_EXT)uint32_t RTR;	//RTR位,消息类型:数据帧(CAN_RTR_DATA)或遥控帧(CAN_RTR_REMOTE)uint32_t DLC;	//数据字节数,最多8字节,设置范围是0~8FunctionalState TransmitGlobalTime;	//是否使用时间戳,取值ENABLE或DISABLE
}CAN_TxHeaderTypeDef;

        其中,成员变量IDE表示帧格式类型,有两个宏定义表示标准ID和扩展ID。

#define CAN_ID_STD (0x00000000U) //标准ID
#define CAN_ID_EXT (0x00000004U) //扩展ID

        成员变量RTR表示消息类型,只能是数据帧或遥控帧,有两个宏定义用于此变量的取值。

#define CAN_RTR_DATA     (0x00000000U) //数据帧
#define CAN_RTR_REMOTE   (0x00000002U) //遥控帧

        CAN模块发送数据是将消息写入模块的发送邮箱,然后由CAN控制器将邮箱内的消息发送出去。CAN模块发送消息只有HAL_CAN_AddTxMessage()这一个函数,不像串口、SPI等其他外设有中断模式、DMA方式的专用函数。

        将消息写入邮箱后,可以用函数HAL_CAN_IsTxMessagePending(查询邮箱里的消息是否发送出去了,这个函数的原型定义是:

uint32_t HAL_CAN_IsTxMessagePending(CAN_HandleTypeDef *hcan,uint32_t TxMailboxes);

        其中,参数TxMailboxes是发送邮箱号。函数返回值如果是0,则表示没有等待发送的消息,也就是消息已经被发送出去了;如果返回值为1,则表示邮箱里的消息仍然在等待发送。CAN总线上可能有很多个节点,需要通过总线仲裁获得CAN总线使用权之后,节点才能将邮箱里的消息发送出去。

        CAN模块也有表示消息发送出去的中断事件,如果打开了相应的中断事件使能控制位,也可以在中断里做出响应。在后面会专门介绍CAN的中断。

5、消息接收

        每个CAN模块有两个接收FIFO(Receive FIFO),每个FIFO有3个邮箱。FIFO完全由硬件管理,当有邮箱接收到有效消息时,就会产生相应的事件中断标志,可以产生CAN RX硬件中断。FIFO0和FIFO1有各自的中断地址。从邮箱中读出消息后,邮箱就自动释放。如果一个FIFO的3个邮箱都接收到消息而没有及时读出,再有消息进入时就会产生上溢。根据是否设置FIFO锁定,有两种处理情况。

  • 如果禁止FIFO锁定,则新传入的消息会覆盖FIFO中存储的最后一条消息。
  • 如果启用FIFO锁定,则新传入的消息会被舍弃。

        用户可以通过轮询方式或中断方式读取接收邮箱中的消息。CAN模块接收消息的相关函数如表:

函数名

功能描述

HAL_CAN_GetRxFifoFillLevel()

查询一个FIFO中存在未读消息的邮箱个数

HAL_CAN_GetRxMessage()

读取一个接收邮箱中的消息

        函数HAL_CAN_GetRxFifoFillLevel()用于查询某个FIF()存在未读消息的邮箱个数,函数原型定义如下:

uint32_t HAL_CAN_GetRxFifoFillLevel(CAN_HandleTypeDef *hcan,uint32_t RxFifo)

        其中,参数RxFifo是FIFO编号,一个CAN模块有两个FIFO,可使用如下的两个宏作为此参数的取值。

#define CAN_RX_FIFO0(0x00000000U) //CAN模块FIFO0
#define CAN_RX_FIFO1(0x00000001U) //CAN模块FIFO1

        如果查询到有未读取的消息,就用函数HAL_CAN_GetRxMessage(读取接收的消息,此函数的原型定义如下:

HAL_StatusTypeDef HAL_CAN_GetRxMessage(CAN_HandleTypeDef *hcan,uint32_t RxFifo,CAN_RxHeaderTypeDef *pHeader,uint8_t aData[])

        其中,参数RxFifo是FIFO编号,用宏CAN_RX_FIFO0和CAN_RX_FIFO1分别表示FIFO0和FIFO1;参数pHeader是CAN_RxHeaderTypeDef结构体类型指针,记录了帧的一些信息;aData[]是接收数据的数组,最多8字节。

        记录帧信息的结构体CAN_RxHeaderTypeDef的定义如下:

typedef struct
{uint32_t StdId;		//11位的标准标识符,范围是0~0x7FFuint32_t ExtId;		//29位的扩展标识符,范围是0~0x1FFFFFFFuint32_t IDE;		//帧格式类型,标准ID(CAN_ID_STD)或扩展ID(CAN_ID_EXT)uint32_t RTR;		//RTR位,消息类型:数据帧或遥控帧uint32_t DLC;		//数据字节数,最多8字节uint32_t Timestamp;	//时间戳数据,数值范围是0~0xFFFEuint32_t FilterMatchIndex;	//匹配的筛选器索引
}CAN_RxHeaderTypeDef;

        结构体CAN_RxHeaderTypeDef的部分成员变量与结构CAN_TxHeaderTypeDef的相同,只有后面两个成员变量CAN_RxHeaderTypeDef特有的。

6、标识符筛选

(1)标识符筛选原理

        在CAN网络中,发送节点是以广播方式发送消息的,所有CAN节点都可以收到消息。数据帧和遥控帧带有标识符,标识符一般表示了消息的类型。一个CAN节点一般只对特定的消息感兴趣,如果用软件对接收的帧ID进行判别,将消耗接收节点的大量CPU时间。STM32F4的两个CAN控制器有28个共用的标识符筛选器组(Filter Bank),可以完全用硬件方式对接收的帧ID进行筛选,只允许符合条件的帧进入接收邮箱,自动放弃不符合条件的帧。

        每个筛选器组包含两个32位寄存器,分别是CAN_FxR1和CAN_FxR2。这两个寄存器可以被配置为两个32位长度筛选器或4个16位长度筛选器,筛选器可以是掩码模式或列表模式,所以一个筛选器组有4种配置模式:

 

1)1个32位筛选器——标识符掩码模式

        在这种模式下,寄存器CAN_FxR1存储一个32位ID,这个ID与11位标准ID(STID[10:0])、18位扩展ID(EXID[17:0])、IDE位、RTR位的位置对应关系如图中所示。IDE为0时表示标准格式帧,否则表示扩展格式帧。

        CAN_FxR2存储一个32位掩码,如果掩码为1,则表示该位必须与ID中的位一致,如果为0,则表示不用一致。

        例如,如果让一个CAN节点只接收标准ID为奇数的标准格式数据帧,则设置寄存器CAN_FxR1表示的ID时,STID[0]位必须设置为1,IDE位必须设置为0(表示标准格式帧),RTR位必须设置为0(表示数据帧)。设置寄存器CAN_FxR2表示的掩码时,对应的这些位必须设置为1,其他位设置为0。

映射

STID[10:3]

STID[2:0]

EXID[17:13]

EXID[12:5]

EXID[4:0]

I

D

E

R

T

R

0

ID

×

×

×

×

×

×

×

×

×

×

1

×

×

×

×

×

×

×

×

×

×

×

×

×

×

×

×

×

×

0

0

0

掩码

0

0

0

0

0

0

0

0

0

0

1

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

1

1

1

2)2个32位筛选器——标识符列表模式

        在这种模式下,寄存器CAN_FxR1和CAN_FxR2各存储一个32位ID,ID的组成与模式(1)相同。只有匹配这两个ID的帧才能通过筛选。

3)2个16位筛选器——标识符掩码模式

        在这种模式下,寄存器CAN_FxR1的高16位组成一个ID,低16位组成一个掩码;寄存器CAN_FxR2的高16位组成一个ID,低16位组成一个掩码。

4)个16位筛选器——标识符列表模式

        在这种模式下,寄存器CAN_FxR1表示2个16位ID,寄存器CAN_FxR2表示2个16位ID。用户可以为一个FIFO设置多个筛选器组,但是一个筛选器组只能配置给一个FIFO。如果为FIFO设置了筛选器,并且接收的帧与所有筛选器都不匹配,那么该帧会被丢弃。只要通过了一个筛选器,帧就会被存入接收邮箱。

(2)函数HAL_CAN_ConfigFilter()

        函数HAL_CAN_ConfigFilter()用于设置CAN模块的标识符筛选器,应该在执行HAL_CAN_Start()启动一个CAN模块之前调用这个函数。其原型定义如下:

HAL_StatusTypeDef HAL_CAN_ConfigFilter(CAN_HandleTypeDef *hcan,CAN_FilterTypeDef *sFilterConfig)

        其中,参数sFilterConfig是结构体CAN_FilterTypeDef类型指针,它保存了筛选器的设置。这个结构体定义如下:

typedef struct
{uint32_t FilterIdHigh;		//CAN_FxR1寄存器的高16位,取值范围为0~0xFFFFuint32_t FilterIdLow;		//CAN_FxR1寄存器的低16位,取值范围为0~0xFFFEuint32_t FilterMaskIdHigh;	//CAN_FxR2寄存器的高16位,取值范围为0~0xFFFFuint32_t FilterMaskIdLow;	//CAN_FxR2寄存器的低16位,取值范围为0~0xFFFF/*筛选器应用于哪个FIFO,使用宏CAN_FILTER_FIFO0或CAN_FILTER_FIFO1 */uint32_t FilterFIFOAssignment;/*筛选器组编号,具有双CAN模块的MCU有28个筛选器组,编号范围为0~27*/uint32_t FilterBank;/*筛选器模式,ID掩码模式(CAN_FILTERMODE_IDMASK)或ID列表模式(CAN_FILTERMODE_IDLIST)*/uint32_t FilterMode;/*筛选器长度,即32位(CAN_FILTERSCALE_32BIT)或16位(CAN_FILTERSCALE_16BIT)*/uint32_t FilterScale;uint32_t FilterActivation;		//是否启用此筛选器,ENABLE或者DISABLEuint32_t SlaveStartFilterBank;	//设置应用于从CAN控制器的筛选器的起始编号
}CAN_FilterTypeDef;

        某些变量的取值具有相应的宏定义,例如,FilterMode是筛选器模式,有两个宏定义可用于此变量的取值,宏定义如下:

#define CAN_FILTERMODE_IDMASK	(0x00000000U)	//ID掩码模式
#define CAN_FILTERMODE_IDLIST	(0×00000001U)	//ID列表模式

        总之,筛选器的设置是CAN模块使用中比较复杂的环节

7、中断及处理

(1)中断和中断事件

        一个CAN模块有4个中断,对应4个ISR。例如,CAN1的4个中断及其ISR。

中断名称

中断中文名称

说明

ISR名称

CAN1_TX

发送中断

任何一个发送邮箱发送完成时产生的中断

CAN1_TX_IRQHandler()

CAN1_RX0

FIFO0接收中断

FIFO0接收消息、满或上溢时产生的中断

CAN1_RX0_IRQHandler()

CAN1_RX1

FIFO1接收中断

FIFO1接收消息、满或上溢时产生的中断

CAN1_RX1_IRQHandler()

CAN1_SCE

状态改变和错误
中断

状态改变或发生错误时产生的中断

CAN1_SCE_IRQHandler()

        每个中断又有1个或多个中断事件源,HAL驱动程序中为每个中断事件源定义了中断类型宏定义,也就是中断事件使能控制位的宏定义。例如,CAN1_TX只有一个中断事件源,为其定义中断事件类型的宏定义如下:

#define CAN_IT_TX_MAILBOX_EMPTY  ((uint32_t)CAN_IER_TMEIE)

        HAL驱动程序中有两个宏函数可以开启或禁止某个具体的中断事件源。

__HAL_CAN_ENABLE_IT(__HANDLE__,__INTERRUPT__) //开启某个中断事件源__HAL_CAN_DISABLE_IT(__HANDLE__,__INTERRUPT__) //禁用某个中断事件源

        其中,_HANDLE_是CAN模块对象指针,__INTERRUPT__是表示中断事件类型的宏,例如CAN_IT_TX_MAILBOX_EMPTY。

        在CubeMX为CAN模块的4个硬件中断生成的ISR中,都调用了函数HAL_CAN_IRQHandler(),这是CAN中断处理通用函数。函数HAL_CAN_IRQHandler()会根据中断使能寄存器、中断标志寄存器的内容判断具体发生了哪个中断事件,再调用相应的回调函数。CAN的HAL驱动程序中为常用的中断事件定义了回调函数,只要搞清楚中断事件与回调函数的对应关系,编程时重新实现关联的回调函数,就可以对某个中断事件做出处理。

(2)发送中断的事件源和回调函数

        发送中断(CAN1_TX)只有一个中断事件源CAN_IT_TX_MAILBOX_EMPTY,在3个发送邮箱中任何一个发送完成时都产生该事件中断,但是3个邮箱有各自的回调函数:

中断事件类型宏

中断事件说明

回调函数

CAN_IT_TX_MAILBOX_EMPTY

邮箱0发送完成

HAL_CAN_TxMailbox0CompleteCallback()

邮箱1发送完成

HAL_CAN_TxMailbox1CompleteCallback()

邮箱2发送完成

HAL_CAN_TxMailbox2CompleteCallback()

        另外,调用函数HAL_CAN_AbortTxRequestO中止某个邮箱的发送后,也会调用相应的回调函数,只是这几个回调函数不是由中断引起的,而是由函数HAL_CAN_AbortTxRequest()引起的。

(3)FIFO0的中断事件源和回调函数

        FIFO0接收中断(CAN1_RX0)是在FIFO0接收消息、满或上溢时触发的中断。这个中断有3个中断事件源,对应的回调函数如表所示:

中断事件类型宏

中断事件说明

回调函数

CAN_IT_RX_FIFO0_MSG_PENDING

FIFO0接收新消息

HAL_CAN_RxFifo0MsgPendingCallback()

CAN_IT_RX_FIFO0_FULL

FIFO0满

HAL_CAN_RxFifo0FullCallback()

CAN_IT_RX_FIFO0_OVERRUN

FIFO0发生上溢

_

         其中,接收新消息的中断事件是比较有用的,因为CAN模块接收消息一般是使用中断方式。

(4)FIFO1的中断事件源和回调函数

        FIFO1接收中断(CAN1_RX1)是在FIFO1接收消息、满或上溢时触发的中断。这个中断也有3个中断事件源,对应的回调函数如表所示:

中断事件类型宏

中断事件说明

回调函数

CAN_IT_RX_FIFO1_MSG_PENDING

FIFO1接收新消息

HAL_CAN_RxFifo1MsgPendingCallback()

CAN_IT_RX_FIFO1_FULL

FIFO1满

HAL_CAN_RxFifolFullCallback()

CAN_IT_RX_FIFO1_OVERRUN

FIFO1发生上溢

——

(5)状态改变或错误的中断事件源和回调函数

        状态改变或错误中断(CAN1_SCE)在CAN模块发生状态改变或错误时触发,例如,CAN模块进入睡眠状态或从睡眠状态被唤醒,或出现总线错误等。CAN1_SCE的中断事件源和回调函数如表所示:

中断事件宏定义

中断事件说明

回调函数

CAN_IT_SLEEP_ACK

CAN模块进入睡眠状态

HAL_CAN_SleepCallback()

CAN_IT_WAKEUP

监测到消息,被唤醒

HAL_CAN_WakeUpFromRxMsgCallback()

CAN_IT_ERROR
CAN_IT_BUSOFF等多种

有多种错误事件源,通过错
误状态寄存器CAN_ESR的
内容判断具体错误类型

HAL_CAN_ErrorCallback()


http://www.mrgr.cn/news/81995.html

相关文章:

  • WebRTC线程的启动与运行
  • std optional 的使用
  • GraphRAG: 一种结合图结构和检索增强生成的工程实现思路
  • 数据结构与算法之动态规划: LeetCode 674. 最长连续递增序列 (Ts版)
  • 大模型系列17-RAGFlow搭建本地知识库
  • UE4.27 Android环境下获取手机电量
  • (二)当人工智能是一个函数时,怎么去训练它?
  • CGAL windows 安装教程
  • 大厂高频总线协议面试题及参考答案(几百家面试题挑选最高频精华)
  • 斗鱼Android面试题及参考答案
  • MOS管驱动方案汇总
  • Unity设置中文
  • PHP框架+gatewayworker实现在线1对1聊天--gatewayworker说明(2)
  • 使用 commitlint 和 husky 检查提交描述是否符合规范要求
  • STM32 软件I2C读写
  • Prometheus 专栏 —— Prometheus安装、配置
  • 仙盟系统开发——启动app失败
  • Leecode刷题C语言之二叉树中的链表
  • o1到o3的发展历程
  • 【2024年-7月-20日-开源社区openEuler实践记录】openEuler - Docker - Images:容器化世界的得力助手
  • 【Qt】容器控件、布局管理控件
  • 24.小R的随机播放顺序<字节青训营-中等题>
  • PySide6 一些基础资料
  • 选择器(结构伪类选择器,伪元素选择器),PxCook软件,盒子模型
  • Flutter封装一个三方ViewPager学习
  • 如何规范的提交Git?