最近在做研华工控机控制输出IO,因为工控机的IO板卡比较贵,而且后面必须接继电器,这样会造成我们机器的电控柜比较大,电路走线非常困难,在这个原因的困扰下,我提出自己做一块IO驱动板的需求,最后在我们硬件工程师的努力下,总算完成IO驱动板的设计任务,并且他们写了板子的程序,同时他们也测试过稳定性及功能。
因为要在工地测试我们机器,一直没有时间写CAN控制IO驱动的程序,最近在以前开发CANopen的基础上,编写CAN2.OB的通信程序。首先,我们有两个需求:(1)要能通过CAN分析仪进行控制IO输入输出。(2)能够通过研华CAN口控制IO输入输出。我们的工控机因为一开始设计程序的人用Window系统,人机界面是通过C#的Winform写的,同时跟硬件相关的制作成dll库进行调用,所以我们只能在已有的基础进行开发,通过读取研华工控机的说明书,他们的硬件支持can2.0b:
在了解到硬件支持扩展2.0b,接下来就是看看他们的数据接收和发送接口是否有,或者怎样知道发送是扩展帧还是标准帧,又查找他们的说明书,最后也发现,他们的里面有写:
确定了支持发送和接收API后,就是要跟PCB板进行通信,我们要看IO驱动板的协议是自己定的,所以我们贴出我们的协议(同事陈麟制定):
1.1 报文支持
本文介绍的协议,是基于CAN2.0B(29位ID的拓展格式帧)实现的高层协议。虽然CAN2.0B协议支持数据、远程、错误和溢出帧,但此高层协议只使用数据帧。
1.2 CAN2.0B报文格式
表1-1 CAN2.0B报文格式
CAN2.0B报文使用29位(bit)帧ID(11Bit SID + 18Bit EID),下文直接合并为29Bit ID域。DLC域指示数据段有多少个字节。本高层协议只使用了CAN2.0B的数据帧,所以该报文的其他域本文不再详细明。
1.3 高层协议报文格式
图1-1 高层协议说明
本协议的帧格式由6大部分组成:
- 优先权:控制该报文在CAN总线上的传输优先级,值越小,优先级越高;
- 功能码:指示该报文的功能;
- 目的地址:接收该报文的模块地址;
- 源地址:发送该报文的地址;
- 可选数据载荷:个别报文(功能码)会携带一些控制数据。
2.1 特别约定
从can通信特点来说,在can总线上的设备都有同等的通信地位。但在本协议约定的拓扑架构中,从通信地位上划分都有上位机和下位机之分,本协议只适用于具有上位机和下位机的拓扑架构模型,其模型图如下:
图2-1 设备拓扑
2.2 设备地址约定
本协议使用8位地址,最大支持256个设备。为了统一管理,对地址作以下约定:
表2-1 地址使用范围
地址区间 | 使用方式描述 |
255 | 广播地址 |
254 | 主机地址 |
0~253 | 从机地址 |
2.3 重启从机
表2-2 复位命令请求(主机发、从机收)
域 | 值 |
功能码 | 0x01 |
源地址 | 254 |
目的地址 | 0xFF(全部设备),其他(指定的设备地址),254无效 |
数据域 | 无 |
2.4 读取输入IO电平
表2-2 读取输入IO电平命令请求(主机发、从机收)
域 | 值 |
功能码 | 0x02 |
源地址 | 254 |
目的地址 | 0xFF(全部设备),其他(指定的设备地址),254无效 |
数据域 | 无 |
表2-3 读取输入IO电平明令回应(从机发、主机收)
域 | 值 |
功能码 | 0x03 |
源地址 | 从机地址 |
目的地址 | 254 |
数据域 | 2个字节,Bit0 ~ Bit15分别对应一个通道的电平,如Bit0的值为1代表通道0为高电平,值为0代表通道0为低电平。 |
2.5 设置输出IO电平
表2-4 设置输出IO电平命令(主机发、从机收)
域 | 值 |
功能码 | 0x04 |
源地址 | 254 |
目的地址 | 0xFF(全部设备),其他(指定的设备地址),254无效 |
数据域 | Byte0~Byte1为控制值,Byte2~Byte3为通道掩码,详细见下文 |
数据域又细分为两个域:
- 控制值,16位,每一位分别指示每一个输出IO的电平,值为1代表高电平,值为0代表低电平。
- 通道掩码,16位。每一位分别对应一个IO通道,该位值位1代表控制值对应位的值有效,值为0代表控制器对应位的值无效。
举例,要设置通道0和通道3为高电平:
控制值为:0x0009(00000000 00001001),除了位0和位3,其他位的值随意。
通道掩码为:0x0009(00000000 00001001)。
2.6 读取输出IO电平
表2-5 读取输出IO电平命令请求(主机发、从机收)
域 | 值 |
功能码 | 0x05 |
源地址 | 254 |
目的地址 | 0xFF(全部设备),其他(指定的设备地址),254无效 |
数据域 | 无 |
表2-6 读取输出IO电平命令回应(从机发、主机收)
域 | 值 |
功能码 | 0x06 |
源地址 | 从机地址 |
目的地址 | 254 |
数据域 | 2个字节,Bit0 ~ Bit15分别对应一个通道的电平,如Bit0的值为1代表通道0为高电平,值为0代表通道0为低电平。 |
2.7 设置电机状态
表2-7 设置电机正/反转命令(主机发、从机收)
域 | 值 |
功能码 | 0x07 |
源地址 | 254 |
目的地址 | 0xFF(全部设备),其他(指定的设备地址),254无效 |
数据域 | Byte0~Byte1为控制值,Byte2为通道掩码,详细见下文 |
1)控制值
表2-8 控制值说明(功能码0x07)
位 | 值 |
[0 : 1] | 通道0控制值:0,待机,输出都为高阻;1,反转;2,正转;3,停止,输出都为低电平。 |
[2 : 3] | 通道1控制值,同上 |
[4 : 5] | 通道2控制值,同上 |
[6 : 7] | 通道3控制值,同上 |
[8 : 9] | 通道4控制值,同上 |
[10 : 11] | 通道5控制值,同上 |
[12 : 13] | 通道6控制值,同上 |
[14 : 15] | 通道7控制值,同上 |
- 通道掩码
共8位(1Byte),每一位的值代表控制值里对应的通道控制值是否有效:1有效,0无效。
举例,控制电机0反转,电机3正转:
- 控制值:0x0042 (01 00 00 10), 除了[0:1]和[6:7],其他位的值随意。
- 通道掩码:0x09 (0000 1001), 位0和位3为1,其余为0。
2.8 读取电机状态
表2-9 读取电机状态命令请求(主机发、从机收)
域 | 值 |
功能码 | 0x08 |
源地址 | 254 |
目的地址 | 0xFF(全部设备),其他(指定的设备地址),254无效 |
数据域 | 无 |
表2-10 读取电机状态命令回应(从机发、主机收)
域 | 值 |
功能码 | 0x09 |
源地址 | 从机地址 |
目的地址 | 254 |
数据域 | 2个字节,解析请参考表2-8 |
2.9 自动上报输入IO状态
如果输入IO的电平有变动,从机会主动将以下报文发送到主机,主动上报IO的电平。
表2-11 输入IO电平变动通知报文(从机发、主机收)
域 | 值 |
功能码 | 0x0A |
源地址 | 从机地址 |
目的地址 | 254 |
数据域 | Byte0~Byte1为通道值,Byte2~Byte3为通道掩码,详细见下文 |
- 通道值:16位,每一位分别指示对应通道的电平值:1为高电平,0为低电平。
- 通道掩码:16位,每一位的值指示对应通道的电平是否发生变动:1,为发生变动, 0无变动。
最终调试结果就是我们通过上位机,可以控制我们工控机进行输入输出控制和读取数据。