CAN协议通信 学习笔记

news2024/9/26 5:21:31

文章目录

  • 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       CAN0


int32_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 60M ,60000/(20*(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 = 536870911
    can_tx_msg.tx_sfid = 0x56;//标准帧才其作用,低11位有效,故0xfff的实际有效值是0b111 1111 1111 = 2047
    can_tx_msg.tx_ff = CAN_FF_STANDARD;
    can_tx_msg.tx_ft = CAN_FT_DATA;//数据帧 CAN_FF_STANDARD  CAN_FF_EXTENDED
    can_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       CAN0


int32_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 60M ,60000/(20*(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 = 536870911
    can_tx_msg.tx_sfid = 0x56;//标准帧才其作用,低11位有效,故0xfff的实际有效值是0b111 1111 1111 = 2047
    can_tx_msg.tx_ff = CAN_FF_STANDARD;
    can_tx_msg.tx_ft = CAN_FT_DATA;//数据帧 CAN_FF_STANDARD  CAN_FF_EXTENDED
    can_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 60M ,60000/(20*(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控制器进行确认回复,才能发送成功,否则是发送不成功的
③过滤器标准帧和过滤去拓展帧的区别

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2089517.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python-FastApi框架

文章目录 FastApi一. 简介二. 特性三. 安装1. 安装fastapi模块2. 安装ASGI服务器( Uvicorn 或者 Hypercorn) 四. 实例1. 创建**main.py**文件(GET请求)2. 运行3. 测试4. 更新main_py(加入PUT请求) 五. 自动化API文档1. Swagger UI(交互式文档)2. ReDoc(可选式文档) FastApi 一…

华为云征文|Flexus云服务器X,云上性能新飞跃,开启业务增长新纪元

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;CSDN领军人物&#xff0c;全栈领域优质创作者✌&#xff0c;CSDN博客专家&#xff0c;阿里云社区专家博主&#xff0c;2023年6月CSDN上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;AWS/阿里云资深使用…

想告诉所有人,我找到脸书视频保存方法啦!

各位集美集帅们&#xff0c;我可算找到脸书视频的保存教学啦。作为社媒体人&#xff0c;在脸书看到有趣的素材却保存不了时真的要急的爆炸了。试了好多方式&#xff0c;这软件是最给力哒&#xff0c;我不管&#xff0c;下面的步骤介绍你一定要看完&#xff01; 打开脸书&#x…

JVM面试(一)什么是虚拟机?什么是class文件?

什么是java虚拟机&#xff1f; 如果通俗点来讲&#xff0c;我们在电脑上一行行敲出来的代码&#xff0c;电脑本身是不认识的&#xff0c;最终是要转成电脑可以运行的101001这种字节。 但是这些我们又不可能手动来转换&#xff0c;所以呢&#xff0c;就需要一个工具&#xff0…

关于redis存储数据类型选择

项目使用的spring-boot&#xff0c;操作redis使用的是spring redis的api 在序列化的时候&#xff0c;如果往redis存入的是比较小的数字&#xff0c;反序列化的时候&#xff0c;会是integer类型 如果字段定义的是Long类型&#xff0c;因为比较小&#xff0c;所以被反序列化成i…

Cadence高速板设计技巧(全志H3)

市场上一般的电视屏幕是4K的&#xff1a; cadence查找&#xff1a; 右侧的面板FIND里面输入要查找的名字就可以进行查找。 全局查找需要鼠标点击到.DSN的&#xff0c;进入全局。 在视图里选择一个层就可以单独查看这个层的东西&#xff0c;屏蔽掉其他层的东西&#xff1a; 共…

linux命令:用于删除空目录的命令行工具rmdir详细介绍

目录 一、概述 二、用法 1、基本语法 &#xff08;1&#xff09;选项 &#xff08;2&#xff09;目录... 2、主要选项 &#xff08;1&#xff09;-p, --parents &#xff08;2&#xff09; -v, --verbose &#xff08;3&#xff09; -h, --help &#xff08;4&#x…

Mysql基础练习题 596.查询至少有5个学生的所有班级 (力扣)

596.查询至少有5个学生的所有班级 建表插入数据&#xff1a; Create table If Not Exists Courses (student varchar(255), class varchar(255)) Truncate table Courses insert into Courses (student, class) values (A, Math) insert into Courses (student, class) value…

指针进阶(多级指针)

0.多级指针命名 多级指针命名&#xff0c;最主要的是要知道该指针指向的是什么数据。 一.1级指针 - 指向一个变量 若定义一个变量 int a&#xff0c;那么 目标类型就为 int。 所以该指针应该定义为 int *p; /* 目标 */ int a;/* 目标类型 *p */ int *p;/* 指向目标 */ p a;二…

服务器数据恢复—磁盘坏扇区导致raid6阵列崩溃的数据恢复案例

服务器存储数据恢复环境&#xff1a; 一台存储中有一组由12块SAS硬盘组建的raid6磁盘阵列&#xff0c;划分了1个卷&#xff0c;由数台Vmware ESXI主机共享存储。卷中存放了大量的Windows系统虚拟机。这些虚拟机系统盘大小一致&#xff0c;数据盘大小不确定&#xff0c;数据盘都…

安卓主板_MTK安卓主板定制_联发科主板/开发板方案

这款安卓主板采用了联发科的MTK8788、MTK8768及MTK8766系列芯片平台&#xff0c;运用了64位四核/八核 Cortex-A53/A73架构&#xff0c;主频高达2.0 GHz。主板配置了4GB LPDDR3内存和64GB eMMC存储&#xff0c;同时配备了ARM Mail-T450 MP2图形处理单元(GPU)&#xff0c;使其在4…

PbootCMS程序安全设置建议

近期遇到部分使用PbootCMS开源程序的客户反馈网站被挂马/入侵等情况&#xff0c;我司核实原因是由于此程序存在漏洞&#xff0c;用户可以按照以下建议进行安全设置。 虚拟主机 一、程序建议&#xff1a; 1、登录后台&#xff0c;将程序升级到最新版本&#xff0c;密码重置为…

算力网络痛点;对象存储OSS;CPN功能模块

目录 算力网络 算力网络痛点:度量困难、种类繁多、分布广泛、归属复杂。 CPN功能模块 对象存储OSS 算力网络 在分析算力资源的特点前,我们首先要明确算力的概念。算力,也称为计算力或计算能力。该词的最早来源已经不可查证,互联网上的资料大多与区块链相关。这是因为区…

MyBatis拦截器面试题

JDBC的执行流程 &#xff08;面试题一&#xff09; MyBatis执行流程&#xff08;面试题二&#xff09; (我的猜测:1执行器通过工厂执行Mapper类,,2.语句映射器处理mappe文件成对象,3把前端传过来的参数映射到对象里,4输出结果映射) Mybatis拦截器(四个) 拦截的执行顺序是Execu…

ubuntu设置为自己需要的屏幕分辨率

先说一下我处理该问题的大体背景&#xff1a;我是学习Linux的新手&#xff0c;刚学完嵌入式Linux驱动开发相关课程。现在想接着学习一下QT开发。我是在电脑上装了虚拟机之后安装的ubuntu系统。因为换了电脑&#xff0c;所以重新装了ubuntu系统。但是&#xff0c;装完ubuntu系统…

AI Agent从实操体验到代码理解

一、从体验出发 从chatbot到co-pilot&#xff0c;LLM应为的场景不断在扩大&#xff0c;形式也越来越多样化&#xff0c;到如今&#xff0c;chatbot的风头基本已过&#xff0c;co-pilot正在成为大模型嵌入到产品里面的主要形态&#xff0c;但随着PMF&#xff08;产品市场契合度&…

基于asp.net的webform图书管理系统附源码

今天给大家分享一个c#的图书管理系统&#xff0c;采用的SQLserver数据库&#xff0c;VS开发&#xff0c;详情如下 1.主要功能 主要功能包含用户注册登陆、借书管理、还书管理、管理员登陆、个人中心 、密码修改、图书分类、图书管理等等。2.开发工具及其环境 系统采用VS2013…

MySQL批量插入测试数据的2种方式

在开发过程中我们不管是用来测试性能还是在生产环境中页面展示好看一点, 又或者学习验证某一知识点经常需要一些测试数据, 这个时候如果手敲的话, 十行二十行还好, 多了就很死亡了, 接下来介绍两种常用的MySQL测试数据批量生成方式&#xff1a; 存储方式函数 Navicat的数据生成…

投屏软件哪个好用又免费?4款投屏软件,远程且兼容多平台!

投屏软件哪个好用又免费&#xff1f;不论是在工作中做演示、在家中娱乐观影&#xff0c;还是在学习中进行远程教学&#xff0c;投屏软件都能让你的设备实现多屏互动&#xff0c;让体验更为流畅和便捷。今天&#xff0c;我就为大家推荐几款热门的投屏软件。 1. 乐播投屏&#xf…

第二证券:白酒股,全线飘红

A股首要指数走势分化&#xff0c;到午间收盘&#xff0c;沪指跌0.45%&#xff0c;深成指涨1.03%&#xff0c;创业板指涨0.8%。近4000只个股上涨&#xff0c;两市半日成交3854亿元&#xff0c;较上日同期放量629亿元。 盘面上&#xff0c;光伏设备板块冲高&#xff0c;快可电子…