学习CANopen --- [5] SDO
文章目录
- 前言
- 一 工作原理
- 二 使用范围
- 三 例子
- 1. COB-ID分析
- 2. 报文内容分析 --- 读操作
- 命令提示符
- 索引和子索引
- 报文中的数据值
- 3. 报文内容分析 --- 写操作
- 命令提示符
- 索引和子索引
- 报文中的数据值
- 3. Segment传输
- 读的发起
- 读的数据传输
- 写的发起和数据传输
- 4. Block传输
- 5. Abort
- 四 总结
前言
SDO是Service Data Object的缩写,中文叫服务数据对象,关键是这个服务二字,指的是读写服务,Client可以通过SDO读写Server里的对象字典(Object Dictionary,简称OD)
OD存在于Server中,用户在和Server通信之前,也会有一份相同的OD表,不然没法继续操作。如果用户想读写OD里的值,那么就可以使用SDO。
PS:要记住:用户是Client,CANOpen设备是Server;如果2个CANOpen设备间互相通信,那么发起通信的那个就是Client,这里统一使用Client指代
一 工作原理
SDO的收发有点像TCP,读写请求发送出去后必须要有一个来自Server的应答,如下图
如果是读,那么应答里会包含读取的数据,如果是写,那么应答里会包含写成功的标志。如果读写发生错误,Server这边会返回一个紧急报文,里面包含错误码。
Client一般都会设置个SDO超时时间,如果在规定时间内没有收到应答,Client这边就会报错。
PS:CANOpen文档里读叫upload,写叫download,感觉是从Server角度来看的
二 使用范围
只有当Server处于Pre-Operational和Operational状态下,Client才可以使用SDO去和Server通信,如下图,
一般来说CANOen设备启动后会自动进入Pre-Operational状态,所以可以直接使用SDO,如果设备特殊,那么就要看下该设备的详细资料。
关于状态切换,请查看讲NMT的那篇文章。
三 例子
下面讲例子,实战可以让理解更加深入,首先使用pythonCANOpen来创建2个文件:server.py和client.py,如下,
'''
server.py
'''
import signal
import canopen
import timerunning = Truedef sigint_handler(signum, frame):global runningprint('')running = Falseexit(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:time.sleep(0.6)
client.py如下,
'''
client.py
'''
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')# 读取Index为0x2341,Subindex是2的OD项
data = node.sdo[0x2341][2].raw
print('0x2341_02: 0x{:X}'.format(data))time.sleep(2)# 修改Index为0x2341,Subindex是2的OD项
node.sdo[0x2341][2].raw = 0x123time.sleep(2)# 再读一次验证一下
data = node.sdo[0x2341][2].raw
print('0x2341_02: 0x{:X}'.format(data))
PS:eds文件来自这个
然后创建vcan0,
$ sudo modprobe vcan
$ sudo ip link add dev vcan0 type vcan
$ sudo ip link set up vcan0
创建好之后开一个终端,然后使用candump进行观察,
candump vcan0
最后是运行,先运行server.py,
python3 server.py
接着运行client.py,
python3 client.py
client运行结束后,观察得到的CAN报文如下,有三组,注意数字都是16进制,
1. COB-ID分析
先看606和586,这2个是SDO报文的COB-ID,
- 对于发送的SDO报文来说,其COB-ID是0x600+设备id,例子中Server的id是6,所以是0x606,由Client发出,表示发给id是6的设备
- 对于返回的应答报文来说,其COB-ID是0x580+设备id,例子中Server的id是6,所以是0x586,由Server发出,表示该报文是由id为6的设备返回的
PS:0x600号0x580是专属于SDO的,且是固定的
2. 报文内容分析 — 读操作
报文内容的基本结构如下,发送和接收的报文结构一样,
总共8个字节,
- 字节0是命令说明符,用来描述报文意义,简写为CS,占1个字节
- 字节1-2是Server对象字典项的Index,占2个字节
- 字节3是Server对象字典项的Subindex,占1个字节
- 字节4-7是报文内容,占4个字节
命令提示符
例子中第一个SDO报文是读取字典项0x2341_02,读操作对应的CS字节如下,注意这是一个字节,
Client发送报文的CS值是0x40,只要是读,其CS都是0x40
Server返回报文的CS值则需要考虑被读字典项的数据类型,n, e, s的含义如下,
PS:n是number的首字母,e是expedited的首字母,s是size的首字母
例子中返回的CS是0x4B,转为二进制后得出:n值为10b,十进制数字为2,e=1b,s=1b,根据上述含义可以得出:
- 字节8-n到字节7,即字节6-7不包含数据,那么有效数据在字节4-5,总共2个字节
- e为1b表示这是expedited传输,意思是数据可以一次传输完成,可以想象只要数据字节数不大于4,那么都可以一次传输完成
- s为1b,但是在expedited传输里s是没有意义的,只有在e=0时s才有意义
然后打开eds文件,找到0x2341_02对应的描述,如下,
其DataType是0x0006,根据官方文档,其对应的数据长度就是2个字节,和前面分析相符,
索引和子索引
字节1的值为0x41,字节2的值是0x23,从右往左读就是0x2341,即字典项的索引
字节3的值是0x02,也就是字典项的子索引
发送报文和返回报文在字节1~3写入的都是相同的索引和子索引
报文中的数据值
发送报文的最后四个字节都是0,因为是读,这4个字节都是无意义的,虽然无意义,但是还是要传输,所以就写0
返回报文里字节5的值为0x7F,字节4的值为0xFF,合起来就是0x7FFF,与EDS文件里看到的这个字典项对应的默认值相同
3. 报文内容分析 — 写操作
写操作的内容结构和读一样,
命令提示符
写操作的CS值如下,
可以看到发送报文的CS值和字典项的数据类型有关,n,e,s的含义和读操作相同,所以这里是2B
返回报文的CS值则是固定的0x60,表示写入OK
索引和子索引
与读操作相同,就是把0x2341_02写入到字节1~3里
报文中的数据值
由于是写,所以在发送报文的字节4~5里填入期望的目标值,这里是0x123,字节4是0x23,字节5是0x01,字节6和7则为0
在返回报文里字节4~7都是无意义的,虽然无意义,但是还是要传输,所以都写0
3. Segment传输
前面的读写操作都是expedited传输,即一个来回就能传输完成,要求数据长度<=4字节,但有的字典项长度是大于4字节的,这样一次就无法传输完成,此时就需要Segment传输,也叫段传输
在eds文件里,字典项0x1009的默认值超过了4,其数据类型是0x0009,即Visible String,
这里改下,把其DefaultValue改长一点,如下,总共32个字节,不包含尾巴的’\0’,
然后重启server.py,client.py内容改为如下,
'''
client.py
'''
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')# 读取Index为0x1009的OD项
data = node.sdo[0x1009].raw
print('0x1009: {}'.format(data))
运行client.py后观察到的报文如下,2道橘黄色的横线之间是一次收发,
可以看到总共出现了6次收发,需要逐个分析
读的发起
第一次收发是读的发起,其CS含义如下,
和之前分析expedited读是一样,只是返回报文里的n,e,s的值变了:n为00b,e为0b,s为1b,根据前面n,e,s的释义,可以得出以下结论:
- n的值在此次报文里无效,因为e不为1
- e为0表示这不是expedited传输,需要多次传输才能把数据传完
- s为1表示返回报文里字节4~7的值是字典项的size,这里是0x20,即32,和前面修改后的长度一致;也可以理解为需要传输的字节数
读的数据传输
剩余的5次收发就是读的数据传输,其CS值定义如下,
t,n,c含义如下,
Client发送报文的CS值比较简单,只有个toggle bit在变化,所以其CS值在0x60和0x70间互相变化,第一次是0x60
Server返回的报文里,除了t,还有n和c,字面意义也比较好理解,要注意这里的n是3个bit,expedited传输里n是2个bit。
只要不是最后一次收发,那么n和c都为0,CS值在0x00和0x10间互相变化,第一次是0x00;每次能传输7个字节,4次收发传了28个字节;
第6次收发是最后一次,此时还剩32-28=4个字节,那么n就为3,即011b,c为1,即1b,组合在一起是0111b,另外此次的t是0b,那么最后一次返回的报文的CS就是0x07
写的发起和数据传输
写也是类似,分为写的发起和写的数据传输,分别对应下面2张图
由于这个eds文件里超过4字节的字典项都是const的,不可以修改,那么就需要改个其它项来做测试,这里修改0x2FF4_4,其原始内容如下,
改之后如下,只是改了DataType,
然后重启server.py,client.py内容改成如下,
'''
client.py
'''
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')# 修改0x2FF4_04的OD项
node.sdo[0x2FF4][4].raw = "AABBCCDDEEFFGGHH"
运行client.py后观察得到的can报文如下,
总共发生了4次收发,第一次是写的发起,通过CS值来告诉Server总共有16个字节要写;后面三次是数据传输,内容分析和读类似,只要注意CS的高3位不一样就行了。
4. Block传输
请查看这篇文章
5. Abort
Server和Client都可以发送abort报文来中断报文传输,其CS值定义如下,
即0x80
四 总结
本文讲述了SDO报文的含义和实践,通过例子+理论,可以更加深刻的理解SDO及其用途。
学习CANopen --- [5] SDO相关推荐
- CANOpen中SDO和PDO的COB-ID理解
CANOpen用来收发数据的通信对象有SDO(Service Data Object)和PDO(Process Data Object) 一 SDO和PDO的区别 SDO使用对象字典的Index和Su ...
- 学习CANopen --- [2] PythonCANopen简单用法
本文主要讲述如何在Linux下使用python进行简单的CANopen通信,使用了一个叫CANopen for Python的库,地址是https://github.com/christiansand ...
- 基于STM32F4的CANopen快速SDO通信(超级详细)
基于STM32的CANopen快速SDO通信 前言 1 快速sdo介绍 2 工程配置 2.1 主站词典配置 2.2节点词典配置 3 快速SDO调用代码 4 末尾 本专题相关教程: 基于STM32F4的 ...
- 学习CANopen --- [3] NMT报文
本文主要讲述CANopen中的NMT报文,即网络管理(Network ManagemenT),该服务可以用于启动网络和监控设备. NMT报文由NMT主机发送,对从机进行启动.监控和重启,在CANope ...
- CANopen中SDO、PDO、以及COB-ID理解
CAN 总线是一种串行通信协议,具有较高的通信速率的和较强的抗干扰能力,可以作为现场总线应用于电磁噪声较大的场合. 由于CAN 总线本身只定义ISO/OSI 模型中的第一层(物理层)和第二层(数据链路 ...
- CANopen之SDO,PDO
一.简述 CAN 总线是一种串行通信协议,具有较高的通信速率的和较强的抗干扰能力,可以作为现场总线应用于电磁噪声较大的场合.由于 CAN 总线本身只定义ISO/OSI 模型中的第一层(物理层)和第二层 ...
- 理论学习-协议栈学习-CANopen协议梳理
CANopen协议梳理 开放式系统互联模型 开放式系统互联模型 #mermaid-svg-vgZNn3jf2tTpscn0 .label{font-family:'trebuchet ms', ver ...
- 学习CANopen --- [10] 汽车外接OBD模块原理
在某宝上搜索汽车OBD,可以发现很多卖OBD模块的,通过接入OBD模块可以增加车子本身没有的功能,如锁车升窗,行车自动落锁和后视镜折叠等,那么其实现原理是什么呢?使用时会造成亏电吗? 一 原理 OBD ...
- 机器人开发--CanOpen
机器人开发--CanOpen 1 介绍 1.1 概述 1.2 应用 2 历史发展 3 协议 3.1 协议细节(来自CiA文档) 3.2 协议框架 OSI 模型 服务 COB-ID NMT SYNC 紧 ...
最新文章
- 你心动了吗?2014年iOS应用开发者收入超好莱坞美国票房
- 机器学习:SVM算法的对偶形式
- electron 入坑记
- 数字图像处理:视觉系统中的坐标系介绍
- ora28500 mysql_Oracle使用 ODBC+DBLINK 访问 Mysql
- Java后端知识---数据结构(1)
- linux 对于Vim配置的方法
- 深入PHP使用技巧之变量
- Node:项目文件使用async报错var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _calle
- 【TeachNLP】文本数据处理-词表获取
- 基于大数据的数据挖掘算法-大数据
- Drools规则引擎快速入门(一)
- shping cloud搭建大觅网-之sping cloud初体验
- 睡眠质量不好怎么改善,几个助眠好物帮助你睡安稳觉
- OSI的7层模型和TCP/IP四层模型
- 轻松创建FB和Ins故事广告,提升内容曝光率
- STM32全链路开发实战教程专栏总目录(2022.10.19更新)
- html语言margin,margin在html中的意思
- 【Python妙用】用200行Python代码制作一个迷宫小游戏
- Android开发 PDA网络调试
热门文章
- java 扑克游戏_Java扑克游戏——红心大战
- pycharm ValueError: source code string cannot contain null bytes
- tera服务器不显示,tera无法登陆怎么解决_tera无法登陆的快速解决方法介绍
- react ts环境搭建及ts格式写法
- 【科技杂谈】记录学习装重装系统的过程(非常规重装系统教学)
- Echarts实现世界地图
- 新农慕课python第四周答案_Python编程_章节测验,期末考试,慕课答案查询公众号
- 以下python注释代码、叙述中不正确的是_关于 Python 语言的注释,以下选项中描述错误的是( )_学小易找答案...
- 幼年产品狗如何养成?这是完全自我修炼教程!
- 开源项目-汽车管理系统