C#与PLC的数据类型
- 1. PLC与二进制
- 2. PLC数据类型
- 2.1 PLC与C#主要数据类型
- 2.2 PLC与C#的其他数据类型
- 3. PLC寻址
1. PLC与二进制
地球上的CPU和RAM在硬件层面仅计算和存储二进制0和1(量子计算硬件除外),PLC内部本质上也只存储、计算二进制0和1,上层应用系统会通过系统内默认的编码规则(比如ASCII或者UTF8)将信息编码后最终得到Bit二进制数据写入PLC逻辑控制器,以实现工业设备的自动化,同时从PLC寻址读取数据后解码Bit二进制数据以监视工业自动化设备的状态。
2. PLC数据类型
2.1 PLC与C#主要数据类型
| PLC数据类型 | Byte长度 | Bit长度 | 对齐要求 | 数值范围 | 地址说明 | C#数据类型 | 数值范围 |
|---|---|---|---|---|---|---|---|
| Bool | 1字节 | 1位 | 无严格对齐要求,但通常按字节或字边界访问以提高效率,比如:VB101.0 | 0 或 1 | Byte是内存的最小寻址单元,会强制分配1个Byte(8 Bits)的内存空间,但实际只用1个Bit位(最右边的低位) | bool | true / false |
| Byte | 1字节 | 8位 | 无严格对齐要求,但通常按字节或字边界访问以提高效率,比如:VB102 | 无符号整数:0 ~ 255;有符号整数:-128 ~ 127 | 存储简单数据,如设备状态编码 | byte | 无符号整数:0 ~ 255(PLC程序的编码规则可以将Bit数据解析为负数,而C#是强类型语言不允许byte为负数) |
| Word | 2字节 | 16位 | 起始地址必须是偶数(能被2整除),比如:VW106 | 无符号整数:0 ~ 65535 | 存储中等规模数据,处理无符号整数运算 | ushort | 与PLC一致 |
| DWord | 4字节 | 32位 | 起始地址必须是偶数(能被4整除),比如:VD110 | 无符号整数:0 ~ 4294967295 | 存储大规模数据,进行复杂计算 | uint | 与PLC一致 |
| Int | 2字节 | 16位 | 起始地址必须是偶数(能被2整除),比如:VW114 | 有符号整数:-32768 ~ 32767 | 处理有符号整数运算 | short | 与PLC一致 |
| DINT | 4字节 | 32位 | 起始地址必须是偶数(能被4整除),比如:VD118 | 有符号整数:-2147483648 ~ 2147483647 | 处理大范围有符号整数运算,精度与性能平衡 | int | 与PLC一致 |
| Real | 4字节 | 32位 | 起始地址必须是偶数(能被4整除),比如:VD122 | 浮点数,范围广泛,精度最高为6位有效数字 | 表示带有小数部分的数值,进行高精度计算 | float | 与PLC一致 |
2.2 PLC与C#的其他数据类型
C#除了上述数据类型之外,还有其他的数据类型,比如:long、double、decimal、string等常见数据类型,那么这些数据类型如何在PLC里面读写呢?
通用的做法,就是在C#控制数据最大的长度(注意:string是可变的),在每次写入前,先将Encoding得到的Bit数据里面的高位补0,再把输入写入PLC地址即可。
publicbyte[]PrepareStringDataOptimized(stringtext,intmaxLength){byte[]buffer=newbyte[maxLength];byte[]textBytes=Encoding.ASCII.GetBytes(text);intcopyLength=Math.Min(textBytes.Length,maxLength);// 一次性复制数据Buffer.BlockCopy(textBytes,0,buffer,0,copyLength);// 高效清零剩余部分if(copyLength<maxLength){Array.Clear(buffer,copyLength,maxLength-copyLength);}returnbuffer;}或者使用以下方法
publicclassPLCStringManager{// 方法1:使用结束符publicstaticbyte[]ToCString(stringtext,inttotalLength){byte[]buffer=newbyte[totalLength];byte[]data=Encoding.ASCII.GetBytes(text);// 复制数据intcopyLength=Math.Min(data.Length,totalLength-1);Array.Copy(data,buffer,copyLength);// 添加结束符buffer[copyLength]=0;returnbuffer;}// 方法2:长度寄存器+数据publicvoidWriteStringWithLength(stringtext,intdataAddress,intlengthAddress){byte[]data=Encoding.ASCII.GetBytes(text);// 1. 先写长度(单独的WORD/DINT)plc.Write(lengthAddress,data.Length);// 2. 再写数据byte[]buffer=newbyte[data.Length];Array.Copy(data,buffer,data.Length);plc.WriteBytes(dataAddress,buffer);}// 方法3:定长数组+空格填充publicstaticbyte[]ToFixedLengthString(stringtext,intlength){byte[]buffer=newbyte[length];byte[]data=Encoding.ASCII.GetBytes(text.PadRight(length,' '));Array.Copy(data,buffer,Math.Min(data.Length,length));returnbuffer;}}3. PLC寻址
PLC地址只是告诉CPU:“去内存的哪个位置拿多少位Bit数据”,至于这些位应该被解释成什么(整数、浮点数、字符),CPU完全不知道也不关心。
比如,声明一个变量V100(V100 ~ V104没有被占用),先写入一个REAL值,再复位V100 ~ V104为0,然后写入Word值再读取Word值,最后也能正确读取之前写入的Word值。所以,PLC寻址跟数据类型没有半毛钱关系。