<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      XuGang

      記錄一個程序員的成長

       

      Modbus RTU 通信工具設計

       

      Modbus 是一個工業(yè)上常用的通訊協(xié)議、一種通訊約定。

      ModBus 協(xié)議是應用層報文傳輸協(xié)議(OSI 模型第7層),它定義了一個與通信層無關的協(xié)議數(shù)據(jù)單元(PDU),即PDU=功能碼+數(shù)據(jù)域。
      ModBus 協(xié)議能夠應用在不同類型的總線或網絡。對應不同的總線或網絡,Modbus 協(xié)議引入一些附加域映射成應用數(shù)據(jù)單元(ADU),即ADU=附加域+PDU。目前,Modbus 有下列三種通信方式:
      1.    以太網,對應的通信模式是Modbus TCP。
      2.    異步串行傳輸(各種介質如有線RS-232-/422/485/;光纖、無線等),對應的通信模式是 Modbus RTU 或 Modbus ASCII。
             Modbus 的ASCII、RTU 協(xié)議規(guī)定了消息、數(shù)據(jù)的結構、命令和應答的方式,數(shù)據(jù)通訊采用Maser/Slave方式。
      3.    高速令牌傳遞網絡,對應的通信模式是Modbus PLUS。

       

      Modbus 需要對數(shù)據(jù)進行校驗,串行協(xié)議中除有奇偶校驗外,ASCII 模式采用LRC 校驗;RTU 模式采用16位CRC 校驗;TCP 模式沒有額外規(guī)定校驗,因為TCP 是一個面向連接的可靠協(xié)議。

       

      Modbus 協(xié)議的應用中,最常用的是Modbus RTU 傳輸模式。

       

      RTU 傳輸模式 

      當設備使用RTU (Remote Terminal Unit) 模式在 Modbus  串行鏈路通信, 報文中每個8位字節(jié)含有兩個4位十六進制字符。這種模式的主要優(yōu)點是較高的數(shù)據(jù)密度,在相同的波特率下比ASCII 模式有更高的吞吐率。每個報文必須以連續(xù)的字符流傳送。 

       

      RTU 模式每個字節(jié) ( 11 位 ) 的格式為:

             編碼系統(tǒng):  8位二進制。 報文中每個8位的字節(jié)含有兩個4位十六進制字符(0–9, A–F)

       Bits per Byte:  1 起始位

                                 8 數(shù)據(jù)位, 首先發(fā)送最低有效位

                                 1 位作為奇偶校驗

                                 1 停止位

      偶校驗是要求的,其它模式 ( 奇校驗, 無校驗 ) 也可以使用。為了保證與其它產品的最大兼容性,同時支持無校驗模式是建議的。默認校驗模式模式 必須為偶校驗。注:使用無校驗要求2 個停止位。 

       

      字符的串行傳送方式:

      每個字符或字節(jié)均由此順序發(fā)送(從左到右):最低有效位 (LSB) . . . 最高有效位 (MSB)

      圖1:RTU 模式位序列 

       

      設備配置為奇校驗、偶校驗或無校驗都可以接受。如果無奇偶校驗,將傳送一個附加的停止位以填充字符幀:

      圖2:RTU 模式位序列 (無校驗的特殊情況)

       

      幀檢驗域:循環(huán)冗余校驗 (CRC)

      在RTU 模式包含一個對全部報文內容執(zhí)行的,基于循環(huán)冗余校驗 (CRC - Cyclical Redundancy Checking) 算法的錯誤檢驗域。

      CRC 域檢驗整個報文的內容。不管報文有無奇偶校驗,均執(zhí)行此檢驗。

      CRC 包含由兩個8位字節(jié)組成的一個16位值。  

      CRC 域作為報文的最后的域附加在報文之后。計算后,首先附加低字節(jié),然后是高字節(jié)。CRC 高字節(jié)為報文發(fā)送的最后一個子節(jié)。

      附加在報文后面的CRC 的值由發(fā)送設備計算。接收設備在接收報文時重新計算 CRC 的值,并將計算結果于實際接收到的CRC 值相比較。如果兩個值不相等,則為錯誤。

      CRC 的計算,開始對一個16位寄存器預裝全1。 然后將報文中的連續(xù)的8位子節(jié)對其進行后續(xù)的計算。只有字符中的8個數(shù)據(jù)位參與生成CRC 的運算,起始位,停止位和校驗位不參與 CRC 計算。

      CRC 的生成過程中, 每個 8–位字符與寄存器中的值異或。然后結果向最低有效位(LSB)方向移動(Shift) 1位,而最高有效位(MSB)位置充零。 然后提取并檢查 LSB:如果LSB 為1, 則寄存器中的值與一個固定的預置值異或;如果LSB 為 0, 則不進行異或操作。

      這個過程將重復直到執(zhí)行完8次移位。完成最后一次(第8次)移位及相關操作后,下一個8位字節(jié)與寄存器的當前值異或,然后又同上面描述過的一樣重復8次。當所有報文中子節(jié)都運算之后得到的寄存器中的最終值,就是CRC。

      當CRC 附加在報文之后時,首先附加低字節(jié),然后是高字節(jié)。

      CRC 算法如下:

      private bool CheckResponse(byte[] response)
      {
          //Perform a basic CRC check:
          byte[] CRC = new byte[2];
          GetCRC(response, ref CRC);
          if (CRC[0] == response[response.Length - 2] && CRC[1] == response[response.Length - 1])
          return true;
          else
          return false;
      }
      
      private void GetCRC(byte[] message, ref byte[] CRC)
      {
          //Function expects a modbus message of any length as well as a 2 byte CRC array in which to 
          //return the CRC values:
      
          ushort CRCFull = 0xFFFF;
          byte CRCHigh = 0xFF, CRCLow = 0xFF;
          char CRCLSB;
      
          for (int i = 0; i < (message.Length) - 2; i++)
          {
          CRCFull = (ushort)(CRCFull ^ message[i]);
      
          for (int j = 0; j < 8; j++)
          {
              CRCLSB = (char)(CRCFull & 0x0001);
              CRCFull = (ushort)((CRCFull >> 1) & 0x7FFF);
      
              if (CRCLSB == 1)
              CRCFull = (ushort)(CRCFull ^ 0xA001);
          }
          }
          CRC[1] = CRCHigh = (byte)((CRCFull >> 8) & 0xFF);
          CRC[0] = CRCLow = (byte)(CRCFull & 0xFF);
      }

       

      幀描述 (如下圖所示) :

      圖3:RTU 報文幀

      注意:Modbus  RTU 幀最大為256字節(jié)。

       

      下面是我為公司設計的一個 Modbus RTU 通信測試小工具,界面截圖如下:

      圖4:Modbus RTU 通信工具

       

      我的通用Modbus RTU 動態(tài)庫,modbus.cs 如下:

      modbus.cs
      using System;
      using System.Collections.Generic;
      using System.Text;
      using System.IO.Ports;
      using System.Threading;
      
      namespace SerialPort_Lib
      {
          public class modbus
          {
              private SerialPort sp = new SerialPort();
              public string modbusStatus;
      
              #region Constructor / Deconstructor
              public modbus()
              {
              }
              ~modbus()
              {
              }
              #endregion
      
              #region Open / Close Procedures
              public bool Open(string portName, int baudRate, int databits, Parity parity, StopBits stopBits)
              {
                  //Ensure port isn't already opened:
                  if (!sp.IsOpen)
                  {
                      //Assign desired settings to the serial port:
                      sp.PortName = portName;
                      sp.BaudRate = baudRate;
                      sp.DataBits = databits;
                      sp.Parity = parity;
                      sp.StopBits = stopBits;
                      //These timeouts are default and cannot be editted through the class at this point:
                      sp.ReadTimeout = -1;
                      sp.WriteTimeout = 10000;
      
                      try
                      {
                          sp.Open();
                      }
                      catch (Exception err)
                      {
                          modbusStatus = "Error opening " + portName + ": " + err.Message;
                          return false;
                      }
                      modbusStatus = portName + " opened successfully";
                      return true;
                  }
                  else
                  {
                      modbusStatus = portName + " already opened";
                      return false;
                  }
              }
              public bool Close()
              {
                  //Ensure port is opened before attempting to close:
                  if (sp.IsOpen)
                  {
                      try
                      {
                          sp.Close();
                      }
                      catch (Exception err)
                      {
                          modbusStatus = "Error closing " + sp.PortName + ": " + err.Message;
                          return false;
                      }
                      modbusStatus = sp.PortName + " closed successfully";
                      return true;
                  }
                  else
                  {
                      modbusStatus = sp.PortName + " is not open";
                      return false;
                  }
              }
              #endregion
      
              #region CRC Computation
              private void GetCRC(byte[] message, ref byte[] CRC)
              {
                  //Function expects a modbus message of any length as well as a 2 byte CRC array in which to 
                  //return the CRC values:
      
                  ushort CRCFull = 0xFFFF;
                  byte CRCHigh = 0xFF, CRCLow = 0xFF;
                  char CRCLSB;
      
                  for (int i = 0; i < (message.Length) - 2; i++)
                  {
                      CRCFull = (ushort)(CRCFull ^ message[i]);
      
                      for (int j = 0; j < 8; j++)
                      {
                          CRCLSB = (char)(CRCFull & 0x0001);
                          CRCFull = (ushort)((CRCFull >> 1) & 0x7FFF);
      
                          if (CRCLSB == 1)
                              CRCFull = (ushort)(CRCFull ^ 0xA001);
                      }
                  }
                  CRC[1] = CRCHigh = (byte)((CRCFull >> 8) & 0xFF);
                  CRC[0] = CRCLow = (byte)(CRCFull & 0xFF);
              }
              #endregion
      
              #region Build Message
              private void BuildMessage(byte address, byte type, ushort start, ushort registers, ref byte[] message)
              {
                  //Array to receive CRC bytes:
                  byte[] CRC = new byte[2];
      
                  message[0] = address;
                  message[1] = type;
                  message[2] = (byte)(start >> 8);
                  message[3] = (byte)start;
                  message[4] = (byte)(registers >> 8);
                  message[5] = (byte)registers;
      
                  GetCRC(message, ref CRC);
                  message[message.Length - 2] = CRC[0];
                  message[message.Length - 1] = CRC[1];
              }
              #endregion
      
              #region Check Response
              private bool CheckResponse(byte[] response)
              {
                  //Perform a basic CRC check:
                  byte[] CRC = new byte[2];
                  GetCRC(response, ref CRC);
                  if (CRC[0] == response[response.Length - 2] && CRC[1] == response[response.Length - 1])
                      return true;
                  else
                      return false;
              }
              #endregion
      
              #region Get Response
              private void GetResponse(ref byte[] response)
              {
                  //There is a bug in .Net 2.0 DataReceived Event that prevents people from using this
                  //event as an interrupt to handle data (it doesn't fire all of the time).  Therefore
                  //we have to use the ReadByte command for a fixed length as it's been shown to be reliable.
                  for (int i = 0; i < response.Length; i++)
                  {
                      response[i] = (byte)(sp.ReadByte());
                  }
              }
              #endregion
      
              #region GetModbusData 獲得接收數(shù)據(jù)
              public bool GetModbusData(ref byte[] values)
              {
                  //Ensure port is open:
                  if (sp.IsOpen)
                  {
                      // 等待線程進入 
                      //Monitor.Enter(sp);
      
                      //Clear in/out buffers:
                      //sp.DiscardOutBuffer();
                      //sp.DiscardInBuffer();
      
                      //Message is 1 addr + 1 type + N Data + 2 CRC
                          
                      try
                      {
                          //GetResponse(ref readBuffer);
                          //string str = readBuffer.ToString();
      
                          int count = sp.BytesToRead;
                          if (count > 0)
                          {
                              byte[] readBuffer = new byte[count];
      
                              GetResponse(ref readBuffer);
      
                              //   readData = new byte[29];
                              //   Array.Copy(readBuffer, readData, readData.Length);
      
                              // CRC 驗證
                              if (CheckResponse(readBuffer))
                              {
                                  //顯示輸入數(shù)據(jù)
                                  values = readBuffer;
      
                                  modbusStatus = "Write successful";
      
                                  sp.DiscardInBuffer();
      
                                  //values = System.Text.Encoding.ASCII.GetString(readData);
                                  return true;
                              }
                              else
                              {
                                  modbusStatus = "CRC error";
      
                                  sp.DiscardInBuffer();
      
                                  return false;
                              }
                          }
                          else return false;
                      }
                      catch (Exception err)
                      {
                          modbusStatus = "Error in write event: " + err.Message;
      
                          sp.DiscardInBuffer();
      
                          return false;
                      }
      
                      //finally
                      //{
                          // 通知其它對象
                          //Monitor.Pulse(sp);
                          // 釋放對象鎖 
                          //Monitor.Exit(sp);
                      //}
                  }
                  else
                  {
                      modbusStatus = "Serial port not open";
                      return false;
                  }
              }
              #endregion
      
              #region SendModbusData 打包發(fā)送數(shù)據(jù)
              public bool SendModbusData(ref byte[] values)
              {
                  //Ensure port is open:
                  if (sp.IsOpen)
                  {
                      //Clear in/out buffers:
                      sp.DiscardOutBuffer();
                      sp.DiscardInBuffer();
      
                      //Function 3 response buffer:
                      byte[] response = new byte[values.Length + 2];
                      Array.Copy(values, response, values.Length);
      
                      //BuildMessage(address, (byte)3, start, registers, ref message);
      
                      //打包帶有 CRC 驗證的modbus 數(shù)據(jù)包:
                      byte[] CRC = new byte[2];
                      GetCRC(response, ref CRC);
                      response[response.Length - 2] = CRC[0];
                      response[response.Length - 1] = CRC[1];
      
                      values = response; //返回帶有 CRC 驗證的modbus 數(shù)據(jù)包
      
                      //Send modbus message to Serial Port:
                      try
                      {
                          sp.Write(response, 0, response.Length);
                          //GetResponse(ref response);
                          return true;
                      }
                      catch (Exception err)
                      {
                          modbusStatus = "Error in read event: " + err.Message;
                          return false;
                      }
                      //Evaluate message:
                      //if (CheckResponse(response))
                      //{
                      //    //Return requested register values:
                      //    for (int i = 0; i < (response.Length - 5) / 2; i++)
                      //    {
                      //        values[i] = response[2 * i + 3];
                      //        values[i] <<= 8;
                      //        values[i] += response[2 * i + 4];
                      //    }
                      //    modbusStatus = "Read successful";
                      //    return true;
                      //}
                      //else
                      //{
                      //    modbusStatus = "CRC error";
                      //    return false;
                      //}
                  }
                  else
                  {
                      modbusStatus = "Serial port not open";
                      return false;
                  }
      
              }
              #endregion
      
          }
      }
       

      調用的主要代碼如下:

      modbus類的winform調用代碼
      public partial class FormConfig : Form,IModbusData
      {
          //業(yè)務處理類
          B_ModbusData ModbusDataBLL = new B_ModbusData();
      
          modbus mb = new modbus();
          //SerialPort sp = new SerialPort();
          System.Timers.Timer timer = new System.Timers.Timer();
      
          public FormConfig()
          {
              InitializeComponent();
             
              timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
          }
      
          #region Timer Elapsed 事件處理程序
          bool runEnd = true;
          void timer_Elapsed(object sender, ElapsedEventArgs e)
          {
              if (runEnd == true)
              {
                  runEnd = false;
                  PollFunction();
                  runEnd = true;
              }
          }
      
          //定時器調用方法
          private void PollFunction()
          {
              byte[] values = null;
              try
              {
                  mb.GetModbusData(ref values);
                  //while (!mb.SendFc3(Convert.ToByte(txtSlaveID.Text), pollStart, pollLength, ref values)) ;
              }
              catch (Exception err)
              {
                  DoGUIStatus("Error in modbus read: " + err.Message);
              }
      
              if (values != null)
              {
                  //業(yè)務處理
                  byte[] sendData = ModbusDataProcess(values);
              }
          }
          #endregion
      
          #region IModbusData 接口成員處理
          public byte[] ModbusDataProcess(byte[] _data)
          {
             byte[] sendData = ModbusDataBLL.ModbusDataProcess(_data);
      
             // CRC驗證,并打包發(fā)送數(shù)據(jù)。
             mb.SendModbusData(ref sendData);
      
             return sendData;
          }
          #endregion
      }
       

      其實,三步就能成功調用:

      modbus mb = new modbus();
      mb.GetModbusData(ref values); // 從串口設備獲得數(shù)據(jù)。
      byte[] sendData = ModbusDataBLL.ModbusDataProcess(values); // 你的業(yè)務處理,并產生最終返回數(shù)據(jù)。
      mb.SendModbusData(ref sendData); // CRC驗證,并打包發(fā)送數(shù)據(jù)。


      主要代碼已全部提供,由于工作原因暫不提供完整工具源代碼,見諒!

      (完)

       

      posted on 2012-12-13 10:09  鋼鋼  閱讀(21366)  評論(7)    收藏  舉報

      導航

      主站蜘蛛池模板: 亚洲国产在一区二区三区| 国产精品中文字幕二区| 久久久久久人妻一区精品| 亚洲综合伊人久久大杳蕉| 国产午夜亚洲精品国产成人| 国产漂亮白嫩美女在线观看| 成人网站免费观看永久视频下载| 99精品国产成人一区二区| 亚洲色最新高清AV网站| 欧洲lv尺码大精品久久久| 国产精品午夜福利视频| 国产毛1卡2卡3卡4卡免费观看| 国语对白做受xxxxx在线中国| 国产三级国产精品国产专| 久草热在线视频免费播放| 综合色久七七综合尤物| 扶余县| 亚洲国产av剧一区二区三区| 九九热在线精品免费视频| 亚洲精品岛国片在线观看| 东京热高清无码精品| 动漫av网站免费观看| 日本熟妇浓毛hdsex| 亚洲综合精品第一页| A级日本乱理伦片免费入口| 国产精品国产三级国快看| 天天做天天爱夜夜爽| 少妇撒尿一区二区在线视频| 国产亚洲精品AA片在线爽| 国产综合久久99久久| 99精品人妻少妇一区| 好爽好紧好大的免费视频| 亚洲精品香蕉一区二区| 无码日韩人妻精品久久| 日本久久99成人网站| 财经| 视频一区视频二区亚洲视频| 在线看无码的免费网站| 社旗县| 亚洲 另类 小说 国产精品无码| 久久99国内精品自在现线|