手把手一起使用WPF MVVM制作USB调试助手Demo

编程入门 行业动态 更新时间:2024-10-27 14:29:32

<a href=https://www.elefans.com/category/jswz/34/1765412.html style=手把手一起使用WPF MVVM制作USB调试助手Demo"/>

手把手一起使用WPF MVVM制作USB调试助手Demo

1、USB调试助手Demo

该Demo使用WPF框架,基于MVVM设计模式,实现USB调试助手,效果如图所示:


实现功能:上位机(USB调试助手)与下位机(ZYNQ)通过USB通信,实现收发数据
实验环境:Visual Studio 2022
控件库:HandyControl 手把手一起使用开源WPF控件HandyControl
USB库:LibUsbDotNet
完整工程:手把手一起使用WPF MVVM制作USB调试助手Demo完整工程文件
Gitee链接: .git

2、功能演示

该Demo代码结构如图所示,参照MVVM设计模式完成


项目运行后界面如图所示:

USB设备通过两个ID进行连接,默认ID为随机输入的,故直接点连接USB设备会弹出没有发现设备,如图所示:


下位机连接正常后,下拉框可以选择相应USB设备,如图所示:


发送和接收的字节数同样可以设置,如图所示:


设置完成后,点击连接USB设备即可,此时可以打开Bus Hound超级软件总线协议分析器,观察发送和接收数据,如图所示:


USB发送数据区输入数据,点击开始发送,可以看到Bus Hound显示已经发送,如图所示,发送数据格式之所以如此,是因为自定义了上位机与下位机的通信协议头帧、校验帧及尾帧:

点击开始接收,下位机将数据返回至上位机界面,同时Bus Hound显示已经接收的数据,两者一致,如图所示:

再次进行发送和接收数据:


最后,点击断开USB设备,如图所示:

3、UI界面XAML

UI界面代码MainWindow.xaml如下:

<Windowxmlns=""xmlns:x=""xmlns:d=""xmlns:mc=""xmlns:local="clr-namespace:Wpf_USB"xmlns:hc="" x:Class="Wpf_USB.MainWindow"mc:Ignorable="d"Title="WPF_USB" Height="480" Width="800" ResizeMode="CanMinimize"><Grid><Grid.ColumnDefinitions><ColumnDefinition Width="160*"/><ColumnDefinition Width="160*"/></Grid.ColumnDefinitions><Grid.RowDefinitions><RowDefinition Height="60*"/><RowDefinition Height="60*"/><RowDefinition Height="60*"/><RowDefinition Height="60*"/></Grid.RowDefinitions><GroupBox Header="USB设备信息" BorderBrush="Black" FontSize="20" FontWeight="Bold" Margin="5,5,5,5" Grid.RowSpan="2"><StackPanel><StackPanel Orientation="Horizontal"><TextBlock TextWrapping="Wrap" Text="VendorID:" Width="auto"  RenderTransformOrigin="0.478,1.336" Padding="0,10,0,0" Height="44" Margin="10"/><ComboBox Width="177" Margin="15,10,5,10" ItemsSource="{Binding UsbPar.VendorID}" SelectedItem="{Binding CuPar.VendorID}" IsEnabled="{Binding CuPar.EnableSelect}"/></StackPanel><StackPanel Orientation="Horizontal"><TextBlock TextWrapping="Wrap" Text="ProductID:" Width="auto" RenderTransformOrigin="0.478,1.336" Padding="0,10,0,0" Height="44" Margin="10"/><ComboBox Width="177" Margin="10,10,5,10" ItemsSource="{Binding UsbPar.ProductID}" SelectedItem="{Binding CuPar.ProductID}" IsEnabled="{Binding CuPar.EnableSelect}"/></StackPanel><StackPanel Orientation="Horizontal"><Button Content="连接USB设备" Margin="20,0,10,0" FontSize="16" Height="38" Command="{Binding OpenUsbDev}"/><Button Content="断开USB设备" Margin="20,0,10,0" FontSize="16" Height="38" Command="{Binding CloseUsbDev}"/></StackPanel></StackPanel></GroupBox><GroupBox Header="USB接收数据区" FontSize="20"  FontWeight="Bold" BorderBrush="#FF7ED866" Margin="5,5,5,5" Grid.Row="2" Grid.RowSpan="2"><ScrollViewer VerticalScrollBarVisibility="Auto"><TextBox Name="ReceiveData" TextWrapping="Wrap" Text="{Binding CuPar.ReadDataString}" FontWeight="Normal" FontSize="16"/></ScrollViewer></GroupBox><GroupBox Header="接收数据设置" FontSize="20"  FontWeight="Bold" Margin="5,5,5,5" BorderBrush="#FF5570C7" Grid.Column="1"><StackPanel Orientation="Horizontal"><TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="BytesRead:" Padding="0,15,0,0"/><ComboBox Width="130" Height="40" ItemsSource="{Binding UsbPar.BytesRead}" SelectedItem="{Binding CuPar.BytesRead}" IsEnabled="{Binding CuPar.EnableSelect}"/><Button Content="开始接收" FontSize="20" Height="58" Margin="5" Command="{Binding UsbDevRead}"/></StackPanel></GroupBox><GroupBox Header="发送数据设置" FontSize="20"  FontWeight="Bold" Margin="5,5,5,5"  BorderBrush="#FF5D29A0" Grid.Column="1" Grid.Row="1"><StackPanel Orientation="Horizontal"><TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="BytesWrite:" Padding="0,15,0,0"/><ComboBox Width="130" Height="40" ItemsSource="{Binding UsbPar.BytesWrite}" SelectedItem="{Binding CuPar.BytesWrite}" IsEnabled="{Binding CuPar.EnableSelect}"/><Button Content="开始发送" FontSize="20" Height="58" Margin="5" Command="{Binding UsbDevWrite}"/></StackPanel></GroupBox><GroupBox Header="USB发送数据区" FontSize="20"  FontWeight="Bold" Margin="5,5,5,5" BorderBrush="#FFA42D84" Grid.Column="1" Grid.RowSpan="2" Grid.Row="2"><ScrollViewer VerticalScrollBarVisibility="Auto"><TextBox Name="SendData" TextWrapping="Wrap" Text="{Binding CuPar.WriteData}" FontWeight="Normal" FontSize="16"/></ScrollViewer></GroupBox></Grid>
</Window>

由于使用了开源控件库:HandyControl,因此App.xaml代码需要如下编写:

<Application x:Class="Wpf_USB.App"xmlns=""xmlns:x=""xmlns:local="clr-namespace:Wpf_USB"StartupUri="MainWindow.xaml"><Application.Resources><ResourceDictionary><ResourceDictionary.MergedDictionaries><ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml" /><ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/Theme.xaml" /></ResourceDictionary.MergedDictionaries></ResourceDictionary></Application.Resources>
</Application>

4、后台逻辑CS

后台MainWindow.xaml.cs代码如下:

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 Wpf_USB.ViewModels;namespace Wpf_USB
{/// <summary>/// MainWindow.xaml 的交互逻辑/// </summary>public partial class MainWindow : Window{public MainWindow(){InitializeComponent();this.DataContext = new MainWindowViewModel();}}
}

USB各参数初始化类,即界面初始化的参数,UsbParameter.cs代码如下:

using LibUsbDotNet.Main;
using LibUsbDotNet;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;namespace Wpf_USB.ViewModels
{class UsbParameter : NotificationObject{private ObservableCollection<int> vendorid = new ObservableCollection<int>() { 1234 };public ObservableCollection<int> VendorID {get { return vendorid; }set{vendorid = value;this.RaisePropertyChanged("VendorID");}}private ObservableCollection<int> productid = new ObservableCollection<int>() { 1 };public ObservableCollection<int> ProductID{get { return productid; }set{productid = value;this.RaisePropertyChanged("ProductID");}}private ObservableCollection<int> bytesread = new ObservableCollection<int>() { 512, 1024, 2048, 4096, 8192, 16384 };public ObservableCollection<int> BytesRead{get { return bytesread; }set{bytesread = value;this.RaisePropertyChanged("BytesRead");}}private ObservableCollection<int> byteswrite = new ObservableCollection<int>() { 512, 1024, 2048, 4096, 8192, 16384 };public ObservableCollection<int> BytesWrite{get { return byteswrite; }set{byteswrite = value;this.RaisePropertyChanged("BytesWrite");}}public void UsbDeviceFind(){UsbRegDeviceList allDevices = UsbDevice.AllDevices;if (allDevices != null){foreach (UsbRegistry usbRegistry in allDevices){VendorID.Add(usbRegistry.Vid);ProductID.Add(usbRegistry.Pid);}UsbDevice.Exit();}else { MessageBox.Show("UsbDevive Not Found."); }            }}
}

USB更新数据类,CurrentParameter.cs代码如下:

using LibUsbDotNet;
using LibUsbDotNet.Main;
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows;
using System.Runtime.Remoting.Messaging;
using System.Collections.ObjectModel;
using System.Reflection;
using Wpf_USB.Models;namespace Wpf_USB.ViewModels
{class CurrentParameter : NotificationObject{public CurrentParameter(){GloVar = new GlobalVariable();DataPro = new DataProcess();}private GlobalVariable gloVar;public GlobalVariable GloVar{get { return gloVar; }set{gloVar = value;}}private DataProcess dataPro;public DataProcess DataPro{get { return dataPro; }set{dataPro = value;}}private int vendorid;public int VendorID{get { return vendorid; }set{vendorid = value;this.RaisePropertyChanged("VendorID");}}private int productid;public int ProductID{get { return productid; }set{productid = value;this.RaisePropertyChanged("ProductID");}}private int bytesread;public int BytesRead{get { return bytesread; }set{bytesread = value;this.RaisePropertyChanged("BytesRead");}}private int byteswrite;public int BytesWrite{get { return byteswrite; }set{byteswrite = value;this.RaisePropertyChanged("BytesWrite");}}private UsbDevice usbDevice;private UsbEndpointReader epReader;private UsbEndpointWriter epWriter;public UsbDevice UsbDevice{get { return usbDevice; }set { usbDevice = value; this.RaisePropertyChanged("UsbDevice"); }}private byte[] readData;private string writeData;private string readDataString;private bool isOpen;private bool enableSelect = true;public bool EnableSelect{get { return enableSelect; }set { enableSelect = value; this.RaisePropertyChanged("EnableSelect"); }}public byte[] ReadData{get { return readData; }set { readData = value; this.RaisePropertyChanged("ReadData"); }}public string ReadDataString{get { return readDataString; }set { readDataString = value; this.RaisePropertyChanged("ReadDataString"); }}public string WriteData{get { return writeData; }set { writeData = value; this.RaisePropertyChanged("WriteData"); }}public bool IsOpen{get { return isOpen; }set { isOpen = value; this.RaisePropertyChanged("IsOpen"); }}public bool UsbDevicesOpen(){if (usbDevice != null && usbDevice.IsOpen){return UsbDevicesClose();}try{UsbDeviceFinder UsbFinder = new UsbDeviceFinder(this.VendorID, this.ProductID);usbDevice = UsbDevice.OpenUsbDevice(UsbFinder);if (usbDevice == null) throw new Exception("Device Not Found.");IUsbDevice wholeUsbDevice = usbDevice as IUsbDevice;if (!ReferenceEquals(wholeUsbDevice, null)){wholeUsbDevice.SetConfiguration(1);wholeUsbDevice.ClaimInterface(0);}epReader = usbDevice.OpenEndpointReader(ReadEndpointID.Ep01);epWriter = usbDevice.OpenEndpointWriter(WriteEndpointID.Ep01);EnableSelect = false;if (usbDevice.IsOpen){return IsOpen = true;}else{return IsOpen = false;}}catch (Exception ex){MessageBox.Show(ex.Message);}return IsOpen = false;}public bool UsbDevicesClose(){try{EnableSelect = true;if (usbDevice == null) throw new Exception("Device Not Found.");else{if (usbDevice.IsOpen){IUsbDevice wholeUsbDevice = usbDevice as IUsbDevice;if (!ReferenceEquals(wholeUsbDevice, null)){// Release interface #0.wholeUsbDevice.ReleaseInterface(0);}usbDevice.Close();return IsOpen = false;}usbDevice = null;UsbDevice.Exit();return IsOpen = false;}}catch (Exception ex){MessageBox.Show(ex.Message);return IsOpen = false;}}public void UsbDevicesRead(){try {if (usbDevice == null) throw new Exception("Device Not Found.");else{int bytesReadCount = BytesRead;readData = new byte[bytesReadCount];ErrorCode ec = epReader.Read(ReadData, 2000, out bytesReadCount);this.DataPro.ReadDataCRC(ReadData);ReadDataString += GloVar.CMD_H.ToString("X2") + " ";ReadDataString += GloVar.CMD_L.ToString("X2") + " ";ReadDataString += GloVar.RESULT_F.ToString("X2") + " ";for (int i = 0; i < GloVar.ReadValidData.Length; i++)ReadDataString += GloVar.ReadValidData[i].ToString("X2") + " ";if (bytesReadCount == 0) MessageBox.Show("No More Bytes.");}}catch (Exception ex){MessageBox.Show(ex.Message);}}public void UsbDevicesWrite(){try {if (usbDevice == null) throw new Exception("Device Not Found.");else{if (!String.IsNullOrEmpty(WriteData)){int bytesWriteCount = BytesWrite;DataPro.WriteDataCRC(WriteData);ErrorCode ec = epWriter.Write(GloVar.WriteValidData, 2000, out bytesWriteCount);if (ec != ErrorCode.None) MessageBox.Show("Write Fail.");}else{MessageBox.Show("WriteData Is Empty.");}}}catch (Exception ex){MessageBox.Show(ex.Message);}}}
}

响应数据实时更新类,NotificationObject.cs代码如下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace Wpf_USB.ViewModels
{class NotificationObject : INotifyPropertyChanged{public event PropertyChangedEventHandler PropertyChanged;public void RaisePropertyChanged(string propertyName){if (this.PropertyChanged != null){this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));}}}
}

按键命令类,DelegateCommand.cs代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;namespace Wpf_USB.ViewModels
{class DelegateCommand : ICommand{public bool CanExecute(object parameter){if (CanExecuteFunc == null)return true;return this.CanExecuteFunc(parameter);}public event EventHandler CanExecuteChanged;public void Execute(object parameter){if (ExecuteAction == null){return;}this.ExecuteAction(parameter);}public Action<object> ExecuteAction { get; set; }public Func<object, bool> CanExecuteFunc { get; set; }}
}

MainWindowViewModel.cs代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace Wpf_USB.ViewModels
{class MainWindowViewModel : NotificationObject{public MainWindowViewModel(){UsbPar = new UsbParameter();CuPar = new CurrentParameter();this.UsbPar.UsbDeviceFind();CuPar.VendorID = UsbPar.VendorID[0];CuPar.ProductID = UsbPar.ProductID[0];CuPar.BytesRead = UsbPar.BytesRead[3];CuPar.BytesWrite = UsbPar.BytesWrite[3];this.OpenUsbDev = new DelegateCommand();this.OpenUsbDev.ExecuteAction = new Action<object>(this.OpenUsb);this.CloseUsbDev = new DelegateCommand();this.CloseUsbDev.ExecuteAction = new Action<object>(this.CloseUsb);this.UsbDevRead = new DelegateCommand();this.UsbDevRead.ExecuteAction = new Action<object>(this.UsbRead);this.UsbDevWrite = new DelegateCommand();this.UsbDevWrite.ExecuteAction = new Action<object>(this.UsbWrite);}private UsbParameter usbPar;public UsbParameter UsbPar{get { return usbPar; }set{usbPar = value;this.RaisePropertyChanged("UsbPar");}}private CurrentParameter cuPar;public CurrentParameter CuPar{get { return cuPar; }set{cuPar = value;this.RaisePropertyChanged("CuPar");}}public DelegateCommand OpenUsbDev { get; set; }private void OpenUsb(object parameter){this.CuPar.UsbDevicesOpen();}public DelegateCommand CloseUsbDev { get; set; }private void CloseUsb(object parameter){this.CuPar.UsbDevicesClose();}public DelegateCommand UsbDevRead { get; set; }private void UsbRead(object parameter){ this.CuPar.UsbDevicesRead(); }public DelegateCommand UsbDevWrite { get; set; }private void UsbWrite(object parameter){ this.CuPar.UsbDevicesWrite();}}
}

数据处理类,包括自定义的通信协议,DataProcess.cs代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using Wpf_USB.ViewModels;namespace Wpf_USB.Models
{class DataProcess : NotificationObject{public DataProcess() {GloVar = new GlobalVariable();}private GlobalVariable gloVar;public GlobalVariable GloVar{get { return gloVar; }set{gloVar = value;this.RaisePropertyChanged("GloVar");}}public void ReadDataCRC(byte[] ReadData){int CheckoutSum = 0;int DataLength = ReadData[2] * 256 + ReadData[3];if (ReadData[0].ToString("X") != "7E" || ReadData[1].ToString("X") != "7E"){GloVar.FrameData_F = true;MessageBox.Show("Data Frame Header Error.");}for (int i=0; i< DataLength; i++){CheckoutSum += ReadData[i + 4];}if (Convert.ToByte(CheckoutSum & 0xFF) != Convert.ToByte(ReadData[DataLength+4])){GloVar.FrameData_F = true;MessageBox.Show("Data Frame Check Error.");}GloVar.CMD_H = ReadData[4];GloVar.CMD_L = ReadData[5];GloVar.RESULT_F = ReadData[6];GloVar.ReadValidData = new byte[DataLength-3];for (int i = 0; i < (DataLength-3); i++){GloVar.ReadValidData[i] = ReadData[i+7];}if (ReadData[DataLength + 5].ToString("X") != "45" || ReadData[DataLength + 6].ToString("X") != "4E" || ReadData[DataLength + 7].ToString("X") != "44"){GloVar.FrameData_F = true;MessageBox.Show("Data Frame End Error.");}}public void WriteDataCRC(string WriteData){int CheckoutSum = 0 ;string WriteDataHex = WriteData.Replace(" ", "");WriteDataHex += WriteDataHex.Length % 2 != 0 ? "0" : "";int WriteValidDataLength = WriteDataHex.Length / 2;GloVar.WriteValidData = new byte[WriteValidDataLength+8];GloVar.WriteValidData[0] = 0x7E;GloVar.WriteValidData[1] = 0x7E;GloVar.WriteValidData[2] = Convert.ToByte((WriteValidDataLength >> 8) & 0xFF);GloVar.WriteValidData[3] = Convert.ToByte(WriteValidDataLength & 0xFF);for (int i=0; i<WriteValidDataLength; i++){GloVar.WriteValidData[4+i] = Convert.ToByte(WriteDataHex.Substring(i * 2, 2), 16);CheckoutSum += GloVar.WriteValidData[4+i];}GloVar.WriteValidData[4 + WriteValidDataLength] = Convert.ToByte(CheckoutSum & 0xFF);GloVar.WriteValidData[5 + WriteValidDataLength] = 0x45;GloVar.WriteValidData[6 + WriteValidDataLength] = 0x4E;GloVar.WriteValidData[7 + WriteValidDataLength] = 0x44;}}
}

全局变量类,GlobalVariable.cs代码如下:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Wpf_USB.ViewModels;namespace Wpf_USB.Models
{class GlobalVariable : NotificationObject{private string globalSystemVersion = "1.0.0";public string GlobalSystemVersion{get { return globalSystemVersion; }set{globalSystemVersion = value;this.RaisePropertyChanged("GlobalSystemVersion");}}private string globalUpdateTime = "2023.7.27";public string GlobalUpdateTime{get { return globalUpdateTime; }set{globalUpdateTime = value;this.RaisePropertyChanged("GlobalUpdateTime");}}private static int cMD_H;public int CMD_H{get { return cMD_H; }set{cMD_H = value;this.RaisePropertyChanged("CMD_H");}}private static int cMD_L;public int CMD_L{get { return cMD_L; }set{cMD_L = value;this.RaisePropertyChanged("CMD_L");}}private static int rESULT_F;public int RESULT_F{get { return rESULT_F; }set{rESULT_F = value;this.RaisePropertyChanged("RESULT_F");}}public static byte[] readValidData;public byte[] ReadValidData{get { return readValidData; }set{readValidData = value;this.RaisePropertyChanged("ReadValidData");}}public static byte[] writeValidData;public byte[] WriteValidData{get { return writeValidData; }set{writeValidData = value;this.RaisePropertyChanged("WriteValidData");}}public static bool frameData_F = false;public bool FrameData_F{get { return frameData_F; }set{frameData_F = value;this.RaisePropertyChanged("FrameData_F");}}}
}

该Demo实现过程中,参考了一些大佬(CSDN和Github)的代码,在此表示感谢

希望本文对大家有帮助,上文若有不妥之处,欢迎指正

分享决定高度,学习拉开差距

更多推荐

手把手一起使用WPF MVVM制作USB调试助手Demo

本文发布于:2024-03-08 22:21:26,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1722623.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:手把手   助手   WPF   MVVM   USB

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!