经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C# » 查看文章
C#Modbus Rtu的实现
来源:cnblogs  作者:′Peter.Pan  时间:2019/5/13 9:07:59  对本文有异议

Modbus Rtu的实现与Modbus Tcp的实现类似 C#ModBus Tcp的学习及Master的实现

我们还是需要借用一个开源库NModbus4,在vs中.打开NuGet管理器.安装NModbus4

 

具体实现,具体实现与之前的Modbus Tcp的实现类似 ,只是在实例化master时将TCPClient换为串行端口资源SerialPort,并在实例化是设置好端口所需参数(端口名,波特率,校验位,停止位,数据位)

  1. 1 using System;
  2. 2 using System.Collections.Generic;
  3. 3 using System.ComponentModel;
  4. 4 using System.Data;
  5. 5 using System.Drawing;
  6. 6 using System.Linq;
  7. 7 using System.Text;
  8. 8 using System.Threading.Tasks;
  9. 9 using System.Windows.Forms;
  10. 10 using Modbus.Device;
  11. 11 using System.Net.Sockets;
  12. 12 using System.Threading;
  13. 13 using System.IO.Ports;
  14. 14
  15. 15 namespace ModbusRtu
  16. 16 {
  17. 17 public partial class Form1 : Form
  18. 18 {
  19. 19 private static IModbusMaster master;
  20. 20 private static SerialPort port;
  21. 21 //写线圈或写寄存器数组
  22. 22 private bool[] coilsBuffer;
  23. 23 private ushort[] registerBuffer;
  24. 24 //功能码
  25. 25 private string functionCode;
  26. 26 //参数(分别为站号,起始地址,长度)
  27. 27 private byte slaveAddress;
  28. 28 private ushort startAddress;
  29. 29 private ushort numberOfPoints;
  30. 30 //串口参数
  31. 31 private string portName;
  32. 32 private int baudRate;
  33. 33 private Parity parity;
  34. 34 private int dataBits;
  35. 35 private StopBits stopBits;
  36. 36
  37. 37 public Form1()
  38. 38 {
  39. 39 InitializeComponent();
  40. 40 }
  41. 41 private void Form1_Load(object sender, EventArgs e)
  42. 42 {
  43. 43 cmb_portname.SelectedIndex = 0;
  44. 44 cmb_baud.SelectedIndex = 5;
  45. 45 cmb_parity.SelectedIndex = 2;
  46. 46 cmb_databBits.SelectedIndex = 1;
  47. 47 cmb_stopBits.SelectedIndex = 0;
  48. 48 }
  49. 49 private SerialPort InitSerialPortParameter()
  50. 50 {
  51. 51 if (cmb_portname.SelectedIndex < 0 || cmb_baud.SelectedIndex < 0 || cmb_parity.SelectedIndex < 0 || cmb_databBits.SelectedIndex < 0 || cmb_stopBits.SelectedIndex < 0)
  52. 52 {
  53. 53 MessageBox.Show("请选择串口参数");
  54. 54 return null;
  55. 55 }
  56. 56 else
  57. 57 {
  58. 58
  59. 59 portName = cmb_portname.SelectedItem.ToString();
  60. 60 baudRate = int.Parse(cmb_baud.SelectedItem.ToString());
  61. 61 switch (cmb_parity.SelectedItem.ToString())
  62. 62 {
  63. 63 case "":
  64. 64 parity = Parity.Odd;
  65. 65 break;
  66. 66 case "":
  67. 67 parity = Parity.Even;
  68. 68 break;
  69. 69 case "":
  70. 70 parity = Parity.None;
  71. 71 break;
  72. 72 default:
  73. 73 break;
  74. 74 }
  75. 75 dataBits = int.Parse(cmb_databBits.SelectedItem.ToString());
  76. 76 switch (cmb_stopBits.SelectedItem.ToString())
  77. 77 {
  78. 78 case "1":
  79. 79 stopBits = StopBits.One;
  80. 80 break;
  81. 81 case "2":
  82. 82 stopBits = StopBits.Two;
  83. 83 break;
  84. 84 default:
  85. 85 break;
  86. 86 }
  87. 87 port = new SerialPort(portName, baudRate, parity, dataBits, stopBits);
  88. 88 return port;
  89. 89 }
  90. 90 }
  91. 91 /// <summary>
  92. 92 /// 读/写
  93. 93 /// </summary>
  94. 94 /// <param name="sender"></param>
  95. 95 /// <param name="e"></param>
  96. 96 private void button1_Click(object sender, EventArgs e)
  97. 97 {
  98. 98 try
  99. 99 {
  100. 100 //初始化串口参数
  101. 101 InitSerialPortParameter();
  102. 102
  103. 103 master = ModbusSerialMaster.CreateRtu(port);
  104. 104
  105. 105 ExecuteFunction();
  106. 106 }
  107. 107 catch (Exception)
  108. 108 {
  109. 109 MessageBox.Show("初始化异常");
  110. 110 }
  111. 111 }
  112. 112
  113. 113 private async void ExecuteFunction()
  114. 114 {
  115. 115 try
  116. 116 {
  117. 117 //每次操作是要开启串口 操作完成后需要关闭串口
  118. 118 //目的是为了slave更换连接是不报错
  119. 119 if (port.IsOpen == false)
  120. 120 {
  121. 121 port.Open();
  122. 122 }
  123. 123 if (functionCode != null)
  124. 124 {
  125. 125 switch (functionCode)
  126. 126 {
  127. 127 case "01 Read Coils"://读取单个线圈
  128. 128 SetReadParameters();
  129. 129 coilsBuffer = master.ReadCoils(slaveAddress, startAddress, numberOfPoints);
  130. 130
  131. 131 for (int i = 0; i < coilsBuffer.Length; i++)
  132. 132 {
  133. 133 SetMsg(coilsBuffer[i] + " ");
  134. 134 }
  135. 135 SetMsg("\r\n");
  136. 136 break;
  137. 137 case "02 Read DisCrete Inputs"://读取输入线圈/离散量线圈
  138. 138 SetReadParameters();
  139. 139
  140. 140 coilsBuffer = master.ReadInputs(slaveAddress, startAddress, numberOfPoints);
  141. 141 for (int i = 0; i < coilsBuffer.Length; i++)
  142. 142 {
  143. 143 SetMsg(coilsBuffer[i] + " ");
  144. 144 }
  145. 145 SetMsg("\r\n");
  146. 146 break;
  147. 147 case "03 Read Holding Registers"://读取保持寄存器
  148. 148 SetReadParameters();
  149. 149 registerBuffer = master.ReadHoldingRegisters(slaveAddress, startAddress, numberOfPoints);
  150. 150 for (int i = 0; i < registerBuffer.Length; i++)
  151. 151 {
  152. 152 SetMsg(registerBuffer[i] + " ");
  153. 153 }
  154. 154 SetMsg("\r\n");
  155. 155 break;
  156. 156 case "04 Read Input Registers"://读取输入寄存器
  157. 157 SetReadParameters();
  158. 158 registerBuffer = master.ReadInputRegisters(slaveAddress, startAddress, numberOfPoints);
  159. 159 for (int i = 0; i < registerBuffer.Length; i++)
  160. 160 {
  161. 161 SetMsg(registerBuffer[i] + " ");
  162. 162 }
  163. 163 SetMsg("\r\n");
  164. 164 break;
  165. 165 case "05 Write Single Coil"://写单个线圈
  166. 166 SetWriteParametes();
  167. 167 await master.WriteSingleCoilAsync(slaveAddress, startAddress, coilsBuffer[0]);
  168. 168 break;
  169. 169 case "06 Write Single Registers"://写单个输入线圈/离散量线圈
  170. 170 SetWriteParametes();
  171. 171 await master.WriteSingleRegisterAsync(slaveAddress, startAddress, registerBuffer[0]);
  172. 172 break;
  173. 173 case "0F Write Multiple Coils"://写一组线圈
  174. 174 SetWriteParametes();
  175. 175 await master.WriteMultipleCoilsAsync(slaveAddress, startAddress, coilsBuffer);
  176. 176 break;
  177. 177 case "10 Write Multiple Registers"://写一组保持寄存器
  178. 178 SetWriteParametes();
  179. 179 await master.WriteMultipleRegistersAsync(slaveAddress, startAddress, registerBuffer);
  180. 180 break;
  181. 181 default:
  182. 182 break;
  183. 183 }
  184. 184
  185. 185 }
  186. 186 else
  187. 187 {
  188. 188 MessageBox.Show("请选择功能码!");
  189. 189 }
  190. 190 port.Close();
  191. 191 }
  192. 192 catch (Exception ex)
  193. 193 {
  194. 194
  195. 195 MessageBox.Show(ex.Message);
  196. 196 }
  197. 197 }
  198. 198 private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
  199. 199 {
  200. 200 if (comboBox1.SelectedIndex >= 4)
  201. 201 {
  202. 202 groupBox2.Enabled = true;
  203. 203 groupBox1.Enabled = false;
  204. 204 }
  205. 205 else
  206. 206 {
  207. 207 groupBox1.Enabled = true;
  208. 208 groupBox2.Enabled = false;
  209. 209 }
  210. 210 comboBox1.Invoke(new Action(() => { functionCode = comboBox1.SelectedItem.ToString(); }));
  211. 211 }
  212. 212
  213. 213 /// <summary>
  214. 214 /// 初始化读参数
  215. 215 /// </summary>
  216. 216 private void SetReadParameters()
  217. 217 {
  218. 218 if (txt_startAddr1.Text == "" || txt_slave1.Text == "" || txt_length.Text == "")
  219. 219 {
  220. 220 MessageBox.Show("请填写读参数!");
  221. 221 }
  222. 222 else
  223. 223 {
  224. 224 slaveAddress = byte.Parse(txt_slave1.Text);
  225. 225 startAddress = ushort.Parse(txt_startAddr1.Text);
  226. 226 numberOfPoints = ushort.Parse(txt_length.Text);
  227. 227 }
  228. 228 }
  229. 229 /// <summary>
  230. 230 /// 初始化写参数
  231. 231 /// </summary>
  232. 232 private void SetWriteParametes()
  233. 233 {
  234. 234 if (txt_startAddr2.Text == "" || txt_slave2.Text == "" || txt_data.Text == "")
  235. 235 {
  236. 236 MessageBox.Show("请填写写参数!");
  237. 237 }
  238. 238 else
  239. 239 {
  240. 240 slaveAddress = byte.Parse(txt_slave2.Text);
  241. 241 startAddress = ushort.Parse(txt_startAddr2.Text);
  242. 242 //判断是否写线圈
  243. 243 if (comboBox1.SelectedIndex == 4 || comboBox1.SelectedIndex == 6)
  244. 244 {
  245. 245 string[] strarr = txt_data.Text.Split(' ');
  246. 246 coilsBuffer = new bool[strarr.Length];
  247. 247 //转化为bool数组
  248. 248 for (int i = 0; i < strarr.Length; i++)
  249. 249 {
  250. 250 // strarr[i] == "0" ? coilsBuffer[i] = true : coilsBuffer[i] = false;
  251. 251 if (strarr[i] == "0")
  252. 252 {
  253. 253 coilsBuffer[i] = false;
  254. 254 }
  255. 255 else
  256. 256 {
  257. 257 coilsBuffer[i] = true;
  258. 258 }
  259. 259 }
  260. 260 }
  261. 261 else
  262. 262 {
  263. 263 //转化ushort数组
  264. 264 string[] strarr = txt_data.Text.Split(' ');
  265. 265 registerBuffer = new ushort[strarr.Length];
  266. 266 for (int i = 0; i < strarr.Length; i++)
  267. 267 {
  268. 268 registerBuffer[i] = ushort.Parse(strarr[i]);
  269. 269 }
  270. 270 }
  271. 271 }
  272. 272 }
  273. 273
  274. 274 /// <summary>
  275. 275 /// 清除文本
  276. 276 /// </summary>
  277. 277 /// <param name="sender"></param>
  278. 278 /// <param name="e"></param>
  279. 279 private void button2_Click(object sender, EventArgs e)
  280. 280 {
  281. 281 richTextBox1.Clear();
  282. 282 }
  283. 283 /// <summary>
  284. 284 /// SetMessage
  285. 285 /// </summary>
  286. 286 /// <param name="msg"></param>
  287. 287 public void SetMsg(string msg)
  288. 288 {
  289. 289 richTextBox1.Invoke(new Action(() => { richTextBox1.AppendText(msg); }));
  290. 290 }
  291. 291
  292. 292 }
  293. 293 }
View Code

接下来开始测试

在这里 因为要用到串口,而我的笔记本没有串口,所以需要借助一个工具

Virtual Serial Port Dirver 虚拟串口工具

链接:https://pan.baidu.com/s/1opGre3GS-HWFoA_dP9qYYg

提取码:2afu 

借用这个工具我们添加两个虚拟串口 COM1和COM2 点击Add Virtual Pair 添加

设置Modbus Slave,选择连接方式为串口,选择对应端口,模式选择RTU,建立连接

 

接下来运行我们自己的Modbus RTU Master

设置串口参数(波特率,数据位,奇偶校验,停止位)要与Slave的串口参数一致

我们测试 功能码 0x01 读一组线圈

测试完成,数据正常,其他的功能码经测试数据正常,有兴趣的可以自行测试

到此为止,Modbus的学习到此告一段落

以上都为我自行学习并实现,如有错误之处,望大家不吝赐教,感谢(抱拳~)

 

程序源代码:

链接:https://pan.baidu.com/s/1mPAhRixLbsDb7h2ePENTRA
提取码:b5w6

 

原文链接:http://www.cnblogs.com/pandefu/p/10849823.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号