正点原子嵌入式linux驱动开发——Linux CAN驱动

CAN是目前应用非常广泛的现场总线之一,主要应用于汽车电子和工业领域,尤其是汽车领域,汽车上大量的传感器与模块都是通过CAN总线连接起来的。CAN总线目前是自动化领域发展的热点技术之一,由于其高可靠性,CAN总线目前广泛的应用于工业自动化、船舶、汽车、医疗和工业设备等方面。

STM32MP1自带了CAN外设,因此可以开发CAN相关的设备,本章就来学习一下如何驱动正点原子的STM32MP1开发板上的CAN接口。

CAN协议简析

这里可以直接看正点原子资料里面的《CAN入门教程》。这里我之前裸机开发的时候,看正点原子的视频也看过CAN总线,但是这里内容比较多,已经有点忘了,而且实验的话当时只有一块板子也没做过通讯,现在重新学一遍。

何为CAN

CAN的全称为ControllerArea Network,也就是控制局域网络,简称为CAN。CAN最早是由德国BOSCH(博世)开发的,目前已经是国际标准(ISO 11898),是当前应用最广泛的现场总线之一。BOSCH主要是做汽车电子的,因此CAN一开始主要是为汽车电子准备的,事实也是如此,CAN协议目前已经是汽车网络的标准协议。当然了,CAN不仅仅应用于汽车电子,经过几十年的发展,CAN协议的高性能和高可靠性已经得到了业界的认可,目前除了汽车电子以外也广泛应用于工业自动化、医疗、工业和船舶等领域。

以汽车电子为例,汽车上有空调、车门、发动机、大量传感器等,这些部件都是通过CAN总线连在一起形成一个网络,车载网络结构如下图所示:

正点原子嵌入式linux驱动开发——Linux CAN驱动_第1张图片

上图中各个单元通过CAN总线连接在一起,每个单元都是独立的CAN节点同一个CAN网络中所有单元的通信速度必须一致,不同的网络之间通信速度可以不同。比如上图中125Kbps 的CAN网络下所有的节点速度都是125Kbps 的,整个网络由一个网关与其他的网络连接。

CAN的特点主要有以下几点:

多主控制

在总线空闲时,所有单元都可以发送消息(多主控制),而两个以上的单元同时开始发送消息时,根据标识符(Identifier,以下称为ID)决定优先级。ID并不是表示发送的目的地址,而是表示访问总线的消息的优先级两个以上的单元同时开始发送消息时,对各消息ID 的每个位进行逐个仲裁比较。仲裁获胜(被判定为优先级最高)的单元可继续发送消息,仲裁失利的单元则立刻停止发送而进行接收工作

系统的柔软性

与总线相连的单元没有类似于“地址”的信息。因此在总线上增加单元时,连接在总线上的其它单元的软硬件及应用层都不需要改变。

通信速度快、距离远

最高1Mbps(距离小于40M),最远可达10KM(速率低于5Kbps),最新的CAN FD速度
可以到5Mbps。

具有错误检测、错误通知和错误恢复功能

所有单元都可以检测错误(错误检测功能),检测出错误的单元会立即同时通知其他所有单元(错误通知功能),正在发送消息的单元一旦检测出错误,会强制结束当前的发送。强制结束发送的单元会不断反复地重新发送此消息直到成功发送为止(错误恢复功能)。

故障封闭功能

CAN可以判断出错误的类型是总线上暂时的数据错误(如外部噪声等)还是持续的数据错误(如单元内部故障、驱动器故障、断线等)。由此功能,当总线上发生持续数据错误时,可将引起此故障的单元从总线上隔离出去

连接节点多

CAN总线是可同时连接多个单元的总线。可连接的单元总数理论上是没有限制的。但实际上可连接的单元数受总线上的时间延迟及电气负载的限制。降低通信速度,可连接的单元数增加;提高通信速度,则可连接的单元数减少

CAN电气属性

CAN总线使用两根线来连接各个单元CAN_H 和CAN_L,CAN控制器通过判断这两根线上的电位差来得到总线电平,CAN总线电平分为显性电平和隐性电平两种显性电平表示逻辑“0”,此时CAN_H电平比CAN_L 高,分别为3.5V和1.5V,电位差为2V。隐形电平表示逻辑“1”,此时CAN_H和CAN_L电压都为2.5V左右,电位差为0V。CAN总线就通过显性和隐形电平的变化来将具体的数据发送出去,如下图所示:

正点原子嵌入式linux驱动开发——Linux CAN驱动_第2张图片

CAN总线上没有节点传输数据的时候一直处于隐性状态,也就是说总线空闲状态的时候一直处于隐性。CAN网络中的所有单元都通过CAN_H和CAN_L这两根线连接在一起,如下图所示:

正点原子嵌入式linux驱动开发——Linux CAN驱动_第3张图片

上图中所有的CAN节点单元都采用CAN_H和CAN_L这两根线连接在一起,CAN_H接CAN_H、CAN_L接 CAN_L,CAN总线两端要各接一个120Ω的端接电阻,用于匹配总线阻抗,吸收信号反射及回拨,提高数据通信的抗干扰能力以及可靠性。

CAN总线传输速度可达1Mbps/S,最新的CAN-FD最高速度可达5Mbps/S,甚至更高。CAN传输速度和总线距离有关,总线距离越短,传输速度越快

CAN协议

通过CAN总线传输数据是需要按照一定协议进行的,CAN协议提供了5种帧格式来传输数据:数据帧、遥控帧、错误帧、过载帧和帧间隔。其中数据帧和遥控帧有标准格式和扩展格式两种标准格式有11位标识符(ID),扩展格式有29个标识符(ID)。这5种帧的用途见下图:

正点原子嵌入式linux驱动开发——Linux CAN驱动_第4张图片

数据帧

数据帧由7段组成:

  1. 帧起始,表示数据帧开始的段。
  2. 仲裁段,表示该帧优先级的段。
  3. 控制段,表示数据的字节数及保留位的段。
  4. 数据段,数据的内容,一帧可发送0-8个字节的数据。
  5. CRC段,检查帧的传输错误的段。
  6. ACK段,表示确认正常接收的段。
  7. 帧结束,表示数据帧结束的段。

数据帧结构如下图所示:

正点原子嵌入式linux驱动开发——Linux CAN驱动_第5张图片

上图给出了数据帧标准格式和扩展格式两种帧结构,图中D表示显性电平0、R表示隐性电平 1,D/R表示显性或隐性,也就是0或1,来简单分析一下数据帧的这7个段。

1、帧起始

帧起始很简单,标准格式和扩展格式都是由一个位的显性电平0来表示帧起始

2、仲裁段

仲裁段表示帧优先级,仲裁段结构如下图所示:

正点原子嵌入式linux驱动开发——Linux CAN驱动_第6张图片

标准格式和扩展格式的仲裁段不同,从上图可以看出,标准格式的ID为11位,发送顺序是从ID10到ID0,最高7位ID10-ID4不能全为隐性(1),也就是禁止0X1111111XXXXX这样的ID。扩展格式的ID为29位,基本ID从ID28到ID18,扩展ID由ID17到ID0,基本ID与标准格式一样,禁止最高7位都为隐性。

3、控制段

控制段由6个位构成,表示数据段的字节数,标准格式和扩展格式的控制段略有不同,如下图所示:

正点原子嵌入式linux驱动开发——Linux CAN驱动_第7张图片

上图中r1和r0为保留位,保留位必须以显性电平发送DLC为数据长度,高位在前,DLC段有效值范围为0-8。

4、数据段

数据段也就是帧的有效数据,标准格式和扩展格式相同,可以包含0-8个字节的数据,从最高位(MSB)开始发送,结构如下图所示:

正点原子嵌入式linux驱动开发——Linux CAN驱动_第8张图片

注意,上图中数据段的0-64为bit,对应到字节就是0-8字节。

5、CRC段

CRC段保存CRC校准值,用于检查帧传输错误,标准格式和扩展格式相同,CRC段结构如下图所示:

正点原子嵌入式linux驱动开发——Linux CAN驱动_第9张图片

从上图可以看出,CRC段由15位的CRC值与1位的CRC界定符组成。CRC值的计算范围包括:帧起始、仲裁段、控制段、数据段,接收方以同样的算法进行计算,然后用计算得到的CRC值与此CRC段进行比较,如果不一致的话就会报错

6、ACK段

ACK段用来确认接收是否正常,标准格式和扩展格式相同,ACK段结构如下图所示:

正点原子嵌入式linux驱动开发——Linux CAN驱动_第10张图片

从上图可以看出,ACK段由ACK槽(ACK Slot)和ACK界定符两部分组成发送单元的ACK,发送2个隐性位,而接收到正确消息的单元在ACK槽(ACK Slot)发送显性位,通知发送单元正常接收结束,这个过程叫发送ACK/返回ACK发送ACK的是所有接收单元中接收到正常消息的单元,所谓正常消息是指不含填充错误、格式错误、CRC错误的消息,这些接收单元既不处于总线关闭态也不处于休眠态的所有接收单元中。

7、帧结束

最后就是帧结束段,标准格式和扩展格式相同,帧结束段结构如下图所示:

正点原子嵌入式linux驱动开发——Linux CAN驱动_第11张图片

从上图可以看出,帧结束段很简单,由7位隐性位构成

遥控帧

接收单元向发送单元请求数据的时候就用遥控帧,遥控帧由6个段组成:

  1. 帧起始,表示数据帧开始的段。
  2. 仲裁段,表示该帧优先级的段。
  3. 控制段,表示数据的字节数及保留位的段。
  4. CRC段,检查帧的传输错误的段。
  5. ACK段,表示确认正常接收的段。
  6. 帧结束,表示数据帧结束的段。

遥控帧结构如下图所示:

正点原子嵌入式linux驱动开发——Linux CAN驱动_第12张图片

从上图可以看出,遥控帧结构基本和数据帧一样,最主要的区别就是遥控帧没有数据段遥控帧的RTR位为隐性的,数据帧的RTR位为显性,因此可以通过RTR位来区分遥控帧和没有数据的数据帧。遥控帧没有数据,因此DLC表示的是所请求的数据帧数据长度,遥控帧的其他段参考数据帧的描述即可。

错误帧

当接收或发送消息出错的时候使用错误帧来通知,错误帧由错误标志和错误界定符两部分
组成
,错误帧结构如下图所示:

正点原子嵌入式linux驱动开发——Linux CAN驱动_第13张图片

错误标志有主动错误标志和被动错误标志两种,主动错误标志是6个显性位,被动错误标志是6个隐性位,错误界定符由8个隐性位组成。

过载帧

接收单元尚未完成接收准备的话就会发送过载帧,过载帧由过载标志和过载界定符构成,过载帧结构如下图所示:

正点原子嵌入式linux驱动开发——Linux CAN驱动_第14张图片

过载标志由6个显性位组成,与主动错误标志相同,过载界定符由8个隐性位组成,与错误帧中的错误界定符构成相同。

帧间隔

帧间隔用于分隔数据帧和遥控帧,数据帧和遥控帧可以通过插入帧间隔来将本帧与前面的任何帧隔开,过载帧和错误帧前不能插入帧间隔,帧间隔结构如下图所示:

正点原子嵌入式linux驱动开发——Linux CAN驱动_第15张图片

上图中间隔由3个隐性位构成,总线空闲为隐性电平,长度没有限制,本状态下表示总线空闲,发送单元可以访问总线。延迟发送由8个隐性位构成,处于被动错误状态的单元发送一个消息后的帧间隔中才会有延迟发送。

CAN速率

CAN总线以帧的形式发送数据,但是最终到总线上的就是“0”和“1”这样的二进制数据,这里就涉及到了通信速率,也就是每秒钟发送多少位数据,前面说了CAN2.0最高速度为1Mbps/S。对于CAN总线,一个位分为4段:

  1. 同步段(SS)
  2. 传播时间段(PTS)
  3. 相位缓冲段1(PBS1)
  4. 相位缓冲段2(PBS2)

这些段由Tq(Time Quantum)组成,Tq是CAN总线的最小时间单位。帧由位构成,一个位由4个段构成,每个段又由若干个Tq组成,这个就是位时序。1位由多少个Tq构成、每个段又由多少个Tq构成等,可以任意设定位时序。通过设定位时序,多个单元可同时采样,也可任意设定采样点。各段的作用和Tq数如下图所示:

正点原子嵌入式linux驱动开发——Linux CAN驱动_第16张图片

1个位的构成如下图所示:

正点原子嵌入式linux驱动开发——Linux CAN驱动_第17张图片

上图中的采样点是指读取总线电平,并将读到的电平作为位值的点。位置在PBS1结束处。根据这个位时序,就可以计算CAN通信的波特率了。具体计算方法,等下再介绍,前面提到的CAN协议具有仲裁功能,下面来看看是如何实现的。

在总线空闲态,最先开始发送消息的单元获得发送权。

当多个单元同时开始发送时,各发送单元从仲裁段的第一位开始进行仲裁。连续输出显性电平最多的单元可继续发送。实现过程,如下图所示:

正点原子嵌入式linux驱动开发——Linux CAN驱动_第18张图片

上图中,单元1和单元2同时开始向总线发送数据,开始部分他们的数据格式是一样的,故无法区分优先级,直到T时刻,单元1输出隐性电平,而单元2输出显性电平,此时单元 仲裁失利,立刻转入接收状态工作,不再与单元2竞争,而单元2则顺利获得总线使用权,继续发送自己的数据。这就实现了仲裁,让连续发送显性电平多的单元获得总线使用权

关于CAN协议就讲到这里,关于CAN协议更详细的内容请参考《CAN 入门教程》。

CAN FD简介

随着汽车电子的发展,CAN的速度已经跟不上需求,因此CAN FD应运而生。BOSCH在2011年推出了CAN FD方案,CAN FD在CAN的基础上发展而来,主要扩展了CAN的带宽以及数据长度

CAN FD采用两种速率,控制段和总裁段等使用标准的CAN速率,但是数据段会切换到更高的通信速率,比如5Mbps。CAN FD也对数据长度做了扩展,最大支持64字节。CAN FD 数据帧格式如下图所示:

正点原子嵌入式linux驱动开发——Linux CAN驱动_第19张图片

CAN FD相较于CAN改进有以下几点:

增加一些控制位

CAN FD在帧报文里面主要增加了EDL、BRS和ESI这三位:

  • EDL:用于指示当前为CAN帧还是CAN FD帧,标准CAN中此位为显性,在CAN FD中此位为隐性。
  • BRS:速率转换开关,为隐性的时候转换为可变速率,此时速率可变,BRS到CRC之间使用可变速率。为显性的时候速率不变。
  • ESI:错误状态指示位,隐性的时候表示节点处于被动错误状态,显性的时候表示节点处于主动错误状态。

优化CRC算法

传统的CAN协议中,使用位填充的方式来保持同步,但是这种方法会对CRC造成干扰,导致错帧漏检。CAN FD为此对CRC算法进行了优化,将填充位加入到差错校验码中进行计算,也就是以填充位的位流进行计算。

关于CAN FD就简单介绍这些,它主要的目的就是为了应对车载电子对通信带宽的需求,提供更高的数据速率以及数据长度

STM32MP1 FDCAN简介

STM32MP1带有CAN控制器外设,叫做FDCAN,FDCAN子系统是有两个CAN模块、一个共享消息RAM存储器和一个时钟校准单元组成。这两个CAN模块(FDCAN1和FDCAN2)均符合CAN2.0版本A、B部分和CAN FD1.0 版本。此外,第一个CAN模块的FDCAN1还支持ISO 11898-4中规定的时间触发CAN(TTCAN)。STM32MP1自带的 FDCAN 模块特性如下:

  1. 符合CAN2.0版A、B部分和ISO 11898-1: 2015, -4
  2. CAN FD最多支持收发64个数据字节
  3. 硬件完全支持TTCAN协议级别1和级别2(仅限 FDCAN1)
  4. 支持事件同步时间触发通讯(仅限FDCAN1)
  5. CAN 错误记录
  6. 两个可配置接收FIFO
  7. 64个专用接收缓冲区和32个专用发送缓冲区
  8. 可配置发送事件FIFO
  9. ……

FDCAN支持5种模式:正常模式(Normal operation)、受限操作模式(Restricted-operation mode)、总线监控模式(Bus-monitoring mode)、外部环回模式(External loop-back mode)、内部环回模式(Internal loop-back mode)。除了正常模式外其它的模式都要配置CCCR寄存器的TEST位为1

  1. 正常模式:把CCCR寄存器的INIT位和TEST位清零后,FDCAN会正常接收或发送消息帧,所有的CAN协议功能都使能。
  2. 受限操作模式:当CCCR寄存器ASM位置1的时候使能此模式,在此模式下能够正常接收数据帧、接收远程帧和确认有效帧,不支持发送数据帧、发送远程帧和活动错误帧或发送过载帧。
  3. 总线监控模式:当CCCR寄存器MON位置1的时候使能此模式,在此模式下能够正常接收有效的数据帧和接收有效的远程帧,不支持确认有效帧。
  4. 外部环回模式:把TEST中的LBCK位置1,并且CCCR中的MON位置 0,那么FDCAN将进入外部环回模式,此模式用于硬件自检。
  5. 内部环回模式:把TEST中的LBCK位置1,并且CCCR中的MON位置 1,那么FDCAN将进入内部环回模式,此模式用于硬件自检。

FDCAN是支持时钟校准单元功能(CCU),此功能允许用户校准FDCAN,开启方式为CCCR中的CCE和INIT位都置1,并且FDCAN在CAN2.0模式下运行时,才能使用CCU。

正点原子的STM32MP1开发板FDCAN接口都支持CAN_FD,CAN_FD和CAN最大的区别于数据段是可以切换更高的通讯波特率,数据传输率可大于1Mbit/s,5Mbit/s(STM32MP1目
前最大支持 5Mbit/s)。

硬件原理图分析

正点原子STM32MP1开发板FD CAN接口原理图如下图所示:

正点原子嵌入式linux驱动开发——Linux CAN驱动_第20张图片

上图中CAN1_TX和CAN1_RX是STM3MP1 FDCAN的发送和接收引脚,对应STM32MP1 的PH13和PI9这个两个引脚。正点原子STM32MP1开发板上的CAN FD收发芯片有三种:MCP2562FDT、TJA1042T/3或者SIT1042T/3,其中SIT1042T/3为国产FD CAN芯片,也是正点原子开发板主推的!这三种芯片Pin to Pin兼容,使用起来没有区别,具体芯片以实际为准。这里就以SIT1042T/3为例,通过此收发器向外界提供CAN_H和CAN_L总线,R36是一个120欧的端接匹配电阻。CAN_FD_STBY是SIT1042T/3的高速与待机模式选择引脚,低电平为高速模式,连接在了STM32MP157的PF12引脚上,在本实验中没有用到CAN_FD_STBY。

实验程序编写

修改设备树

ST原厂提供的设备树已经配置好了FDCAN1的节点信息,但是还是要来看一下如何配置STM32MP1的CAN1节点。首先看一下STM32MP1 的FDCAN设备树绑定文档,打开Documentation/devicetree/bindings/net/can/m_can.txt,此文档描述了FDCAN节点下的相关属性信息。

FDCAN1 pinctrl配置

首先肯定是配置CAN1的引脚配置,打开stm32mp15-pinctrl.dtsi,找到如下所示内容:

示例代码 49.3.1.1 CAN1 的 pinctrl 配置
1  m_can1_pins_a: m-can1-0 {
2      pins1 {
3          pinmux = <STM32_PINMUX('H', 13, AF9)>; /* CAN1_TX */
4          slew-rate = <1>;
5          drive-push-pull;
6          bias-disable;
7      };
8      pins2 {
9          pinmux = <STM32_PINMUX('I', 9, AF9)>; /* CAN1_RX */
10         bias-disable;
11     };
12 };
13
14 m_can1_sleep_pins_a: m_can1-sleep-0 {
15     pins {
16         pinmux = <STM32_PINMUX('H', 13, ANALOG)>, /* CAN1_TX */
17         <STM32_PINMUX('I', 9, ANALOG)>; /* CAN1_RX */
18     };
19 };

可以看出,这里设置了CAN1的default和sleep两种模式下的引脚配置,其中CAN1_TX和CAN1_RX的引脚分别为PH13和PI9,复位为 AF9,也就是复用为CAN引脚。这个配置和正点原子的开发板上的引脚一致,所以无需修改,如果使用了其他引脚作为CAN1的收发引脚就需要自行修改。

FDCAN1控制器节点信息

打开stm32mp153.dtsi文件,找到名为“m_can”的节点,内容如下:

示例代码 49.3.1.2 m_can1 节点信息
1  m_can1: can@4400e000 {
2      compatible = "bosch,m_can";
3      reg = <0x4400e000 0x400>, <0x44011000 0x1400>;
4      reg-names = "m_can", "message_ram";
5      interrupts = <GIC_SPI 19 IRQ_TYPE_LEVEL_HIGH>,
6                   <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
7      interrupt-names = "int0", "int1";
8      clocks = <&scmi0_clk CK_SCMI0_HSE>, <&rcc FDCAN_K>;
9      clock-names = "hclk", "cclk";
10     bosch,mram-cfg = <0x0 0 0 32 0 0 2 2>;
11     status = "disabled";
12 };

根据第2行的compatible属性就可以找到STM32MP1的FD CAN驱动源文件,驱动文名为drivers/net/can/m_can/m_can_platform.c。第11行的status属性为disabled,所以FDCAN1默认关闭的。在stm32mp157d-atk.dts中添加使能FDCAN1的相关操作,添加如下所示代码:

示例代码 49.3.1.3 stm32mp157d-atk.dts 要追加的 m_can1 节点
1 &m_can1 {
2     pinctrl-names = "default", "sleep";
3     pinctrl-0 = <&m_can1_pins_a>;
4     pinctrl-1 = <&m_can1_sleep_pins_a>;
5     status = "okay";
6 };

第3-4行指定FDCAN1两个状态的pinctrl节点分别为:m_can1_pins_a和m_can1_sleep_pins_a,也就是示例代码49.3.1.1中的两个pinctrl节点。

第5行将m_can1节点的status属性为“okay”,也就是使能FDCAN1。

重新编译设备树。

修复m_can_platform.c

正点原子的教程出的时候,发现ST官方5.4.31版本内核下的CAN驱动不能进行收发数据,想进官网找答案,官网居然在维护。只能够使用ST官方提供4.19版本内核下的CAN驱动,只需要用此驱动替换掉5.4.31 内核中的drivers/net/can/m_can/m_can_platform.c即可。替换好以后重新编译内核,然后使用新的内核和设备树启动开发板。

这里需要自行实验尝试一下,暂时存疑。

使能Linux内核自带的FDCAN驱动

正常来说还需要配置Linux 内核,使能内核里面的FDCAN驱动,这些ST官方已经做好了,不需要重新配置内核。这里只是学习一下配置的方法。

使能CAN总线

首先打开CAN总线子系统,在Linux下CAN总线是作为网络子系统的,配置路径如下:

-> Networking support
-> <*> CAN bus subsystem support //打开 CAN总线子系统

如下图所示:

正点原子嵌入式linux驱动开发——Linux CAN驱动_第21张图片

使能STM32MP1的FDCAN外设驱动

接着使能STM32MP1的FDCAN外设驱动,配置路径如下:

-> Networking support
-> CAN bus subsystem support
-> CAN Device Drivers
-> Platform CAN drivers with Netlink support
-> Bosch M_CAN support
-> <*> Bosch M_CAN support for io-mapped devices(选中)

配置如下图所示:

正点原子嵌入式linux驱动开发——Linux CAN驱动_第22张图片

CAN测试

检查CAN网卡

使用新编译的设备树启动开发板,然后输入如下命令:

ifconfig -a //查看所有网卡

前面说了,Linux系统中把CAN总线接口设备作为网络设备进行统一管理,因此如果FDCAN驱动工作正常的话就会看到CAN对应的网卡接口,如下图所示:

正点原子嵌入式linux驱动开发——Linux CAN驱动_第23张图片

从上图可以看出,有一个名为“can0”的网卡,这个就是正点原子的STM32MP1开发板上的CAN1接口对应的can网卡设备。如果使能了STM32MP1上的FDCAN2的话也会出现一个名为“can1”的can网卡设备。

移植iproute2和can-utils工具

移植iproute2

iproute2是Linux上有关TCP/IP网络的一系列工具。需要此工具进行配置CAN网络设备。还是在buildroot的源码目录下,进入图形化界面,按照以下路径进入配置:

-> Target packages
-> Networking applications
-> [*] iproute2(选中)

结果如下图所示:

正点原子嵌入式linux驱动开发——Linux CAN驱动_第24张图片

移植can-utils工具

can0网卡已经出现了,但是工作正不正常还不知道,必须要进行数据收发测试。这里使用can-utils这个工具来对can0网卡进行测试

按照以下路径进入配置can-utils:

-> Target packages
-> Networking applications
-> [*] can-utils (选中)

结果如下图所示:

正点原子嵌入式linux驱动开发——Linux CAN驱动_第25张图片

保存和退出配置选项,然后运行以下命令重新编译buildroot:

sudo make

编译完成后,进入output/images 目录,运行以下命令把文件系统替换进去:

cd output/images/ //进入到 output/images 目录
sudo tar -axvf rootfs.tar -C /home/zuozhongkai/linux/nfs/rootfs //解压到 nfsroot 目录

上述命令将buildroot中output/images/rootfs.tar这个压缩包解压到/home/zuozhongkai/linux/nfs/rootfs这个目录中,这个目录就是正点原子教程中nfsroot 目录,根据自己的实际情况解压到对应的目录文件中。

重启开发板。

CAN通信测试

正点原子的STM32MP1开发板上只有一个CAN接口,因此还需要另外一个CAN设备,可以使用另一块STM32MP1开发板或者USB转CAN设备。之前学习时有说过,STM32MP1的CAN设备支持正常的CAN协议和CAN FD协议,这里两个都测试一下:

CAN测试

准备一块STM32MP1开发板和一个USB转CAN设备,然后将CAN接口连接起来,STM32MP1开发板上CAN接线端子如下图所示:

正点原子嵌入式linux驱动开发——Linux CAN驱动_第26张图片

将两个开发板的CAN接口连接起来(CAN 设备和开发板连接),注意,CAN_H接CAN_H,CAN_L接CAN_L!

MCP2560FDT 所允许的最小速率为14.4Kbps,TJA1042和SIT1042所允许的最低速率为40kbps!

1、收发测试

首先使用ip命令设置两个开发板的CAN接口,首先设置CAN接口的速度,输入如下所示命令:

ip link set can0 type can bitrate 1000000

上述命令设置can0速度为1000Kbit/S,两个CAN设备的速度要设置为一样的!速度设置好以后打开can0网卡,命令如下:

ifconfig can0 up //打开 can0

can0打开后就可以使用can-utils中的工具进行数据收发测试。开发板输入如下命令进行接收:

candump can0 //接收数据

通过USB转CAN设备发送8个字节的数据0X00、0X01、0X02、0X03、0X04、0X05、0X06、0X07。

此时开发板就会接收到这8个字节的数据,如下图所示:

开发板接收到的数据

接下来测试一下开发板向USB转CAN设备发送设备,在开发板上输入如下命令:

cansend can0 5A1#11.22.33.44.55.66.77.88

cansend命令用于发送can数据,“5A1”是帧ID,“#”号后面的“11.22.33.44.55.66.77.88”就是要发送的数据,十六进制。CAN2.0一次最多发送8个字节的数据,8个字节的数据之间用“.”隔开

如果USB转CAN设备工作正常的话接收端就会接收到上面发送过来的这8个字节的数据。

如果要关闭can0的话输入如下命令:

ifconfig can0 down

2、CAN FD协议测试

CAN FD的波特率最高为1000kBit/s,数据波特率为5000 kBit/s。这里使用USB转CAN FD设备,首先设置开发板的CAN0接口,配置CAN FD的速率,三选一,只需要选择下面其中一条指令来配置即可(这里只是测到这个三个波特率能使用,其它波特率各位自行测试)。

ip link set can0 up type can bitrate 1000000 dbitrate 5000000 fd on
ip link set can0 up type can bitrate 200000 dbitrate 1000000 fd on
ip link set can0 up type can bitrate 100000 dbitrate 500000 fd on

接着运行以下命令使能开发板的接收功能:

candump can0 // candump接收数据

上位机可以开始发送数据,开发板就能够接收数据,CAN FD最大能接受64位数据。

接下来测试从开发板发送数据到USB转CAN设备,输入如下命令:

cansend can0 123##300.11.22.33.44.55.66.77.88.99.aa.bb.cc.dd.ee.ff.00.11.22.33.44.55.66.77.8 8.99.aa.bb.cc.dd.ee.ff.00.11.22.33.44.55.66.77.88.99.aa.bb.cc.dd.ee.ff.00.11.22.33.44.55.66.77.88.99.a a.bb.cc.dd.ee.ff

关于CAN驱动就讲解到这里,如果要编写CAN总线应用的话就直接使用Linux提供的CAN接口,使用方法类似网络通信。

CAN 500k收发异常处理方法

在测试标准CAN的时候会发现500K波特率的时候开发板接收有问题,这个并不是板子硬件有问题,而是需要修改TF-A下的PLL4频率。打开TF-A中fdts/stm32mp157d-atk.dtsi文件,找到pll4的时钟配置,如下图所示:

正点原子嵌入式linux驱动开发——Linux CAN驱动_第27张图片

上图中是TF-A下的PLL4的时钟配置,具体配置方法参考TF-A 下的绑定文档:docs/devicetree/bindings/clock/st,stm32mp1-rcc.txt。ST默认配置FDCAN的时钟源为PLL4R,根据st,stm32mp1-rcc.txt文档描述,可以算出默认配置下PLL4R=74.25MHz。这里需要将PLL4R 的频率改为60MHz,也就是将FDCAN的时钟源设置为60MHz。将上图中第211行的cfg改为:

cfg = <1 34 3 4 6 PQR(1,1,1)>;

修改后如下图所示:

正点原子嵌入式linux驱动开发——Linux CAN驱动_第28张图片

经过上面修改以后PLL4P=105MHz,PLL4Q=84MHz,PLL4R=60MHz。重新编译 TF-A,然后使用新的TF-A启动开发板。

系统启动以后再次测试标准CAN 500KHz的时候数据收发,此时收发就正常了!

总结

经过本章,再一次温故学习了一遍CAN总线的基础知识,CAN在实际应用中还是非常多的,要对时序以及每一个帧的内容更加熟悉一点,还是得有时间多来看看。

具体的驱动在Linux内核里面已经有写好的了,我们需要做的就是在设备树和pinctrl之中添加好对应节点和引脚配置,这里pinctrl是已经有的,节点自行添加就可以了,只要关联上pinctrl就好。

你可能感兴趣的:(linux学习,linux,驱动开发,学习,笔记,stm32)