您的位置:首页 > 汽车 > 新车 > CAN协议通信 学习笔记

CAN协议通信 学习笔记

2024/9/17 2:59:12 来源:https://blog.csdn.net/weixin_43871650/article/details/138485154  浏览:    关键词:CAN协议通信 学习笔记

文章目录

  • 1.CAN通信简介
  • 2.物理层
    • 2.1 CAN总线的电气特性
    • 2.2 CAN的位同步机制(了解,用于理解CAN的初始化参数的配置原理)
      • 硬同步方式
      • 重新同步方式
    • 2.3 CAN对比其他常用协议的优势
  • 3. 数据链路层
    • 3.1 CAN协议的数据帧
    • 3.2 仲裁机制
    • 3.3 访问控制
    • 3.4 确认机制
    • 3.5 错误检测和处理
    • 3.6 CAN的时间触发通信
  • 4.GD32上的CAN
    • 4.1 特性
    • 4.2 框图
    • 4.3 通信模式
    • 4.4 数据发送
    • 4.5 数据接收
    • 4.6 过滤功能
    • 4.7 GD32的CAN数据收发过程
  • 5.GD32的CAN代码编写及测试
    • 5.1 GD32中CAN的初始化
    • 5.2 CAN回环模式代码及测试
    • 5.3 CAN普通模式代码及测试
    • 5.4 过滤器的测试代码
    • 5.5 总结的注意要点

1.CAN通信简介

CAN 是指Controller Area Network 的缩写,该协议 是一种常用于汽车控制系统的通讯协议,它能够将汽车仪表、变速箱、辅助刹车系统、ECU(Electronic Control Unit)、控制模块、各种传感器等多个控制单元连接在一起,实现信息的实时同步。
在这里插入图片描述


该协议由研发和生产汽车电子产品著称的德国 BOSCH 公司开发的,并最终成为国际标准(ISO11519以及ISO11898)。ISO11519以及ISO11898差异点如下:
在这里插入图片描述


对应OSI模型,CAN协议定义了其中的4层的内容,分别是物理层,数据链路层,传输层。

在这里插入图片描述

2.物理层

2.1 CAN总线的电气特性

CAN采用双绞线传输差分信号,因此CAN传输的信号抗干扰能力很强。
在这里插入图片描述


既然CAN总线用到了差分信号,故其中有两条线用于传输信号,分别是①CAN高线、②CAN低线。

那么CAN总线中的数字信号是怎么表示的呢?

以高速CAN为例,CAN高为3.5V,CAN低为1.5V,电位差为2V,此时定义为显性,对应数字信号:0;CAN高与CAN低 均为2.5V时,电位差为0V,此时定义为隐性,对应数字信号:1。如下图所示

在这里插入图片描述
注意混淆点:显性电平表示数字信号0;隐形电平表示是数字信号1.


在了解完CAN总线的信号表示后,我们再了解CAN总线的线路结构。

CAN总线线路结构有闭环开环两种形式。

  • 闭环结构:种CAN总线网络由ISO 11898标准定义,是高速、短距离的CAN网络结构。在闭环结构的CAN总线网络中,总线两端各连接一个120欧的电阻,两根信号线形成回路。这种结构支持高速通信,通信速率通常在125kbit/s到1Mbit/s之间。
  • 开环结构:这种CAN总线网络由ISO 11519-2标准定义,是低速、远距离的CAN网络结构。在开环结构的CAN总线网络中,两根信号线独立,各自串联一个2.2k欧的电阻。这种结构支持低速但长距离的通信,通信速率最高可达125kbit/s。
    在这里插入图片描述

由CAN总线的线路结构,我们再想想CAN总线是什么类型的拓扑结构呢?

can是总线型拓扑结构

百度:拓扑结构定义

总线型拓扑的特点是:结点之间按广播方式通信,一个结点发出的信息,总线上的其它结点均可“收听”到

所有在CAN(Controller Area Network)通信中,并没有严格意义上的“主”和“从”的概念,这与传统的主从式通信有所不同。

CAN总线是一种基于消息广播的通信模式,采用多主竞争式总线结构,网络中各节点均可主动向其他节点发送信息。

在这里插入图片描述

有些网页提到CAN还有星型拓扑,树形拓扑,其实只是形式像是星型,树形。

如下图,只是形状是星型,但是没有中间的星型结点,本质还是总线型拓扑。
在这里插入图片描述

星型拓扑需要有中间结点。
在这里插入图片描述

2.2 CAN的位同步机制(了解,用于理解CAN的初始化参数的配置原理)

在讲CAN的位同步机制之前,我们先想一想CAN的通信方式是什么方式?是同步通信还是异步通信?

首先我们先回顾一下同异步通信的概念。
同步通信是一种比特同步通信技术,要求发收双方具有同频同相的同步时钟信号,只需在传送报文的最前面附加特定的同步字符,使发收双方建立同步,此后便在同步时钟的控制下逐位发送/接收。
异步通信是指发送端和接收端没有统一的时钟信号,它们各自使用自己的时钟控制数据传输。

由上述内容可知,CAN总线只有两根线:①CAN高线、②CAN低线。
CAN总线上是 没有时钟信号线的,而每个节点都有自己的时钟,故CAN通信是一种异步通信

然而,在 CAN 总线上,虽然每个节点都有自己的时钟,这些时钟并不完全同步,存在一定的偏差,称为时钟漂移。为了解决时钟漂移问题,CAN 协议采用了位同步机制,保证数据传输的准确性。


CAN 的位同步是通过 硬同步 (Hard Synchronization)重新同步 (Resynchronization) 来实现的,下面是两种手段的简单介绍。

  • 硬同步
    时机: 发生在每个数据帧的起始位置,即检测到起始符 (SOF) 时。
    原理: 接收端检测到 SOF 时,会立即将自己的时钟设置为 SOF 的起始时间点,从而实现与发送端的时钟同步。
    作用: 将接收端的时钟“重置”为发送端的时钟,保证初始的时钟同步。

  • 重新同步
    时机: 发生在数据帧传输过程中,通常是在每个数据位的边缘(位边沿)。
    原理: 接收端会根据每个数据位实际传输时间,计算出时钟偏差。如果偏差超过一定阈值,接收端会根据重同步段的位时间调整自己的时钟,以减少偏差。
    作用: 根据数据传输的实际情况,对接收端的时钟进行微调,保证数据传输的准确性。


硬同步方式

为了实现 位同步 的硬同步方式 ,CAN协议定义了位时序这个重要的概念。

位时序指的是 位时间采样点 的配置。我们在编写CAN驱动程序的时候,就需要对位时序进行配置。


位时间

位时间是指一个数据位(比特位)的时间,它是由四个周期段组成:同步段传播段相位段 1相位段 2

下图中,Tq可以理解为节点自身CAN时钟一个节拍的时间。一个完整的位一般由 8~25 个 Tq 组成。
为方便表示,下图中的高低电平直接代表信号逻辑 0 或逻辑 1(不是差分信号)。
在这里插入图片描述

  • 同步段 (Synch Segment):
    用来校准接收端时钟,使接收端与发送端同步。
    比如当总线上出现帧起始信号(SOF)时,其它节点上的控制器根据总线上的这个下降沿,对自己的位时序进行调整,把该下降沿包含到 SS 段内,这样根据起始帧来进行同步的方式称为硬同步
    在这里插入图片描述

  • 传播段 (Propagation Segment):
    考虑信号在总线上传播的时间,确保所有节点都接收到数据位。

  • 相位段 1 (Phase Segment 1):
    用来灵活调整数据位采样的时间点。

  • 相位段 2 (Phase Segment 2):
    同样用来灵活调整数据位采样的时间点。


采样点

每个数据位传输期间,接收端在特定时间点进行采样,判断该数据位的值是 0 还是 1。
采样点通常位于相位段 1 和相位段 2 的交界处,可以根据实际应用需求进行调整。

在这里插入图片描述


硬同步举例

在硬同步阶段,当节点检测到本身SS段并不在总线电平下降沿跳变处,节点则会把自己的位时序中的 SS 段平移至总线出现下降沿的部分,后面三段也跟着上去,以获得同步。(可以理解为节点在检测到帧起始信号时才开始“设置段”)
在这里插入图片描述


位时序的灵活配置

CAN 协议允许根据实际应用需求,对位时序进行灵活配置,例如调整各个时钟周期的长度、采样点的位置等。

不同的位时序配置会影响数据传输的性能,例如:更长的传播段可以提高抗干扰能力,但会降低数据传输速率。

不同的采样点可以影响数据位的识别精度,影响数据传输的可靠性。

以下是一个常见的位时序配置的例子:

  • 位时间 = 10 个时钟周期(即10Tq)
  • 同步段 = 1 个时钟周期
  • 传播段 = 3 个时钟周期
  • 相位段 1 = 3 个时钟周期
  • 相位段 2 = 3 个时钟周期
  • 采样点 = 相位段 1 和相位段 2 的交界处

重新同步方式

前面的硬同步只是对帧的第一位数据才起作用,即只对帧起始信号起作用。
但如何保证 帧的第二位数据帧的第三位数据,…,帧的第n位数据也是正确的呢?

因而需要引入重新同步方式,它利用普通数据位的高至低电平的跳变沿来同步 (帧起始信号是特殊的跳变沿)。


重新同步硬同步方式相似的地方是它们都使用 SS 段来进行检测,同步的目的都是使节点内的 SS 段把跳变沿包含起来。

重新同步的方式分为超前滞后两种情况,以总线跳变沿与 SS 段的相对位置进行区分,下面举例设SJW为2Tq

相位超前,节点从总线的边沿跳变中,检测到它内部的时序比总线的时序相对超前 2Tq,这时控制器在下一个位时序中的 PBS1 段增加 2Tq 的时间长度,使得节点与总线时序重新同步。
在这里插入图片描述

相位滞后,节点从总线的边沿跳变中,检测到它的时序比总线的时序相对滞后 2Tq,这时控制器在前一个位时序中的 PBS2 段减少 2Tq 的时间长度,获得同步。
在这里插入图片描述


在重新同步的时候,PBS1 和 PBS2 中增加或减少的这段时间长度被定义为重新同步补偿宽度SJW*(reSynchronization Jump Width)

一般来说 CAN 控制器会限定 SJW 的最大值,如限定了最大 SJW=3Tq 时,单次同步调整的时候不能增加或减少超过 3Tq 的时间长度,若有需要,控制器会通过多次小幅度调整来实现同步。

当控制器设置的 SJW 极限值较大时,可以吸收的误差加大,但通讯的速度会下降.


2.3 CAN对比其他常用协议的优势

相比于RS485,SPI,IIC,为什么在汽车上常用CAN协议呢?

汽车上常用CAN(Controller Area Network)协议而不是RS485、SPI或IIC协议,主要有以下几个原因:

  • 实时性和可靠性:CAN协议专为汽车设计,支持高速数据传输,并且具有错误检测、通知和恢复功能,能够确保数据的完整性和实时性。这对于汽车控制系统中的关键应用(如发动机控制、制动系统、安全系统等)至关重要。
  • 多主通信:CAN总线是一个多主通信的串行总线,这意味着多个设备可以在没有主机的情况下进行通信,每个设备都可以主动发送数据。这种特性使得CAN总线在汽车应用中特别有用,因为汽车中有许多需要相互通信的设备和系统。
  • 灵活性和可扩展性:CAN协议支持不同的消息格式和优先级设置,可以根据需要进行灵活配置。此外,CAN总线可以连接多个设备,并且易于扩展,这使得它非常适合用于复杂的汽车系统。
  • 成本效益:尽管CAN协议的实现可能比某些其他通信协议更复杂,但由于其广泛的应用和成熟的生态系统,CAN硬件和软件的成本已经相对较低。此外,CAN协议的高效性和可靠性也可以降低系统的总体成本。

RS485虽然也可以用于多设备之间的通信,但它没有CAN协议的实时性和可靠性高,只能是一主多从
SPI和IIC协议则主要用于短距离、低速率的数据传输,不适合汽车中需要高速、实时通信的应用。

在这里插入图片描述

3. 数据链路层

数据链路层的功能主要有封装成帧,媒介访问控制,差错控制等功能。

3.1 CAN协议的数据帧

CAN协议的帧格式大概结构如下:

  • 起始符 (SOF)
  • 仲裁域 (arbitration field)
  • 控制域 (control field)
  • 数据域 (data field)
  • CRC校验域 (CRC field)
  • 确认域 (ACK field)
  • 结束符 (EOF)

为了更有效地控制通讯,CAN 一共规定了 5 种类型的帧,它们的类型及用途说明如表 。
在这里插入图片描述
其中,数据帧和遥控帧有标准格式和扩展格式两种格式。
标准格式有 11 个位的标识符(Identifier: 以下称 ID),扩展格式有 29 个位的 ID。


CAN数据帧

数据帧是在 CAN 通讯中最主要、最复杂的报文,我们来了解它的结构,见图
在这里插入图片描述
数据帧以一个显性位 (逻辑 0) 开始,即图中的SOF,以 7 个连续的隐性位 (逻辑 1) 结束,即图中的EOF。在它们之间,分别有仲裁段、控制段、数据段、CRC 段和 ACK 段。

数据帧标准格式各个位的介绍

域段域段名位宽:bit描述
帧起始SOF(Start Of Frame)1数据帧起始标志,固定为1bit显性('b0)
IDIdentify(ID)11本数据帧的 ID 信息, ID 信息的作用:① 如果同时有多个节点发送数据时,作为优先级依据(仲裁机制);② 目标节点通过 ID 信息来接受数据(验收滤波技术)
RTRRemote Transmission Request BIT1RTR标识是否是远程帧(0,数据帧;1,远程帧),在数据帧里这一位为显性('b0)
IDEIdentifier Extension Bit1IDE用于区分标准格式与扩展格式,在标准格式中 IDE 位为显性(‘b0),在扩展格式里 IDE 位为隐性(’b1)
r0保留位11bit保留位,固定为1’b0
DLCdata length4由 4 位组成,MSB 先行(高位先行),它的二进制编码用于表示本报文中的数据段含有多少个字节,DLC 段表示的数字为0到8,若接收方接收到 9~15 的时候并不认为是错误
Datadata数据0~64据帧的核心内容,它由 0~8 个字节(0 ~ 64位)组成,MSB 先行
CRC段CRC15CRC段用于检查帧传输错误,发送方以一定的方法计算包括:帧起始、仲裁段、控制段、数据段;接收方以同样的算法计算 CRC 值并进行比较,如果不同则会向发送端反馈出错信息,重新发送;计算和出错处理一般由 CAN 控制器硬件完成或由软件控制最大重发数。
CRC界定符CRC1CRC 界定符(用于分隔的位),为隐性位(1’b1),主要作用是把CRC 校验码与后面的 ACK 段间隔起来
ACK 槽ACK slot1在 ACK 槽位中,发送端发送的为隐性位,而接收端则在这一位中发送显性位以示应答;发送 ACK/返回 ACK这个过程使用到回读机制,即发送方先在 ACK 槽发送隐性位后,回读到的总线上的电平为显性0,发送方才知道它发送成功了,不用重发
ACK界定符1在 ACK 槽和帧结束之间由 ACK 界定符间隔开,为隐性位
帧结束EOF7由发送端发送 7 个隐性位表示结束

远程帧
在这里插入图片描述
远程帧,相比数据帧,其中缺少了数据域。


其他帧 暂略

3.2 仲裁机制

在总线空闲时,若多个设备同时发送数据,那么哪个设备才能优先占用总线呢?这内容旧涉及到了CAN的仲裁机制。

仲裁机制的工作原理:

  1. 唯一标识符:
    仲裁机制使用到了 仲裁段的 Identify,这个Identify称为节点的标识符
    为了确保每个节点的标识符都是唯一的,CAN 协议规定了每个节点的标识符必须是唯一的。
    CAN 协议规定了节点标识符的优先级,标识符越小,优先级越高。
  2. 总线空闲检测:
    什么情况下,总线是空闲的?
    对于任意一个节点而言,只要它监听到总线上连续出现了11位隐性电平(显/隐性电平: 在总线上隐性电平通常表示逻辑1,而显性电平通常表示逻辑0),那么该节点就会认为总线当前处于空闲状态
    在总线一开始工作的时候,所有节点都输出隐性电平。
    当总线处于空闲状态时,多个设备可能同时开始发送数据。
  3. 逐位比较:
    当多个节点同时发送数据时,每个节点都会将自己的标识符发送到总线上。
    在发送每一位ID时,发送节点也会读取总线上的电平状态,并将其与自己发送的电平进行比较。
    ①若总线上的电平和本节点要发送的电平一致,则本节点继续发送下一位ID。
    ②若总线上的电平和本节点要发送的电平不一致,则说明由更高优先级的消息正在发送,本节点会立即停止发送并转为监听状态。
  4. 仲裁获胜:
    最终,标识符较小的节点会获胜,继续发送数据。标识符较大的节点则会停止发送数据,并进入接收状态。

为什么 标识符越小,优先级越高 ?(了解)

前面讲过,当CAN_H和CAN_L之间的电压差为某一特定值(如2V)时,表示显性电平;而当两根线的电压几乎相同时(电位差接近0V),则表示隐性电平。

当多个设备同时尝试发送数据时,如果总线上同时出现了显性电平和隐性电平,那么由于显性电平的物理特性(即更高的电压差),它会覆盖隐性电平,使得总线状态被置为显性电平。

而显性电平对应逻辑0,而隐性电平对应逻辑1。标识符采用的是MSB,标识符越小,在逐位比较中先比较的位是更有可能输出显性电平,从而能够优先占用总线资源进行传输。


例子如下图

在这里插入图片描述

节点1的ID号是 10110101101,节点2的ID号是 10110101100

在仲裁过程中,在比较到两个节点的ID号的最后一位时,节点2的ID号最后一位是逻辑0,在总线上用显性电平表示;节点2的ID号最后一位是逻辑1,用隐性电平表示。

而此时总线上会表现出显性电平,节点2在读取总线时发现电平与自己发送的隐性电平不一样,这时就让出总线的控制器。节点1仲裁胜利!

总裁核心原理: 将自己要比较的位与总线上的状态相与,只有线与的结果与本身一致时,仲裁才能够通过。

注意:其实在报文发送上去的过程,采用的是广播的方式,在节点1和节点2总裁的同时,总线上所有的节点都能够监听到它们的ID号,只不过也在同时进行验收滤波,只有监听到的ID号存在ID表中,该节点才会选择继续监听该报文后面的数据。

3.3 访问控制

3.4 确认机制

CAN协议中,发送节点如何确认自己发送的数据帧被接收节点正确接收呢?这就涉及到了CAN的确认机制。

在CAN帧结构中,ACK段是一个由两个位组成的区域,包括ACK槽(ACK SLOT)和ACK分隔符(ACK DELIMITER)。发送节点在发送数据或远程帧时,会在ACK槽置入一个隐性位(逻辑1)。


ACK机制的工作原理

  1. 发送过程:
    发送节点在发送数据帧时,会在ACK槽置入一个隐性位(逻辑1)。
    发送节点在发送数据的同时,会对总线上的数据进行回读,以监控ACK槽的状态。
  2. 接收过程:
    接收节点在接收到数据帧后,会检查数据的正确性。
    如果数据正确无误,接收节点会在ACK槽期间发送一个显性位(逻辑0),以此向发送节点确认消息已成功接收。
  3. 确认与重发:
    发送节点通过回读总线,如果检测到ACK槽为显性位(逻辑0),则表示至少有一个接收节点正确接收了数据,发送成功。
    如果检测到ACK槽仍为隐性位(逻辑1),则表示没有接收节点正确接收数据,此时发送节点会重新发送数据帧。

3.5 错误检测和处理

CRC段:
该段用于检查帧传输错误,发送方以一定的方法计算包括:帧起始、仲裁段、控制段、数据段;
接收方以同样的算法计算 CRC 值并进行比较,如果不同则会向发送端反馈出错信息,重新发送;
计算和出错处理一般由 CAN 控制器硬件完成 或 由 软件 控制最大重发数。
该段由 15 个位的 CRC 顺序和 1 个位的 CRC 界定符(用于分隔的位)组成,它为隐性位(逻辑1),主要作用是把CRC 校验码与后面的 ACK 段间隔起来。

3.6 CAN的时间触发通信

时间触发CAN:建立在标准CAN上的高层协议,通过对网络中所有节点的通信进行同步调度,实现每个节点在固定时间内发送信息,无需再进行优先级仲裁。

其他内容暂略

4.GD32上的CAN

资料参考:GD32F305官方资料,GD32F305用户手册

4.1 特性

◼ 支持 CAN 总线协议 2.0A 和 2.0B;
◼ 通信波特率最大为 1Mbit/s;
◼ 支持时间触发通信(Time-triggered communication);
◼ 中断使能和清除。

发送功能
◼ 3 个发送邮箱;
◼ 支持发送优先级;
◼ 支持发送时间戳。

接收功能
◼ 2 个深度为 3 的接收 FIFO;
◼ 在非 GD32F30x CL 系列产品中,具有 14 个过滤器;
◼ 在 GD32F30x CL 系列产品中,具有 28 个过滤器;
◼ FIFO 锁定功能。

时间触发通信
◼ 在时间触发通信模式下禁用自动重传;
◼ 16 位定时器;
◼ 接收时间戳;
◼ 发送时间戳。

4.2 框图

在这里插入图片描述
疑问:
1.发送邮箱就是类似与发送的缓存BUF吗

2.两个深度为3的FIFO,即一个CAN控制器就拥有2*3个接收邮箱吗

4.3 通信模式

CAN 总线控制器有 4 种通信模式:

  • 静默(Silent)通信模式;
    在静默通信模式下,可以从 CAN 总线接收数据,但不向总线发送任何数据。
  • 回环(Loopback)通信模式;
    在回环通信模式下,由 CAN 总线控制器发送的数据可以被自己接收并存入接收FIFO,同时这些发送数据也送至CAN 网络。
  • 回环静默(Loopback and Silent)通信模式;
    在回环静默通信模式下,CAN 的 RX 和 TX 引脚与 CAN 网络断开。CAN 总线控制器既不从CAN 网络接收数据,也不向 CAN 网络发送数据,其发送的数据仅可以被自己接收。
  • 正常(Normal)通信模式。
    CAN 总线控制器通常工作在正常通信模式下,可以从 CAN 总线接收数据,也可以向 CAN 总线发送数据。

4.4 数据发送

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.5 数据接收

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.6 过滤功能

一个待接收的数据帧会根据其标识符(Identifier)进行过滤:硬件会将通过过滤的帧送至接收FIFO,并丢弃没有通过过滤的帧。

4.7 GD32的CAN数据收发过程

在GD32芯片中,片内拥有的是CAN控制器。

发送过程:
CAN控制器将CPU传来的信号转换为逻辑电平(即逻辑0-显性电平或者逻辑1-隐性电平)。CAN发射器接收逻辑电平之后,再将其转换为差分电平输出到CAN总线上。
在这里插入图片描述

接收过程:
CAN接收器将CAN_H 和CAN_L 线上传来的差分电平转换为逻辑电平输出到CAN控制器,CAN控制器再把该逻辑电平转化为相应的信号发送到CPU上。
在这里插入图片描述

5.GD32的CAN代码编写及测试

5.1 GD32中CAN的初始化

gpio的配置
1.1 CAN和GPIO时钟初始化
1.2 CAN管脚复用(需要注意是 完全复用 还是 部分复用)

在这里插入图片描述
在这里插入图片描述


CAN控制器参数的配置

/* CAN initiliaze parameters structure */
typedef struct
{uint8_t working_mode;                         /*!< 配置CAN的工作模式 */ uint8_t resync_jump_width;                    /*!< 重新同步跳跃宽度 */uint8_t time_segment_1;                       /*!< 配置 BS1 段长度 */uint8_t time_segment_2;                       /*!< 配置 BS2 段长度 */ControlStatus time_triggered;                 /*!< 时间触发通信方式 */ControlStatus auto_bus_off_recovery;          /*!< 自动离线管理 */ControlStatus auto_wake_up;                   /*!< 自动唤醒功能 */ControlStatus auto_retrans;                   /*!< 自动重传模式 */ControlStatus rec_fifo_overwrite;             /*!< 配置接收 FIFO 锁定 */ControlStatus trans_fifo_order;               /*!< 配置 FIFO 优先级 */uint16_t prescaler;                           /*!< 配置CAN外设的时钟频率 */
}can_parameter_struct;
  • working_mode
    设置CAN的工作模式,可设置为正常通信模式(CAN_NORMAL_MODE),回环通信模式(CAN_LOOPBACK_MODE),静默通信模式(CAN_SILENT_MODE)以及回环静默通信模式(CAN_SILENT_LOOPBACK_MODE)。
    CAN 总线控制器通常工作在正常通信模式下,可以从 CAN 总线接收数据,也可以向 CAN 总线发送数据。(详情请查看GD32F10x用户手册)

  • resync_jump_width
    设置CAN的再同步补偿宽度SJW,对CAN网络节点同步误差进行补偿占1~4个时间单元。(CAN_BT_SJW_1/2/3/4TQ)

  • time_segment_1
    设置CAN位时序中BS1段长度,可以配置为1~16个时间单元。(CAN_BT_BS1_1 ~ 16TQ)
    注意:time_segment_1是包含了传播时间段和相位缓存段1,所有其范围是1 ~ 16,而不是 1 ~ 8
    在这里插入图片描述

  • time_segment_2
    设置CAN位时序中BS2段长度,可以配置为1~8个时间单元。(CAN_BT_BS2_1 ~ 8TQ)

  • time_triggered
    用于配置是否使用时间触发功能(ENABLE / DISABLE)。在这种通信模式下,自动重发功能是禁止的。

  • auto_bus_off_recovery
    CAN网络中,当节点因为发送错误计数器(TEC)超过一定阈值(如256)时,会进入总线关闭(Bus Off)状态,此时节点既不能接收总线上的报文,也不能向总线发送报文.
    当auto_bus_off_recovery字段被设置为启用(如ENABLE或TRUE)时,CAN控制器会在节点进入Bus Off状态后,自动尝试恢复通信,不需要软件干预。

  • auto_wake_up
    用于配置是否使用自动唤醒功能(ENABLE / DISABLE),使用自动唤醒功能后会在检测到总线活动后自动唤醒。

  • auto_retrans
    用于配置是否使用自动重传功能(ENABLE / DISABLE),使用自动重传功能时,会一直发送报文直到成功为止,否则只会发送一次报文。

  • rec_fifo_overwrite
    ENABLE(启用):当接收FIFO溢出时,新接收到的数据帧将覆盖FIFO中最旧的数据帧。这意味着,如果FIFO的容量不足以存储所有接收到的数据帧,则最早接收到的数据帧将被丢弃。
    DISABLE(禁用):当接收FIFO溢出时,新接收到的数据帧将被丢弃,而FIFO中已有的数据帧将保持不变。这有助于保护关键数据不被意外覆盖。

  • trans_fifo_order
    用于设置是否使用发送报文的优先级判定方法(ENABLE / DISABLE),使能后,以报文存入FIFO的先后顺序来发送,否则按照报文ID的优先级来发送。

  • prescaler
    设置CAN外设的时钟分频,写入的值即为分频值。可控制时间片的时间长度。
    在这里插入图片描述

快速计算:BaudRate = APB1_LCK / ( 1 + time_segment_1 +time_segment_2 ) / prescaler

例:APB1总线时钟频率为54MHZ,BS1=5,BS2=3,prescaler=12,实际波特率为500Kbps。


CAN过滤器的配置

/* CAN filter parameters structure */
typedef struct
{uint16_t filter_list_high;                 /*!< 过滤器列表高位数 */uint16_t filter_list_low;                  /*!< 过滤器列表低位数 */uint16_t filter_mask_high;                 /*!< 滤波掩码数高位数 */uint16_t filter_mask_low;                  /*!< 滤波掩码数低位数 */uint16_t filter_fifo_number;               /*!< 接收与过滤器相关联的FIFO */uint16_t filter_number;                    /*!< 筛选器编号 */uint16_t filter_mode;                      /*!< 列表或掩码模式 */uint16_t filter_bits;                      /*!< 筛选器位宽 */ControlStatus filter_enable;               /*!< 是否使能改筛选器 */
}can_filter_parameter_struct;
  • filter_list_high
    用于存储要过滤的ID,若过滤器工作在32位模式,他存储的是所过滤ID的高16位;若过滤器工作在16位模式,它存储的就是一个完整的要过滤的ID。

  • filter_list_low
    用于存储要过滤的ID,若过滤器工作在32位模式,他存储的是所过滤ID的低16位;若过滤器工作在16位模式,它存储的就是一个完整的要过滤的ID。

  • filter_mask_high
    filter_mask_high的存储的内容分两种情况,当过滤器工作在列表模式时,他的功能与filter_list_high相同,都是存储要过滤的ID;当工作在掩码模式时,它存储的是与filter_list_high成员对应的掩码。

  • filter_mask_low
    filter_mask_low的存储的内容分两种情况,当过滤器工作在列表模式时,他的功能与filter_list_low相同,都是存储要过滤的ID;当工作在掩码模式时,它存储的是与filter_list_low成员对应的掩码。
    在掩码模式下,filter_mask_high与filter_mask_low填入的是要筛选的掩码,掩码为1时,筛选的ID必须与ID相同。
    列表模式下,筛选的ID需与ID相同。

  • filter_fifo_number
    用于设置当报文通过过滤器匹配后,该报文会被存储到哪个接收FIFO中(CAN_FIFO0/1)。

  • filter_number
    用于设计过滤器的编号。可以通过设置多个过滤器对特定的ID进行过滤。
    在这里插入图片描述

  • filter_mode
    设置过滤器的工作模式,可以设置为列表模式(CAN_FILTERMODE_LIST),也可以设置为掩码模式(CAN_FILTERMODE_MASK)。

  • filter_bits
    设置过滤器位宽(32位或16位)。

  • filter_enable
    用于设置是否使能这个过滤器(ENABLE/DISABLE)。

5.2 CAN回环模式代码及测试

调试需要使用回环模式,回环模式下可以不考虑CAN波特率,不考虑ID,便于调试。

正常模式下需要接外部CAN设备,并且双方配置需完全正确,否则一般发送数据就会失败,返回CAN_TRANSMIT_PENDING或者CAN_TRANMIT_FAILED,此时无法确认时双方匹配问题,或者硬件问题,还是自身配置问题,建议使用回环模式调试,先确认自身CAN配置是否正确。

下面代码掩码的过滤模式,过滤全填0,所有数据都会接收,不会过滤,确保接收中断可以接收到数据后,后面再根据实际过滤ID进行填写。

#include "can.h"
#include "gd32f30x.h"
#include "gd32f30x_can.h"
#include <string.h>
#include "log.h"
#include "invt_os.h"#define CAN_RCU              RCU_CAN0
#define CAN_GPIO_PORT_RCU    RCU_GPIOB#define CAN_GPIO_PORT        GPIOB
#define CAN_RX_GPIO_PIN      GPIO_PIN_8
#define CAN_TX_GPIO_PIN      GPIO_PIN_9#define CAN_CONTROLLER       CAN0int32_t can0_init(void)
{can_parameter_struct can_parameter;can_filter_parameter_struct can_filter;/* enable can clock */rcu_periph_clock_enable(RCU_AF);rcu_periph_clock_enable(CAN_RCU);rcu_periph_clock_enable(CAN_GPIO_PORT_RCU);/* configure CAN0 GPIO, CAN0_TX(PB9) and CAN0_RX(PB8) */gpio_init(CAN_GPIO_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CAN_TX_GPIO_PIN);gpio_init(CAN_GPIO_PORT, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, CAN_RX_GPIO_PIN);gpio_pin_remap_config(GPIO_CAN0_PARTIAL_REMAP, ENABLE);  /* 注意:PB8,PB9是在部分复用是才是CAN,而不是全复用!!! *//* initialize CAN structures */can_struct_para_init(CAN_INIT_STRUCT, &can_parameter);can_struct_para_init(CAN_FILTER_STRUCT, &can_filter);/* initialize CAN register */can_deinit(CAN_CONTROLLER);/* initialize CAN */can_parameter.time_triggered = DISABLE;             /* 关闭时间触发通信 */can_parameter.auto_bus_off_recovery = ENABLE;       /* 允许离线自行回复 */can_parameter.auto_wake_up = ENABLE;                /* 允许自唤醒 */can_parameter.auto_retrans = ENABLE;                /* 允许自动重传 */can_parameter.rec_fifo_overwrite = DISABLE;         /* FIFO满了,不允许重盖写入 */can_parameter.trans_fifo_order = DISABLE;           /* 使能后,以报文存入FIFO的先后顺序来发送,否则按照报文ID的优先级来发送 */can_parameter.working_mode = CAN_LOOPBACK_MODE;       /* 回环模式 */can_parameter.resync_jump_width = CAN_BT_SJW_1TQ;   /* 对CAN网络节点同步误差进行补偿占1个CAN时钟节拍 */can_parameter.time_segment_1 = CAN_BT_BS1_7TQ;      /* 相位1使用7个CAN时钟节拍 */can_parameter.time_segment_2 = CAN_BT_BS2_4TQ;      /* 相位2使用4个CAN时钟节拍 *//* AHB1 54M ,54000/(18*(7+4+1)) = 250kbps */can_parameter.prescaler = 20;                       /* CAN时钟的分配系数 */can_init(CAN_CONTROLLER, &can_parameter);/* initialize filter *//* CAN0 filter number *///3.配置CAN过滤器// can_filter.filter_list_high = (((uint32_t)0x000<<21|CAN_FT_DATA|CAN_FF_STANDARD)&0xFFFF0000)>>16;   /* 过滤器高字节 */// can_filter.filter_list_low =  (((uint32_t)0x000<<21|CAN_FT_DATA|CAN_FF_STANDARD) & 0x0000FFFF);     /* 过滤器低字节 */can_filter.filter_list_high = 0x56;can_filter.filter_list_low = 0x56;can_filter.filter_mask_high = 0;                    /* 过滤器掩码数高位,0表示全盘接收 */can_filter.filter_mask_low = 0;                     /* 过滤器掩码数低位,0表示全盘接收 */can_filter.filter_fifo_number = CAN_FIFO0;          /* 滤过器关联FIFO0 */can_filter.filter_number = 0;                       /* 0号过滤器 */can_filter.filter_mode = CAN_FILTERMODE_MASK;       /* 掩码模式 */can_filter.filter_bits = CAN_FILTERBITS_32BIT;      /* 32位 */can_filter.filter_enable = ENABLE;can_filter_init(&can_filter);//4.配置中断nvic_irq_enable(CAN0_RX0_IRQn, 0, 0); can_interrupt_enable(CAN0,CAN_INT_RFNE0);return 0;
}int32_t can0_deinit(void)
{return 0;
}int32_t can0_write(const char *buffer, size_t len)
{uint8_t i,mbox;can_trasnmit_message_struct can_tx_msg;uint8_t timeout = 1000;if (len > 8) {len = 8;}can_tx_msg.tx_efid = 0x123;//扩展帧才起作用,低29位有效,故这里0xffffffff的实际有效值是0b1 1111 1111 1111 1111 1111 1111 1111 = 536870911can_tx_msg.tx_sfid = 0x56;//标准帧才其作用,低11位有效,故0xfff的实际有效值是0b111 1111 1111 = 2047can_tx_msg.tx_ff = CAN_FF_STANDARD;can_tx_msg.tx_ft = CAN_FT_DATA;//数据帧 CAN_FF_STANDARD  CAN_FF_EXTENDEDcan_tx_msg.tx_dlen = len;for(i=0;i<len;i++){can_tx_msg.tx_data[i] = buffer[i];}mbox = can_message_transmit(CAN0,&can_tx_msg);				//发送CAN报文//等待发送完成while(can_transmit_states(CAN0,mbox) == CAN_TRANSMIT_PENDING) {if(timeout-- == 0){LOG_ERRO("CAN transmit failed.\n");return 1;}ios_sleep(1);  // 1ms delay for the CAN bus to be free. If not, the next transmit may fail.}if(can_transmit_states(CAN0,mbox) == CAN_TRANSMIT_OK){return 0;}return 1;
}can_receive_message_struct can_rx_msg;
int32_t can0_read(char *buffer, size_t len)
{LOG_INFO("sfid:%d, rx_efid:%d, rx_ff;%d, rx_dlen:%d\n", can_rx_msg.rx_sfid, can_rx_msg.rx_efid, can_rx_msg.rx_ff, can_rx_msg.rx_dlen);memcpy(buffer, can_rx_msg.rx_data, 8);return 0;
}void CAN0_RX0_IRQHandler(void)
{/* check the receive message */if(can_interrupt_flag_get(CAN0,CAN_INT_FLAG_RFL0)){memset(&can_rx_msg, 0, sizeof(can_rx_msg));//清空接收结构体can_message_receive(CAN0, CAN_FIFO0, &can_rx_msg);}
}

测试主函数

    can0_init();char recv_buf[10] = {0};while(1) {can0_write("123578", sizeof("123578")-1);ios_sleep(1000);memset(recv_buf, 0, sizeof(recv_buf));can0_read(recv_buf, 8);LOG_INFO("can read:%s\n", recv_buf);}

查看输出的日志,可以设备自身自发自收
在这里插入图片描述

5.3 CAN普通模式代码及测试

相比与上面的回环模式,下面的发送代码中,加了发送超时则失败的判断

#include "can.h"
#include "gd32f30x.h"
#include "gd32f30x_can.h"
#include <string.h>
#include "log.h"
#include "invt_os.h"#define CAN_RCU              RCU_CAN0
#define CAN_GPIO_PORT_RCU    RCU_GPIOB#define CAN_GPIO_PORT        GPIOB
#define CAN_RX_GPIO_PIN      GPIO_PIN_8
#define CAN_TX_GPIO_PIN      GPIO_PIN_9#define CAN_CONTROLLER       CAN0int32_t can0_init(void)
{can_parameter_struct can_parameter;can_filter_parameter_struct can_filter;/* enable can clock */rcu_periph_clock_enable(RCU_AF);rcu_periph_clock_enable(CAN_RCU);rcu_periph_clock_enable(CAN_GPIO_PORT_RCU);/* configure CAN0 GPIO, CAN0_TX(PB9) and CAN0_RX(PB8) */gpio_init(CAN_GPIO_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CAN_TX_GPIO_PIN);gpio_init(CAN_GPIO_PORT, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, CAN_RX_GPIO_PIN);gpio_pin_remap_config(GPIO_CAN0_PARTIAL_REMAP, ENABLE);  /* 注意:PB8,PB9是在部分复用是才是CAN,而不是全复用!!! *//* initialize CAN structures */can_struct_para_init(CAN_INIT_STRUCT, &can_parameter);can_struct_para_init(CAN_FILTER_STRUCT, &can_filter);/* initialize CAN register */can_deinit(CAN_CONTROLLER);/* initialize CAN */can_parameter.time_triggered = DISABLE;             /* 关闭时间触发通信 */can_parameter.auto_bus_off_recovery = ENABLE;       /* 允许离线自行回复 */can_parameter.auto_wake_up = ENABLE;                /* 允许自唤醒 */can_parameter.auto_retrans = ENABLE;                /* 允许自动重传 */can_parameter.rec_fifo_overwrite = DISABLE;         /* FIFO满了,不允许重盖写入 */can_parameter.trans_fifo_order = DISABLE;           /* 使能后,以报文存入FIFO的先后顺序来发送,否则按照报文ID的优先级来发送 */can_parameter.working_mode = CAN_NORMAL_MODE;       /* 普通模式 */can_parameter.resync_jump_width = CAN_BT_SJW_1TQ;   /* 对CAN网络节点同步误差进行补偿占1个CAN时钟节拍 */can_parameter.time_segment_1 = CAN_BT_BS1_7TQ;      /* 相位1使用7个CAN时钟节拍 */can_parameter.time_segment_2 = CAN_BT_BS2_4TQ;      /* 相位2使用4个CAN时钟节拍 *//* AHB1 54M ,54000/(18*(7+4+1)) = 250kbps */can_parameter.prescaler = 20;                       /* CAN时钟的分配系数 */can_init(CAN_CONTROLLER, &can_parameter);/* initialize filter *//* CAN0 filter number *///3.配置CAN过滤器// can_filter.filter_list_high = (((uint32_t)0x000<<21|CAN_FT_DATA|CAN_FF_STANDARD)&0xFFFF0000)>>16;   /* 过滤器高字节 */// can_filter.filter_list_low =  (((uint32_t)0x000<<21|CAN_FT_DATA|CAN_FF_STANDARD) & 0x0000FFFF);     /* 过滤器低字节 */can_filter.filter_list_high = 0x56;can_filter.filter_list_low = 0x56;can_filter.filter_mask_high = 0;                    /* 过滤器掩码数高位,0表示全盘接收 */can_filter.filter_mask_low = 0;                     /* 过滤器掩码数低位,0表示全盘接收 */can_filter.filter_fifo_number = CAN_FIFO0;          /* 滤过器关联FIFO0 */can_filter.filter_number = 0;                       /* 0号过滤器 */can_filter.filter_mode = CAN_FILTERMODE_MASK;       /* 掩码模式 */can_filter.filter_bits = CAN_FILTERBITS_32BIT;      /* 32位 */can_filter.filter_enable = ENABLE;can_filter_init(&can_filter);//4.配置中断nvic_irq_enable(CAN0_RX0_IRQn, 0, 0); can_interrupt_enable(CAN0,CAN_INT_RFNE0);return 0;
}int32_t can0_deinit(void)
{return 0;
}int32_t can0_write(const char *buffer, size_t len)
{uint8_t i,mbox;can_trasnmit_message_struct can_tx_msg;uint8_t timeout = 1000;if (len > 8) {len = 8;}can_tx_msg.tx_efid = 0x123;//扩展帧才起作用,低29位有效,故这里0xffffffff的实际有效值是0b1 1111 1111 1111 1111 1111 1111 1111 = 536870911can_tx_msg.tx_sfid = 0x56;//标准帧才其作用,低11位有效,故0xfff的实际有效值是0b111 1111 1111 = 2047can_tx_msg.tx_ff = CAN_FF_STANDARD;can_tx_msg.tx_ft = CAN_FT_DATA;//数据帧 CAN_FF_STANDARD  CAN_FF_EXTENDEDcan_tx_msg.tx_dlen = len;for(i=0;i<len;i++){can_tx_msg.tx_data[i] = buffer[i];}mbox = can_message_transmit(CAN0,&can_tx_msg);				//发送CAN报文//等待发送完成while(can_transmit_states(CAN0,mbox) == CAN_TRANSMIT_PENDING) {if(timeout-- == 0){LOG_ERRO("CAN transmit failed.\n");return 1;}ios_sleep(1);  // 1ms delay for the CAN bus to be free. If not, the next transmit may fail.}if(can_transmit_states(CAN0,mbox) == CAN_TRANSMIT_OK){return 0;}return 1;
}can_receive_message_struct can_rx_msg;
int32_t can0_read(char *buffer, size_t len)
{LOG_INFO("sfid:%d, rx_efid:%d, rx_ff;%d, rx_dlen:%d\n", can_rx_msg.rx_sfid, can_rx_msg.rx_efid, can_rx_msg.rx_ff, can_rx_msg.rx_dlen);memcpy(buffer, can_rx_msg.rx_data, 8);return 0;
}void CAN0_RX0_IRQHandler(void)
{/* check the receive message */if(can_interrupt_flag_get(CAN0,CAN_INT_FLAG_RFL0)){memset(&can_rx_msg, 0, sizeof(can_rx_msg));//清空接收结构体can_message_receive(CAN0, CAN_FIFO0, &can_rx_msg);}
}

测试主函数

    can0_init();char recv_buf[10] = {0};while(1) {can0_write("123578", sizeof("123578")-1);ios_sleep(1000);memset(recv_buf, 0, sizeof(recv_buf));can0_read(recv_buf, 8);LOG_INFO("can read:%s\n", recv_buf);}

测试工具
通过usb-to-can调试器进行调试

插上usb-to-can,安装驱动,设备管理器中如下显示:
在这里插入图片描述
打开pcanView调试助手:
在这里插入图片描述

选择好CAN的波特率,以及接收的帧格式。这里选择250KBit/s,标准帧。
在这里插入图片描述

在没有连接上USB to CAN 模块前,设备日志输出发送错误。
在这里插入图片描述

连接上USB to CAN 模块后,设备数据正常输出,并且在PCAN-VIEW软件中查看到接收的数据。
在这里插入图片描述


通过PCAN-View软件发送标准数据帧,看出设备日志。
在这里插入图片描述

看到设备能够正确收到数据
在这里插入图片描述


改变发送帧的配置,使用拓展帧,并改变ID
在这里插入图片描述

查看到设备输出日志有所变化,ID变了,rx_ff:4表明是用了扩展帧。
在这里插入图片描述

5.4 过滤器的测试代码

把CAN初始化代码改成如下代码,则只有ID=0x56的标准帧才会通过 过滤器,被设备接收到。

int32_t can0_init(void)
{uint32_t filter_id = 0;can_parameter_struct can_parameter;can_filter_parameter_struct can_filter;/* enable can clock */rcu_periph_clock_enable(RCU_AF);rcu_periph_clock_enable(CAN_RCU);rcu_periph_clock_enable(CAN_GPIO_PORT_RCU);/* configure CAN0 GPIO, CAN0_TX(PB9) and CAN0_RX(PB8) */gpio_init(CAN_GPIO_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CAN_TX_GPIO_PIN);gpio_init(CAN_GPIO_PORT, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, CAN_RX_GPIO_PIN);gpio_pin_remap_config(GPIO_CAN0_PARTIAL_REMAP, ENABLE);  /* 注意:PB8,PB9是在部分复用是才是CAN,而不是全复用!!! *//* initialize CAN structures */can_struct_para_init(CAN_INIT_STRUCT, &can_parameter);can_struct_para_init(CAN_FILTER_STRUCT, &can_filter);/* initialize CAN register */can_deinit(CAN_CONTROLLER);/* initialize CAN */can_parameter.time_triggered = DISABLE;             /* 关闭时间触发通信 */can_parameter.auto_bus_off_recovery = ENABLE;       /* 允许离线自行回复 */can_parameter.auto_wake_up = ENABLE;                /* 允许自唤醒 */can_parameter.auto_retrans = ENABLE;                /* 允许自动重传 */can_parameter.rec_fifo_overwrite = DISABLE;         /* FIFO满了,不允许重盖写入 */can_parameter.trans_fifo_order = DISABLE;           /* 使能后,以报文存入FIFO的先后顺序来发送,否则按照报文ID的优先级来发送 */can_parameter.working_mode = CAN_LOOPBACK_MODE;       /* 回环模式,自己发出去的数据,自己也能接收得到,用于测试 */can_parameter.resync_jump_width = CAN_BT_SJW_1TQ;   /* 对CAN网络节点同步误差进行补偿占1个CAN时钟节拍 */can_parameter.time_segment_1 = CAN_BT_BS1_7TQ;      /* 相位1使用7个CAN时钟节拍 */can_parameter.time_segment_2 = CAN_BT_BS2_4TQ;      /* 相位2使用4个CAN时钟节拍 *//* AHB1 54M ,54000/(18*(7+4+1)) = 250kbps */can_parameter.prescaler = 20;                       /* CAN时钟的分配系数 */can_init(CAN_CONTROLLER, &can_parameter);/* initialize filter *//* CAN0 filter number *///3.配置CAN过滤器// filter_value = (0x111<<3) | CAN_FT_DATA | CAN_FF_STANDARD;// can_filter.filter_list_high = filter_value>>16;   /* 过滤器高字节 */// can_filter.filter_list_low =  filter_value;     /* 过滤器低字节 */filter_id = 0x56;can_filter.filter_list_high = (((uint32_t)filter_id<<21|CAN_FT_DATA|CAN_FF_STANDARD)&0xFFFF0000)>>16;   /* 过滤器高字节 */can_filter.filter_list_low =  (((uint32_t)filter_id<<21|CAN_FT_DATA|CAN_FF_STANDARD) & 0x0000FFFF);     /* 过滤器低字节 */can_filter.filter_mask_high = 0;                    /* 过滤器掩码数高位,0表示全盘接收 */can_filter.filter_mask_low = 0;                     /* 过滤器掩码数低位,0表示全盘接收 */can_filter.filter_fifo_number = CAN_FIFO0;          /* 滤过器关联FIFO0 */can_filter.filter_number = 0;                       /* 0号过滤器 */can_filter.filter_mode = CAN_FILTERMODE_LIST;       /* 掩码模式 */can_filter.filter_bits = CAN_FILTERBITS_32BIT;      /* 32位 */can_filter.filter_enable = ENABLE;can_filter_init(&can_filter);//4.配置中断nvic_irq_enable(CAN0_RX0_IRQn, 0, 0); can_interrupt_enable(CAN0,CAN_INT_RFNE0);return 0;
}

主要关注下面这段代码

    filter_id = 0x56;can_filter.filter_list_high = (((uint32_t)filter_id<<21|CAN_FT_DATA|CAN_FF_STANDARD)&0xFFFF0000)>>16;   /* 过滤器高字节 */can_filter.filter_list_low =  (((uint32_t)filter_id<<21|CAN_FT_DATA|CAN_FF_STANDARD) & 0x0000FFFF);     /* 过滤器低字节 */can_filter.filter_mask_high = 0;                    /* 过滤器掩码数高位,0表示全盘接收 */can_filter.filter_mask_low = 0;                     /* 过滤器掩码数低位,0表示全盘接收 */can_filter.filter_fifo_number = CAN_FIFO0;          /* 滤过器关联FIFO0 */can_filter.filter_number = 0;                       /* 0号过滤器 */can_filter.filter_mode = CAN_FILTERMODE_LIST;       /* 掩码模式 */can_filter.filter_bits = CAN_FILTERBITS_32BIT;      /* 32位 */can_filter.filter_enable = ENABLE;can_filter_init(&can_filter);

在这里插入图片描述
FDATA分为三部分,SFID为标准ID,EFID为扩展ID,FF为数据或遥控帧,FT为标准帧或扩展帧,高16位为filter_list_high,低16位为filter_list_low。

根据FDATA的分段,我们需要注意filter_list_highfilter_list_low两个字段的设置。
若是过滤标准帧ID,则是 (((uint32_t)filter_id<< 21 |CAN_FT_DATA| CAN_FF_STANDARD )&0xFFFF0000)
若是要过滤拓展帧ID,则是 (((uint32_t)filter_id<< 3 |CAN_FT_DATA| CAN_FF_EXTENDED )&0xFFFF0000)

5.5 总结的注意要点

①注意CAN的GPIO口重映射问题,有的是完全映射,有点是部分映射。
②CAN发送数据,需要其他CAN控制器进行确认回复,才能发送成功,否则是发送不成功的
③过滤器标准帧和过滤去拓展帧的区别

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com