赞
踩
项目要求:下位机使用单片机,不断发送一个随机数值给上位机,上位机收到数据,显示在显示框中。
实验环境: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
1.首先我们参照网上的串口调试助手将UI界面的大体框架完成。
2.其中数据接收区域和发送区域的文本需要实现自动换行功能,根据网上资料,需要设置TextWrapping的属性为"Wrap"。
3.接下来是对串口号、波特率、校验位等的一些内容设置,其中较复杂的是串口号的设置,需要根据电脑的端口实际情况来确定串口数量,不能同设置波特率属性一般,直接在程序里指定唯一值。
string[] ports = SerialPort.GetPortNames();//获取当前计算机的串行端口名的数组。
for(int index =0;index<ports.Length;index++)
{
cb_SerialPortNumber.Items.Add(ports[index]);//添加item
cb_SerialPortNumber.SelectedIndex = index; //设置显示的item索引
}
在官方文档中找到获取当前计算机的串行端口名的数组的函数GetPortNames (),接着使用循环,利用comboBox的Items.Add方法将数组内的元素添加到comboBox中。
<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>
using System.IO.Ports;
public SerialPort _serialPort = new SerialPort ();
(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 = "";
}
(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;//校验位
}
(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; } }
(1)官方文档上说明需要使用SerialErrorReceivedEventHandler(注解原文:“创建SerialErrorReceivedEventHandler委托时,需要标识将处理该事件的方法。若要将事件与事件处理程序关联,请将该委托的一个实例添加到事件中”)。在文档中找到了DataReceived,具体写法是:
SerialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);后来发现可以简写 serialPort1.DataReceived += new DataReceivedHandler; 其中的DataReceivedHandler就是数据接收的具体实现方法。
//在串口打开点击事件中添加
_serialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);//添加数据接收事件
(1)获取数据
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 += "-";//字符分隔-
});
}
(1)使用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)“ 停止接收数据”和“继续接收数据”
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 = "停止接收"; } }
(1)当用户将程序关闭时,如果串口处于开启状态时,应该在关闭程序时将串口关闭。
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
string strContent = this.bt_SerialSwitch.Content.ToString();
if (strContent == "关闭串口")
{
_serialPort.Close();
}
}
(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]);//添加item cb_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(); } } } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。