# 通讯方式
PLC(Programmable Logic Controller),可编程逻辑控制器是种专门为在工业环境下应用而设计的数字运算操作电子系统。它采用一种可编程的存储器,在其内部存储执行逻辑运算、顺序控制、定时、计数和算术运算等操作的指令,通过数字式或模拟式的输入输出来控制各种类型的机械设备或生产过程。
PLC通信方式有很多厂家是自己独有的,如施耐德的MB+;AB的DH+,CONTROLNET;西门子的PROFIBUS,MPI;还有很多通用的比如,RS232、RS485、以太网、GPRS等等。
# 串口通信
数据传输一般都是以字节(Byte)传输的,一个字节8个位(Bit)。
常见的串口通信是指异步串行通信。传数据只有一根线,一次只能传一个位,传一个字节需要传8次。
与串行通信相对的是并行通信。也就是会有8根线,每一根线代表一个位,一次传输就可以传一个字节。
# TTL串口
是由三根线组成的,分别是数据发送线(TXD),数据接收线(RXD),和公共地(GND)。在连接两个TTL串口设备的时候,需要将一片的TXD线连接到另一片的RXD线上,一片的RXD线连接到另一片的TXD线上,两片的GND线连接在一起。
# RS232
RS232接口可以实现点对点的通信方式,传输距离一般不超过20m。能同时发送和接收数据(全双工),当然也可以用于半双工传送。但是不能实现联网功能。基本采用DB9连接器。
在DB9的9个引脚中,并不是所有的信号端都使用的,比如说RTS/CTS只有在半双工方式中作发送和接收时的切换用,而在全双工方式中,因配置双向通道所以不需要。一般来说,在全双工方式中RS232标准接线只要三条线就足够了,两根数据信号线TXD/RXD,一根信号地线GND。双方连接的方式是将TXD和RXD交叉连接,信号地直接相接,然后将各自的RTS/CTS,DSR/DTR短接,将DCD和RI悬空就可以。
# RS485
RS485是为了解决232通信距离的问题。它的最大传输距离为1200米,实际可达3000米,传输速率最高可达10Mbit/s。它只有2根信号线,每次只能作发送或者接收数据(半双工),RS485两线一般定义为: "A、B"或"Date+、Date-" ,也就是我们即常说的485+、485- 。
# RS422
RS422既实现全双工又可以远距离传输。RS422 有4 根信号线:两根发送、两根接收。
# 串口通讯的工作原理
- 第一位起始位,永远都是低电平(起始位是协议规定,就是发送数据的第一个数)
- 最后一位是停止位,永远都是高电平
- 中间是8个二进制数据分别是1、2、4、8、16、32、64、128
- 把收到的波形的每一位相加:1+64=65 => A
- 发送的时候必须要有时间间隔,波特率9600代表:1秒内发送9600位这样的数据,每位数据的间隔是100微妙
- 发送的时间间隔一定要和波特率对应
# 字节和位
- 一个字节(Byte)由8个二进制位(Bit)组成。也就是1字节Byte等于8比特Bit。
- 一位16进制数(用二进制表示是1111)最多只表示到15(即对应16进制的F),要表示到255,就还需要第二位(
FF=F * 161+F * 160=255
)。所以1个字节=8个二进制=2个16进制,一个16进制位=0.5个字节。
# 存储顺序
计算机存储最小单位是字节,右边的是低位,左边是高位。
- 小端模式:低位字节在前、高位在后,如:16进制的数为A2F3,但在计算机中这个数被存为F3A2。低位字节序内容存放在低地址处,高位字节序的内容存放在高地址处。一个数0x12345678存放在一个4字节空间里
- 大端模式:高位字节在前,低位字节在后,这是人类读写数值的方法,即先保存高位字节,再保存低位。低位字节序的内容放在高地址处,高位字节序存的内容放在低地址处。一个数0x12345678存放在一个4字节空间里
如果我们将0x1234abcd写入到以0x0000开始的内存中,则结果为:
big-endian little-endian
0x0000 0x12 0xcd
0x0001 0x23 0xab
0x0002 0xab 0x34
0x0003 0xcd 0x12
# ASCII编码
ASCII码转换表 (opens new window)是美国信息互换标准代码,是一套基于拉丁字母的字符编码,其中包含了33个控制字符(具有某些特殊功能)和95个可显示字符,总共定义了128个字符。ASCII码当中一个汉字占两个字节空间,一个英文字母(不区分大小写)占一个字节空间。ASCII 编码是最简单的西文编码方案。
# Bin编码
Bin编码就是2进制编码,是字符的ascii码值的2进制表示
# Oct编码
Oct编码就是8进制编码,是字符的ascii码值的8进制表示
# Dec编码
Dec编码就是10进制编码,是字符的ascii码值的10进制表示
# Hex编码
hex编码就是16进制编码,是字符的ascii码值的16进制表示
# Unicode编码
Unicode编码是ASCII码的一个扩展,采用双字节对字符进行编码。一个英文等于两个字节,一个中文(含繁体)也等于两个字节。英文标点占用一个字节,中文标点则占用2个字节。
# UTF-8编码
UTF-8编码是一种多字节编码,也是目前互联网应用最广泛的一种Unicode编码方式。最大特点就是可变长,可根据字符的不同变换长度。一个英文字符占用一个字节,一个中文(含繁体)占用三个字节,GBK编码中一个汉字(含繁体)占两个字节。英文标点占用1个字节,中文标点同样占用3个字节。UTF-8包含了全世界所有国家需要用到的字符,是国际编码,通用性极强。使用这种编码的话,一旦文章中同时出现中文、英文或者繁体,浏览器都会支持,而不会出现乱码。
# BCD码
通俗的可以理解为用四位二进制数表示一位十进制数字。例如,256就可以用bcd码表示为:0010_1001_0110。
# 上位机与下位机
上位机是可以直接发出操控命令的计算机,一般是PC,屏幕上显示各种信号变化(液压,水位,温度等)。
下位机是直接控制设备获取设备状况的计算机,一般是PLC/单片机之类的。
上位机发出的命令首先给下位机,下位机再根据此命令解释成相应时序信号直接控制相应设备。
# 进制转换
- 十进制:以0-9这九个数字组成
- 二进制:由0和1两个数字组成
- 八进制:由0-7数字组成,不存在8和9
- 十六进制:由0-9和A-F组成。A-F对应的是10-15
# 十进制和二进制
- 十进制转二进制:一直除以2直到商为0,再反向取余数
- 二进制转十进制:最后一位数开始是2^0,然后一直按照指数递增的方式进行加法运算
# 十进制八进制
- 十进制转八进制:一直除以8直到商为0,再反向取余数
135(十进制)转(八进制)207
- 八进制转十进制:最后一位数开始是8^0,然后一直按照指数递增的方式进行加法运算
946(十进制)转(十六进制)3B2
# 十进制十六进制
- 十进制转十六进制:一直除以16直到商为0,再反向取余数
135(十进制)转(八进制)207
- 十六进制转十进制:最后一位数开始是16^0,然后一直按照指数递增的方式进行加法运算
2C1(十六进制)转(十进制)705
# 二进制八进制
- 二进制转八进制:最后一位数开始取三合一,不够3位前面补0
- 八进制转二进制:操作反过来,取一分三。将1个八进制数分为3个二进制数
# 二进制十六进制
- 二进制转十六进制:最后一位数开始取四合一,不够4位前面补0
0010110110(二进制)转(十六进制)4B6
- 十六进制转二进制:所有数拆分成4位,再按顺序排列取得二进制数。高位为0可省略
38D(十六进制)转(二进制)1110001101
# CRC校验
在线各种CRC8/16/32/64算法 (opens new window)
数据通信领域中最常用的一种差错校验码,代表循环冗余校验和,其信息字段和校验字段长度可以任意指定,但要求通信双方定义的CRC标准一致。从性能上和开销上考虑,均远远优于奇偶校验及算术和校验等方式。
# CRC几个组成部分或者说计算概念
# 1、多项式公式
对于CRC标准除数,一般使用多项式(或二项式)公式表示,如除数11011(poly值为0x1b=>1和1011)的二项式为G(X)=X4+X3+X+1。
# 2、多项式简记式
通过对CRC的基本了解我们知道,多项式的首尾必定为1,而这个1的位置在下一步计算一定为0,所以就把前面这个1给省略掉了,出现了一个叫简记式的东西,如上例中除数11011的简记式为1011,很多看过CRC高级语言源码的人会知道,对于CRC_16标准下G(X)=X16+X15+X2+1(16#18005)的poly值实际上是8005,这里使用的就是简记式。
# 3、数据宽度
数据宽度指的就是CRC校验码的长度(二进制位数),CRC长度始终要比除数位数少1,与简记式长度是一致的。
# 4、初始值与结果异或值
在一些标准中,规定了初始值,则数据在进行上述二项式运算之前,需要先将要计算的数据与初始值的最低字节进行异或,然后再与多项式进行计算。而在结果异或值不为零的情况下,则需要将计算得到的CRC结果值再与结果异或值进行一次异或计算,得到的最终值才是我们需要的CRC校验码。这里可以看出,初始值与结果值的位数要求与数据宽度一致。
# 5、输入值反转与输出值反转
输入值反转的意思是在计算之前先将二项式反转,然后再用得到的新值和数据进行计算。如对于G(X)=X16+X15+X2+1(16#18005),其正向值为1 1000 0000 0000 0101,反转值则为1010 0000 0000 0001 1,输出值反转则是将最终得到的CRC结果反转。通常,输入值反转后的结果值也会是反转的,所以这两个选项一般是同向的,我们只有在在线CRC计算器中会看到自由选择正反转的情况存在。
# CRC校验算法前置知识
# 异或
异或,就是不同为1,相同为0,运算符号是^
0^0 = 0
0^1 = 1
1^1 = 0
1^0 = 1
异或运算存在如下几个规律,需要了解
0^x = x 即0 异或任何数等于任何数
1^x = ~x 即1异或任何数等于任何数取反
x^x = 0 即任何数与自己异或,结果为0
a ^ b = b ^ a 交换律
a ^ (b ^ c) = (a ^ b) ^c 结合律
# 模2加法
模2加法相对于普通的算术加法,主要的区别在模2加法,不做进位处理。具体结果如下。0+0 = 0 0+1 = 1 1+1 = 0 1+0 = 1 我们发现模2加法的计算结果,同异或运算结果一模一样。进一步推演,我们会发现,异或运算的5个规律,同样适合于模2加法。这里,就不在一一列举了。
# 模2减法
模2减法相对于普通的算术减法,主要的区别在模2减法,不做借位处理。具体结果如下。0-0 = 0 0-1 = 1 1-1 = 0 1-0 = 1 我们发现模2减法的计算结果,同模2加法,以及异或的运算结果一模一样。进一步推演,我们会发现,异或运算的5个规律,同样适合于模2减法。
# 模2除法
模2除法相对于普通的算术除法,主要的区别在模2除法,它既不向上位借位,也不比较除数和被除数的相同位数值的大小,只要以相同位数进行相除即可。
# CRC原理
CRC原理:在K位信息码(目标发送数据)后再拼接R位校验码,使整个编码长度为N位,因此这种编码也叫(N,K)码。
通俗的说,就是在需要发送的信息后面附加一个数(即校验码),生成一个新的发送数据发送给接收端。这个数据要求能够使生成的新数据被一个特定的数整除。这里的整除需要引入模 2除法的概念。
那么,CRC校验的具体做法就是
(1)选定一个标准除数(K位二进制数据串)
(2)在要发送的数据(m位)后面加上K-1位0,然后将这个新数(M+K-1位)以模2除法的方式除以上面这个标准除数,所得到的余数也就是该数据的CRC校验码(注:余数必须比除数少且只少一位,不够就补0)
(3)将这个校验码附在原m位数据后面,构成新的M+K-1位数据,发送给接收端。
(4)接收端将接收到的数据除以标准除数,如果余数为0则认为数据正确。
注意:CRC校验中有两个关键点:
一是要预先确定一个发送端和接收端都用来作为除数的二进制比特串(或多项式)。
二是把原始帧与上面选定的除进行二进制除法运算,计算出FCS。
前者可以随机选择,也可按国际上通行的标准选择,但最高位和最低位必须均为“1”。
# 循环冗余的计算
由于CRC-32、CRC-16、CCITT和CRC-4的编码过程基本一致,只有位数和生成多项式不一样,下面就举例,来说明CRC校验码生成过程。
对于数据1110 0101(16#E5),以指定除数11011求它的CRC校验码,其过程如下:
使用上面计算的校验和和消息数据,可以创建要传输的码字。
有时候,我们需要填充checksum到制定的位置,这就涉及到字节序问题,建议用memcpy()进行拷贝。
# 计算代码
public uint GetCRC32(byte[] buffer)
{
int length = buffer.Length;
byte data;
uint crc = 0xFFFFFFFF; //初始值(16位举个例子0xFFFF,8位0xFF)
int i;
for (int j=0;j<length;j++)
{
data = buffer[j];
data = (byte)Reverse8(data); //输入是否反转,如果不需要则这句注释掉;
crc = (uint)(crc ^(data << 24)); //32位左移24位,16位左移8位,8位左移0位;
for (i = 0; i < 8; i++)
{
if ((crc & 0x80000000)== 0x80000000)//16位 0x8000 8位 0x80
{
crc = (crc << 1) ^ 0x04C11DB7;//多项式,没什么好说的;
}
else
{
crc <<= 1;
}
}
}
//输出是否需要反转,如果不需要则注释掉这句话
//注意如果是16位则调用Reverse16(crc),8位调用Reverse8(crc)
crc = Reverse32(crc);
crc = crc ^ 0x00000000;// xor 异或值(与上面初始值类似,16位 0x0000 8位 0x00;
return crc;
}
private uint Reverse32(uint data)
{
byte i;
uint temp = 0;
for (i = 0; i < 32; i++)
{
temp |= ((data >> i) & 0x01) << (31 - i);
}
return temp;
}
private uint Reverse16(uint data)
{
byte i;
uint temp = 0;
for (i = 0; i < 16; i++)
{
temp |= ((data >> i) & 0x01) << (15 - i);
}
return temp;
}
private byte Reverse8(byte data)
{
byte i;
byte temp = 0;
for (i = 0; i < 8; i++)
{
temp = (byte)(temp|(((data >> i) & 0x01) << (7 - i)));
}
return temp;
}
# Modbus通信
# 搭建ModbusTCP仿真环境
- 第三方软件:ModbusScan、ModbusSlave等
- 使用西门子PLC来做
首先需要搭建一个西门子PLC的仿真环境
[http://www.dotnetswj.com/forum.php?mod=viewthread&tid=31&extra=page%3D1]