UI界面
xmal 文件
<Window x:Class="Read串口讀取速度Ports.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="600" Width="525" Loaded="Window_Loaded" Closing="Window_Closing" Closed="Window_Closed">
<Border>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50"></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition Height="30"></RowDefinition>
</Grid.RowDefinitions>
<Border Grid.Row="0">
<StackPanel Orientation="Horizontal">
<Button x:Name="OpenPortBtn" Content="OpenProt" Click="OpenPortBtn_Click"></Button>
<Separator Margin="10,0,10,0" />
<Button x:Name="ClosePortBtn" Content="ClosePort" Click="ClosePortBtn_Click"></Button>
<Separator Margin="10,0,10,0" />
<Button x:Name="sendPortBtn" Content="SendPort" Click="SendPortBtn_Click"></Button>
<Separator Margin="10,0,10,0" />
<CheckBox x:Name="check" Content="check" VerticalAlignment="Center" Checked="check_Yes"></CheckBox>
</StackPanel>
</Border>
<Border Grid.Row="1">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Border Grid.Column="0">
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Border Grid.Row="0">
<ScrollViewer Name="LrecScrol" VerticalScrollBarVisibility="Auto" Padding="0,0,0,0">
<TextBox x:Name="LrecTBox" TextWrapping="Wrap" ScrollViewer.VerticalScrollBarVisibility="Auto" AcceptsReturn="True" Background="{x:Null}" ClipToBounds="True" Foreground="Black" />
</ScrollViewer>
</Border>
<Border Grid.Row="1">
<Canvas x:Name="LCanvas">
<Image x:Name="LImage" Height="200" Width="80" Stretch="Uniform" HorizontalAlignment="Center"></Image>
</Canvas>
</Border>
</Grid>
</Border>
<Border Grid.Column="1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Border Grid.Row="0">
<ScrollViewer Name="RrecScrol" VerticalScrollBarVisibility="Auto" Padding="0,0,0,0">
<TextBox x:Name="RrecTBox" TextWrapping="Wrap" ScrollViewer.VerticalScrollBarVisibility="Auto" AcceptsReturn="True" Background="{x:Null}" ClipToBounds="True" Foreground="Black" />
</ScrollViewer>
</Border>
<Border Grid.Row="1">
<Canvas x:Name="RCanvas">
<Image x:Name="RImage" Height="200" Width="80" HorizontalAlignment="Center" Stretch="Uniform"></Image>
</Canvas>
</Border>
</Grid>
</Border>
</Grid>
</Border>
<Border Grid.Row="2">
<StatusBar>
<StatusBar.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</StatusBar.ItemsPanel>
<StatusBarItem>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Com1Count:"></TextBlock>
<TextBlock x:Name="SerialL" VerticalAlignment="Center"></TextBlock>
</StackPanel>
</StatusBarItem>
<StatusBarItem Grid.ColumnSpan="2"></StatusBarItem>
<StatusBarItem Grid.Column="2">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Com2:"></TextBlock>
<TextBlock x:Name="SerialR" VerticalAlignment="Center"></TextBlock>
</StackPanel>
</StatusBarItem>
<StatusBarItem Grid.Column="3"></StatusBarItem>
</StatusBar>
</Border>
</Grid>
</Border>
</Window>
後臺代碼
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Management;
using System.Security.Permissions;
using System.Text;
using System.Threading;
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.Windows.Threading;
namespace Read串口讀取速度Ports
{
/// <summary>
/// MainWindow.xaml 的交互邏輯
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
//一些變量的聲明
SerialPort ComPortL = new SerialPort();
SerialPort ComPortR = new SerialPort();
DispatcherTimer autoSendTickL = new DispatcherTimer();
DispatcherTimer autoSendTickR = new DispatcherTimer();
private bool Listening = false;//用於檢測是否沒有執行完invoke相關操作,僅在單線程收發使用,但是在公共代碼區有相關設置,所以未用#define隔離
private bool WaitClose = false;//invoke裏判斷是否正在關閉串口是否正在關閉串口,執行Application.DoEvents,並阻止再次invoke ,解決關閉串口時,程序假死,具體參見http://news.ccidnet.com/art/32859/20100524/2067861_4.html 僅在單線程收發使用,但是在公共代碼區有相關設置,所以未用#define隔離
private bool recStaus = true;//接收狀態字
Queue recQueue = new Queue();//接收數據過程中,接收數據線程與數據處理線程直接傳遞的隊列,先進先出
private static bool Sending = false;//正在發送數據狀態字
private static Thread _ComSend;//發送數據線程
private SendSetStr SendSet = new SendSetStr();//發送數據線程傳遞參數的結構體
private struct SendSetStr//發送數據線程傳遞參數的結構體格式
{
public string SendSetData;//發送的數據
public bool? SendSetMode;//發送模式
}
public string[] port_Collect = new string[2]; //端口名字存儲
private void Window_Loaded(object sender, RoutedEventArgs e)
{
///串口獲取方式一:
List<string> Portstr = new List<string>();
port_Collect = new string[2];
bool kb = MulGetHardwareInfo(HardwareEnum.Win32_PnPEntity, "Name", ref Portstr);
#region
///串口
//foreach (var item in Portstr)
//{
// Console.WriteLine(item);
//}
///串口獲取方式二
//string[] Ports = SerialPort.GetPortNames();
//foreach (var item in Ports)
//{
// Console.WriteLine(item);
//}
#endregion
if (port_Collect == null)
{
port_Collect = new string[2];
}
if (kb == true)
{
for (int i = 0; i < Portstr.Count; i++)
{
port_Collect[i] = Portstr[i];
}
}
else
{
if( Portstr.Count<0)
{
MessageBox.Show("沒找到串口,請重新檢測");
return;
}
}
if (port_Collect[0] == null || port_Collect[0].Length < 1)
{
MessageBox.Show("請重新連接左端口");
}
else
{
ComPortL.PortName = port_Collect[0];
ComPortL.BaudRate = 192000;
ComPortL.DataBits = 8;
ComPortL.Parity = Parity.None;
ComPortL.StopBits = StopBits.One;
ComPortL.ReceivedBytesThreshold = 1;
//ComPortL.DtrEnable = true;
//ComPortL.RtsEnable = true;
//找到之後,進行初始化
ComPortL.ReadTimeout = 8000;
ComPortL.WriteTimeout = 8000;
ComPortL.ReadBufferSize = 15360;
ComPortL.WriteBufferSize = 15360;
ComPortL.DataReceived += new SerialDataReceivedEventHandler(ComReceiveL);//串口接受中斷
autoSendTickL.Tick += new EventHandler(autoSendL);//定時發送中斷
Thread _ComRec = new Thread(new ThreadStart(ComRec)); //查詢串口接收數據線程聲明
_ComRec.Start();//啓動線程
}
if (port_Collect[1] == null || port_Collect[1].Length < 1 )
{
MessageBox.Show("請重新連接右端口");
}
else
{
ComPortR.PortName = port_Collect[1];
//初始化右端口
}
}
public bool recModeCheck = false;
void ComRec()//接收線程,窗口初始化中就開始啓動運行
{
while (true)//一直查詢串口接收線程中是否有新數據
{
if (recQueue.Count > 0)//當串口接收線程中有新的數據時候,隊列中有新進的成員recQueue.Count > 0
{
string recData;//接收數據轉碼後緩存
byte[] recBuffer = (byte[])recQueue.Dequeue();//出列Dequeue(全局)
recData = System.Text.Encoding.Default.GetString(recBuffer);//轉碼
UIAction(() =>
{
if (recModeCheck == false)//接收模式爲ASCII文本模式
{
LrecTBox.Text += recData;//加顯到接收區
}
else
{
StringBuilder recBuffer16 = new StringBuilder();//定義16進制接收緩存
for (int i = 0; i < recBuffer.Length; i++)
{
recBuffer16.AppendFormat("{0:X2}" + " ", recBuffer[i]);//X2表示十六進制格式(大寫),域寬2位,不足的左邊填0。
}
LrecTBox.Text += recBuffer16.ToString();//加顯到接收區
if(LrecTBox.Text.Length>1000)
{
LrecTBox.Clear();
Console.WriteLine("Clear");
}
}
//SerialL.Text = (Convert.ToInt32(SerialL.Text) + recBuffer.Length).ToString();//接收數據字節數
SerialL.Text = recBuffer.Length.ToString();
LrecScrol.ScrollToBottom();//接收文本框滾動至底部
});
}
else
{
Thread.Sleep(100);//如果不延時,一直查詢,將佔用CPU過高
}
}
}
void autoSendL(object sender, EventArgs e)//自動發送
{
Send();//調用發送方法
}
void Send()//發送數據,分爲多線程方式和單線程方式
{
try
{
if (Sending == true) return;//如果當前正在發送,則取消本次發送,本句註釋後,可能阻塞在ComSend的lock處
_ComSend = new Thread(new ParameterizedThreadStart(ComSend)); //new發送線程
SendSet.SendSetData = @"t";//發送數據線程傳遞參數的結構體--發送的數據
SendSet.SendSetMode = false;//發送數據線程傳遞參數的結構體--發送方式
_ComSend.Start(SendSet);//發送線程啓動
}
catch
{
return;
}
}
private void ComSend(Object obj)//發送數據 獨立線程方法 發送數據時UI可以響應
{
lock (this)//由於send()中的if (Sending == true) return,所以這裏不會產生阻塞,如果沒有那句,多次啓動該線程,會在此處排隊
{
Sending = true;//正在發生狀態字
byte[] sendBuffer = null;//發送數據緩衝區
string sendData = SendSet.SendSetData;//複製發送數據,以免發送過程中數據被手動改變
if (SendSet.SendSetMode == true)//16進制發送
{
try //嘗試將發送的數據轉爲16進制Hex
{
sendData = sendData.Replace(" ", "");//去除16進制數據中所有空格
sendData = sendData.Replace("\r", "");//去除16進制數據中所有換行
sendData = sendData.Replace("\n", "");//去除16進制數據中所有換行
if (sendData.Length == 1)//數據長度爲1的時候,在數據前補0
{
sendData = "0" + sendData;
}
else if (sendData.Length % 2 != 0)//數據長度爲奇數位時,去除最後一位數據
{
sendData = sendData.Remove(sendData.Length - 1, 1);
}
List<string> sendData16 = new List<string>();//將發送的數據,2個合爲1個,然後放在該緩存裏 如:123456→12,34,56
for (int i = 0; i < sendData.Length; i += 2)
{
sendData16.Add(sendData.Substring(i, 2));
}
sendBuffer = new byte[sendData16.Count];//sendBuffer的長度設置爲:發送的數據2合1後的字節數
for (int i = 0; i < sendData16.Count; i++)
{
sendBuffer[i] = (byte)(Convert.ToInt32(sendData16[i], 16));//發送數據改爲16進制
}
}
catch //無法轉爲16進制時,出現異常
{
UIAction(() =>
{
autoSendTickL.Stop();//關閉自動發送
MessageBox.Show("請輸入正確的16進制數據");
});
Sending = false;//關閉正在發送狀態
_ComSend.Abort();//終止本線程
return;//輸入的16進制數據錯誤,無法發送,提示後返回
}
}
else //ASCII碼文本發送
{
if(sendData == null)
{
return;
}
sendBuffer = System.Text.Encoding.Default.GetBytes(sendData);//轉碼
}
try//嘗試發送數據
{//如果發送字節數大於1000,則每1000字節發送一次
int sendTimes = (sendBuffer.Length / 1000);//發送次數
for (int i = 0; i < sendTimes; i++)//每次發生1000Bytes
{
ComPortL.Write(sendBuffer, i * 1000, 1000);//發送sendBuffer中從第i * 1000字節開始的1000Bytes
UIAction(() =>//激活UI
{
// sendCount.Text = (Convert.ToInt32(sendCount.Text) + 1000).ToString();//刷新發送字節數
});
}
if (sendBuffer.Length % 1000 != 0)//發送字節小於1000Bytes或上面發送剩餘的數據
{
ComPortL.Write(sendBuffer, sendTimes * 1000, sendBuffer.Length % 1000);
UIAction(() =>
{
// sendCount.Text = (Convert.ToInt32(sendCount.Text) + sendBuffer.Length % 1000).ToString();//刷新發送字節數
});
}
}
catch//如果無法發送,產生異常
{
UIAction(() =>//激活UI
{
if (ComPortL.IsOpen == false)//如果ComPort.IsOpen == false,說明串口已丟失
{
SetComLose();//串口丟失後的設置
}
else
{
MessageBox.Show("無法發送數據,原因未知!");
}
});
}
//sendScrol.ScrollToBottom();//發送數據區滾動到底部
Sending = false;//關閉正在發送狀態
_ComSend.Abort();//終止本線程
}
}
//接收數據 中斷只標誌有數據需要讀取,讀取操作在中斷外進行
private void ComReceiveL(object sender, SerialDataReceivedEventArgs e)
{
if (WaitClose) return;//如果正在關閉串口,則直接返回
//發送和接收均爲文本時,接收中爲加入判斷是否爲文字的算法,發送你(C4E3),接收可能識別爲C4,E3,可用在這裏加延時解決
Thread.Sleep(5);
if (recStaus)//如果已經開啓接收
{
byte[] recBuffer;//接收緩衝區
try
{
recBuffer = new byte[ComPortL.BytesToRead];//接收數據緩存大小
ComPortL.Read(recBuffer, 0, recBuffer.Length);//讀取數據
recQueue.Enqueue(recBuffer);//讀取數據入列Enqueue(全局)
}
catch
{
UIAction(() =>
{
if (ComPortL.IsOpen == false)//如果ComPort.IsOpen == false,說明串口已丟失
{
SetComLose();//串口丟失後相關設置
}
else
{
MessageBox.Show("無法接收數據,原因未知!");
}
});
}
}
else//暫停接收
{
ComPortL.DiscardInBuffer();//清接收緩存
}
}
void UIAction(Action action)//在主線程外激活線程方法
{
System.Threading.SynchronizationContext.SetSynchronizationContext(new System.Windows.Threading.DispatcherSynchronizationContext(App.Current.Dispatcher));
System.Threading.SynchronizationContext.Current.Post(_ => action(), null);
}
private void SetComLose()//成功關閉串口或串口丟失後的設置
{
autoSendTickL.Stop();//串口丟失後要關閉自動發送
//autoSendCheck.IsChecked = false;//自動發送改爲未選中
WaitClose = true;//;//激活正在關閉狀態字,用於在串口接收方法的invoke裏判斷是否正在關閉串口
while (Listening)//判斷invoke是否結束
{
DispatcherHelper.DoEvents(); //循環時,仍進行等待事件中的進程,該方法爲winform中的方法,WPF裏面沒有,這裏在後面自己實現
}
MessageBox.Show("串口已丟失");
WaitClose = false;//關閉正在關閉狀態字,用於在串口接收方法的invoke裏判斷是否正在關閉串口
}
//模擬 Winfrom 中 Application.DoEvents() 詳見 http://www.silverlightchina.net/html/study/WPF/2010/1216/4186.html?1292685167
public static class DispatcherHelper
{
[SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
public static void DoEvents()
{
DispatcherFrame frame = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(ExitFrames), frame);
try { Dispatcher.PushFrame(frame); }
catch (InvalidOperationException) { }
}
private static object ExitFrames(object frame)
{
((DispatcherFrame)frame).Continue = false;
return null;
}
}
//獲取串口端口號,依據Guid獲取
/// <summary>
/// 枚舉win32 api
/// </summary>
public enum HardwareEnum
{
// 硬件
Win32_Processor, // CPU 處理器
Win32_PhysicalMemory, // 物理內存條
Win32_Keyboard, // 鍵盤
Win32_PointingDevice, // 點輸入設備,包括鼠標。
Win32_FloppyDrive, // 軟盤驅動器
Win32_DiskDrive, // 硬盤驅動器
Win32_CDROMDrive, // 光盤驅動器
Win32_BaseBoard, // 主板
Win32_BIOS, // BIOS 芯片
Win32_ParallelPort, // 並口
Win32_SerialPort, // 串口
Win32_SerialPortConfiguration, // 串口配置
Win32_SoundDevice, // 多媒體設置,一般指聲卡。
Win32_SystemSlot, // 主板插槽 (ISA & PCI & AGP)
Win32_USBController, // USB 控制器
Win32_NetworkAdapter, // 網絡適配器
Win32_NetworkAdapterConfiguration, // 網絡適配器設置
Win32_Printer, // 打印機
Win32_PrinterConfiguration, // 打印機設置
Win32_PrintJob, // 打印機任務
Win32_TCPIPPrinterPort, // 打印機端口
Win32_POTSModem, // MODEM
Win32_POTSModemToSerialPort, // MODEM 端口
Win32_DesktopMonitor, // 顯示器
Win32_DisplayConfiguration, // 顯卡
Win32_DisplayControllerConfiguration, // 顯卡設置
Win32_VideoController, // 顯卡細節。
Win32_VideoSettings, // 顯卡支持的顯示模式。
// 操作系統
Win32_TimeZone, // 時區
Win32_SystemDriver, // 驅動程序
Win32_DiskPartition, // 磁盤分區
Win32_LogicalDisk, // 邏輯磁盤
Win32_LogicalDiskToPartition, // 邏輯磁盤所在分區及始末位置。
Win32_LogicalMemoryConfiguration, // 邏輯內存配置
Win32_PageFile, // 系統頁文件信息
Win32_PageFileSetting, // 頁文件設置
Win32_BootConfiguration, // 系統啓動配置
Win32_ComputerSystem, // 計算機信息簡要
Win32_OperatingSystem, // 操作系統信息
Win32_StartupCommand, // 系統自動啓動程序
Win32_Service, // 系統安裝的服務
Win32_Group, // 系統管理組
Win32_GroupUser, // 系統組帳號
Win32_UserAccount, // 用戶帳號
Win32_Process, // 系統進程
Win32_Thread, // 系統線程
Win32_Share, // 共享
Win32_NetworkClient, // 已安裝的網絡客戶端
Win32_NetworkProtocol, // 已安裝的網絡協議
Win32_PnPEntity,//all device
}
/// <summary>
/// WMI取硬件信息
/// </summary>
/// <param name="hardType"></param>
/// <param name="propKey"></param>
/// <returns></returns>
public static bool MulGetHardwareInfo(HardwareEnum hardType, string propKey, ref List<string> strs)
{
try
{
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher("select * from " + hardType))
{
var hardInfos = searcher.Get();
foreach (var hardInfo in hardInfos)
{
//Console.WriteLine(hardInfo.Properties[propKey].Value.ToString()); 將來限制的時候,在這裏進行判斷處理,
if (hardInfo.Properties[propKey].Value.ToString().ToUpper().Contains("COM") &&
hardInfo.Properties[propKey].Value.ToString().ToUpper().Contains("USB-SERIAL CH340"))
{
strs.Add(cutSerStr(hardInfo.Properties[propKey].Value.ToString()));
}
}
searcher.Dispose();
}
return false;
}
catch
{
return true;
}
}
/// <summary>
/// 獲取串口號
/// </summary>
/// <returns></returns>
public static string cutSerStr(string str)
{
int i = str.IndexOf("(");
int j = str.LastIndexOf(")");
return str.Substring(i + 1, j - i - 1);
}
//串口的開關閉合
private void OpenPortBtn_Click(object sender, RoutedEventArgs e)
{
if(ComPortL.PortName.Length<0)
{
MessageBox.Show("端口不存在");
return;
}
if(ComPortL.IsOpen == false)
{
try //嘗試打開串口
{
ComPortL.PortName = port_Collect[0];
ComPortL.BaudRate = 192000;
ComPortL.DataBits = 8;
ComPortL.Parity = Parity.None;
ComPortL.StopBits = StopBits.One;
ComPortL.ReceivedBytesThreshold = 1;
}
catch
{
MessageBox.Show("無法打開串口,請檢測此串口是否有效或被其他佔用!");
return;//無法打開串口,提示後直接返回
}
}
ComPortL.Open();
WaitClose = false;//等待關閉串口狀態改爲false
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
MessageBoxResult result = MessageBox.Show("確認是否要退出?", "退出", MessageBoxButton.YesNo);//顯示確認窗口
if (result == MessageBoxResult.No)
{
e.Cancel = true;//取消操作
}
}
private void Window_Closed(object sender, EventArgs e)
{
Application.Current.Shutdown();//先停止線程,然後終止進程.
Environment.Exit(0);//直接終止進程.
}
private void ClosePortBtn_Click(object sender, RoutedEventArgs e)
{
ComPortL.Close();
ComPortL.Dispose();
}
private void SendPortBtn_Click(object sender, RoutedEventArgs e)
{
//Send();
this.Dispatcher.Invoke(new Action(delegate
{
LrecTBox.Clear();
}));
}
private void check_Yes(object sender, RoutedEventArgs e)
{
if((bool)check.IsChecked == true)
{
autoSendTickL.Interval = TimeSpan.FromMilliseconds(0);//設置自動發送間隔
autoSendTickL.Start();//開始自動發送定時器
}
else
{
autoSendTickL.Stop();
}
}
}
}
以上基本包含了全部的功能,一些細節也指出了