C#实现串口调试工具

作者:痕迹g 时间:2022-06-09 12:04:04 

前文

由于经常用到串口调试, 尽管有现成的软件, 因为前端时间涉及一个二次开发, 就因为一个RtsEnable设置, 折腾半天, 网上各种版本的也很多, 功能扩展的很开也多。所以现在自己做了一个够用版,基于自己的需求,简单的实现发送接收功能, 至于那些扩展功能可以自己根据需求添加。

先上个运行效果图:

C#实现串口调试工具

项目架构

该实例用的GalaSoft.Mvvm, 该插件可以直接在NuGet中并且添加。

C#实现串口调试工具

1.串口参数 , 为了方便, 端口号并没有用动态加载的方式, 如下枚举结构:

namespace System.Windows.Data
{
   //
   // 摘要:
   //     提供将自定义逻辑应用于绑定的方法。
   public interface IValueConverter
   {
       //
       // 摘要:
       //     转换值。
       //
       // 参数:
       //   value:
       //     绑定源生成的值。
       //
       //   targetType:
       //     绑定目标属性的类型。
       //
       //   parameter:
       //     要使用的转换器参数。
       //
       //   culture:
       //     要用在转换器中的区域性。
       //
       // 返回结果:
       //     转换后的值。 如果该方法返回 null,则使用有效的 null 值。
       object Convert(object value, Type targetType, object parameter, CultureInfo culture);
       //
       // 摘要:
       //     转换值。
       //
       // 参数:
       //   value:
       //     绑定目标生成的值。
       //
       //   targetType:
       //     要转换为的类型。
       //
       //   parameter:
       //     要使用的转换器参数。
       //
       //   culture:
       //     要用在转换器中的区域性。
       //
       // 返回结果:
       //     转换后的值。 如果该方法返回 null,则使用有效的 null 值。
       object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture);
   }
}

2.串口参数配置类 ,

作用: 主要用于绑定界面的参数选项。

/// <summary>
   /// 串口参数设置类
   /// </summary>
   public class ComParameterConfig : ViewModelBase
   {
       public ComParameterConfig()
       {
           Port = System.Enum.GetValues(typeof(Port));
           CheckMode = System.Enum.GetValues(typeof(CheckMode));
           StopBit = System.Enum.GetValues(typeof(StopBit));
           BaudRate = new List<int>() { 110, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 56000, 57600, 115200, };
           DataBit = new List<int>() { 6, 7, 8 };
       }

private Array port;
       private Array checkMode;
       private Array stopBit;
       private List<int> dataBit;
       private List<int> baudRate;

/// <summary>
       /// 端口
       /// </summary>
       public Array Port
       {
           get { return port; }
           set { port = value; RaisePropertyChanged(); }
       }

/// <summary>
       /// 校验模式
       /// </summary>
       public Array CheckMode
       {
           get { return checkMode; }
           set { checkMode = value; RaisePropertyChanged(); }
       }

/// <summary>
       /// 停止位
       /// </summary>
       public Array StopBit
       {
           get { return stopBit; }
           set { stopBit = value; RaisePropertyChanged(); }
       }

/// <summary>
       /// 波特率
       /// </summary>
       public List<int> BaudRate
       {
           get { return baudRate; }
           set { baudRate = value; RaisePropertyChanged(); }
       }

/// <summary>
       /// 数据位
       /// </summary>
       public List<int> DataBit
       {
           get { return dataBit; }
           set { dataBit = value; RaisePropertyChanged(); }
       }

}

3.当前配置参数类

作用: 用于保存当前的串口参数、串口功能开关接收数据等业务。

核心代码:

/// <summary>
   /// 当前配置参数
   /// </summary>
   public class CurrentParameter : ViewModelBase
   {
       #region Private

private int baudRdate = 9600;
       private int dataBit = 8;
       private Port port;
       private CheckMode checkMode;
       private StopBit stopBit = StopBit.One;
       private SerialPort serialPort;

private string dataReceiveInfo;
       private string sendData;
       private bool isOpen;
       private bool receiveFormat16 = true;
       private bool sendFormat16 = true;

private int sendCount;
       private int receiveCount;

#endregion

#region UI绑定参数

/// <summary>
       /// 发送数量
       /// </summary>
       public int SendCount
       {
           get { return sendCount; }
           set { sendCount = value; RaisePropertyChanged(); }
       }

/// <summary>
       /// 接收数量
       /// </summary>
       public int ReceiveCount
       {
           get { return receiveCount; }
           set { receiveCount = value; RaisePropertyChanged(); }
       }

/// <summary>
       /// 接收区16进制
       /// </summary>
       public bool ReceiveFormat16
       {
           get { return receiveFormat16; }
           set { receiveFormat16 = value; RaisePropertyChanged(); }
       }

/// <summary>
       /// 接收区数据
       /// </summary>
       public string DataReceiveInfo
       {
           get { return dataReceiveInfo; }
           set { dataReceiveInfo = value; RaisePropertyChanged(); }
       }

/// <summary>
       /// 发送数据
       /// </summary>
       public string SendData
       {
           get
           {
               return sendData;
           }
           set { sendData = value; RaisePropertyChanged(); }
       }

/// <summary>
       /// 发送区16进制
       /// </summary>
       public bool SendFormat16
       {
           get { return sendFormat16; }
           set { sendFormat16 = value; RaisePropertyChanged(); }
       }

#endregion

#region 串口参数信息

/// <summary>
       /// 开关
       /// </summary>
       public bool IsOpen
       {
           get { return isOpen; }
           set { isOpen = value; RaisePropertyChanged(); }
       }

/// <summary>
       /// 数据位
       /// </summary>
       public int DataBit
       {
           get { return dataBit; }
           set { dataBit = value; RaisePropertyChanged(); }
       }

/// <summary>
       /// 波特率
       /// </summary>
       public int BaudRdate
       {
           get { return baudRdate; }
           set { baudRdate = value; RaisePropertyChanged(); }
       }

/// <summary>
       /// 端口
       /// </summary>
       public Port Port
       {
           get { return port; }
           set { port = value; RaisePropertyChanged(); }
       }

/// <summary>
       /// 校验
       /// </summary>
       public CheckMode CheckMode
       {
           get { return checkMode; }
           set { checkMode = value; RaisePropertyChanged(); }
       }

/// <summary>
       /// 停止位
       /// </summary>
       public StopBit StopBit
       {
           get { return stopBit; }
           set { stopBit = value; RaisePropertyChanged(); }
       }

/// <summary>
       /// COM
       /// </summary>
       public SerialPort SerialPort
       {
           get { return serialPort; }
           set { serialPort = value; RaisePropertyChanged(); }
       }

#endregion

#region 串口操作方法

/// <summary>
       /// 开启串口
       /// </summary>
       /// <returns></returns>
       public bool Open()
       {
           if (serialPort != null && serialPort.IsOpen)
           {
               return Close();
           }
           try
           {
               serialPort = new SerialPort();
               serialPort.DataBits = this.DataBit;
               serialPort.StopBits = ComHelper.GetStopBits(this.StopBit.ToString());
               serialPort.Parity = ComHelper.GetParity(this.CheckMode.ToString());
               serialPort.PortName = this.Port.ToString();
               serialPort.RtsEnable = true;
               serialPort.DataReceived += SerialPort_DataReceived;
               serialPort.Open();

if (serialPort.IsOpen)
                   return IsOpen = true;
               else
                   return IsOpen = false;
           }
           catch (Exception ex)
           {
               MessageBox.Show(ex.Message);
           }
           return IsOpen = false;
       }

/// <summary>
       /// 关闭串口
       /// </summary>
       /// <returns></returns>
       public bool Close()
       {
           try
           {
               if (serialPort.IsOpen)
               {
                   serialPort.Close();
                   return IsOpen = serialPort.IsOpen;
               }
               else
               {
                   return IsOpen = serialPort.IsOpen;
               }
           }
           catch (Exception ex)
           {
               MessageBox.Show(ex.Message);
               return IsOpen = false;
           }
       }

/// <summary>
       /// 发送数据
       /// </summary>
       public void Send()
       {

if (SendFormat16)
           {
               byte[] bytes = CRC.StringToHexByte(SendData);
               this.SerialPort.Write(bytes, 0, bytes.Length);
               SendCount = bytes.Length; //不做增量
           }
           else
           {
               this.SerialPort.Write(SendData);
               SendCount = SendData.Length;
           }
           Messenger.Default.Send("", "PlaySendFlashing");
       }

/// <summary>
       /// 清空接收区
       /// </summary>
       public void Clear()
       {
           this.DataReceiveInfo = string.Empty;
       }

/// <summary>
       /// 清空发送区和缓存区
       /// </summary>
       public void ClearText()
       {
           this.SendData = string.Empty;
           this.SendCount = 0;
           this.ReceiveCount = 0;
       }

#endregion

/// <summary>
       /// 返回事件
       /// </summary>
       /// <param name="sender"></param>
       /// <param name="e"></param>
       private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
       {
           Messenger.Default.Send("", "PlayReciveFlashing");

byte[] readBuffer = new byte[SerialPort.ReadBufferSize];
           SerialPort.Read(readBuffer, 0, readBuffer.Length);

ReceiveCount = SerialPort.ReceivedBytesThreshold; //不做增量

if (ReceiveFormat16)
           {
               //不做增量
               DataReceiveInfo = CRC.ByteToString(readBuffer, true);
           }
           else
           {
               DataReceiveInfo = Encoding.ASCII.GetString(readBuffer);
           }

}
   }

4.核心MainViewModel类

作用: 关联首页的上下文, 通过DataContext绑定, 关联界面元素、命令等作用。

public class MainViewModel : ViewModelBase
   {
       /// <summary>
       /// Initializes a new instance of the MainViewModel class.
       /// </summary>
       public MainViewModel()
       {
           ComParameterConfig = new ComParameterConfig();
           CurrentParameter = new CurrentParameter();
       }

private ComParameterConfig comParameter;

/// <summary>
       /// 参数类
       /// </summary>
       public ComParameterConfig ComParameterConfig
       {
           get { return comParameter; }
           set { comParameter = value; RaisePropertyChanged(); }
       }

private CurrentParameter currentParameter;

/// <summary>
       /// 当前配置参数
       /// </summary>
       public CurrentParameter CurrentParameter
       {
           get { return currentParameter; }
           set { currentParameter = value; RaisePropertyChanged(); }
       }

#region Command

private RelayCommand _ToOpen;
       public RelayCommand ToOpen
       {
           get
           {
               if (_ToOpen == null)
               {
                   _ToOpen = new RelayCommand(Open);
               }
               return _ToOpen;
           }
           set
           {
               _ToOpen = value;
           }
       }
       /// <summary>
       /// 根据配置打开端口
       /// </summary>
       public void Open()
       {
           this.CurrentParameter.Open();
       }

private RelayCommand _ToClick;
       public RelayCommand ToClick
       {
           get
           {
               if (_ToClick == null)
               {
                   _ToClick = new RelayCommand(Click);
               }
               return _ToClick;
           }
           set
           {
               _ToClick = value;
           }
       }

/// <summary>
       /// 发送数据
       /// </summary>
       public void Click()
       {
           this.CurrentParameter.Send();
       }

private RelayCommand _ToClear;

public RelayCommand ToClear
       {
           get
           {
               if (_ToClear == null)
               {
                   _ToClear = new RelayCommand(Clear);
               }
               return _ToClear;
           }
           set
           {
               _ToClear = value;
           }
       }

/// <summary>
       /// 清空接收区
       /// </summary>
       public void Clear()
       {
           this.CurrentParameter.Clear();
       }

private RelayCommand _ToClearText;

public RelayCommand ToClearText
       {
           get
           {
               if (_ToClearText == null)
               {
                   _ToClearText = new RelayCommand(ClearText);
               }
               return _ToClearText;
           }
           set
           {
               _ToClearText = value;
           }
       }

/// <summary>
       /// 清空界面值
       /// </summary>
       public void ClearText()
       {
           this.CurrentParameter.ClearText();
       }

#endregion
   }

5.CRC校验核心类

作用:主要实现数据校验, 含ModbusCR标准校验

/// <summary>
   /// CRC校验
   /// </summary>
   public class CRC
   {

#region  CRC16

public static byte[] CRC16(byte[] data)
       {
           int len = data.Length;
           if (len > 0)
           {
               ushort crc = 0xFFFF;

for (int i = 0; i < len; i++)
               {
                   crc = (ushort)(crc ^ (data[i]));
                   for (int j = 0; j < 8; j++)
                   {
                       crc = (crc & 1) != 0 ? (ushort)((crc >> 1) ^ 0xA001) : (ushort)(crc >> 1);
                   }
               }
               byte hi = (byte)((crc & 0xFF00) >> 8);  //高位置
               byte lo = (byte)(crc & 0x00FF);         //低位置

return new byte[] { hi, lo };
           }
           return new byte[] { 0, 0 };
       }
       #endregion

#region  ToCRC16

public static string ToCRC16(string content)
       {
           return ToCRC16(content, Encoding.UTF8);
       }

public static string ToCRC16(string content, bool isReverse)
       {
           return ToCRC16(content, Encoding.UTF8, isReverse);
       }

public static string ToCRC16(string content, Encoding encoding)
       {
           return ByteToString(CRC16(encoding.GetBytes(content)), true);
       }

public static string ToCRC16(string content, Encoding encoding, bool isReverse)
       {
           return ByteToString(CRC16(encoding.GetBytes(content)), isReverse);
       }

public static string ToCRC16(byte[] data)
       {
           return ByteToString(CRC16(data), true);
       }

public static string ToCRC16(byte[] data, bool isReverse)
       {
           return ByteToString(CRC16(data), isReverse);
       }
       #endregion

#region  ToModbusCRC16

public static string ToModbusCRC16(string s)
       {
           return ToModbusCRC16(s, true);
       }

public static string ToModbusCRC16(string s, bool isReverse)
       {
           return ByteToString(CRC16(StringToHexByte(s)), isReverse);
       }

public static string ToModbusCRC16(byte[] data)
       {
           return ToModbusCRC16(data, true);
       }

public static string ToModbusCRC16(byte[] data, bool isReverse)
       {
           return ByteToString(CRC16(data), isReverse);
       }
       #endregion

#region  ByteToString

public static string ByteToString(byte[] arr, bool isReverse)
       {
           try
           {
               byte hi = arr[0], lo = arr[1];
               return Convert.ToString(isReverse ? hi + lo * 0x100 : hi * 0x100 + lo, 16).ToUpper().PadLeft(4, '0');
           }
           catch (Exception ex) { throw (ex); }
       }

public static string ByteToString(byte[] arr)
       {
           try
           {
               return ByteToString(arr, true);
           }
           catch (Exception ex) { throw (ex); }
       }

#endregion

#region  StringToHexString

public static string StringToHexString(string str)
       {
           StringBuilder s = new StringBuilder();
           foreach (short c in str.ToCharArray())
           {
               s.Append(c.ToString("X4"));
           }
           return s.ToString();
       }

#endregion

#region  StringToHexByte

private static string ConvertChinese(string str)
       {
           StringBuilder s = new StringBuilder();
           foreach (short c in str.ToCharArray())
           {
               if (c <= 0 || c >= 127)
               {
                   s.Append(c.ToString("X4"));
               }
               else
               {
                   s.Append((char)c);
               }
           }
           return s.ToString();
       }

private static string FilterChinese(string str)
       {
           StringBuilder s = new StringBuilder();
           foreach (short c in str.ToCharArray())
           {
               if (c > 0 && c < 127)
               {
                   s.Append((char)c);
               }
           }
           return s.ToString();
       }

/// <summary>
       /// 字符串转16进制字符数组
       /// </summary>
       /// <param name="hex"></param>
       /// <returns></returns>
       public static byte[] StringToHexByte(string str)
       {
           return StringToHexByte(str, false);
       }

/// <summary>
       /// 字符串转16进制字符数组
       /// </summary>
       /// <param name="str"></param>
       /// <param name="isFilterChinese">是否过滤掉中文字符</param>
       /// <returns></returns>
       public static byte[] StringToHexByte(string str, bool isFilterChinese)
       {
           string hex = isFilterChinese ? FilterChinese(str) : ConvertChinese(str);

//清除所有空格
           hex = hex.Replace(" ", "");
           //若字符个数为奇数,补一个0
           hex += hex.Length % 2 != 0 ? "0" : "";

byte[] result = new byte[hex.Length / 2];
           for (int i = 0, c = result.Length; i < c; i++)
           {
               result[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16);
           }
           return result;
       }
       #endregion

}

WPF技术点:

1.自定义样式按钮

<Style x:Key="CommonButtonBase" TargetType="{x:Type Button}">
               <Setter Property="BorderBrush" Value="Transparent"/>
               <Setter Property="BorderThickness" Value="0"/>
               <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
               <Setter Property="HorizontalContentAlignment" Value="Center"/>
               <Setter Property="VerticalContentAlignment" Value="Center"/>
               <Setter Property="Padding" Value="1"/>
               <Setter Property="Cursor" Value="Hand"/>
               <Setter Property="Template">
                   <Setter.Value>
                       <ControlTemplate TargetType="{x:Type Button}">
                           <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}"
                                           CornerRadius="4"
                                           BorderThickness="{TemplateBinding BorderThickness}"
                                            Background="{TemplateBinding Background}"
                                            SnapsToDevicePixels="true">
                               <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                             Margin="{TemplateBinding Padding}" RecognizesAccessKey="True"
                                             SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                             VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                           </Border>
                           <ControlTemplate.Triggers>
                               <Trigger Property="IsEnabled" Value="False">
                                   <Setter Property="Background" Value="#000000"/>
                                   <Setter Property="Opacity" Value="0.1"/>
                               </Trigger>
                               <Trigger Property="IsMouseOver" Value="true">
                                   <Setter Property="Foreground" Value="#FFFF00"/>
                               </Trigger>
                               <Trigger Property="IsMouseOver" Value="false">
                                   <Setter Property="Foreground" Value="White"/>
                               </Trigger>
                           </ControlTemplate.Triggers>
                       </ControlTemplate>
                   </Setter.Value>
               </Setter>
           </Style>

<Style TargetType="{x:Type Button}" x:Key="Btn0093EABase" BasedOn="{StaticResource CommonButtonBase}">
               <Setter Property="Background" Value="#0093EA"/>
               <Setter Property="Foreground" Value="White"/>
               <Setter Property="FontSize" Value="22"/>
               <Setter Property="Height" Value="40"/>
               <Setter Property="Margin" Value="5"/>
           </Style>

2.转换器用于绑定按钮

public class FontConverters : IValueConverter
   {
       public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
       {
           if (value != null && bool.TryParse(value.ToString(), out bool result))
           {
               if (result)
               {
                   return "关闭串口";
               }
           }
           return "打开串口";
       }

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
       {
           throw new NotImplementedException();
       }
   }
//用于绑定UI的颜色状态显示    public class ColorConverters : IValueConverter
   {
       public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
       {
           if (value != null && bool.TryParse(value.ToString(), out bool result))
           {
               if (result)
               {
                   return new SolidColorBrush((Color)System.Windows.Media.ColorConverter.ConvertFromString("#2E8B57"));
               }
           }
           return new SolidColorBrush((Color)System.Windows.Media.ColorConverter.ConvertFromString("#FF6347"));
       }

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
       {
           throw new NotImplementedException();
       }
   }

3.引用字体

<TextBlock Text="&#xe6e4;" Margin="20 5 0 5" FontFamily="pack://application:,,,/Font/#iconfont"
                                  Foreground="White" FontSize="30" VerticalAlignment="Center"/>

4.绑定命令和元素

<TextBlock Text="端     口:"  Style="{DynamicResource TxtComStyle}"/>
                       <ComboBox  Grid.Row="0" Grid.Column="2" Style="{StaticResource ComboBoxStyle}" SelectedItem="{Binding CurrentParameter.Port}"
                                 ItemsSource="{Binding ComParameterConfig.Port}"
                                  />
                       <TextBlock Text="波 特 率:" Style="{DynamicResource TxtComStyle}"/>
                       <ComboBox  Grid.Row="0" Grid.Column="2" Style="{StaticResource ComboBoxStyle}" SelectedItem="{Binding CurrentParameter.BaudRdate}"
                                  ItemsSource="{Binding ComParameterConfig.BaudRate}"
                                  />
                       <TextBlock Text="数 据 位:" Style="{DynamicResource TxtComStyle}"/>
                       <ComboBox  Grid.Row="0" Grid.Column="2" Style="{StaticResource ComboBoxStyle}"  SelectedItem="{Binding CurrentParameter.DataBit}"
                                  ItemsSource="{Binding ComParameterConfig.DataBit}"
                                  />
                       <TextBlock Text="校 验 位:" Style="{DynamicResource TxtComStyle}"/>
                       <ComboBox  Grid.Row="0" Grid.Column="2" Style="{StaticResource ComboBoxStyle}"  SelectedItem="{Binding CurrentParameter.CheckMode}"
                                  ItemsSource="{Binding ComParameterConfig.CheckMode}"
                                  />
                       <TextBlock Text="停 止 位:" Style="{DynamicResource TxtComStyle}"/>
                       <ComboBox  Grid.Row="0" Grid.Column="2" Style="{StaticResource ComboBoxStyle}"  SelectedItem="{Binding CurrentParameter.StopBit}"
                                  ItemsSource="{Binding ComParameterConfig.StopBit}"
                                  />
                       <TextBlock Text="状     态:" Style="{DynamicResource TxtComStyle}"/>
                       <TextBlock Text="&#xe692;" Style="{DynamicResource TxtComStyle1}"  Foreground="{Binding CurrentParameter.IsOpen,Converter={StaticResource ColorConverters}}" />

写在最后

主项目的结构图 , 如下:

C#实现串口调试工具

来源:https://www.cnblogs.com/zh7791/p/9317042.html

标签:C#,串口,调试,工具
0
投稿

猜你喜欢

  • Android 中CheckBox多项选择当前的position信息提交的示例代码

    2022-05-15 19:13:51
  • C#通过html调用WinForm的方法

    2022-02-26 03:53:16
  • Java Springboot的目的你知道吗

    2022-12-20 14:16:24
  • Springboot如何使用filter对request body参数进行校验

    2023-09-14 05:28:46
  • Android图像处理之绘制圆形、三角形及扇形的头像

    2022-10-13 14:01:48
  • Java RPC框架熔断降级机制原理解析

    2023-06-07 06:01:48
  • Java使用ArrayList实现扑克牌的示例代码

    2021-10-29 15:17:17
  • Android如何判断一个点在不在多边形区域内

    2023-07-06 14:06:13
  • 详解Android Automotive车载应用对驾驶模式Safe Drive Mode的适配

    2022-01-15 07:12:20
  • 不可不知道的10个java谎言

    2022-01-21 10:25:40
  • 详解java WebSocket的实现以及Spring WebSocket

    2023-11-24 13:16:25
  • mybatis 通过拦截器打印完整的sql语句以及执行结果操作

    2023-07-06 04:26:42
  • Android使用Intent.ACTION_SEND分享图片和文字内容的示例代码

    2023-12-17 02:57:28
  • 详谈java命令的本质逻辑揭秘

    2022-03-02 05:26:32
  • 使用Java8 Stream流的skip + limit实现批处理的方法

    2023-11-29 06:17:39
  • spring boot集成p6spy的最佳实践

    2023-04-11 23:40:36
  • MyBatis动态Sql之if标签的用法详解

    2023-04-20 14:02:26
  • android高仿小米时钟(使用Camera和Matrix实现3D效果)

    2022-05-15 01:46:18
  • Eclipse代码格式化设置简单介绍

    2023-10-26 21:59:31
  • java匿名内部类实例简析

    2022-12-26 21:25:15
  • asp之家 软件编程 m.aspxhome.com