• 基于Modbus的C#串口调试开发


    说明:本文主要研究的是使用C# WinForm开发的串口调试软件(其中包含Modbus协议相关操作)。Modbus相关协议可以查阅百度文库等,可参考: 《http://wenku.baidu.com/link?url=J-QZeQVLfvfZh7_lh8Qf0MdwANZuVjEoTqox6zJYrSnKyfgES2RTb_bjC5ZTn8-xgsuUAyiELRYVA3-3FBkBGywWhQ9YGoavJOzwB0IxTyK 》。

      (1)先测试串口设置,发送和接收数据。

      (2)发送modbus的命令帧数据和使用DataReceived接收缓冲区的数据。

    一、简单的串口调试工具

      下图为串口调试工具的界面,主要包括串口基本设置,功能操作,状态框以及发送接收框。由于这里只是简单的初始化数据,所以当需要发送数据的时候需要点击“串口检测”,来测试当前可用的串口,然后输入需要发送的数据,最后点击“发送数据”(由于测试需要,让发送什么数据就返回什么数据,这里的底层硬件做了短接处理,使用短接貌P30-P31,具体操作可以自行百度)

      

      

      1.1 发送数据操作

        (1)点击 串口检测

        (2)输入发送数据

        (3)点击 发送数据

        下面开始时具体代码:

        #1 软件打开时候初始化操作:Form1_Load(),主要初始化操作串口设置的下拉列表。

     1 private void Form1_Load(object sender, EventArgs e)
     2         {
     3             
     4             //设置窗口大小固定
     5             this.MaximumSize = this.Size;
     6             this.MinimumSize = this.Size;
     7 
     8             //1、设置串口下拉列表
     9             for ( int i = 0; i < 10; i++ ) 
    10             {
    11                 cbxCOMPort.Items.Add("COM" + (i + 1).ToString());
    12             }
    13             cbxCOMPort.SelectedIndex = 2;//默认选项
    14 
    15 
    16             //2、设置常用波特率
    17             int bt = 300;
    18             for (int i = 0; i < 8; i++)
    19             {
    20                 cbxBaudRate.Items.Add(bt.ToString());
    21                 bt *= 2;
    22             }
    23 
    24             cbxBaudRate.Items.Add("38400");
    25             cbxBaudRate.Items.Add("43000");
    26             cbxBaudRate.Items.Add("56000"); 
    27             cbxBaudRate.Items.Add("57600");
    28             cbxBaudRate.Items.Add("115200");
    29             cbxBaudRate.SelectedIndex = 5;
    30 
    31 
    32             //3、列出停止位
    33             cbxStopBits.Items.Add("0");
    34             cbxStopBits.Items.Add("1");
    35             cbxStopBits.Items.Add("1.5");
    36             cbxStopBits.Items.Add("2");
    37             cbxStopBits.SelectedIndex = 1;
    38 
    39             //4、设置奇偶检验
    40             cbxParity.Items.Add("");
    41             cbxParity.Items.Add("奇校验");
    42             cbxParity.Items.Add("偶校验");
    43             cbxParity.SelectedIndex = 0;
    44 
    45             //5、设置数据位
    46             cbxDataBits.Items.Add("8");
    47             cbxDataBits.Items.Add("7");
    48             cbxDataBits.Items.Add("6");
    49             cbxDataBits.Items.Add("5");
    50             cbxDataBits.SelectedIndex = 0;
    51 
    52 
    53         }
    private void Form1_Load(object sender, EventArgs e)

        

        #2 检查串口基本设置的参数:CheckPortSetting(),

     1 /// <summary>
     2         /// 【检测端口设置】
     3         /// </summary>
     4         /// <returns></returns>
     5         private bool CheckPortSetting()
     6         {
     7             //检测端口设置
     8             if (cbxCOMPort.Text.Trim() == "" || cbxBaudRate.Text.Trim() == "" || cbxStopBits.Text.Trim() == "" || cbxParity.Text.Trim() == "" || cbxDataBits.Text.Trim() == "")
     9                 return false;
    10             return true;
    11         }
    private bool CheckPortSetting()

        #3 设置串口属性,创建SerialPort对象  

     1 /// <summary>
     2         /// 【设置串口属性】
     3         /// </summary>
     4         private void SetPortProperty()
     5         {
     6             //1、设置串口的属性
     7             sp = new SerialPort();
     8 
     9             sp.ReceivedBytesThreshold = 1;//获取DataReceived事件发生前内部缓存区字节数
    10             sp.DataReceived += new SerialDataReceivedEventHandler(serialPort_DataReceived);//设置委托
    11 
    12             sp.PortName = cbxCOMPort.Text.Trim();
    13 
    14             //2、设置波特率
    15             sp.BaudRate =  Convert.ToInt32( cbxBaudRate.Text.Trim());
    16 
    17             //3、设置停止位
    18             float f = Convert.ToSingle( cbxStopBits.Text.Trim());
    19 
    20             if (f == 0) 
    21             {
    22                 sp.StopBits = StopBits.None;//表示不使用停止位
    23             }
    24             else if (f == 1.5)
    25             {
    26                 sp.StopBits = StopBits.OnePointFive;//使用1.5个停止位
    27             }
    28             else if (f == 2)
    29             {
    30                 sp.StopBits = StopBits.Two;//表示使用两个停止位
    31             }
    32             else
    33             {
    34                 sp.StopBits = StopBits.One;//默认使用一个停止位
    35             }
    36 
    37             //4、设置数据位
    38             sp.DataBits = Convert.ToInt16(cbxDataBits.Text.Trim());
    39 
    40             //5、设置奇偶校验位
    41             string s = cbxParity.Text.Trim();
    42             if (s.CompareTo("") == 0) 
    43             {
    44                 sp.Parity = Parity.None;//不发生奇偶校验检查
    45             }
    46             else if (s.CompareTo("奇校验") == 0)
    47             {
    48                 sp.Parity = Parity.Odd;//设置奇校验
    49             }
    50             else if (s.CompareTo("偶校验") == 0)
    51             {
    52                 sp.Parity = Parity.Even;//设置偶检验
    53             }
    54             else
    55             {
    56                 sp.Parity = Parity.None;
    57             }
    58 
    59             //6、设置超时读取时间
    60             sp.ReadTimeout = -1;
    61 
    62             //7、打开串口
    63             try
    64             {
    65                 sp.Open();
    66                 isOpen = true;
    67             }
    68             catch(Exception)
    69             {
    70                 lblStatus.Text = "打开串口错误!";
    71             }
    72 
    73         }
    private void SetPortProperty()

         

        #4 “发送数据”按钮点击事件:btnSend_Click(), 在发送数据需要进行,#2,#3验证,然后开始通过串口对象写入数据

     1     /// <summary>
     2         /// 【发送数据】
     3         /// </summary>
     4         /// <param name="sender"></param>
     5         /// <param name="e"></param>
     6         private void btnSend_Click(object sender, EventArgs e)
     7         {
     8             //发送串口数据
     9 
    10             //1、检查串口设置
    11             if (!CheckPortSetting()) 
    12             {
    13                 MessageBox.Show("串口未设置!", "错误提示");
    14                 return;
    15             }
    16 
    17             //2、检查发送数据是否为空
    18             if(tbxSendData.Text.Trim() == ""){
    19                  MessageBox.Show("发送数据不能为空");
    20                  return;
    21             }
    22 
    23             //3、设置
    24             if (!isSetProperty) 
    25             {
    26                 SetPortProperty();
    27                 isSetProperty = true;
    28             }
    29 
    30             //4、写串口数据
    31             if (isOpen)
    32             {
    33                 //写出口数据
    34                 try
    35                 {
    36                     sp.Write(tbxSendData.Text);
    37                     tbxStatus.Text = "发送成功!";
    38                     
    39 
    40                     tbxRecvData.Text += sp.ReadLine();//读取发送的数据                    
    41 
    42                 }
    43                 catch
    44                 {
    45                     tbxStatus.Text = "发送数据错误";
    46                 }
    47             }
    48             else
    49             {
    50                 MessageBox.Show("串口未打开", "错误提示");
    51             }
    52 
    53             
    54         }                        

      1.2 接受数据操作

        接收数据和发送数据有点类似   

     1 /// <summary>
     2         /// 【读取数据】
     3         /// </summary>
     4         /// <param name="sender"></param>
     5         /// <param name="e"></param>
     6         private void btnRecv_Click(object sender, EventArgs e)
     7         {
     8             if(isOpen) 
     9             { 
    10                 try  
    11                 {
    12                     //读取串口数据
    13   
    14                     tbxRecvData.Text += sp.ReadLine()+"
    "15                 }  
    16                 catch(Exception) 
    17                 {  
    18                     lblStatus.Text = "读取串口时发生错误!"19                     return20                 } 
    21             } 
    22             else 
    23             {  
    24               MessageBox.Show("串口未打开!""错误提示"); 
    25               return26                 
    27             } 
    28         }

      最后附上该窗体的后台代码:Form1.cs 

      1 using System;
      2 using System.Collections.Generic;
      3 using System.ComponentModel;
      4 using System.Data;
      5 using System.Drawing;
      6 using System.IO.Ports;
      7 using System.Linq;
      8 using System.Text;
      9 using System.Threading.Tasks;
     10 using System.Windows.Forms;
     11 
     12 
     13 namespace 串口调试
     14 {
     15     public partial class Form1 : Form
     16     {
     17         SerialPort sp = null;
     18 
     19         bool isOpen = false;//是否打开
     20 
     21         bool isSetProperty = false;//是否通过串口设置
     22 
     23 
     24         public Form1()
     25         {
     26             InitializeComponent();
     27         }
     28 
     29         private void Form1_Load(object sender, EventArgs e)
     30         {
     31             
     32             //设置窗口大小固定
     33             this.MaximumSize = this.Size;
     34             this.MinimumSize = this.Size;
     35 
     36             //1、设置串口下拉列表
     37             for ( int i = 0; i < 10; i++ ) 
     38             {
     39                 cbxCOMPort.Items.Add("COM" + (i + 1).ToString());
     40             }
     41             cbxCOMPort.SelectedIndex = 2;//默认选项
     42 
     43 
     44             //2、设置常用波特率
     45             int bt = 300;
     46             for (int i = 0; i < 8; i++)
     47             {
     48                 cbxBaudRate.Items.Add(bt.ToString());
     49                 bt *= 2;
     50             }
     51 
     52             cbxBaudRate.Items.Add("38400");
     53             cbxBaudRate.Items.Add("43000");
     54             cbxBaudRate.Items.Add("56000"); 
     55             cbxBaudRate.Items.Add("57600");
     56             cbxBaudRate.Items.Add("115200");
     57             cbxBaudRate.SelectedIndex = 5;
     58 
     59 
     60             //3、列出停止位
     61             cbxStopBits.Items.Add("0");
     62             cbxStopBits.Items.Add("1");
     63             cbxStopBits.Items.Add("1.5");
     64             cbxStopBits.Items.Add("2");
     65             cbxStopBits.SelectedIndex = 1;
     66 
     67             //4、设置奇偶检验
     68             cbxParity.Items.Add("");
     69             cbxParity.Items.Add("奇校验");
     70             cbxParity.Items.Add("偶校验");
     71             cbxParity.SelectedIndex = 0;
     72 
     73             //5、设置数据位
     74             cbxDataBits.Items.Add("8");
     75             cbxDataBits.Items.Add("7");
     76             cbxDataBits.Items.Add("6");
     77             cbxDataBits.Items.Add("5");
     78             cbxDataBits.SelectedIndex = 0;
     79 
     80 
     81         }
     82 
     83 
     84         /// <summary>
     85         /// 【串口检测按钮】
     86         /// </summary>
     87         /// <param name="sender"></param>
     88         /// <param name="e"></param>
     89         private void btnCheckCOM_Click(object sender, EventArgs e)
     90         {
     91             //1、检测哪些端口可用
     92             cbxCOMPort.Items.Clear();
     93             cbxCOMPort.Text = "";
     94             
     95             lblStatus.Text = "执行中...";
     96             string str = "";
     97             for (int i = 0; i < 10; i++)
     98             {
     99                 try
    100                 {
    101             ////把所有可能的串口都测试一遍,打开关闭操作,只有可用的串口才可会放到下拉列表中
    102                     SerialPort sp = new SerialPort("COM" + (i + 1).ToString());
    103                     sp.Open();
    104                     sp.Close();
    105                     cbxCOMPort.Items.Add("COM" + (i + 1).ToString());
    106                 }
    107                 catch
    108                 {
    109                     str += "COM" + (i + 1).ToString() + "";
    110                     continue;
    111                 }
    112             }
    113         
    114         //如果当前下拉列表有可用的串口,则默认选择第一个
    115             if(cbxCOMPort.Items.Count > 0)
    116                 cbxCOMPort.SelectedIndex = 0;
    117             lblStatus.Text = "完成";
    118             tbxStatus.Text = str;
    119         }
    120 
    121 
    122         /// <summary>
    123         /// 【检测端口设置】
    124         /// </summary>
    125         /// <returns></returns>
    126         private bool CheckPortSetting()
    127         {
    128             //检测端口设置
    129             if (cbxCOMPort.Text.Trim() == "" || cbxBaudRate.Text.Trim() == "" || cbxStopBits.Text.Trim() == "" || cbxParity.Text.Trim() == "" || cbxDataBits.Text.Trim() == "")
    130                 return false;
    131             return true;
    132         }
    133 
    134         /// <summary>
    135         /// 【检测发送数据是否为空】
    136         /// </summary>
    137         /// <returns></returns>
    138         private bool CheckSendData()
    139         {
    140             if (tbxSendData.Text.Trim() == "")
    141                 return false;
    142             return true;
    143         }
    144 
    145 
    146         /// <summary>
    147         /// 【设置串口属性】
    148         /// </summary>
    149         private void SetPortProperty()
    150         {
    151             //1、设置串口的属性
    152             sp = new SerialPort();
    153 
    154             sp.PortName = cbxCOMPort.Text.Trim();
    155 
    156             //2、设置波特率
    157             sp.BaudRate =  Convert.ToInt32( cbxBaudRate.Text.Trim());
    158 
    159             //3、设置停止位
    160             float f = Convert.ToSingle( cbxStopBits.Text.Trim());
    161 
    162             if (f == 0) 
    163             {
    164                 sp.StopBits = StopBits.None;//表示不使用停止位
    165             }
    166             else if (f == 1.5)
    167             {
    168                 sp.StopBits = StopBits.OnePointFive;//使用1.5个停止位
    169             }
    170             else if (f == 2)
    171             {
    172                 sp.StopBits = StopBits.Two;//表示使用两个停止位
    173             }
    174             else
    175             {
    176                 sp.StopBits = StopBits.One;//默认使用一个停止位
    177             }
    178 
    179             //4、设置数据位
    180             sp.DataBits = Convert.ToInt16(cbxDataBits.Text.Trim());
    181 
    182             //5、设置奇偶校验位
    183             string s = cbxParity.Text.Trim();
    184             if (s.CompareTo("") == 0) 
    185             {
    186                 sp.Parity = Parity.None;//不发生奇偶校验检查
    187             }
    188             else if (s.CompareTo("奇校验") == 0)
    189             {
    190                 sp.Parity = Parity.Odd;//设置奇校验
    191             }
    192             else if (s.CompareTo("偶校验") == 0)
    193             {
    194                 sp.Parity = Parity.Even;//设置偶检验
    195             }
    196             else
    197             {
    198                 sp.Parity = Parity.None;
    199             }
    200 
    201             //6、设置超时读取时间
    202             sp.ReadTimeout = -1;
    203 
    204             //7、打开串口
    205             try
    206             {
    207                 sp.Open();
    208                 isOpen = true;
    209             }
    210             catch(Exception)
    211             {
    212                 lblStatus.Text = "打开串口错误!";
    213             }
    214 
    215         }
    216 
    217        
    218 
    219         /// <summary>
    220         /// 【发送数据】
    221         /// </summary>
    222         /// <param name="sender"></param>
    223         /// <param name="e"></param>
    224         private void btnSend_Click(object sender, EventArgs e)
    225         {
    226             //发送串口数据
    227 
    228             //1、检查串口设置
    229             if (!CheckPortSetting()) 
    230             {
    231                 MessageBox.Show("串口未设置!", "错误提示");
    232                 return;
    233             }
    234 
    235             2、检查发送数据是否为空
    236             if (!CheckSendData())
    237             {
    238                 MessageBox.Show("请输入要发送的数据!", "错误提示");
    239                 return;
    240             }
    241 
    242             //3、设置
    243             if (!isSetProperty) 
    244             {
    245                 SetPortProperty();
    246                 isSetProperty = true;
    247             }
    248 
    249             //4、写串口数据
    250             if (isOpen)
    251             {
    252                 //写出口数据
    253                 try
    254                 {
    255                     sp.Write(tbxSendData.Text);
    256                     tbxStatus.Text = "发送成功!";
    257 
    258                     tbxRecvData.Text += sp.ReadLine()+"
    ";
    259                 }
    260                 catch
    261                 {
    262                     tbxStatus.Text = "发送数据错误";
    263                 }
    264             }
    265             else
    266             {
    267                 MessageBox.Show("串口未打开", "错误提示");
    268             }
    269 
    270             
    271         }
    272 
    273         /// <summary>
    274         /// 【读取数据】
    275         /// </summary>
    276         /// <param name="sender"></param>
    277         /// <param name="e"></param>
    278         private void btnRecv_Click(object sender, EventArgs e)
    279         {
    280             if(isOpen) 
    281             { 
    282                 try  
    283                 {
    284                     //读取串口数据
    285   
    286                     tbxRecvData.Text += sp.ReadLine()+"
    "287                 }  
    288                 catch(Exception) 
    289                 {  
    290                     lblStatus.Text = "读取串口时发生错误!"291                     return292                 } 
    293             } 
    294             else 
    295             {  
    296               MessageBox.Show("串口未打开!""错误提示"); 
    297               return298                 
    299             } 
    300         }
    301  
    302     }
    303 }
    Form1.cs

    二、基于Modbus协议的数据发送和接收

       这里主要是在前面的基础上,把发送和接收的数据进行格式化(符合Modbus的数据帧格式),如下左图所示,右图为加入Modbus协议的窗体,主要添加了命令帧的输入框组:

      

      

      2.1 获取字节的的高位和低位:WORD_LO()、WORD_HI()   

     1 /// <summary>
     2         /// 【获取低位字节】
     3         /// </summary>
     4         /// <param name="crcCLo"></param>
     5         /// <returns></returns>
     6         public static byte WORD_LO(ushort crcCLo)
     7         {
     8             crcCLo = (ushort)(crcCLo & 0X00FF);
     9             return (byte)crcCLo;
    10         }
    11 
    12         /// <summary>
    13         /// 【获取高位字节】
    14         /// </summary>
    15         /// <param name="crcHI"></param>
    16         /// <returns></returns>
    17         public static byte WORD_HI(ushort crcHI)
    18         {
    19             crcHI = (ushort)(crcHI >> 8 & 0X00FF);
    20             return (byte)crcHI;
    21         }
    WORD_LO() WORD_HI()

      

      2.2 CRC高位表和低位表 

     1  #region CRC高位表 byte[] _auchCRCHi
     2         private static readonly byte[] _auchCRCHi = new byte[]//crc高位表
     3         {
     4             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 
     5             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 
     6             0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 
     7             0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 
     8             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 
     9             0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 
    10             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 
    11             0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 
    12             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 
    13             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 
    14             0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 
    15             0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 
    16             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 
    17             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 
    18             0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 
    19             0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 
    20             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 
    21             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 
    22             0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 
    23             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 
    24             0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 
    25             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 
    26             0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 
    27             0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 
    28             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 
    29             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
    30         };
    31         #endregion
    32 
    33         #region CRC低位表 byte[] _auchCRCLo
    34         private static readonly byte[] _auchCRCLo = new byte[]//crc低位表
    35         {
    36             0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 
    37             0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 
    38             0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 
    39             0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 
    40             0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4, 
    41             0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, 
    42             0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 
    43             0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 
    44             0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 
    45             0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 
    46             0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 
    47             0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, 
    48             0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 
    49             0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 
    50             0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 
    51             0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 
    52             0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 
    53             0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5, 
    54             0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 
    55             0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 
    56             0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 
    57             0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 
    58             0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 
    59             0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, 
    60             0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 
    61             0x43, 0x83, 0x41, 0x81, 0x80, 0x40
    62         };
    63         #endregion
    CRC高低位表

      2.3 CRC校验方法:CRC16()

     1 /// <summary>
     2         /// 【CRC校验】
     3         /// </summary>
     4         /// <param name="buffer">命令帧合适前6字节</param>
     5         /// <param name="Sset">开始位</param>
     6         /// <param name="Eset">结束位</param>
     7         /// <returns>CRC校验码</returns>
     8         public static ushort CRC16(Byte[] buffer, int Sset, int Eset)
     9         {
    10             byte crcHi = 0xff;  // 高位初始化
    11 
    12             byte crcLo = 0xff;  // 低位初始化
    13 
    14             for (int i = Sset; i <= Eset; i++)
    15             {
    16                 int crcIndex = crcHi ^ buffer[i]; //查找crc表值
    17 
    18                 crcHi = (byte)(crcLo ^ _auchCRCHi[crcIndex]);
    19                 crcLo = _auchCRCLo[crcIndex];
    20             }
    21 
    22             return (ushort)(crcHi << 8 | crcLo);
    23         }
    public static ushort CRC16(Byte[] buffer, int Sset, int Eset)

       

      2.4 获取数据帧,把需要发送的数据格式化成Modbus协议数据帧

       

     1 /// <summary>
     2         /// 【获取读数据命令,返回命令帧】
     3         /// </summary>
     4         /// <param name="mdaddr">地址码</param>
     5         /// <param name="R_CMD">功能码</param>
     6         /// <param name="min_reg">寄存器地址</param>
     7         /// <param name="data_len">寄存器个数</param>
     8         /// <param name="R_CMD_LEN">命令长度</param>
     9         /// <returns></returns>
    10         public byte[] GetReadFrame(byte mdaddr, byte R_CMD, ushort min_reg, ushort data_len, int R_CMD_LEN)
    11         {
    12             //主机命令帧格式
    13             //  字节    功能描述            例子
    14             //
    15             //  1         地址码             0x01
    16             //  2         功能码             0x03
    17             //  3         寄存器地址高       0x00
    18             //  4         寄存器地址低       0x00
    19             //  5         寄存器个数高       0x00
    20             //  6         寄存器个数低       0x02
    21             //  7         CRC检验码低        0xC4
    22             //  8         CRC校验码高        0x0B
    23 
    24             ushort crc;
    25             byte[] message = new byte[8];
    26 
    27             //设置模块号
    28             message[0] = mdaddr;
    29             //设置命令字
    30             message[1] = R_CMD;
    31 
    32             //设置开始寄存器
    33             message[2] = WORD_HI(min_reg);
    34             message[3] = WORD_LO(min_reg);
    35 
    36             //设置数据长度
    37             message[4] = WORD_HI(data_len);
    38             message[5] = WORD_LO(data_len);
    39 
    40             //设置 CRC
    41             crc = CRC16(message, 0, R_CMD_LEN - 3);
    42 
    43             message[6] = WORD_HI(crc);//CRC校验码高位
    44             message[7] = WORD_LO(crc);//CRC校验码低位
    45 
    46 
    47             return message;
    48         }

       2.6 对于DataReceived的使用

        #1 设置委托和方法

           1 private delegate void myDelegate(byte[] readBuffer);     

    1 /// <summary>
    2         /// 【显示接收返回的数据】
    3         /// </summary>
    4         /// <param name="resbuffer"></param>
    5         public void ShowRst(byte[] resbuffer) 
    6         {
    7             MyModbus modbus = new MyModbus();
    8             tbxRecvData.Text += "Recv:" + modbus.SetText(resbuffer) + "
    ";
    9         }

         #2 设置属性:ReceivedBytesThreshold = 1

    1  //1、设置串口的属性
    2             sp = new SerialPort();
    3 
    4             sp.ReceivedBytesThreshold = 1;//获取DataReceived事件发生前内部缓存区字节数
    5             sp.DataReceived += new SerialDataReceivedEventHandler(serialPort_DataReceived);//设置委托

         #3 点击“发送数据”按钮的事件如下:  

     1         /// <summary>
     2         /// 【发送数据】
     3         /// </summary>
     4         /// <param name="sender"></param>
     5         /// <param name="e"></param>
     6         private void btnSend_Click(object sender, EventArgs e)
     7         {
     8             //发送串口数据
     9 
    10             //1、检查串口设置
    11             if (!CheckPortSetting()) 
    12             {
    13                 MessageBox.Show("串口未设置!", "错误提示");
    14                 return;
    15             }
    16 
    17             //2、检查发送数据是否为空
    18             //if (!CheckSendData())
    19             //{
    20             //    MessageBox.Show("请输入要发送的数据!", "错误提示");
    21             //    return;
    22             //}
    23 
    24             //3、设置
    25             if (!isSetProperty) 
    26             {
    27                 SetPortProperty();
    28                 isSetProperty = true;
    29             }
    30 
    31             //4、写串口数据
    32             if (isOpen)
    33             {
    34                 //写出口数据
    35                 try
    36                 {
    37                     //sp.Write(tbxSendData.Text);
    38                     tbxStatus.Text = "发送成功!";
    39                     //tbxSendData.Text += tbxAddress.Text;
    40                     
    41                 
    42                     byte address = Convert.ToByte( tbxAddress.Text.Trim(), 16);//地址码
    43                     byte cmd = Convert.ToByte(tbxCmd.Text.Trim(),16);//命令帧
    44                     byte regAddr = Convert.ToByte(tbxRegAddr.Text.Trim(), 16);//寄存器地址
    45                     byte regNum = Convert.ToByte(tbxRegNum.Text.Trim(), 16);//寄存器数量
    46 
    47                     
    48                     //Modbus相关处理对象
    49                     MyModbus modbus = new MyModbus();
    50                     byte[] text = modbus.GetReadFrame(address, cmd, regAddr, regNum, 8);
    51 
    52                     sp.Write(text, 0, 8);
    53                     tbxRecvData.Text += "Send:" + BitConverter.ToString(text)+ "
    ";      
    54 
    55                 }
    56                 catch
    57                 {
    58                     tbxStatus.Text = "发送数据错误";
    59                 }
    60             }
    61             else
    62             {
    63                 MessageBox.Show("串口未打开", "错误提示");
    64             }
    65 
    66             
    67         }

        

      2.7 附加代码

        #1 这里的MyModbus主要为Modbus相关一些操作,包括把发送数据封装成Modbus数据帧等。   

      1 using System;
      2 using System.Collections.Generic;
      3 using System.IO.Ports;
      4 using System.Linq;
      5 using System.Text;
      6 using System.Threading.Tasks;
      7 
      8 namespace 串口调试
      9 {
     10     class MyModbus
     11     {
     12        
     13         #region CRC高位表 byte[] _auchCRCHi
     14         private static readonly byte[] _auchCRCHi = new byte[]//crc高位表
     15         {
     16             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 
     17             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 
     18             0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 
     19             0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 
     20             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 
     21             0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 
     22             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 
     23             0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 
     24             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 
     25             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 
     26             0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 
     27             0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 
     28             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 
     29             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 
     30             0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 
     31             0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 
     32             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 
     33             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 
     34             0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 
     35             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 
     36             0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 
     37             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 
     38             0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 
     39             0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 
     40             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 
     41             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
     42         };
     43         #endregion
     44 
     45         #region CRC低位表 byte[] _auchCRCLo
     46         private static readonly byte[] _auchCRCLo = new byte[]//crc低位表
     47         {
     48             0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 
     49             0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 
     50             0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 
     51             0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 
     52             0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4, 
     53             0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, 
     54             0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 
     55             0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 
     56             0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 
     57             0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 
     58             0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 
     59             0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, 
     60             0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 
     61             0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 
     62             0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 
     63             0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 
     64             0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 
     65             0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5, 
     66             0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 
     67             0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 
     68             0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 
     69             0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 
     70             0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 
     71             0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, 
     72             0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 
     73             0x43, 0x83, 0x41, 0x81, 0x80, 0x40
     74         };
     75         #endregion
     76 
     77         /// <summary>
     78         /// 【获取读数据命令,返回命令帧】
     79         /// </summary>
     80         /// <param name="mdaddr">地址码</param>
     81         /// <param name="R_CMD">功能码</param>
     82         /// <param name="min_reg">寄存器地址</param>
     83         /// <param name="data_len">寄存器个数</param>
     84         /// <param name="R_CMD_LEN">命令长度</param>
     85         /// <returns></returns>
     86         public byte[] GetReadFrame(byte mdaddr, byte R_CMD, ushort min_reg, ushort data_len, int R_CMD_LEN)
     87         {
     88             //主机命令帧格式
     89             //  字节    功能描述            例子
     90             //
     91             //  1         地址码             0x01
     92             //  2         功能码             0x03
     93             //  3         寄存器地址高       0x00
     94             //  4         寄存器地址低       0x00
     95             //  5         寄存器个数高       0x00
     96             //  6         寄存器个数低       0x02
     97             //  7         CRC检验码低        0xC4
     98             //  8         CRC校验码高        0x0B
     99 
    100             ushort crc;
    101             byte[] message = new byte[8];
    102 
    103             //设置模块号
    104             message[0] = mdaddr;
    105             //设置命令字
    106             message[1] = R_CMD;
    107 
    108             //设置开始寄存器
    109             message[2] = WORD_HI(min_reg);
    110             message[3] = WORD_LO(min_reg);
    111 
    112             //设置数据长度
    113             message[4] = WORD_HI(data_len);
    114             message[5] = WORD_LO(data_len);
    115 
    116             //设置 CRC
    117             crc = CRC16(message, 0, R_CMD_LEN - 3);
    118 
    119             message[6] = WORD_HI(crc);//CRC校验码高位
    120             message[7] = WORD_LO(crc);//CRC校验码低位
    121 
    122 
    123             return message;
    124         }
    125 
    126         /// <summary>
    127         /// 【格式化输出,校验读取的数据】
    128         /// </summary>
    129         /// <param name="readBuffer"></param>
    130         /// <returns></returns>
    131         public string SetText(byte[] readBuffer)
    132         {
    133             //将byte 转换成string 用于显示
    134             //string readstr = string.Empty;
    135             if (readBuffer != null)
    136             {
    137                 ushort crc = CRC16(readBuffer, 0, readBuffer.Length - 3);
    138                 if (readBuffer[readBuffer.Length - 2] == WORD_HI(crc) && readBuffer[readBuffer.Length - 1] == WORD_LO(crc))//crc校验
    139                 { 
    140                     return ToHexString(readBuffer);
    141                 }
    142                 else
    143                 {
    144                     return "CRC校验错误";
    145                 }
    146             }
    147 
    148             return "程序出错";
    149         }
    150 
    151 
    152         /// <summary>
    153         /// 【CRC校验】
    154         /// </summary>
    155         /// <param name="buffer">命令帧合适前6字节</param>
    156         /// <param name="Sset">开始位</param>
    157         /// <param name="Eset">结束位</param>
    158         /// <returns>CRC校验码</returns>
    159         public static ushort CRC16(Byte[] buffer, int Sset, int Eset)
    160         {
    161             byte crcHi = 0xff;  // 高位初始化
    162 
    163             byte crcLo = 0xff;  // 低位初始化
    164 
    165             for (int i = Sset; i <= Eset; i++)
    166             {
    167                 int crcIndex = crcHi ^ buffer[i]; //查找crc表值
    168 
    169                 crcHi = (byte)(crcLo ^ _auchCRCHi[crcIndex]);
    170                 crcLo = _auchCRCLo[crcIndex];
    171             }
    172 
    173             return (ushort)(crcHi << 8 | crcLo);
    174         }
    175 
    176         /// <summary>
    177         /// 【获取大写字母】
    178         /// </summary>
    179         /// <param name="bytes"></param>
    180         /// <returns></returns>
    181         public static string ToHexString(byte[] bytes) // 0xae00cf => "AE00CF "
    182         {
    183             string hexString = string.Empty;
    184 
    185             if (bytes != null)
    186             {
    187 
    188                 StringBuilder strB = new StringBuilder();
    189 
    190                 for (int i = 0; i < bytes.Length; i++)
    191                 {
    192 
    193                     strB.Append(bytes[i].ToString("X2"));
    194 
    195                 }
    196 
    197                 hexString = strB.ToString();
    198 
    199             } return hexString;
    200 
    201         }
    202 
    203         //取Word变量的高位字节、低位字节
    204         /// <summary>
    205         /// 【获取低位字节】
    206         /// </summary>
    207         /// <param name="crcCLo"></param>
    208         /// <returns></returns>
    209         public static byte WORD_LO(ushort crcCLo)
    210         {
    211             crcCLo = (ushort)(crcCLo & 0X00FF);
    212             return (byte)crcCLo;
    213         }
    214 
    215         /// <summary>
    216         /// 【获取高位字节】
    217         /// </summary>
    218         /// <param name="crcHI"></param>
    219         /// <returns></returns>
    220         public static byte WORD_HI(ushort crcHI)
    221         {
    222             crcHI = (ushort)(crcHI >> 8 & 0X00FF);
    223             return (byte)crcHI;
    224         }
    225     }
    226 }
    MyModbus.cs

        

        #2 下面为Form1.cs的代码,主要包括窗体一些基本操作。   

      1 using System;
      2 using System.Collections.Generic;
      3 using System.ComponentModel;
      4 using System.Data;
      5 using System.Drawing;
      6 using System.IO.Ports;
      7 using System.Linq;
      8 using System.Text;
      9 using System.Threading.Tasks;
     10 using System.Windows.Forms;
     11 
     12 
     13 namespace 串口调试
     14 {
     15     public partial class Form1 : Form
     16     {
     17         SerialPort sp = null;
     18 
     19         bool isOpen = false;//是否打开
     20 
     21         bool isSetProperty = false;
     22 
     23 
     24         //数据接收使用的代理
     25         private delegate void myDelegate(byte[] readBuffer);
     26 
     27         public Form1()
     28         {
     29             InitializeComponent();
     30         }
     31 
     32         private void Form1_Load(object sender, EventArgs e)
     33         {
     34             
     35             //设置窗口大小固定
     36             this.MaximumSize = this.Size;
     37             this.MinimumSize = this.Size;
     38 
     39             //1、设置串口下拉列表
     40             for ( int i = 0; i < 10; i++ ) 
     41             {
     42                 cbxCOMPort.Items.Add("COM" + (i + 1).ToString());
     43             }
     44             cbxCOMPort.SelectedIndex = 2;//默认选项
     45 
     46 
     47             //2、设置常用波特率
     48             int bt = 300;
     49             for (int i = 0; i < 8; i++)
     50             {
     51                 cbxBaudRate.Items.Add(bt.ToString());
     52                 bt *= 2;
     53             }
     54 
     55             cbxBaudRate.Items.Add("38400");
     56             cbxBaudRate.Items.Add("43000");
     57             cbxBaudRate.Items.Add("56000"); 
     58             cbxBaudRate.Items.Add("57600");
     59             cbxBaudRate.Items.Add("115200");
     60             cbxBaudRate.SelectedIndex = 5;
     61 
     62 
     63             //3、列出停止位
     64             cbxStopBits.Items.Add("0");
     65             cbxStopBits.Items.Add("1");
     66             cbxStopBits.Items.Add("1.5");
     67             cbxStopBits.Items.Add("2");
     68             cbxStopBits.SelectedIndex = 1;
     69 
     70             //4、设置奇偶检验
     71             cbxParity.Items.Add("");
     72             cbxParity.Items.Add("奇校验");
     73             cbxParity.Items.Add("偶校验");
     74             cbxParity.SelectedIndex = 0;
     75 
     76             //5、设置数据位
     77             cbxDataBits.Items.Add("8");
     78             cbxDataBits.Items.Add("7");
     79             cbxDataBits.Items.Add("6");
     80             cbxDataBits.Items.Add("5");
     81             cbxDataBits.SelectedIndex = 0;
     82 
     83 
     84         }
     85 
     86 
     87         /// <summary>
     88         /// 【串口检测按钮】
     89         /// </summary>
     90         /// <param name="sender"></param>
     91         /// <param name="e"></param>
     92         private void btnCheckCOM_Click(object sender, EventArgs e)
     93         {
     94             //1、检测哪些端口可用
     95             cbxCOMPort.Items.Clear();
     96             cbxCOMPort.Text = "";
     97             
     98             lblStatus.Text = "执行中...";
     99             string str = "";
    100             for (int i = 0; i < 10; i++)
    101             {
    102                 try
    103                 {
    104                     SerialPort sp = new SerialPort("COM" + (i + 1).ToString());
    105                     sp.Open();
    106                     sp.Close();
    107                     cbxCOMPort.Items.Add("COM" + (i + 1).ToString());
    108                 }
    109                 catch
    110                 {
    111                     str += "COM" + (i + 1).ToString() + "";
    112                     continue;
    113                 }
    114             }
    115 
    116             if(cbxCOMPort.Items.Count > 0)
    117                 cbxCOMPort.SelectedIndex = 0;
    118             lblStatus.Text = "完成";
    119             tbxStatus.Text = str;
    120         }
    121 
    122 
    123         public void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
    124         {
    125             myDelegate md = new myDelegate(ShowRst);
    126             try
    127             {
    128                 if (sp.IsOpen)
    129                 {
    130                     int count = sp.BytesToRead;
    131                     if (count > 0)
    132                     {
    133                         byte[] readBuffer = new byte[count];
    134                         sp.Read(readBuffer, 0, count);//读取串口数据
    135                         //     serialPort1.Write(readBuffer, 0, count);
    136                         Invoke(md, readBuffer);
    137                     }
    138                 }
    139             }
    140             catch (Exception err)
    141             {
    142                 throw err;
    143             }
    144 
    145         }
    146 
    147         /// <summary>
    148         /// 【显示接收返回的数据】
    149         /// </summary>
    150         /// <param name="resbuffer"></param>
    151         public void ShowRst(byte[] resbuffer) 
    152         {
    153             MyModbus modbus = new MyModbus();
    154             tbxRecvData.Text += "Recv:" + modbus.SetText(resbuffer) + "
    ";
    155             tbxRecvData.Text += "
    ";
    156         }
    157 
    158         /// <summary>
    159         /// 【检测端口设置】
    160         /// </summary>
    161         /// <returns></returns>
    162         private bool CheckPortSetting()
    163         {
    164             //检测端口设置
    165             if (cbxCOMPort.Text.Trim() == "" || cbxBaudRate.Text.Trim() == "" || cbxStopBits.Text.Trim() == "" || cbxParity.Text.Trim() == "" || cbxDataBits.Text.Trim() == "")
    166                 return false;
    167             return true;
    168         }
    169 
    170         /// <summary>
    171         /// 【检测发送数据是否为空】
    172         /// </summary>
    173         /// <returns></returns>
    174         private bool CheckSendData()
    175         {
    176             if (tbxSendData.Text.Trim() == "")
    177                 return false;
    178             return true;
    179         }
    180 
    181 
    182         /// <summary>
    183         /// 【设置串口属性】
    184         /// </summary>
    185         private void SetPortProperty()
    186         {
    187             //1、设置串口的属性
    188             sp = new SerialPort();
    189 
    190             sp.ReceivedBytesThreshold = 1;//获取DataReceived事件发生前内部缓存区字节数
    191             sp.DataReceived += new SerialDataReceivedEventHandler(serialPort_DataReceived);//设置委托
    192 
    193             sp.PortName = cbxCOMPort.Text.Trim();
    194 
    195             //2、设置波特率
    196             sp.BaudRate =  Convert.ToInt32( cbxBaudRate.Text.Trim());
    197 
    198             //3、设置停止位
    199             float f = Convert.ToSingle( cbxStopBits.Text.Trim());
    200 
    201             if (f == 0) 
    202             {
    203                 sp.StopBits = StopBits.None;//表示不使用停止位
    204             }
    205             else if (f == 1.5)
    206             {
    207                 sp.StopBits = StopBits.OnePointFive;//使用1.5个停止位
    208             }
    209             else if (f == 2)
    210             {
    211                 sp.StopBits = StopBits.Two;//表示使用两个停止位
    212             }
    213             else
    214             {
    215                 sp.StopBits = StopBits.One;//默认使用一个停止位
    216             }
    217 
    218             //4、设置数据位
    219             sp.DataBits = Convert.ToInt16(cbxDataBits.Text.Trim());
    220 
    221             //5、设置奇偶校验位
    222             string s = cbxParity.Text.Trim();
    223             if (s.CompareTo("") == 0) 
    224             {
    225                 sp.Parity = Parity.None;//不发生奇偶校验检查
    226             }
    227             else if (s.CompareTo("奇校验") == 0)
    228             {
    229                 sp.Parity = Parity.Odd;//设置奇校验
    230             }
    231             else if (s.CompareTo("偶校验") == 0)
    232             {
    233                 sp.Parity = Parity.Even;//设置偶检验
    234             }
    235             else
    236             {
    237                 sp.Parity = Parity.None;
    238             }
    239 
    240             //6、设置超时读取时间
    241             sp.ReadTimeout = -1;
    242 
    243             //7、打开串口
    244             try
    245             {
    246                 sp.Open();
    247                 isOpen = true;
    248             }
    249             catch(Exception)
    250             {
    251                 lblStatus.Text = "打开串口错误!";
    252             }
    253 
    254         }
    255 
    256        
    257 
    258         /// <summary>
    259         /// 【发送数据】
    260         /// </summary>
    261         /// <param name="sender"></param>
    262         /// <param name="e"></param>
    263         private void btnSend_Click(object sender, EventArgs e)
    264         {
    265             //发送串口数据
    266 
    267             //1、检查串口设置
    268             if (!CheckPortSetting()) 
    269             {
    270                 MessageBox.Show("串口未设置!", "错误提示");
    271                 return;
    272             }
    273 
    274             //2、检查发送数据是否为空
    275             //if (!CheckSendData())
    276             //{
    277             //    MessageBox.Show("请输入要发送的数据!", "错误提示");
    278             //    return;
    279             //}
    280 
    281             //3、设置
    282             if (!isSetProperty) 
    283             {
    284                 SetPortProperty();
    285                 isSetProperty = true;
    286             }
    287 
    288             //4、写串口数据
    289             if (isOpen)
    290             {
    291                 //写出口数据
    292                 try
    293                 {
    294                     //sp.Write(tbxSendData.Text);
    295                     tbxStatus.Text = "发送成功!";
    296                     //tbxSendData.Text += tbxAddress.Text;
    297                     
    298                 
    299                     byte address = Convert.ToByte( tbxAddress.Text.Trim(), 16);//地址码
    300                     byte cmd = Convert.ToByte(tbxCmd.Text.Trim(),16);//命令帧
    301                     byte regAddr = Convert.ToByte(tbxRegAddr.Text.Trim(), 16);//寄存器地址
    302                     byte regNum = Convert.ToByte(tbxRegNum.Text.Trim(), 16);//寄存器数量
    303 
    304                     
    305                     //Modbus相关处理对象
    306                     MyModbus modbus = new MyModbus();
    307                     byte[] text = modbus.GetReadFrame(address, cmd, regAddr, regNum, 8);
    308 
    309                     sp.Write(text, 0, 8);
    310                     tbxRecvData.Text += "Send:" + BitConverter.ToString(text)+ "
    ";      
    311 
    312                 }
    313                 catch
    314                 {
    315                     tbxStatus.Text = "发送数据错误";
    316                 }
    317             }
    318             else
    319             {
    320                 MessageBox.Show("串口未打开", "错误提示");
    321             }
    322 
    323             
    324         }
    325 
    326         /// <summary>
    327         /// 【读取数据】
    328         /// </summary>
    329         /// <param name="sender"></param>
    330         /// <param name="e"></param>
    331         private void btnRecv_Click(object sender, EventArgs e)
    332         {
    333             if(isOpen) 
    334             { 
    335                 try  
    336                 {
    337                     //读取串口数据
    338   
    339                     tbxRecvData.Text += sp.ReadLine()+"
    "340                 }  
    341                 catch(Exception) 
    342                 {  
    343                     lblStatus.Text = "读取串口时发生错误!"344                     return345                 } 
    346             } 
    347             else 
    348             {  
    349               MessageBox.Show("串口未打开!""错误提示"); 
    350               return351                 
    352             } 
    353         }
    354  
    355     
    356     }
    357 }
    Form1.cs

     三、最后说一句

      由于需要查询大量资料,有些代码是引用别人的,所以这里要感谢那些分享的人,秉着分享精神,希望可以帮助更多的人。

      如果发现有什么错误,或者建议,请留言,谢谢!

  • 相关阅读:
    根据对象的属性排序数组
    将多维数组的元素全部取出,组成一维数组的方法
    微信公众号报错 config:invalid signature
    改造业务代码
    微信公众号调用扫一扫
    JQuery :contains选择器,可做搜索功能,搜索包含关键字的dom
    【转】超全功能测试方法集锦——(通用黑盒功能:测试新人必收攻略)
    Oracle 查看表空间的大小及使用情况sql语句
    oracle数据库 参数open_cursors和session_cached_cursor详解!
    使用MITab操作MapInfo地图文件
  • 原文地址:https://www.cnblogs.com/reader/p/6561119.html
Copyright © 2020-2023  润新知