c# MODBUS协议 上位机

C#写了一款上位机监控软件,基于MODBUS_RTU协议。 软件的基本结构:

  1. 采用定时器(Timer控件)为时间片。
  2. 串口采用serialPort1_DataReceived中断接收,并进行MODBUS格式判断。
  3. 把正确接收的数据取出,转换为有特定的结构体中。
  4. 数据通过时间片实时刷新。
  5. MODBUS协议(这里不介绍了,网上有很多的权威资料)。

 

  串口接收问题

这里采用的是MODBUS_RTU协议,是没有回车等明显的结束符的哈。所以在C#也不可以用serialPort1.ReadLine来读取。我用的是serialPort1.BytesToRead先读缓冲区中的数据个数,再通过个数据读数据。这样在用串口软件测试的时候确实很有用,再随之问题又出现了。下位机传上来的数据长度高出8个,就会分断接收。即接收到的两次的长度,第一次是8个,然后再接收到后面的。 原因是因为软件没有接收完一整帧数据后就进行了中断。解决方法:在中断中加入线程阻塞方法,然后再读取串口中的数据。

  发送读数据和发送写数据的结构

 

写了多个MODBUS协议的上位机后,总结了些经验,并将这部分程序封装在一个类中。

使用时只需对其接口函数调用即可,有很强的移植性。在写软件时不用再在协议这部分花太多的时间。

基本的使用方法在注释中。程序总体感觉 可能过于臃肿,希望各位大神批评指点。

以下是源代码:

  1 /*
  2  * MODBUS协议
  3  * 
  4  * 
  5  * 介绍:
  6  * 此modbus上位机 协议类 具有较强的通用性
  7  * 本协议类最主要的思想是 把所有向下位机发送的指令 先存放在缓冲区中(命名为管道)
  8  * 再将管道中的指令逐个发送出去。
  9  * 管道遵守FIFO的模式。管道中所存放指令的个数 在全局变量中定义。
 10  * 管道内主要分为两部分:1,定时循环发送指令。2,一次性发送指令。
 11  * 定时循环发送指令:周期性间隔时间发送指令,一般针对“输入寄存器”或“输入线圈”等实时更新的变量。
 12  * 这两部分的长度由用户所添加指令个数决定(所以自由性强)。
 13  * 指令的最大发送次数,及管道中最大存放指令的个数在常量定义中 可进行设定。
 14  * 
 15  * 使用说明:
 16  * 1,首先对所定义的寄存器或线圈进行分组定义,并定义首地址。
 17  * 2,在MBDataTable数组中添加寄存器或线圈所对应的地址。 注意 寄存器:ob = new UInt16()。线圈:ob = new byte()。
 18  * 3,对所定义的地址 用属性进行定义 以方便在类外进行访问及了解所对应地址的含义。
 19  * 4,GetAddressValueLength函数中 对使用说明的"第一步"分组 的元素个数进行指定。
 20  * 5,在主程序中调用MBConfig进行协议初始化(初始化内容参考函数)。
 21  * 6,在串口中断函数中调用MBDataReceive()。
 22  * 7,定时器调用MBRefresh()。(10ms以下)
 23  *    指令发送间隔时间等于实时器乘以10。 例:定时器5ms调用一次  指令发送间隔为50ms。
 24  * 8,在主程序初始化中添加固定实时发送的指令操作 用MBAddRepeatCmd函数。
 25  * 9,在主程序运行过程中 根据需要添加 单个的指令操作(非固定重复发送的指令)用MBAddCmd函数。
 26  * 
 27  * 
 28  * 作者:王宏强
 29  * 时间:2012.7.2
 30  * 
 31  * 
 32  * 
 33  * 
 34  * 
 35  * 
 36 */
 37 
 38 using System;
 39 using System.Collections.Generic;
 40 using System.ComponentModel;
 41 using System.Data;
 42 using System.Drawing;
 43 using System.Text;
 44 using System.Windows.Forms;
 45 using System.IO.Ports;
 46 
 47 namespace WindowsApplication1
 48 {
 49 
 50     public class Modbus
 51     {
 52         #region 所用结构体
 53         /// <summary>
 54         /// 地址对应表元素单元
 55         /// </summary>
 56         public struct OPTable{
 57             public volatile int addr;
 58             public volatile byte type;
 59             public volatile object ob;
 60         };
 61         /// <summary>
 62         /// 当前的指令
 63         /// </summary>
 64         public struct MBCmd
 65         {
 66             public volatile int addr;           //指令首地址
 67             public volatile int stat;           //功能码
 68             public volatile int len;            //所操作的寄存器或线圈的个数
 69             public volatile int res;            //返回码的状态, 0:无返回,1:正确返回
 70         };
 71         /// <summary>
 72         /// 当前操作的指令管道
 73         /// </summary>
 74         public struct MBSci
 75         {
 76             public volatile MBCmd[] cmd;             //指令结构体
 77             public volatile int index;               //当前索引
 78             public volatile int count;               //当前功能码执行的次数
 79             public volatile int maxRepeatCount;      //最大发送次数
 80             public volatile int rtCount;             //实时读取的指令各数(无限间隔时间读取)
 81         };
 82         #endregion
 83 
 84         #region 常量定义
 85         public const byte MB_READ_COILS = 0x01;             //读线圈寄存器
 86         public const byte MB_READ_DISCRETE = 0x02;          //读离散输入寄存器
 87         public const byte MB_READ_HOLD_REG = 0x03;          //读保持寄存器
 88         public const byte MB_READ_INPUT_REG = 0x04;         //读输入寄存器
 89         public const byte MB_WRITE_SINGLE_COIL = 0x05;      //写单个线圈
 90         public const byte MB_WRITE_SINGLE_REG = 0x06;       //写单寄存器
 91         public const byte MB_WRITE_MULTIPLE_COILS = 0x0f;   //写多线圈
 92         public const byte MB_WRITE_MULTIPLE_REGS = 0x10;    //写多寄存器
 93 
 94         private const int MB_MAX_LENGTH = 120;               //最大数据长度
 95         private const int MB_SCI_MAX_COUNT = 15;             //指令管道最大存放的指令各数
 96         private const int MB_MAX_REPEAT_COUNT = 3;           //指令最多发送次数
 97         #endregion
 98 
 99         #region 全局变量
100         private static volatile bool sciLock = false;                       //调度器锁 true:加锁  false:解锁
101         private static volatile byte[] buff = new byte[MB_MAX_LENGTH];      //接收缓冲器
102         private static volatile int buffLen = 0;
103         private static volatile byte[] rBuff = null;                  //正确接收缓冲器
104         private static volatile byte[] wBuff = null;                     //正确发送缓冲器
105         public static MBSci gMBSci = new MBSci() { cmd = new MBCmd[MB_SCI_MAX_COUNT], index = 0, maxRepeatCount = MB_MAX_REPEAT_COUNT, rtCount = 0, count = 0 };
106         private static SerialPort comm = null;
107         private static int mbRefreshTime = 0;
108         #endregion
109 
110         #region MODBUS 地址对应表
111         //modbus寄存器和线圈分组 首地址定义
112         public const int D_DIO = 0x0000;
113         public const int D_BASE = 0x0014;
114         public const int D_RANGE = 0x0018;
115         public const int D_PWM = 0x001A;
116         public const int D_PID = 0x001E;
117 
118         /// <summary>
119         /// 变量所对应的地址 在此位置
120         /// </summary>
121         public static volatile OPTable[] MBDataTable = 
122         {
123             new OPTable(){addr = D_DIO,         type = MB_READ_INPUT_REG,      ob = new UInt16()},      //0
124             new OPTable(){addr = D_DIO + 1,     type = MB_READ_INPUT_REG,      ob = new UInt16()},
125             new OPTable(){addr = D_DIO + 2,     type = MB_READ_INPUT_REG,      ob = new UInt16()},
126             new OPTable(){addr = D_DIO + 3,     type = MB_READ_INPUT_REG,      ob = new UInt16()},
127             new OPTable(){addr = D_DIO + 4,     type = MB_READ_INPUT_REG,      ob = new Int16()},
128             new OPTable(){addr = D_DIO + 5,     type = MB_READ_INPUT_REG,      ob = new Int16()},
129 
130             new OPTable(){addr = D_BASE,        type = MB_READ_HOLD_REG,      ob = new Int16()},        //6
131             new OPTable(){addr = D_BASE + 1,    type = MB_READ_HOLD_REG,      ob = new Int16()},
132             new OPTable(){addr = D_BASE + 2,    type = MB_READ_HOLD_REG,      ob = new Int16()},
133             new OPTable(){addr = D_BASE + 3,    type = MB_READ_HOLD_REG,      ob = new Int16()},
134 
135             new OPTable(){addr = D_RANGE,       type = MB_READ_HOLD_REG,      ob = new Int16()},        //10
136             new OPTable(){addr = D_RANGE + 1,   type = MB_READ_HOLD_REG,      ob = new Int16()},
137 
138             new OPTable(){addr = D_PWM,         type = MB_READ_HOLD_REG,      ob = new Int16()},        //12
139             new OPTable(){addr = D_PWM + 1,     type = MB_READ_HOLD_REG,      ob = new Int16()},
140             new OPTable(){addr = D_PWM + 2,     type = MB_READ_HOLD_REG,      ob = new Int16()},
141             new OPTable(){addr = D_PWM + 3,     type = MB_READ_HOLD_REG,      ob = new Int16()},
142 
143             new OPTable(){addr = D_PID,         type = MB_READ_HOLD_REG,      ob = new UInt16()},        //16
144             new OPTable(){addr = D_PID + 1,     type = MB_READ_HOLD_REG,      ob = new UInt16()},
145             new OPTable(){addr = D_PID + 2,     type = MB_READ_HOLD_REG,      ob = new UInt16()},
146             new OPTable(){addr = D_PID + 3,     type = MB_READ_HOLD_REG,      ob = new UInt16()},
147             new OPTable(){addr = D_PID + 4,     type = MB_READ_HOLD_REG,      ob = new UInt16()},
148             new OPTable(){addr = D_PID + 5,     type = MB_READ_HOLD_REG,      ob = new UInt16()},
149 
150         };
151         public static UInt16 gDioX { get { return Convert.ToUInt16(MBDataTable[0].ob); } set { MBDataTable[0].ob = value; } }
152         public static UInt16 gDioY { get { return Convert.ToUInt16(MBDataTable[1].ob); } set { MBDataTable[1].ob = value; } }
153         public static UInt16 gDioZ { get { return Convert.ToUInt16(MBDataTable[2].ob); } set { MBDataTable[2].ob = value; } }
154         public static UInt16 gDioD { get { return Convert.ToUInt16(MBDataTable[3].ob); } set { MBDataTable[3].ob = value; } }
155         public static Int16 gDioXx { get { return (Int16)Convert.ToInt32(MBDataTable[4].ob); } set { MBDataTable[4].ob = value; } }
156         public static Int16 gDioXy { get { return (Int16)Convert.ToInt32(MBDataTable[5].ob); } set { MBDataTable[5].ob = value; } }
157 
158         public static Int16 gBaseF1 { get { return (Int16)Convert.ToInt32(MBDataTable[6].ob); } set { MBDataTable[6].ob = value; } }
159         public static Int16 gBaseF2 { get { return (Int16)Convert.ToInt32(MBDataTable[7].ob); } set { MBDataTable[7].ob = value; } }
160         public static Int16 gBaseF3 { get { return (Int16)Convert.ToInt32(MBDataTable[8].ob); } set { MBDataTable[8].ob = value; } }
161         public static Int16 gBaseF4 { get { return (Int16)Convert.ToInt32(MBDataTable[9].ob); } set { MBDataTable[9].ob = value; } }
162 
163         public static Int16 gRangeMax { get { return (Int16)Convert.ToInt32(MBDataTable[10].ob); } set { MBDataTable[10].ob = value; } }
164         
  • 2
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
* * MODBUS协议 * * * 介绍: * 此modbus上位 协议类 具有较强的通用性 * 本协议类最主要的思想是 把所有向下位发送的指令 先存放在缓冲区中(命名为管道) * 再将管道中的指令逐个发送出去。 * 管道遵守FIFO的模式。管道中所存放指令的个数 在全局变量中定义。 * 管道内主要分为两部分:1,定时循环发送指令。2,一次性发送指令。 * 定时循环发送指令:周期性间隔时间发送指令,一般针对“输入寄存器”或“输入线圈”等实时更新的变量。 * 这两部分的长度由用户所添加指令个数决定(所以自由性强)。 * 指令的最大发送次数,及管道中最大存放指令的个数在常量定义中 可进行设定。 * * 使用说明: * 1,首先对所定义的寄存器或线圈进行分组定义,并定义首地址。 * 2,在MBDataTable数组中添加寄存器或线圈所对应的地址。 注意 寄存器:ob = new UInt16()。线圈:ob = new byte()。 * 3,对所定义的地址 用属性进行定义 以方便在类外进行访问及了解所对应地址的含义。 * 4,GetAddressValueLength函数中 对使用说明的"第一步"分组 的元素个数进行指定。 * 5,在主程序中调用MBConfig进行协议初始化(初始化内容参考函数)。 * 6,在串口中断函数中调用MBDataReceive()。 * 7,定时器调用MBRefresh()。(10ms以下) * 指令发送间隔时间等于实时器乘以10。 例:定时器5ms调用一次 指令发送间隔为50ms。 * 8,在主程序初始化中添加固定实时发送的指令操作 用MBAddRepeatCmd函数。 * 9,在主程序运行过程中 根据需要添加 单个的指令操作(非固定重复发送的指令)用MBAddCmd函数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值