4. CANopen学习¶
4.1. 必看说明¶
本章的实验是基于 2个 野火开发板做的,目的仅为了验证野火开发板可以正常运行CANopen协议,如果你已经有多个开发板或者有了CANopen协议分析的工具,又或者你可以通过CANopen控制某个设备,那么你可以接着往下看,并跟着操作。
4.2. CANopen简介¶
CANopen 协议是在 20 世纪 90 年代末在 CAL(CAN Application Layer)的基础上发展而来的,由CiA301(或EN 50325-4)标准指定,是用于嵌入式控制系统的国际标准化的基于CAN的高层协议,在学习本章之前,你应该对CAN总线有清晰的了解。
CANopen本身不是典型的主/从协议。它更像生产者/消费者协议。也可以在没有主机的情况下运行CANopen网络。例如,预配置的过程数据对象(PDO)从生产者发送。每个PDO可能被多个节点消耗。每个CANopen设备的其他有用的CANopen功能还包括:心跳生产者和消费者,紧急生产者,同步生产者或消费者,时间生产者或消费者,SDO服务器(服务数据对象-服务于对象字典中的变量),NMT从设备(网络管理-启动)或停止部分通讯),LSS从站(节点ID和比特率的配置)。
CANopen网络通常具有一台具有主要功能的设备,用于网络配置。它可能具有其他CANopen功能,例如:NMT主站,LSS主站,SDO客户端,紧急用户。CANopenNode中的主功能是根据标准CiA309-3用Ascii命令行界面实现的。
CANopen 协议通常分为用户应用层、对象字典以及通信三个部分:
其中最为核心的是对象字典,描述了应用对象和 CANopen 报文之间的关系。
CANopen 通信是本文关键部分,其定义了 CANopen 协议通信规则以及与 CAN 控制器驱动之间对应关系,熟悉这部分对全面掌握 CANopen 协议至关重要。
用户应用层是用户根据实际的需求编写的应用对象。
4.3. CANopenNode¶
CANopen是一个标准的协议,有很多代码可以实现这个标准协议,其中就有CANopenNode,它CANopenNode是免费的开源CANopen协议栈,遵循Apache 2.0开源协议,这意味着你可以使用他进行商用,闭源等,当然更具体的协议约束得具体看改协议的内容,CANopenNode可以在任何Linux机器上运行,我们开发板就选择它来验证CANopen协议,代码仓库位于 https://github.com/CANopenNode/CANopenNode 。
除此之外CANopenNode以面向对象的方式用ANSI C语言编写,它可以作为独立应用程序或与RTOS在不同的微控制器上运行。
简单介绍一下CANopenNode实现的流程图,大家看一看就好了:
4.4. 简单测试CANopen¶
4.4.1. 更新¶
为了确保我们安装的软件包的版本是最新版本,让我们使用apt命令更新本地apt包索引和升级系统:
sudo apt-get update
sudo apt-get -y upgrade
4.4.2. 手动安装相关的依赖包¶
这些依赖包是这次测试CANopen必须的,要安装一下。
sudo apt-get -y install git
sudo apt-get -y install make
sudo apt-get -y install gcc
sudo apt-get -y install can-utils
4.4.3. 简单测试CAN总线功能¶
我们要验证一下我们板子上的CAN总线是可以正常工作的,
首先使用下面命令将485总线关闭,打开CAN总线,比如我选择的是打开CAN1与CAN2:
sudo fire-config
重启开发板。
将开发板中CAN总线的跳帽连接上(在开发板的左上角区域)。
使用以下命令查看是否存在CAN总线设备(打开CAN1实际上是CAN0设备,CAN2实际上是CAN1设备,此处我打开了两个)。
ifconfig -a
can0: flags=193<UP,RUNNING,NOARP> mtu 16
unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 10 (UNSPEC)
RX packets 23017 bytes 23407 (22.8 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 17392 bytes 17551 (17.1 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
device interrupt 25
can1: flags=128<NOARP> mtu 16
unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 10 (UNSPEC)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
device interrupt 26
运行以下命令,设置can0总线设备的波特率等信息,并且使能can0总线设备。
sudo ip link set can0 type can bitrate 1000000;sudo ip link set can0 up
在另一个开发板上也是这样子操作一遍。
选择其中一个开发板做数据的发送,一个做数据的接收,接收数据的开发板上运行以下命令:
candump can0
发送数据的开发板上运行以下命令:
cansend can0 123#abcdabcd
你会发现在接收的开发板上收到了以下内容,这样子就表示CAN总线是能正常工作了,我们才能接着下一步操作——使用CANopen协议。
➜ ~ candump can0
can0 123 [4] AB CD AB CD
注意,以上这些操作需要在两个开发板上操作的 。
4.4.4. 拉取CANopenNode仓库¶
接着到github拉取这个仓库,仓库非常小,下载下来是很快的,注意,这里也需要拉取到两个开发板上。
git clone https://github.com/CANopenNode/CANopenNode.git
当然也可以从野火的gitee仓库下载。
git clone https://gitee.com/Embedfire/CANopenNode.git
拉取下来后看到本地有CANopenNode文件夹,我们点进去,可以看到CANopen源码相关的文件与文件夹,我们简单介绍一下:
301文件夹下的内容:主要是CANopen应用层和通信配置文件。
CO_config.h :CANopenNode的配置宏。
CO_driver.h :CAN硬件和CANopenNode之间的接口。
CO_Emergency.h 、 CO_Emergency.c :CANopen紧急协议。
CO_HBconsumer.h 、 CO_HBconsumer.c :CANopen心跳消费者协议。
CO_NMT_Heartbeat.h 、 CO_NMT_Heartbeat.c :CANopen网络管理和心跳生成器协议。
CO_PDO.h 、 CO_PDO.c :CANopen过程数据对象协议。
CO_SDOclient.h、 CO_SDOclient.c :CANopen服务数据对象-客户端协议(主功能)。
CO_SDOserver.h 、 CO_SDOserver.c :CANopen服务数据对象-服务器协议。
CO_SYNC.h 、 CO_SYNC.c :CANopen同步协议(生产者和使用者)。
CO_TIME.h 、 CO_TIME.c:CANopen时间戳协议。
CO_fifo.h 、 CO_fifo.c:用于SDO和网关数据传输的Fifo缓冲区。
crc16-ccitt.h 、 crc16-ccitt.c:CRC 16 CCITT多项式的计算。
example目录下的文件:
CO_driver_target.h :CANopenNode的示例硬件定义。
CO_driver_blank.c :CANopenNode的示例空白接口。
CO_OD.h 、CO_OD.c :CANopen对象字典。自动生成的文件。
main_blank.c:主线和其他线程。
Makefile:编译规则描述文件。
IO.eds:标准CANopen EDS文件,可从CANopen配置工具中使用。自动生成的文件。
_project.xml :XML文件包含CANopen对象字典的所有数据。它是通过使用对象字典编辑器
应用程序,它产生的其他文件。
_project.html :对象字典编辑器启动。
socketCAN目录下的文件内容,主要是Linux socketCAN接口相关的文件。
CO_driver_target.h :针对Linuxopen的CANopenNode定义。
CO_driver.c :Linux socketCAN和CANopenNode之间的接口。
CO_error.h 、 CO_error.c :Linux socketCAN错误处理对象。
CO_error_msgs.h:错误定义字符串和记录功能。
CO_Linux_threads.h 、 CO_Linux_threads.c :用于在Linux中实现CANopen线程的辅助函数。
CO_OD_storage.h 、 CO_OD_storage.c :Linux SocketCAN的对象字典存储对象。
CO_main_basic.c socketCAN的主线程(基本用法)。
其他的一些目录就暂时不做介绍了,感兴趣的可以了解一下
4.4.5. 使用ssh连接开发板¶
为了更方便后续的其他操作,我们可以通过ssh登陆开发板,这样子就可以打开多个终端,具体的操作参考: https://tutorial.linux.doc.embedfire.com/zh_CN/latest/linux_basis/fire-config_brief.html?highlight=ssh#fire-configssh
4.4.6. 编译 & 运行¶
我们进入CANopenNode目录下,然后运行 make
命令即可编译。
make
# 编译输出的内容
···
cc -Wall -g -IsocketCAN -I. -Iexample -IsocketCAN -c example/CO_OD.c -o example/CO_OD.o
cc -Wall -g -IsocketCAN -I. -Iexample -IsocketCAN -c socketCAN/CO_main_basic.c -o socketCAN/CO_main_basic.o
cc -pthread socketCAN/CO_driver.o socketCAN/CO_error.o socketCAN/CO_Linux_threads.o socketCAN/CO_OD_storage.o 301/CO_SDOserver.o 301/CO_Emergency.o 301/CO_NMT_Heartbeat.o 301/CO_HBconsumer.o 301/CO_SYNC.o 301/CO_PDO.o 301/CO_TIME.o 301/CO_SDOclient.o 301/crc16-ccitt.o 301/CO_fifo.o 305/CO_LSSslave.o 305/CO_LSSmaster.o 309/CO_gateway_ascii.o extra/CO_trace.o CANopen.o example/CO_OD.o socketCAN/CO_main_basic.o -o canopend
可以看到在当前目录下生成了canopend可执行文件。
接着我们在接收端的开发板上运行以下命令:
candump can0
在发送端的开发板运行以下命令:
./canopend can0 -i 4 -s od4_storage -a od4_storage_auto
你会看到接收端的开发板收到了很多数据,其中包括心跳以及CANopen协议的其他数据内容。
➜ ~ candump can0
can0 704 [1] 00
can0 704 [1] 7F
can0 084 [8] 00 50 01 2F F3 FF FF FF
can0 704 [1] 7F
can0 704 [1] 7F
can0 704 [1] 7F
can0 704 [1] 7F
can0 704 [1] 7F
can0 704 [1] 7F
can0 704 [1] 7F
4.4.7. 验证其他的相关操作¶
在通过SSH连接开发板后,我们可以打开第二个终端,然后在CANopenNode目录下运行以下命令:
./canopend can0 -i 3 -c "stdio"
这句命令表示了使用 NodeID = 3 启动canopend的第二个实例。使用默认的od_storage文件并在标准IO(终端)上启用命令界面。
你可以在另一个开发板上(接收can数据的开发板上)看到两个CANopen设备,每个设备以一秒钟的间隔发送心跳。其中一个的节点 ID = 4,另一个的节点 ID =3。两个都是可操作的设备。
can0 703 [1] 7F
can0 704 [1] 7F
can0 703 [1] 7F
can0 704 [1] 7F
can0 703 [1] 7F
can0 704 [1] 7F
接着你可以输入 help
查看它的用法,或者输入以下内容:
3 read 0x1017 0 i16
在 ID = 3的 CANopen 设备上读取 Heartbeat 生产者参数。参数位于索引 0x1017,子索引0,它是16位整数,可以看到回应是1000,表示心跳包的时间间隔是1000ms。
回应的内容:
[0] 1000
接着你可以在 ID = 3 的 CANopen 设备上写入 Heartbeat 心跳包的时间间隔,让其以指定的时间间隔发送心跳包,比如5000ms:
3 write 0x1017 0 u16 5000
回应的内容是:
[0] OK
表示已经设置成功,我们可以在另一个开发板上(接收can数据的开发板上)看到两个CANopen设备,节点 ID = 4 的 CANopen 设备的心跳包是1000ms一次,另一个的节点 ID =3 的 CANopen 设备的心跳包是5000ms一次。
can0 703 [1] 7F
can0 704 [1] 7F
can0 704 [1] 7F
can0 704 [1] 7F
can0 704 [1] 7F
can0 704 [1] 7F
can0 703 [1] 7F
can0 704 [1] 7F
can0 704 [1] 7F
其他更丰富的CANopen协议的应用,可以参考源码进行编写,此处仅验证野火开发板是可以使用CANopen协议的。
当然,你也可以在另一个板子上跑这个CANopen协议~
本章完。