项目要求:下位机使用单片机,不断发送一个随机数值给上位机,上位机收到数据,显示在显示框中。
实验环境: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/85561632



一、窗体程序制作:

(一)UI界面

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

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

string[] ports = SerialPort.GetPortNames();//获取当前计算机的串行端口名的数组。for(int index =0;index<ports.Length;index++){cb_SerialPortNumber.Items.Add(ports[index]);//添加itemcb_SerialPortNumber.SelectedIndex = index; //设置显示的item索引}

在官方文档中找到获取当前计算机的串行端口名的数组的函数GetPortNames (),接着使用循环,利用comboBox的Items.Add方法将数组内的元素添加到comboBox中。

UI界面xml代码

<Window x:Class="SerialDebuggingAssistant.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="clr-namespace:SerialDebuggingAssistant"Title="串口调试助手" Height="400" Width="600" ResizeMode="CanMinimize" Closing="Window_Closing"><Grid><Grid.RowDefinitions><RowDefinition Height="192*"/><RowDefinition Height="82*"/><RowDefinition Height="97*"/></Grid.RowDefinitions><Grid.ColumnDefinitions><ColumnDefinition Width="266*"/><ColumnDefinition Width="327*"/></Grid.ColumnDefinitions><GroupBox Header="串口参数设置" BorderBrush="Black"><StackPanel ><StackPanel Orientation="Horizontal"><TextBlock Text="串口号" Width="auto" Margin="5" Padding="0,6,0,0"/><ComboBox Name="cb_SerialPortNumber" Width="65" Margin="5,10,5,10"/><TextBlock Text="波特率" Width="auto" Margin="5" Padding="0,6,0,0"/><ComboBox Name="cb_BaudRate" Width="68" Margin="10,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"><TextBlock Text="校验位" Width="auto" Margin="5" Padding="0,6,0,0"/><ComboBox Width="65" Margin="5,10,5,10" SelectedIndex="0"><ComboBoxItem Content="None"/></ComboBox><TextBlock Text="数据位" Width="auto" Margin="5" Padding="0,6,0,0"/><ComboBox Width="68" Margin="10,10,0,10" SelectedIndex="0"><ComboBoxItem Content="8"/></ComboBox></StackPanel><StackPanel Orientation="Horizontal"><TextBlock Text="停止位" Width="auto" Margin="10,10,0,10" Padding="0,3,0,0"/><ComboBox Width="65" Margin="5,10,5,10" SelectedIndex="0"><ComboBoxItem Content="one"/></ComboBox></StackPanel><StackPanel Orientation="Horizontal"><TextBlock Name="tb_switchStatus" Text="串口为关闭状态" Width="auto" Margin="10" Padding="0,2,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><GroupBox Header="接收数据设置" Grid.Row="1" BorderBrush="Black"><StackPanel Orientation="Horizontal" HorizontalAlignment="Center"><Button Name="ClearReceiveData" Content="清空接收数据" Margin="20" Click="ClearReceiveData_Click"/><Button Name="bt_stopSend" Content="停止接收" Margin="20" Click="bt_stopSend_Click"/></StackPanel></GroupBox><GroupBox Header="发送数据设置" Grid.Row="2" BorderBrush="Black"><StackPanel Orientation="Horizontal" HorizontalAlignment="Center"><Button Name="bt_ClearSendData" Content="清空发送数据" Margin="20,30,20,25" Click="bt_ClearSendData_Click"/><Button Name="bt_send" Content="手动发送" Margin="20,30,20,25" Click="bt_send_Click"/></StackPanel></GroupBox><GroupBox Header="接收数据区" Grid.Column="1" Grid.RowSpan="2" BorderBrush="Black"  Margin="5"><ScrollViewer VerticalScrollBarVisibility="Auto"><TextBlock Name="tb_receiveData" TextWrapping="Wrap"/></ScrollViewer></GroupBox><GroupBox Header="发送数据区" Grid.Row="2" Grid.Column="1"  BorderBrush="Black"><ScrollViewer VerticalScrollBarVisibility="Auto">  <!--滚动条--><TextBox Name="tb_SendData" TextWrapping="Wrap"/></ScrollViewer></GroupBox></Grid>
</Window>

(二)后台逻辑代码(.cs代码部分)

1.添加using引用

using System.IO.Ports;

2.新建串口对象

public SerialPort _serialPort = new SerialPort ();

3.设置一些简单控件的逻辑功能

(1)“清空接收数据”和“清空发送数据”按钮的点击事件,只需要把对应区域的Text属性设为空,Text = “” 。

     private void bt_ClearSendData_Click(object sender, RoutedEventArgs e)//清空发送区域{tb_SendData.Text = "";}private void ClearReceiveData_Click(object sender, RoutedEventArgs e)//清空接受数据{tb_receiveData.Text = "";}

4.设置串口参数

(1)如数据位、奇偶校验位、停止位和波特率等,在官方文档中说明使用PortName、BaudRate、DataBits、StopBits、Parity分别可以设置串口、波特率、数据位、停止位和校验位,其中串口、波特率是根据实际情况来设定的,数据位、停止位和校验位因为基本上在使用时不会去做更改,所以指定唯一值。

     public void initialize()//初始化{_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;//校验位}

5.编写“打开串口”按钮的点击事件

(1)这里主要实现的功能是打开和关闭串口,添加数据接收事件和取消数据接收事件。开关串口需要使用两个函数open()和close()。

     private void bt_SerialSwitch_Click(object sender, RoutedEventArgs e)//串口开关{initialize();//初始化string strContent = this.bt_SerialSwitch.Content.ToString();if (strContent == "打开串口"){_serialPort.Open();_serialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);//添加数据接收事件//_serialPort.DataReceived += DataReceivedHandler;bt_SerialSwitch.Content = "关闭串口";tb_switchStatus.Text = "串口为打开状态";bt_send.IsEnabled = true;bt_stopSend.IsEnabled = true;e_status.Fill = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FF0000"));}else{_serialPort.DataReceived -= DataReceivedHandler;_serialPort.Close();bt_SerialSwitch.Content = "打开串口";tb_switchStatus.Text = "串口为关闭状态";e_status.Fill = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#000000"));bt_send.IsEnabled = false;bt_stopSend.IsEnabled = false;}}

6.添加数据接收事件

(1)官方文档上说明需要使用SerialErrorReceivedEventHandler(注解原文:“创建SerialErrorReceivedEventHandler委托时,需要标识将处理该事件的方法。若要将事件与事件处理程序关联,请将该委托的一个实例添加到事件中”)。在文档中找到了DataReceived,具体写法是:
SerialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);后来发现可以简写 serialPort1.DataReceived += new DataReceivedHandler; 其中的DataReceivedHandler就是数据接收的具体实现方法。

//在串口打开点击事件中添加
_serialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);//添加数据接收事件

7.数据接收

(1)获取数据

  • 编写数据接收事件DataReceivedHandler,功能是读取下位机的数据,显示在数据接收区中。首先是使用BytesToRead获取接收缓冲区中数据的字节数,然后根据获取到的字节数来创建一个对应大小的byte数组,再使用Read函数将获取的数据放入byte数组中,然后还要对byte数组的数据转换成十六进制数,使用到了这个函数BitConverter.ToString(官方解释是“将指定的字节子数组的每个元素的数值转换为它的等效十六进制字符串表示形式”)。
    (2)更新UI界面
  • 将处理好的数据加载到Text属性上,Text += strData;不过访问UI组件必须放在主线程中运行,不然会抛异常“调用线程无法访问此对象,因为另一个线程拥有该对象”。
  • 窗体控件是由主线程执行的,因此在多线程中试图更改控件的内容比如覆盖或追加Textblock的内容,是无法在分线程中执行的,因为Textblock是由UI界面线程控制的,因此采用Dispatcher.Invoke(() =>{ ……});里面调用Textblock控件。
         public void DataReceivedHandler(object sender,SerialDataReceivedEventArgs e)//读取下位机的数据,显示在textBlock中{          int len = this._serialPort.BytesToRead;byte[] buffer = new byte[len];this._serialPort.Read(buffer, 0, len);         string strData = BitConverter.ToString(buffer, 0, len);Dispatcher.Invoke(() =>{this.tb_receiveData.Text += strData;this.tb_receiveData.Text += "-";//字符分隔-});}

8.数据发送

(1)使用Write()方法发送数据

  • 数据发送跟数据接收的原理差不多,只不过不需要使用线程方式。首先将数据发送区域TextBox的Text属性值赋值到一个string类型的变量中,然后使用一个byte数组储存数据,因为输入的字符是十六进制数,所以这边做了个循环用Substring()切割字符串方法,每次取两位字符组成一个16进制,接着使用Write()方法发送数据。
     private void bt_send_Click(object sender, RoutedEventArgs e)//发送按钮{string SendData = tb_SendData.Text;byte[] Data = new byte[20];for (int i = 0; i < SendData.Length / 2; i++){//每次取两位字符组成一个16进制Data[i] = Convert.ToByte(tb_SendData.Text.Substring(i * 2, 2), 16);}this._serialPort.Write(Data, 0, Data.Length);   }

(2)“ 停止接收数据”和“继续接收数据”

  • 这里是分别通过发送0x99和0x66指令到单片机中实现停止和继续发送数据功能,同时还使用取消和添加数据接收事件DataReceivedHandler的方式,其实这两种方式也就是主动和被动的不同罢了,第一种方式更为真实一些。
     private void bt_stopSend_Click(object sender, RoutedEventArgs e)//停止接收数据{string strContent = this.bt_stopSend.Content.ToString();if (strContent == "停止接收"){byte[] data = { 0x99 };_serialPort.DataReceived -= DataReceivedHandler;this._serialPort.Write(data, 0, data.Length);bt_stopSend.Content = "继续接收";}else{byte[] data = { 0x66 };this._serialPort.Write(data, 0, data.Length);_serialPort.DataReceived += DataReceivedHandler;bt_stopSend.Content = "停止接收";}}

9.程序关闭事件

(1)当用户将程序关闭时,如果串口处于开启状态时,应该在关闭程序时将串口关闭。

     private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e){string strContent = this.bt_SerialSwitch.Content.ToString();if (strContent == "关闭串口"){_serialPort.Close();}}

10.测试时引发的异常

(1)我们发现当串口还没被打开时,如果点击“停止接收”和“手动发送”按钮,会出现异常,导致程序无法进行,所以用了这个办法,当串口还没别打开,“停止接收”和“手动发送”按钮的IsEnabled属性就处于false状态,直到串口打开,然后才恢复true状态。
(2)当打开串口后,点击关闭串口按钮时,也会抛异常,并且提示在初始化那一块和关闭串口事件那块,所以我们将这两个地方加了try……catch,让程序不去理他继续运行。

三、实验结果

(一)当程序运行,用户将串口号和波特率设置完成,点击“打开串口”按钮时,“接收数据区域”会显示从下位机传来的数据。
(二)当点击“停止接收”按钮时,下位机停止发送数据,“接收数据区域”停止显示,按钮上的文本变成“继续接收”,当再次点击按钮时,下位机继续发送数据,“接收数据区域”继续显示数据,按钮文本变成“停止接收”。
(三)当点击“清空接收数据”和“清空发送数据”时,对应区域的文本将会清空。
(四)当在“发送数据区域”输入“99”,点击“手动发送”按钮时,下位机停止发送数据,“接收数据区域”停止显示。发送“66”时,下位机继续发送数据,“接收数据区域”继续显示数据。

完整代码

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.Ports;//这是手动添加的
namespace SerialDebuggingAssistant
{/// <summary>/// MainWindow.xaml 的交互逻辑/// </summary>/// public partial class MainWindow : Window{public SerialPort _serialPort = new SerialPort ();public MainWindow(){InitializeComponent();string[] ports = SerialPort.GetPortNames();//获取当前计算机的串行端口名的数组。for(int index =0;index<ports.Length;index++){cb_SerialPortNumber.Items.Add(ports[index]);//添加itemcb_SerialPortNumber.SelectedIndex = index; //设置显示的item索引}bt_send.IsEnabled = false;bt_stopSend.IsEnabled = false;}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;//校验位}catch{}}private void bt_ClearSendData_Click(object sender, RoutedEventArgs e)//清空发送区域{tb_SendData.Text = "";}private void ClearReceiveData_Click(object sender, RoutedEventArgs e)//清空接受数据{tb_receiveData.Text = "";}private void bt_SerialSwitch_Click(object sender, RoutedEventArgs e)//串口开关{initialize();//初始化string strContent = this.bt_SerialSwitch.Content.ToString();if (strContent == "打开串口"){try{_serialPort.Open();_serialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);//添加数据接收事件//_serialPort.DataReceived += DataReceivedHandler;bt_SerialSwitch.Content = "关闭串口";tb_switchStatus.Text = "串口为打开状态";bt_send.IsEnabled = true;bt_stopSend.IsEnabled = true;e_status.Fill = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FF0000"));}catch { }}else{try{_serialPort.DataReceived -= DataReceivedHandler;_serialPort.Close();bt_SerialSwitch.Content = "打开串口";tb_switchStatus.Text = "串口为关闭状态";e_status.Fill = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#000000"));bt_send.IsEnabled = false;bt_stopSend.IsEnabled = false;}catch{}}}public void DataReceivedHandler(object sender,SerialDataReceivedEventArgs e)//读取下位机的数据,显示在textBlock中{          int len = this._serialPort.BytesToRead;byte[] buffer = new byte[len];this._serialPort.Read(buffer, 0, len);         string strData = BitConverter.ToString(buffer, 0, len);Dispatcher.Invoke(() =>{this.tb_receiveData.Text += strData;this.tb_receiveData.Text += "-";});}private void bt_send_Click(object sender, RoutedEventArgs e)//发送按钮{string SendData = tb_SendData.Text;byte[] Data = new byte[20];for (int i = 0; i < SendData.Length / 2; i++){//每次取两位字符组成一个16进制Data[i] = Convert.ToByte(tb_SendData.Text.Substring(i * 2, 2), 16);}this._serialPort.Write(Data, 0, Data.Length);   }private void bt_stopSend_Click(object sender, RoutedEventArgs e)//停止接收数据{string strContent = this.bt_stopSend.Content.ToString();if (strContent == "停止接收"){byte[] data = { 0x99 };_serialPort.DataReceived -= DataReceivedHandler;this._serialPort.Write(data, 0, data.Length);bt_stopSend.Content = "继续接收";}else{byte[] data = { 0x66 };this._serialPort.Write(data, 0, data.Length);_serialPort.DataReceived += DataReceivedHandler;bt_stopSend.Content = "停止接收";}}private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e){string strContent = this.bt_SerialSwitch.Content.ToString();if (strContent == "关闭串口"){_serialPort.Close();}}}
}

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

  1. matlab制作以太网数据接收上位机_Python制作串口通讯上位机

    串口通讯具有简单易用的特点广泛应用于测试设备的通讯和数据传递.单片机与计算机的通讯等,本案例基于Python语言制作一个用于接收燃油质量流量计的串口通讯上位机,实现数据的读取和保存. 1. 相关知识点 ...

  2. <Python>简易串口调试助手(优化版,自由协议+ModbusRTU)

    更新日志: 202210141442:优化功能(增加自动读取和单次读取可选)和界面美化. 202210141627:优化功能(添加菜单和读取优化,连接状态显示) 202210261446:优化功能(添 ...

  3. matlab制作以太网数据接收上位机_3D激光扫描仪设计及数据处理

    本文内容转载自<电子技术应用>2019年第10期,版权归<电子技术应用>编辑部所有. 段清明,王凡,徐琳琳,全文俊 吉林大学仪器科学与电气工程学院 摘要:利用2D激光雷达配合云 ...

  4. 如何制作魔方机器人-01上位机开发环境的搭建

    一.开发环境搭建的流程 0.首先是Win10环境下. 1.Python的安装,这里我使用的是3.7.4版本的Python. 这是Python的官网,请自行下载 https://www.python.o ...

  5. 上位机PHP_使用最流行的脚本语言PHP制作你的物联网上位机系统(一)

    开始前先介绍下用料.OHSCE是高可靠性的PHP通信框架  ,是专业面向工业控制.物联网.行业智能化开发的通信&控制框架,不同于专业面向面向WEB/用户客户端场景的通信框架,OHSCE是专为控 ...

  6. C# WPF界面设计参考 工控机上位机界面 美观炫酷的现代化风格界面设计实际案例分享 界面设计代码

    1.动图效果展示 2.界面1 3.界面2 4.界面3 需要界面效果代码的可留言联系博主

  7. QT制作简易串口调试助手

    文章目录 QT制作简易串口调试助手 0 简介 1 界面制作 2 逻辑实现 2.1 添加与可用串口信息有关的类的准备 2.2 读取可用串口并添加到控件 2.3 打开串口 2.4 关闭串口 2.5 串口收 ...

  8. DSP28335驱动Lcd12864显示Ds18b20采集到的温度,并通过Sci方式传输至PC,使用Matlab制作上位机软件进行数据保存与显示

    这一篇文章是我前一篇文章的续集,主要是为了弥补 CCS无法实时捕捉数据至上位机的缺陷(可能CCS有,但是我却没找到,如果有读者知道具体答案,请留言告诉我.).当然串口传输来的数据也有不足的地方,就是这 ...

  9. 上位机获取单片机发来的数据并进行检验(完整版,附完整源码)

    上位机获取单片机发来的数据 需求 准备 功能设计 界面设计 程序 后续 需求 本设计的主要功能其实是获取RFID的卡号,并进行鉴别.推而广之,可以用来获取单片机发来的数据,并进行检验是否符合要求. 我 ...

  10. 上位机获取单片机发来的数据并进行检验

    上位机获取单片机发来的数据 需求 准备 功能设计 界面设计 程序 后续 需求 本设计的主要功能其实是获取RFID的卡号,并进行鉴别.推而广之,可以用来获取单片机发来的数据,并进行检验是否符合要求. 我 ...

最新文章

  1. 开源自动化机器学习框架
  2. 【MORE协议】基于MORE的改进协议设计的MATLAB仿真
  3. 如何在Hadoop上编写MapReduce程序
  4. 为什么很多人说 Java 不适合编写桌面应用?
  5. 基于Quartus II的在线调试方法
  6. 填充table_Excel Power Query | 向下填充的逆过程
  7. 统计学基础一:基础概念
  8. 有关彩虹猫病毒的一些小小的分析
  9. QS排名前70-100的英国大学怎么选?
  10. 从Alignment 和 Uniformity的角度理解对比表征学习
  11. FSA-Net环境配置
  12. HDMI 4K分辨率 时序
  13. 使用WPS生成二维码,手机扫码访问主机的资源
  14. AWS云、阿里云、腾讯云、华为云--云服务器全球区域对比图
  15. 利用秀米的SVG布局的穿透功能实现横屏长图互动原理+教程
  16. 信息学奥赛一本通C++语言——1058:求一元二次方程
  17. 计算机大赛a类有哪些,A类 B类学科竞赛项目清单
  18. 温湿度控制系统c语言,基于单片机的温湿度控制系统设计
  19. ftp文件夹错误_搞定共享文件夹设置 局域网共享一键修复
  20. mysql 多表 查询慢_详解Mysql多表联合查询效率分析及优化

热门文章

  1. 数字信号的增益matlab,如何控制多频带滤波器通带增益?
  2. 语文学科html代码,[2018年最新整理]学科分类与代码.doc
  3. 计算机网络安全基础-网络监听基本概念
  4. php字库,TCPDF生成内置字库的PDF
  5. excel表格打开是乱码要如何解决?
  6. 如果计算机正执行屏幕保护程序 当用户,计算机一级考试参考试题(含答案)篇节一.doc...
  7. 【matlab安装】手把手图文并茂安装matlab2021(win10版)
  8. g2(t)用matlab,数字信号处理实验报告
  9. 联想计算机不能使用ghost,联想电脑不能GHOST的解决方法
  10. 可以写进简历的十大Java项目