前文
由于经常用到串口调试, 尽管有现成的软件, 因为前端时间涉及一个二次开发, 就因为一个RtsEnable设置, 折腾半天, 网上各种版本的也很多, 功能扩展的很开也多。所以现在自己做了一个够用版,基于自己的需求,简单的实现发送接收功能, 至于那些扩展功能可以自己根据需求添加。
正文
先上个运行效果图:
项目架构
该实例用的GalaSoft.Mvvm, 该插件可以直接在NuGet中并且添加。
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.串口参数配置类 ,
作用: 主要用于绑定界面的参数选项。
////// 串口参数设置类 /// 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() { 110, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 56000, 57600, 115200, }; DataBit = new List () { 6, 7, 8 }; } private Array port; private Array checkMode; private Array stopBit; private List dataBit; private List baudRate; /// /// 端口 /// public Array Port { get { return port; } set { port = value; RaisePropertyChanged(); } } ////// 校验模式 /// public Array CheckMode { get { return checkMode; } set { checkMode = value; RaisePropertyChanged(); } } ////// 停止位 /// public Array StopBit { get { return stopBit; } set { stopBit = value; RaisePropertyChanged(); } } ////// 波特率 /// public ListBaudRate { get { return baudRate; } set { baudRate = value; RaisePropertyChanged(); } } /// /// 数据位 /// public ListDataBit { get { return dataBit; } set { dataBit = value; RaisePropertyChanged(); } } }
3.当前配置参数类
作用: 用于保存当前的串口参数、串口功能开关接收数据等业务。
核心代码:
////// 当前配置参数 /// 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绑定参数 ////// 发送数量 /// public int SendCount { get { return sendCount; } set { sendCount = value; RaisePropertyChanged(); } } ////// 接收数量 /// public int ReceiveCount { get { return receiveCount; } set { receiveCount = value; RaisePropertyChanged(); } } ////// 接收区16进制 /// public bool ReceiveFormat16 { get { return receiveFormat16; } set { receiveFormat16 = value; RaisePropertyChanged(); } } ////// 接收区数据 /// public string DataReceiveInfo { get { return dataReceiveInfo; } set { dataReceiveInfo = value; RaisePropertyChanged(); } } ////// 发送数据 /// public string SendData { get { return sendData; } set { sendData = value; RaisePropertyChanged(); } } ////// 发送区16进制 /// public bool SendFormat16 { get { return sendFormat16; } set { sendFormat16 = value; RaisePropertyChanged(); } } #endregion #region 串口参数信息 ////// 开关 /// public bool IsOpen { get { return isOpen; } set { isOpen = value; RaisePropertyChanged(); } } ////// 数据位 /// public int DataBit { get { return dataBit; } set { dataBit = value; RaisePropertyChanged(); } } ////// 波特率 /// public int BaudRdate { get { return baudRdate; } set { baudRdate = value; RaisePropertyChanged(); } } ////// 端口 /// public Port Port { get { return port; } set { port = value; RaisePropertyChanged(); } } ////// 校验 /// public CheckMode CheckMode { get { return checkMode; } set { checkMode = value; RaisePropertyChanged(); } } ////// 停止位 /// public StopBit StopBit { get { return stopBit; } set { stopBit = value; RaisePropertyChanged(); } } ////// COM /// public SerialPort SerialPort { get { return serialPort; } set { serialPort = value; RaisePropertyChanged(); } } #endregion #region 串口操作方法 ////// 开启串口 /// ///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; } /// /// 关闭串口 /// ///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; } } /// /// 发送数据 /// 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"); } ////// 清空接收区 /// public void Clear() { this.DataReceiveInfo = string.Empty; } ////// 清空发送区和缓存区 /// public void ClearText() { this.SendData = string.Empty; this.SendCount = 0; this.ReceiveCount = 0; } #endregion ////// 返回事件 /// /// /// 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 { ////// Initializes a new instance of the MainViewModel class. /// public MainViewModel() { ComParameterConfig = new ComParameterConfig(); CurrentParameter = new CurrentParameter(); } private ComParameterConfig comParameter; ////// 参数类 /// public ComParameterConfig ComParameterConfig { get { return comParameter; } set { comParameter = value; RaisePropertyChanged(); } } private CurrentParameter currentParameter; ////// 当前配置参数 /// 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; } } ////// 根据配置打开端口 /// 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; } } ////// 发送数据 /// 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; } } ////// 清空接收区 /// 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; } } ////// 清空界面值 /// public void ClearText() { this.CurrentParameter.ClearText(); } #endregion }
5.CRC校验核心类
作用:主要实现数据校验, 含ModbusCR标准校验
////// CRC校验 /// 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(); } ////// 字符串转16进制字符数组 /// /// ///public static byte[] StringToHexByte(string str) { return StringToHexByte(str, false); } /// /// 字符串转16进制字符数组 /// /// /// 是否过滤掉中文字符 ///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.自定义样式按钮
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.引用字体
4.绑定命令和元素
写在最后
主项目的结构图 , 如下:
到此这篇关于C#实现串口调试工具的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持脚本之家。