本文主要讲述CANopen中的NMT报文,即网络管理(Network ManagemenT),该服务可以用于启动网络和监控设备。
NMT报文由NMT主机发送,对从机进行启动、监控和重启,在CANopen网络中只允许有一个活动的NMT主机。如果网络中有多个设备可以配置成主机,那么同一时刻只有一个可以配置成主机。
一 NMT报文及功能
NMT报文属于Master/Slave模式
1. 模块控制报文
该报文用于对Slave从机进行各种控制,报文格式如下,
必须由NMT Master发给NMT Slave(s),报文的COB-ID是0x000
报文数据段传输的第0个字节是CS (Command Specifier),用于表示控制命令,具体有以下5个命令,
PS:NMT Service这一列里有个Remote Node,即远程节点,其实就是Slave节点,这是相对于NMT Master来说的。如果NMT Master也是个CAN设备,那么NMT Master自身可以叫做Local Node
报文数据段传输的第1个字节是Slave从机的Node Id,如果为0,表示网内所有Slave设备都会收到这个报文。
2. 设备监控报文
该功能主要用于监控Slave设备的当前状态,分为Node Guarding和Heartbeat两种方式。
2.1 Node Guarding
NMT Master主动向Slave发送一个CAN远程帧(没有数据段),如下图,
然后Slave会返回一个指定格式的消息给Master,如下图,
Byte0包含一个toggle位(bit7),每一次Node Guard请求都会在0和1之间进行翻转(第一次值为0),翻转的目的是为了区分设备当前状态值和历史状态值;bit 6-0用来表示Slave的当前状态,有以下几种状态,
PS:状态0永远不会出现在Node Guarding的reply里,因为在Initialising状态下,不会对Node Guarding请求做出响应。
2.2 Heartbeat
也叫做心跳报文,由NMT Slave主动发送,NMT Master接收,无需response,这种模式也叫做生产者/消费者模式,NMT Slave是生产者,NMT Master是消费者
有以下4种状态,
当一个使用Heartbeat的CAN设备启动,其第一条心跳报文就是Boot-up,
2.3 小结
Node Guarding和Heartbeat都可以实现设备监控的目的,同一时刻只能使用其中一种,推荐使用Heartbeat,更加简单,另外Node Guarding需要用到远程帧,不是所有CAN设备都支持远程帧。
3. 启动报文
当NMT Slave设备启动时,都会发送这条报文,用来告知Master自己已经从 Initialising状态切换到Pre-operational状态。
启动报文和Heartbeat中的Boot-up有点重合,当一个使用Heartbeat的CAN设备启动,其第一条心跳报文就是Boot-up,但如果使用的是Node Guarding,那么CAN设备启动后发送的第一条报文就是启动报文,后续就是Node Guarding请求了。
二 代码实战
学习了理论后,再通过实践来加深理解。
首先,通过下面的Slave代码,创建1个Slave节点,id是6,初始化完成后自动进入Pre-operational状态,
import signal
import canopen
running = True
def sigint_handler(signum, frame):
global running
print('')
running = False
exit(0)
# 处理按键发送的信号,优雅的关闭程序
signal.signal(signal.SIGINT, sigint_handler)
signal.signal(signal.SIGHUP, sigint_handler)
signal.signal(signal.SIGTERM, sigint_handler)
# 创建一个网络用来表示CAN总线
network = canopen.Network()
# 连接到CAN总线
network.connect(bustype='socketcan', channel='vcan0')
# 创建slave节点,其id是6,对象字典为CANopenSocket.eds
node = network.create_node(6, 'CANopenSocket.eds')
# node向CAN总线上发送启动消息
node.nmt.send_command(0)
# node进入PRE-OPERATIONAL状态
node.nmt.state = 'PRE-OPERATIONAL'
# node发送心跳报文,每隔1s发送一次
node.nmt.start_heartbeat(1000) # 1000ms
# 循环
while running:
pass
运行后在candump窗口下观察如下,
0x7F是127,表示can节点进入了Pre-operational状态。
下面是master代码,会把所有NMT的命令都执行一遍,具体可以看注释,
import time
import canopen
# 创建一个网络用来表示CAN总线
network = canopen.Network()
# 添加slave节点,其id是6,对象字典为CANopenSocket.eds
node = canopen.RemoteNode(6, 'CANopenSocket.eds')
network.add_node(node)
# 连接到CAN总线
network.connect(bustype='socketcan', channel='vcan0')
sleep_sec = 2 # 2秒
# 发送'Start Remode Node'命令,把slave设置为OPERATIONAL状态
node.nmt.state = 'OPERATIONAL'
time.sleep(sleep_sec)
# 发送'Reset Node'命令,把slave设置为Initialising状态
node.nmt.state = 'RESET'
time.sleep(sleep_sec)
# 发送'Enter Pre-operational State'命令,把slave设置为PRE-OPERATIONAL状态
node.nmt.state = 'PRE-OPERATIONAL'
time.sleep(sleep_sec)
# 发送'Start Remode Node'命令,把slave设置为OPERATIONAL状态
node.nmt.state = 'OPERATIONAL'
time.sleep(sleep_sec)
# 发送'Stop Remode Node'命令,把slave设置为STOPPED状态
node.nmt.state = 'STOPPED'
time.sleep(sleep_sec)
# 发送'Reset Communication'命令,把slave设置为Initialising状态
node.nmt.send_command(0x82)
time.sleep(sleep_sec)
# 发送'Enter Pre-operational State'命令,把slave设置为PRE-OPERATIONAL状态
node.nmt.state = 'PRE-OPERATIONAL'
time.sleep(sleep_sec)
# 发送'Start Remode Node'命令,把slave设置为OPERATIONAL状态
node.nmt.state = 'OPERATIONAL'
运行master.py后,在candump窗口可以看到如下状态变化,
已上状态切换完全是按照CANopen DS301中规定的状态机进行的,如下图,
三 总结
本文主要讲述CANopen协议中的NMT报文,并以代码实战展示了如何使用NMT报文以及对应的状态切换。
如果有写的不对的地方,希望能留言指正,谢谢阅读。