CAN 使用指南
1. 模块介绍
1.1. 术语定义
术语 | 定义 | 注释说明 |
---|---|---|
CAN | Controller Area Network | 控制器局域网络 |
socketCAN | socketCAN | 使用Berkeley socket API,Linux网络栈实现的CAN驱动框架 |
NAPI | New API | Linux新的网卡数据处理API,综合了中断方式与轮询方式 |
RTR | Remote Transmission Request | 远程请求帧 |
EFF | Extended Frame Format | 扩展帧 |
1.2. 模块简介
CAN控制器,多应用于汽车控制系统和一般工业环境中的区域网络控制。CAN是一种多主机、多广播的通信协议,CAN总线上的各个节点都可以向总线发送数据,多个节点同时发送时利用仲裁机制,从而确保最高优先级的数据可以正常发送到总线上,具有很高的实时性和可靠性。
该模块的基本特性如下:
- 支持CAN2.0A和CAN2.0B协议
- 支持11位标识符(标准格式)和29位标识符(扩展格式)
- 可编程通信速率最高达1Mbps
- 支持多种操作模式:正常模式、只听模式、自测模式、休眠模式、复位模式
- 错误检测与处理:错误计数、错误报警阈值可配置、错误捕获、仲裁丢失捕获
2. CAN配置指南
2.1. 内核配置
[*] Networking support--->
<*> CAN bus subsystem support--->
CAN Device Drivers--->
<*> Platform CAN drivers with Netlink support
[*] CAN bit-timing calculation
<*> Support for ARTC CAN
2.2. DTS配置
CAN模块基本配置
can0: can@19230000 {
compatible = "artc,artc-can";
reg = <0x0 0x19230000 0x0 0x400>;
interrupts-extended = <&plic0 88 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cmu CLK_CAN0>;
resets = <&rst RESET_CAN0>;
};
can1: can@19231000 {
compatible = "artc,artc-can";
reg = <0x0 0x19231000 0x0 0x400>;
interrupts-extended = <&plic0 89 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cmu CLK_CAN1>;
resets = <&rst RESET_CAN1>;
};
xxx/board.dts配置
&can0 {
pinctrl-names = "default";
pinctrl-0 = <&can0_pins>;
status = "okay";
};
&can1 {
pinctrl-names = "default";
pinctrl-0 = <&can1_pins>;
status = "okay";
};
3. 调试指南
待后续补充
4. 测试指南
4.1. 测试环境
4.1.1. 硬件
- 测试板:带有两个CAN接口的测试板
- PC:用于和测试板交互
- 串口线:连接测试板的调试串口
4.1.2. 软件
- PC端串口终端软件
- can-utils第三方软件包
- iprouter2第三方软件包
4.2. 第三方软件包编译
SDK中已默认编译can-utils/iprouter2软件包,可以直接使用。也可以通过以下两种方式编译测试:
4.2.1. 使用预编译包
SDK中提供了can-utils/iprouter2的预编译包,可以直接将预编译包的目标文件编译到镜像。这种方式不需要编译can-utils/iprouter2源码,节省编译时间。
Third-party packages--->
[*] can-utils--->
[*] use prebuilt binary instead of building from source
Third-party packages--->
[*] iproute2--->
[*] use prebuilt binary instead of building from source
4.2.2. 编译源码包
这种方式直接编译源码,而不使用SDK中的预编译包。
Third-party packages--->
[*] can-utils--->
[ ] use prebuilt binary instead of building from source
Third-party packages--->
[*] iproute2--->
[ ] use prebuilt binary instead of building from source
4.3. CAN收发测试
将测试板上的两个CAN接口对接。使用ip命令设置两个开发板的CAN接口,设置CAN接口的速度为500Kb/s。
ip link set can0 type can bitrate 500000 //设置CAN0
ip link set can1 type can bitrate 500000 //设置CAN1
打开CAN网卡
ifconfig can0 up //打开CAN0
ifconfig can1 up //打开CAN1
设置CAN1接收数据
candump can0 &
CAN0发送数据
cansend can0 5A1#11.22.33.44.55.66.77.88
上述cansend命令中,“5A1”是帧ID,“#”后面的“11.22.33.44.55.66.77.88”是要发送的数据,十六进制。CAN2.0一次最多发送8个字节的数据,8字节数据之间用“.”隔开,can-utils会对数据进行解析。
注解
当CAN总线上只有一个结点时,此时CAN结点发送数据,无法获取到ACK,此时结点检测到错误并将会一直重发数据,该结点会进入被动错误状态,但不会进入总线关闭状态,直到有其它结点接入总线。这是符合CAN总线协议的。
4.4. CAN组网测试
多个CAN结点可进行组网测试,组网测试时应遵循以下原则:
- 不同CAN结点发送不同的帧ID,当多个结点同时发送时,总线根据帧ID进行仲裁,优先级最高的获得总线权,可以向总线发送数据。若结点发送的帧ID相同,则同时发送数据时将无法仲裁。
- 组网测试时应确保总线两端匹配有120欧姆(典型值)的终端电阻。
注解
由于每个demo板都有匹配的终端电阻,多个CAN接入总线时,多个demo板的终端电阻并联接入网络,会严重影响CAN总线通信。应拆除掉多余的终端电阻,确保整个CAN网络的终端电阻为120欧姆。
5. 设计说明
5.1. 源码说明
CAN模块的源码位于:linux-5.10/drivers/net/can/artc_can.c
5.2. 模块架构
较早的linux内核版本的CAN驱动都是基于字符设备驱动实现,提供的功能相对较少,数据包的排队和更高级别的传输协议必须在用户空间的应用程序实现。现在的内核版本普遍采用socketCAN实现CAN模块的驱动。socketCAN是将CAN作为一种网络设备,基于linux内核的网络层实现的软件框架。
5.2.1. CAN分层结构
CAN结点的实现可以分为四层结构,各层的主要功能和作用如下图所示:
CAN总线的对象层和传输层包括所有由ISO/OSI模型定义的数据链路层的服务和功能,相当于是对数据链路层功能的细分。因此,也可以将对象层和传输层看作是CAN的数据链路层。
5.2.2. socketCAN驱动框架
依据CAN的分层模型,socketCAN实现了具体的软件驱动框架,如下图所示(iprouter2/can-utils只是应用层的一个示例,并不属于socketCAN的内容)。
OSI七层网络模型 | CAN模型 | socketCAN框架实现 |
---|---|---|
应用层 | 应用层 | iprouter2/can-utils |
表示层 | ||
会话层 | ||
传输层 | ||
网络层 | ||
数据链路层 | 对象层 | CAN core/CAN driver |
传输层 | CAN控制器 | |
物理层 | 物理层 | CAN收发器 |
CAN core主要是实现了CAN的socket配置,并向CAN driver提供一些调用的接口,该部分内容由socketCAN框架负责。传输层和物理层所对应的CAN控制器和收发器皆由硬件实现,所以驱动开发的主要工作是实现CAN driver部分的代码。
CAN的底层驱动实现主要包括以下几部分:
- 设置CAN的位时序
- 获取CAN的发送/接收计数
- CAN设备的打开/关闭
- CAN设备发送/接收帧信息的操作
- CAN设备错误处理的操作
5.3. 关键流程设计
5.3.1. 初始化流程
CAN模块的初始化流程如下:
- 释放reset和clock信号
- 调用alloc_candev,给struct net_device类型的变量分配空间
- 初始化结构体struct can_priv的各个成员变量
- 调用register_candev注册CAN设备