/*
* MODBUS 协议
*
*
* 介绍:
* 此 modbus 上位机 协议类 具有较强的通用性
* 本协议类最主要的思想是 把所有向下位机发送的指令 先存放在缓冲区中(命名为管道)
* 再将管道中的指令逐个发送出去。
* 管道遵守 FIFO 的模式。管道中所存放指令的个数 在全局变量中定义。
* 管道内主要分为两部分:1,定时循环发送指令。2,一次性发送指令。
* 定时循环发送指令:周期性间隔时间发送指令,一般针对“输入寄存器”或“输入线圈”等实时更新的变量。
* 这两部分的长度由用户所添加指令个数决定(所以自由性强)。
* 指令的最大发送次数,及管道中最大存放指令的个数在常量定义中 可进行设定。
*
* 使用说明:
* 1,首先对所定义的寄存器或线圈进行分组定义,并定义首地址。
* 2,在 MBDataTable 数组中添加寄存器或线圈所对应的地址。注意 寄存器:ob = new UInt16()。线圈:ob = new byte()。
* 3,对所定义的地址 用属性进行定义 以方便在类外进行访问及了解所对应地址的含义。
* 4,GetAddressValueLength 函数中 对使用说明的"第一步"分组 的元素个数进行指定。
* 5,在主程序中调用 MBConfig 进行协议初始化(初始化内容参考函数)。
* 6,在串口中断函数中调用 MBDataReceive()。
* 7,定时器调用 MBRefresh()。(10ms 以下)
*
* 8,在主程序初始化中添加固定实时发送的指令操作 用 MBAddRepeatCmd 函数。
* 9,在主程序运行过程中 根据需要添加 单个的指令操作(非固定重复发送的指令)用 MBAddCmd 函数。
*
*
* 作者:王宏强
* 时间:2012.7.2
*
*
*
*
*
*
*/
指令发送间隔时间等于实时器乘以 10。 例:定时器 5ms 调用一次 指令发送间隔为 50ms。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;
namespace WindowsApplication1
{
public class Modbus
{
#region 所用结构体
///
/// 地址对应表元素单元
///
public struct OPTable{
public volatile int addr;
public volatile byte type;
public volatile object ob;
};
///
/// 当前的指令
///
public struct MBCmd
{
public volatile int addr;
public volatile int stat;
public volatile int len;
public volatile int res;
};
///
/// 当前操作的指令管道
///
public struct MBSci
{
//指令首地址
//功能码
//所操作的寄存器或线圈的个数
//返回码的状态, 0:无返回,1:正确返回
public volatile MBCmd[] cmd;
public volatile int index;
public volatile int count;
public volatile int maxRepeatCount;
public volatile int rtCount;
//指令结构体
//当前索引
//当前功能码执行的次数
//最大发送次数
//实时读取的指令各数(无限间隔时间读取)
};
#endregion
#region 常量定义
public const byte MB_READ_COILS = 0x01;
public const byte MB_READ_DISCRETE = 0x02;
public const byte MB_READ_HOLD_REG = 0x03;
public const byte MB_READ_INPUT_REG = 0x04;
public const byte MB_WRITE_SINGLE_COIL = 0x05;
public const byte MB_WRITE_SINGLE_REG = 0x06;
public const byte MB_WRITE_MULTIPLE_COILS = 0x0f;
public const byte MB_WRITE_MULTIPLE_REGS = 0x10;
//读线圈寄存器
//读离散输入寄存器
//读保持寄存器
//读输入寄存器
//写单个线圈
//写单寄存器
//写多线圈
//写多寄存器
private const int MB_MAX_LENGTH = 120;
private const int MB_SCI_MAX_COUNT = 15;
//最大数据长度
//指令管道最大存放的指令各数
private const int MB_MAX_REPEAT_COUNT = 3;
#endregion
//指令最多发送次数
#region 全局变量
private static volatile bool sciLock = false;
private static volatile byte[] buff = new byte[MB_MAX_LENGTH];
private static volatile int buffLen = 0;
private static volatile byte[] rBuff = null;
private static volatile byte[] wBuff = null;
public static MBSci gMBSci = new MBSci()
//调度器锁 true:加锁 false:解锁
//接收缓冲器
//正确接收缓冲器
//正确发送缓冲器
{ cmd = new MBCmd[MB_SCI_MAX_COUNT],
index = 0,
maxRepeatCount = MB_MAX_REPEAT_COUNT, rtCount = 0, count = 0 };
private static SerialPort comm = null;
private static int mbRefreshTime = 0;
#endregion
#region MODBUS 地址对应表
//modbus 寄存器和线圈分组 首地址定义
public const int D_DIO = 0x0000;
public const int D_BASE = 0x0014;
public const int D_RANGE = 0x0018;
public const int D_PWM = 0x001A;
public const int D_PID = 0x001E;
///
/// 变量所对应的地址 在此位置
///
public static volatile OPTable[] MBDataTable =
{
//0
//6
//10
//12
new OPTable(){addr = D_DIO,
type = MB_READ_INPUT_REG,
ob = new UInt16()},
new OPTable(){addr = D_DIO + 1,
new OPTable(){addr = D_DIO + 2,
new OPTable(){addr = D_DIO + 3,
new OPTable(){addr = D_DIO + 4,
new OPTable(){addr = D_DIO + 5,
type = MB_READ_INPUT_REG,
type = MB_READ_INPUT_REG,
type = MB_READ_INPUT_REG,
type = MB_READ_INPUT_REG,
type = MB_READ_INPUT_REG,
ob = new UInt16()},
ob = new UInt16()},
ob = new UInt16()},
ob = new Int16()},
ob = new Int16()},
new OPTable(){addr = D_BASE,
type = MB_READ_HOLD_REG,
ob = new Int16()},
new OPTable(){addr = D_BASE + 1,
new OPTable(){addr = D_BASE + 2,
new OPTable(){addr = D_BASE + 3,
type = MB_READ_HOLD_REG,
type = MB_READ_HOLD_REG,
type = MB_READ_HOLD_REG,
ob = new Int16()},
ob = new Int16()},
ob = new Int16()},
new OPTable(){addr = D_RANGE,
type = MB_READ_HOLD_REG,
ob = new Int16()},
new OPTable(){addr = D_RANGE + 1,
type = MB_READ_HOLD_REG,
ob = new Int16()},
new OPTable(){addr = D_PWM,
type = MB_READ_HOLD_REG,
ob = new Int16()},
new OPTable(){addr = D_PWM + 1,
type = MB_READ_HOLD_REG,
ob = new Int16()},
new OPTable(){addr = D_PWM + 2,
new OPTable(){addr = D_PWM + 3,
type = MB_READ_HOLD_REG,
type = MB_READ_HOLD_REG,
ob = new Int16()},
ob = new Int16()},
new OPTable(){addr = D_PID,
type = MB_READ_HOLD_REG,
ob = new UInt16()},
new OPTable(){addr = D_PID + 1,
new OPTable(){addr = D_PID + 2,
new OPTable(){addr = D_PID + 3,
new OPTable(){addr = D_PID + 4,
new OPTable(){addr = D_PID + 5,
type = MB_READ_HOLD_REG,
type = MB_READ_HOLD_REG,
type = MB_READ_HOLD_REG,
type = MB_READ_HOLD_REG,
type = MB_READ_HOLD_REG,
ob = new UInt16()},
ob = new UInt16()},
ob = new UInt16()},
ob = new UInt16()},
ob = new UInt16()},
};
public static UInt16 gDioX { get { return Convert.ToUInt16(MBDataTable[0].ob); } set { MBDataTable[0].ob =
public static UInt16 gDioY { get { return Convert.ToUInt16(MBDataTable[1].ob); } set { MBDataTable[1].ob =
public static UInt16 gDioZ { get { return Convert.ToUInt16(MBDataTable[2].ob); } set { MBDataTable[2].ob =
public static UInt16 gDioD { get { return Convert.ToUInt16(MBDataTable[3].ob); } set { MBDataTable[3].ob =
public static Int16 gDioXx { get { return (Int16)Convert.ToInt32(MBDataTable[4].ob); } set { MBDataTable[4].ob =
public static Int16 gDioXy { get { return (Int16)Convert.ToInt32(MBDataTable[5].ob); } set { MBDataTable[5].ob =
public static Int16 gBaseF1 { get { return (Int16)Convert.ToInt32(MBDataTable[6].ob); } set { MBDataTable[6].ob =
public static Int16 gBaseF2 { get { return (Int16)Convert.ToInt32(MBDataTable[7].ob); } set { MBDataTable[7].ob =
public static Int16 gBaseF3 { get { return (Int16)Convert.ToInt32(MBDataTable[8].ob); } set { MBDataTable[8].ob =
public static Int16 gBaseF4 { get { return (Int16)Convert.ToInt32(MBDataTable[9].ob); } set { MBDataTable[9].ob =
//16
value; } }
value; } }
value; } }
value; } }
value; } }
value; } }
value; } }
value; } }
value; } }
value; } }
public
static
Int16 gRangeMax
{
get
{ MBDataTable[10].ob = value; } }
Int16
{ MBDataTable[11].ob = value; } }
public
static
public
public
Int16
{ MBDataTable[12].ob = value; } }
Int16
{ MBDataTable[13].ob = value; } }
Int16
{ MBDataTable[14].ob = value; } }
Int16
{ MBDataTable[15].ob = value; } }
public
public
static
static
static
static
public static float gP
gRangeMin {
get
gPwmF1
gPwmF2
gPwmF3
gPwmF4
{
{
{
{
get
get
get
get
{
{
{
{
{
{
return (Int16)Convert.ToInt32(MBDataTable[10].ob);
return (Int16)Convert.ToInt32(MBDataTable[11].ob);
return
(Int16)Convert.ToInt32(MBDataTable[12].ob);
return
(Int16)Convert.ToInt32(MBDataTable[13].ob);
return
(Int16)Convert.ToInt32(MBDataTable[14].ob);
return
(Int16)Convert.ToInt32(MBDataTable[15].ob);
}
}
}
}
}
}
set
set
set
set
set
set
{
get
{
0xFFFF) << 16);
}
set
{
}
int tmp = (Convert.ToInt32(MBDataTable[16].ob) & 0xFFFF) | ((Convert.ToInt32(MBDataTable[17].ob) &
byte[] arr = BitConverter.GetBytes(tmp);
return BitConverter.ToSingle(arr, 0);
byte[] val = BitConverter.GetBytes(value);
MBDataTable[16].ob = BitConverter.ToUInt16(val, 0);
MBDataTable[17].ob = BitConverter.ToUInt16(val, 2);
}
public static float gI
{
get
{
int tmp = (Convert.ToInt32(MBDataTable[18].ob) & 0xFFFF) | ((Convert.ToInt32(MBDataTable[19].ob) &
0xFFFF) << 16);
byte[] arr = BitConverter.GetBytes(tmp);
return BitConverter.ToSingle(arr, 0);
}
set
{
}
byte[] val = BitConverter.GetBytes(value);
MBDataTable[18].ob = BitConverter.ToUInt16(val, 0);
MBDataTable[19].ob = BitConverter.ToUInt16(val, 2);
}
public static float gD
{
get
{
int tmp = (Convert.ToInt32(MBDataTable[20].ob) & 0xFFFF) | ((Convert.ToInt32(MBDataTable[21].ob) &
0xFFFF) << 16);
byte[] arr = BitConverter.GetBytes(tmp);
return BitConverter.ToSingle(arr, 0);
}
set
{
}
}
byte[] val = BitConverter.GetBytes(value);
MBDataTable[20].ob = BitConverter.ToUInt16(val, 0);
MBDataTable[21].ob = BitConverter.ToUInt16(val, 2);
public static UInt16 gNode = 100;
public static UInt16 gBaud = 38400;
///
/// 获取寄存器或线圈 分组后的成员各数
///
///
首地址
///
成员各数
private static int GetAddressValueLength(int addr)
{
int res = 0;
switch (addr)
{
case D_DIO: res = 6; break;
case D_BASE: res = 4; break;
case D_RANGE: res = 2; break;
case D_PWM: res = 4; break;
case D_PID: res = 6; break;
default: break;
}
return res;
}
///
/// 获取地址所对应的数据
///
///
地址
///
类型
///
获取到的数据
private static object GetAddressValue(int addr, byte type)
{
switch (type)
{
//功能码类型判断
case MB_READ_COILS:
case MB_READ_DISCRETE:
case MB_READ_HOLD_REG:
case MB_READ_INPUT_REG: break;
case MB_WRITE_SINGLE_COIL:
case MB_WRITE_MULTIPLE_COILS: type = MB_READ_DISCRETE; break;
case MB_WRITE_SINGLE_REG:
case MB_WRITE_MULTIPLE_REGS: type = MB_READ_HOLD_REG; break;
default: return null;
}
for (int i = 0; i < MBDataTable.Length; i++)
{
if (MBDataTable[i].addr == addr)
{
if (MBDataTable[i].type == type)
{
return MBDataTable[i].ob;
}
}
}
return null;
}
///
/// 设置地址所对应的数据
///
///
地址
///
类型
///
数据
///
是否成功
private static object SetAddressValue(int addr, byte type, object data)
{
for (int i = 0; i < MBDataTable.Length; i++)
{
if (MBDataTable[i].addr == addr)
{
if (MBDataTable[i].type == type)
{
MBDataTable[i].ob = data;
return true;
}
}
}
return null;
}
///
/// 获取一连串数据
///
///
首地址
///
功能码
///
长度
///
转换后的字节数组
private static byte[] GetAddressValues(int addr, byte type, int len)
{
byte[] arr = null;
object obj;
byte temp;
int temp2;
switch (type)
{
case MB_WRITE_MULTIPLE_COILS:
arr = new byte[(len % 8 == 0) ? (len / 8) : (len / 8 + 1)];
for (int i = 0; i < arr.Length; i++)
{
for (int j = 0; j < 8; j++)
{
//获取地址所对应的数据 并判断所读数据 是否被指定,有没被指定的数据 直接返
回 null
obj = GetAddressValue(addr + i * 8 + j, MB_READ_COILS);
if (obj == null)
return null;
else
temp = Convert.ToByte(obj);
arr[i] |=
(byte)((temp == 0? 0 : 1) << j);
}
}
break;
case MB_WRITE_MULTIPLE_REGS:
arr = new byte[len * 2];
for (int i = 0; i < len; i++)
{
obj = GetAddressValue(addr + i, MB_READ_HOLD_REG);
if (obj == null)
return null;
else
temp2 = Convert.ToInt32(obj);
arr[i * 2] = (byte)(temp2 >> 8);
arr[i * 2 + 1] = (byte)(temp2 & 0xFF);
}
break;
default: break;
}
return arr;
}
#endregion
#region 校验
private static readonly byte[] aucCRCHi = {
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,