WPF制作简易串口调试助手

    xiaoxiao2025-02-09  41

    WPF制作简易串口调试助手(上位机部分)

    实验环境:vs2013 参考文档:https://docs.microsoft.com/zh-cn/dotnet/api/system.io.ports?view=netframework-4.7.2

    下位机部分:https://blog.csdn.net/weixin_42462552/article/details/85937289

    一、窗体程序制作:

    (一)UI界面

    1.首先我们参照网上的串口调试助手将UI界面的大体框架完成

    2.其中数据发送区域的文本需要实现自动换行功能,根据网上资料,需要设置TextWrapping的属性为"Wrap"。 3.接下来是对串口号、波特率、校验位等的一些内容设置,其中较复杂的是串口号的设置,需要根据电脑的端口实际情况来确定串口数量,不能同设置波特率属性一般,直接在程序里指定唯一值。

    UI界面xml代码

    <Window x:Class="WpfApplication7.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="DIY视界-旋转LED改字软件" Height="600" Width="850" WindowState="Maximized" FontFamily="Segoe UI" FontSize="12" Icon="Resources/5.jpg"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="185*"/> <RowDefinition Height="410*"/> </Grid.RowDefinitions> <Grid Grid.Column="1"> <Grid.ColumnDefinitions> <ColumnDefinition Width="200*"/> <ColumnDefinition Width="220*"/> <ColumnDefinition Width="390*"/> </Grid.ColumnDefinitions> <Image HorizontalAlignment="Left" Height="169" VerticalAlignment="Top" Width="211" Source="Resources/9.jpg" Margin="0,0,0,0.2"/> <Image HorizontalAlignment="Left" Height="169" Margin="2,0,0,-0.2" VerticalAlignment="Top" Width="228" Source="Resources/10.jpg" Grid.Column="1" Grid.ColumnSpan="2"/> <Image Grid.Column="2" HorizontalAlignment="Left" Height="169" Margin="0.4,10,-1.8,-10.2" VerticalAlignment="Top" Width="407" Source="Resources/11.jpg"/> </Grid> <TabControl HorizontalAlignment="Left" Height="372" Margin="10,10.4,0,0" Grid.Row="1" VerticalAlignment="Top" Width="823"> <TabItem Header="平面数据"> <Grid Background="#FFE5E5E5"> <GroupBox Grid.Row="2"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="510,270,0,5" Width="291"> <Button x:Name="bt_ClearSendFData" Content="清空平面数据" Margin="20,15,20,15" Click="bt_ClearSendFData_Click" Width="92"/> <Button x:Name="bt_sendf" Content="发送平面数据" Margin="20,15,20,15" Click="bt_sendf_Click" Width="92"/> </StackPanel> </GroupBox> <GroupBox Header="发送平面数据区" BorderBrush="Black" Margin="10,10,10,80"> <ScrollViewer VerticalScrollBarVisibility="Auto"> <TextBox Name="tb_SendFlatData" TextWrapping="Wrap"/> </ScrollViewer> </GroupBox> </Grid> </TabItem> <TabItem Header="立体数据"> <Grid Background="#FFE5E5E5"> <GroupBox Grid.Row="2"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="510,270,0,5" Width="291"> <Button Name="bt_ClearSendSData" Content="清空立体数据" Margin="20,15,20,15" Click="bt_ClearSendSData_Click"/> <Button Name="bt_sends" Content="发送立体数据" Margin="20,15,20,15" Click="bt_sends_Click"/> </StackPanel> </GroupBox> <GroupBox Header="发送立体数据区" BorderBrush="Black" Margin="10,10,10,80"> <ScrollViewer VerticalScrollBarVisibility="Auto"> <TextBox Name="tb_SendStereoData" TextWrapping="Wrap"/> </ScrollViewer> </GroupBox> </Grid> </TabItem> <TabItem Header="串口参数"> <Grid Background="#FFE5E5E5"> <GroupBox BorderBrush="Black" Margin="10,10,9.8,10"> <StackPanel > <StackPanel Orientation="Horizontal"> <TextBlock Text="串口号" Width="auto" Margin="15" Padding="10,6,0,0"/> <ComboBox Name="cb_SerialPortNumber" Width="180" Margin="30,10,0,10"/> <Button x:Name="btn1" Height="21" HorizontalAlignment="Left" Margin="11,12,0,0" VerticalAlignment="Top" Width="22" Click="btn1_Click"> <Button.Background> <ImageBrush ImageSource="Resources/7.jpg"/> </Button.Background> </Button> <TextBlock Text="波特率" Width="auto" Margin="15" Padding="70,6,0,0"/> <ComboBox x:Name="cb_BaudRate" Width="180" Margin="100,10,0,10" SelectedIndex="8"> <ComboBoxItem Content="600"/> <ComboBoxItem Content="1200"/> <ComboBoxItem Content="2400"/> <ComboBoxItem Content="4800"/> <ComboBoxItem Content="9600"/> <ComboBoxItem Content="14400"/> <ComboBoxItem Content="19200"/> <ComboBoxItem Content="28800"/> <ComboBoxItem Content="38400"/> <ComboBoxItem Content="57600"/> <ComboBoxItem Content="115200"/> <ComboBoxItem Content="230400"/> <ComboBoxItem Content="460800"/> </ComboBox> </StackPanel> <StackPanel Orientation="Horizontal" Margin="0,20,0,0"> <TextBlock Text="校验位" Width="auto" Margin="15" Padding="10,6,0,0"/> <ComboBox Width="180" Margin="30,10,0,10" SelectedIndex="0"> <ComboBoxItem Content="None"/> <ComboBoxItem Content="Odd"/> <ComboBoxItem Content="Even"/> <ComboBoxItem Content="Space"/> <ComboBoxItem Content="Mark"/> </ComboBox> <TextBlock Text="数据位" Width="auto" Margin="15" Padding="100,6,0,0"/> <ComboBox Width="180" Margin="100,10,0,10" SelectedIndex="0"> <ComboBoxItem Content="8"/> <ComboBoxItem Content="7"/> <ComboBoxItem Content="6"/> </ComboBox> </StackPanel> <StackPanel Orientation="Horizontal" Margin="0,30,0,0"> <TextBlock Text="停止位" Width="auto" Margin="15" Padding="10,6,0,0"/> <ComboBox Width="180" Margin="30,10,0,10" SelectedIndex="0"> <ComboBoxItem Content="One"/> <ComboBoxItem Content="Two"/> </ComboBox> <TextBlock Text="接受超时位(Ms)" Width="auto" Margin="15" Padding="100,6,0,0"/> <ComboBox x:Name="cb_Timeout" Width="180" Margin="57,10,0,10" SelectedIndex="8"> <ComboBoxItem Content="100"/> <ComboBoxItem Content="200"/> <ComboBoxItem Content="500"/> <ComboBoxItem Content="800"/> <ComboBoxItem Content="1000"/> <ComboBoxItem Content="1500"/> <ComboBoxItem Content="2000"/> <ComboBoxItem Content="3000"/> <ComboBoxItem Content="4000"/> <ComboBoxItem Content="5000"/> <ComboBoxItem Content="6000"/> <ComboBoxItem Content="7000"/> <ComboBoxItem Content="8000"/> <ComboBoxItem Content="9000"/> <ComboBoxItem Content="10000"/> <ComboBoxItem Content="20000"/> </ComboBox> </StackPanel> <StackPanel Orientation="Horizontal" Margin="0,35,0,0"> <TextBlock Name="tb_switchStatus" Text="串口为关闭状态" Width="auto" Margin="15" Padding="10,6,0,0"/> <Ellipse Name="e_status" Fill="#000000" Height="10" Stroke="Black" Margin="5,3,5,0" Width="10" /> <Button Name="bt_SerialSwitch" Content="打开串口" Margin="10" Padding="5,0,5,0" Click="bt_SerialSwitch_Click"/> </StackPanel> </StackPanel> </GroupBox> </Grid> </TabItem> <TabItem Header="接收数据"> <Grid> <TextBox x:Name="tbreceive" HorizontalAlignment="Left" Height="103" Margin="198,27,0,0" TextWrapping="Wrap" Text="Hello,Server" VerticalAlignment="Top" Width="369" /> <Button Content="发送" HorizontalAlignment="Left" Margin="664,61,0,0" VerticalAlignment="Top" Width="65" Click="Button_Click_1" Height="29"/> <TextBlock HorizontalAlignment="Left" Margin="33,27,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="23" Width="68"><Run Language="zh-cn" Text="发送内容:"/></TextBlock> <TextBlock HorizontalAlignment="Left" Margin="33,157,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="23" Width="100"><Run Language="zh-cn" Text="收到服务器应答:"/></TextBlock> <TextBox x:Name="tbsend" HorizontalAlignment="Left" Height="103" Margin="198,157,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="369" /> <Button Content="创意" HorizontalAlignment="Left" Margin="664,203,0,0" VerticalAlignment="Top" Width="65" Click="Button_Click_2" Height="29"/> <Button Content="擦除" HorizontalAlignment="Left" Margin="664,138,0,0" VerticalAlignment="Top" Width="65" Click="Button_Click_3" Height="29"/> </Grid> </TabItem> </TabControl> </Grid> </Window>

    完整代码

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.IO; using System.IO.Ports; using System.Windows.Threading; using System.Threading; using System.Net.Sockets; using System.Net; namespace WpfApplication7 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public SerialPort _serialPort = new SerialPort(); DispatcherTimer timer = new DispatcherTimer(); Socket clientSocket; byte[] receiveBuffer = new byte[1024]; byte[] sendBuffer = new byte[256]; void refreshports() { cb_SerialPortNumber.Items.Clear(); string[] ports = SerialPort.GetPortNames();//获取当前计算机的串行端口名的数组。 for (int index = 0; index < ports.Length; index++) { cb_SerialPortNumber.Items.Add(ports[index]);//添加item cb_SerialPortNumber.SelectedIndex = index; //设置显示的item索引 } bt_sends.IsEnabled = false; bt_sendf.IsEnabled = false; } public void SendFile(string filename) { FileInfo fi = new FileInfo(filename); byte[] len = BitConverter.GetBytes(fi.Length); //首先把文件长度发送过去 clientSocket.BeginSendFile(filename, len, null, TransmitFileOptions.UseDefaultWorkerThread, new AsyncCallback(SendFileCallback), null); } private void SendFileCallback(IAsyncResult iar) { clientSocket.EndSendFile(iar); } public MainWindow() { InitializeComponent(); refreshports(); //添加timer timer.Tick += new EventHandler(timer_Tick); //timer.Interval = TimeSpan.FromSeconds(0.1); //设置刷新的间隔时间 timer.Start(); //创建一个Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try { //设定服务器IP地址 //127.0.0.1或者localhost都是指本机,通常在调试的时候。 //真实的服务器地址是39.104.94.55(也有可能会改变,根据题目来) //clientSocket.Connect("39.104.94.55", 2405); clientSocket.Connect("39.104.94.55", 2405); clientSocket.BeginReceive(receiveBuffer, 0, receiveBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), clientSocket); Console.WriteLine("连接服务器成功"); } catch { Console.WriteLine("连接服务器失败,请按回车键退出!"); } } //这个函数就是接收函数,当服务器有数据返回时,这个函数会被自动调用 //需要解析lora基站协议 //0x9A ID ID ID ID ID ID C L DT[0-127] CRCH CRCL 0xA9 private void ReceiveMessage(IAsyncResult ar) { try { var socket2 = ar.AsyncState as Socket; var length = socket2.EndReceive(ar); if (0x9A == receiveBuffer[0]) { byte len = receiveBuffer[8]; var message = Encoding.Default.GetString(receiveBuffer, 9, len); MessageBox.Show(message); Console.WriteLine("You got a message: " + message); } clientSocket.BeginReceive(receiveBuffer, 0, receiveBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), clientSocket); } catch (Exception ex) { Console.WriteLine("Err:" + ex.Message); } } //这是crc16校验函数,输入待校验数组,输出两个校验值crch和crcl //正常情况下,这个函数会提供好,不需要自己写 private static byte[] CRC16(byte[] data, int ll) { byte[] returnVal = new byte[2]; byte CRC16Lo, CRC16Hi, CL, CH, SaveHi, SaveLo; int i, Flag; CRC16Lo = 0xFF; CRC16Hi = 0xFF; CL = 0x86; CH = 0x68; for (i = 0; i < ll; i++) { CRC16Lo = (byte)(CRC16Lo ^ data[i]);//每一个数据与CRC寄存器进行异或 for (Flag = 0; Flag <= 7; Flag++) { SaveHi = CRC16Hi; SaveLo = CRC16Lo; CRC16Hi = (byte)(CRC16Hi >> 1);//高位右移一位 CRC16Lo = (byte)(CRC16Lo >> 1);//低位右移一位 if ((SaveHi & 0x01) == 0x01)//如果高位字节最后一位为 { CRC16Lo = (byte)(CRC16Lo | 0x80);//则低位字节右移后前面补 否则自动补0 } if ((SaveLo & 0x01) == 0x01)//如果LSB为1,则与多项式码进行异或 { CRC16Hi = (byte)(CRC16Hi ^ CH); CRC16Lo = (byte)(CRC16Lo ^ CL); } } } returnVal[0] = CRC16Hi;//CRC高位 returnVal[1] = CRC16Lo;//CRC低位 return returnVal; } public void timer_Tick(object sender, EventArgs e)//添加时间显示 { this.Title = string.Concat("DIY视界-旋转LED改字软件 ", DateTime.Now.ToString("yyyy/MM/dd hh:mm:ss")); } public void initialize()//初始化 { try { _serialPort.PortName = cb_SerialPortNumber.SelectedItem.ToString();//串口号 ComboBoxItem seletedItem = (ComboBoxItem)this.cb_BaudRate.SelectedItem; _serialPort.BaudRate = Convert.ToInt32(seletedItem.Content.ToString());//波特率 _serialPort.DataBits = 8;//数据位 _serialPort.StopBits = StopBits.One;//停止位 _serialPort.Parity = Parity.None;//校验位 _serialPort.WriteTimeout = 2000;//接受超时位 } catch { } } private void bt_ClearSendSData_Click(object sender, RoutedEventArgs e)//清空立体发送区域 { tb_SendStereoData.Text = ""; } private void bt_ClearSendFData_Click(object sender, RoutedEventArgs e)//清空平面发送区域 { tb_SendFlatData.Text = ""; } private void bt_SerialSwitch_Click(object sender, RoutedEventArgs e)//串口开关 { initialize();//初始化 string strContent = this.bt_SerialSwitch.Content.ToString(); if (strContent == "打开串口") { _serialPort.Open(); bt_SerialSwitch.Content = "关闭串口"; tb_switchStatus.Text = "串口为打开状态"; bt_sends.IsEnabled = true; bt_sendf.IsEnabled = true; } else { _serialPort.Close(); bt_SerialSwitch.Content = "打开串口"; tb_switchStatus.Text = "串口为关闭状态"; bt_sends.IsEnabled = false; bt_sendf.IsEnabled = false; } } byte[] bytes = new byte[1024]; SerialPort sp = null; byte[] buffer = new byte[127]; byte[] erasebuffer = { 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7A, 0x01, 0x00, 0x01, 0xA7, 0x68, 0x86, 0x59 }; byte[] erasbuffer = { 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7A, 0x09, 0x00, 0x09, 0xA7, 0x68, 0x86, 0x59 }; private void bt_sends_Click(object sender, RoutedEventArgs e)//发送立体按钮 { try { if (tb_SendStereoData.Text.ToString().Equals("但愿人长久")) { buffer[0] = 0x95; for (int j = 1; j <= 5; j++) { buffer[j] = 0x00; } buffer[6] = 0x00; buffer[7] = 0x01; buffer[8] = 0x01; buffer[9] = 0x7A;//数据 buffer[10] = 0x06; buffer[11] = 0x00; buffer[12] = 0x06; buffer[13] = 0xA7; buffer[14] = 0x20; buffer[15] = 0x00;//校验码 buffer[16] = 0x59; _serialPort.Write(buffer, 0, 17); //clientSocket.Send(buffer); //http //这里的IMEI=xxx,xxx指的是IMEI设备号 //这里的data=yyy,yyy指的是data数据 //参照pdf可以得到以上两个数据 string param = "IMEI=0867717036739598&data="; string res = ""; foreach (byte b in buffer) { int value = Convert.ToInt32(b); string hex = String.Format("{0:X2}", value); res += hex; } MessageBox.Show(param + res); byte[] bs = Encoding.ASCII.GetBytes(param); //这里的"http://192.168.43.209:2405/transferData?" 是服务器url,注意最后有个? HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create("http://192.168.43.209:2405/transferData"); req.Method = "POST"; req.ContentType = "application/x-www-form-urlencoded"; req.ContentLength = bs.Length; using (Stream reqStream = req.GetRequestStream()) { reqStream.Write(bs, 0, bs.Length); } using (WebResponse wr = req.GetResponse()) { StreamReader reader = new StreamReader(wr.GetResponseStream(), Encoding.UTF8); //result是服务器返回的数据,正确的情况下,应该是success string result = reader.ReadToEnd(); } } if (tb_SendStereoData.Text.ToString().Equals("千里共婵娟")) { buffer[0] = 0x95; for (int j = 1; j <= 5; j++) { buffer[j] = 0x00; } buffer[6] = 0x00; buffer[7] = 0x01; buffer[8] = 0x01; buffer[9] = 0x7A;//数据 buffer[10] = 0x07; buffer[11] = 0x00; buffer[12] = 0x07; buffer[13] = 0xA7; buffer[14] = 0x20; buffer[15] = 0x00;//校验码 buffer[16] = 0x59; } //clientSocket.Send(buffer); _serialPort.Write(buffer, 0, 17); MessageBox.Show("发送平面数据成功!", "正确提示"); } catch (Exception) { MessageBox.Show("发送平面数据时发生错误!", "错误提示"); return; } } private void bt_sendf_Click(object sender, RoutedEventArgs e)//发送平面按钮 { try { if (tb_SendFlatData.Text.ToString().Equals("日期")) { buffer[0] = 0x95; for (int j = 1; j <= 5; j++) { buffer[j] = 0x00; } buffer[6] = 0x00; buffer[7] = 0x01; buffer[8] = 0x01; buffer[9] = 0x7A;//数据 buffer[10] = 0x08; buffer[11] = 0x00; buffer[12] = 0x08; buffer[13] = 0xA7; buffer[14] = 0x20; buffer[15] = 0x00;//校验码 buffer[16] = 0x59; } if (tb_SendFlatData.Text.ToString().Equals("倒计时")) { buffer[0] = 0x95; for (int j = 1; j <= 5; j++) { buffer[j] = 0x00; } buffer[6] = 0x00; buffer[7] = 0x00; buffer[8] = 0x01; buffer[9] = 0x7A;//数据 buffer[10] = 0x08; buffer[11] = 0x00; buffer[12] = 0x08; buffer[13] = 0xA7; buffer[14] = 0x68; buffer[15] = 0x86;//校验码 buffer[16] = 0x59; } //clientSocket.Send(buffer); _serialPort.Write(buffer, 0, 17); MessageBox.Show("发送平面数据成功!", "正确提示"); } catch (Exception) { MessageBox.Show("发送平面数据时发生错误!", "错误提示"); return; } } //创意 private void Button_Click_2(object sender, RoutedEventArgs e) { buffer[0] = 0x95; for (int j = 1; j <= 5; j++) { buffer[j] = 0x00; } buffer[6] = 0x00; buffer[7] = 0x01; buffer[8] = 0x01; buffer[9] = 0x7A;//数据 buffer[10] = 0x09; buffer[11] = 0x00; buffer[12] = 0x09; buffer[13] = 0xA7; buffer[14] = 0x20; buffer[15] = 0x00;//校验码 buffer[16] = 0x59; clientSocket.Send(erasbuffer); } static byte[] ereebuffer = new byte[1024]; //擦除 private void Button_Click_3(object sender, RoutedEventArgs e) { clientSocket.Send(erasebuffer); } //发送 private void Button_Click_1(object sender, RoutedEventArgs e) { //帧头 0x95 sendBuffer[0] = 0x95; //地址ID,6个字节,题目中给出,是设备标识或者组标识 sendBuffer[1] = 0x0; sendBuffer[2] = 0x0; sendBuffer[3] = 0x0; sendBuffer[4] = 0x0; sendBuffer[5] = 0x0; sendBuffer[6] = 0x0; //控制码 sendBuffer[7] = 0x0; //数据长度 string sendMessage = tbreceive.Text.ToString(); byte[] data = Encoding.ASCII.GetBytes(sendMessage); sendBuffer[8] = (byte)data.Length; Array.Copy(data, 0, sendBuffer, 9, data.Length); //校验码 sendBuffer[9 + data.Length] = CRC16(sendBuffer, 9 + data.Length)[0]; sendBuffer[9 + data.Length + 1] = CRC16(sendBuffer, 9 + data.Length)[1]; //帧尾 sendBuffer[9 + data.Length + 2] = 0x59; try { clientSocket.Send(sendBuffer); Console.WriteLine("向服务器发送消息:{0}" + sendMessage); } catch (Exception ee) { ; } Console.WriteLine("发送完毕"); } private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { string strContent = this.bt_SerialSwitch.Content.ToString(); if (strContent == "关闭串口") { _serialPort.Close(); } } private void btn1_Click(object sender, RoutedEventArgs e) { refreshports(); } } }

     

    最新回复(0)