最近一直在学习UWP,其中有的技术参考了WPF,所以又回头再来学习WPF,感觉学的东西很杂,必须记录一下,不然时间长了还得忘掉,于是申请开始写博客,将学习的心得记录一下,以备后用。这次是因为公司内训,刚好想着推广一下开源硬件,所以选择了Arduino,而又结合WPF的强大功能,设计了串口上位机。
1.Arduino UNO作为下位机
利用Arduino作为下位机,理由很简单,语法很简单,上手很快。
1.电路连接
下图为电路原理图,主要利用模拟口A0读取光敏电阻和普通电阻的分压值,然后通过设定逻辑控制LED的状态。之后通过串口将数据发送给电脑。
2.下位机程序
在arduino IDE里完成。代码结构非常简单,setup()中设置IO口及串口,然后在loop()中读取数值,根据数据控制LED状态,并将数值从串口发送出去。
1 void setup() { 2 // put your setup code here, to run once: 3 pinMode(13,OUTPUT); 4 Serial.begin(9600); 5 } 6 7 void loop() { 8 // put your main code here, to run repeatedly: 9 int val=analogRead(0); 10 int time; 11 int result; 12 for(time=0;time<20;time++) 13 { 14 result+=val; 15 } 16 result=result/20; 17 18 if(result<256) 19 { 20 digitalWrite(13,HIGH); 21 } 22 else{ 23 digitalWrite(13,LOW); 24 } 25 26 Serial.println(result); 27 delay(10); 28 }
2.WPF串口上位机。
这里主要使用WPF自带的串口控件、进度条、以及DynamicDataDisplay控件实现上位机数据显示。具体实现是:将arduino发过来的数据在页面上通过进度条显示出来,同时画出曲线。
1.串口控件SerialPort。
对于该控件,简单的使用过程如下:
- 实例化一个串口;
- 配置串口参数,例如波特率、数据位、串口号;
- 打开串口;
- 添加串口接收数据事件;
- 处理数据接收事件。
需要注意的是:多线程问题,由于WPF的控件都在UI线程,而串口数据在另外1个线程。一开始直接将串口数据给进度条赋值会出现错误,后来参考网上资料,使用了相应控件的dispatcher.invoke(Action()),解决了数据更新问题。关于多线程的问题,后续还需要再继续学习,搞清这一部分。
而数据曲线绘制使用了DynamicDataDisplay控件,相关使用方法可参考网上,由于第1次使用该控件,感觉效果还行。
代码写的很嫩。
程序界面如下,这里没有选择串口的选型,因为提前看了串口号。
xaml代码:
1 <Window x:Class="Communication.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:local="clr-namespace:Communication" 7 xmlns:d3="http://research.microsoft.com/DynamicDataDisplay/1.0" 8 mc:Ignorable="d" 9 Title="MainWindow" Height="350" Width="525" 10 Loaded="Window_Loaded"> 11 <Grid> 12 <Grid.RowDefinitions> 13 <RowDefinition Height="40"/> 14 <RowDefinition Height="auto"/> 15 <RowDefinition Height="auto"/> 16 <RowDefinition Height="auto"/> 17 <RowDefinition Height="*"/> 18 </Grid.RowDefinitions> 19 <Grid.ColumnDefinitions> 20 <ColumnDefinition Width="*"/> 21 <ColumnDefinition Width="*"/> 22 </Grid.ColumnDefinitions> 23 <TextBlock HorizontalAlignment="Center" 24 Text="光照度显示程序" 25 Grid.ColumnSpan="2" 26 FontSize="30"/> 27 <StackPanel Orientation="Horizontal" 28 Grid.Row="1" > 29 <TextBlock HorizontalAlignment="Left" 30 Grid.Row="1" 31 Text="光照值:"/> 32 <TextBlock HorizontalAlignment="Left" 33 Grid.Row="1" 34 Name="light_result"/> 35 </StackPanel> 36 37 <StackPanel Grid.Row="1" 38 Grid.Column="1" 39 Orientation="Horizontal" 40 HorizontalAlignment="Center"> 41 <Button x:Name="开始" 42 Content="开始" 43 Click="开始_Click" 44 Height="20" 45 Width="60" 46 Margin='10,0,10,0'/> 47 <Button x:Name="关闭" 48 Content="关闭" 49 Click="关闭_Click" 50 Height="20" 51 Width="60"/> 52 </StackPanel> 53 54 <ProgressBar Grid.Row="2" Grid.ColumnSpan="2" 55 Minimum="0" Maximum="1023" 56 Height="20" Width="300" 57 HorizontalAlignment="Left" 58 x:Name="light_value"/> 59 <d3:ChartPlotter x:Name="plotter" 60 Margin="10,20,10,10" 61 Grid.Row="3" 62 Grid.ColumnSpan="2"> 63 <d3:HorizontalAxis> 64 <d3:HorizontalIntegerAxis/> 65 </d3:HorizontalAxis> 66 <d3:VerticalAxis> 67 <d3:VerticalIntegerAxis/> 68 </d3:VerticalAxis> 69 <d3:Header Content="光照曲线"/> 70 <d3:VerticalAxisTitle Content="光照强度"/> 71 </d3:ChartPlotter> 72 </Grid> 73 </Window>
后台代码:
1 using Microsoft.Research.DynamicDataDisplay; 2 using Microsoft.Research.DynamicDataDisplay.DataSources; 3 using System; 4 using System.IO.Ports; 5 using System.Threading; 6 using System.Windows; 7 using System.Windows.Media; 8 using System.Windows.Threading; 9 10 namespace Communication 11 { 12 /// <summary> 13 /// MainWindow.xaml 的交互逻辑 14 /// </summary> 15 /// 16 17 public partial class MainWindow : Window 18 { 19 20 SerialPort myPort = new SerialPort(); 21 double result; 22 bool portClosing; 23 string lightValue; 24 25 private ObservableDataSource<Point> dataSource = new ObservableDataSource<Point>(); 26 private DispatcherTimer timer = new DispatcherTimer(); 27 int i = 0; 28 29 public MainWindow() 30 { 31 InitializeComponent(); 32 } 33 34 private void 开始_Click(object sender, RoutedEventArgs e) 35 { 36 try 37 { 38 myPort.BaudRate = 9600; 39 myPort.DataBits = 8; 40 myPort.PortName = "COM3"; 41 myPort.NewLine = "\r\n"; 42 myPort.Open(); 43 portClosing = false; 44 } 45 catch (Exception err) 46 { 47 MessageBox.Show(err.Message); 48 49 } 50 51 myPort.DataReceived += MyPort_DataReceived; 52 53 timer.Interval = TimeSpan.FromMilliseconds(200); 54 timer.Tick += drawPoint; 55 timer.IsEnabled = true; 56 plotter.Viewport.FitToView(); 57 58 } 59 60 private void MyPort_DataReceived(object sender, SerialDataReceivedEventArgs e) 61 { 62 if (portClosing) 63 { 64 return; 65 } 66 67 try 68 { 69 lightValue = myPort.ReadLine(); 70 result = double.Parse(lightValue); 71 } 72 73 catch(Exception err1) 74 { 75 //MessageBox.Show(err1.Message); 76 77 } 78 79 light_value.Dispatcher.BeginInvoke(new Action(() => 80 { 81 light_value.Value = result; 82 })); 83 84 light_result.Dispatcher.BeginInvoke(new Action(() => 85 { 86 light_result.Text = result.ToString(); 87 })); 88 } 89 90 private void 关闭_Click(object sender, RoutedEventArgs e) 91 { 92 portClosing = true; 93 94 Thread.Sleep(10); 95 96 if (myPort.IsOpen) 97 { 98 myPort.Close(); 99 100 } 101 else 102 { 103 MessageBox.Show("串口已关闭"); 104 } 105 106 timer.Stop(); 107 } 108 109 private void Window_Loaded(object sender, RoutedEventArgs e) 110 { 111 plotter.AddLineGraph(dataSource, Colors.Green, 2, "光照度"); 112 113 } 114 115 private void drawPoint(object sender, EventArgs e) 116 { 117 double x = i; 118 double y = result; 119 120 Point point = new Point(x,y); 121 dataSource.AppendAsync(base.Dispatcher, point); 122 i++; 123 } 124 } 125 }
程序运行效果:
以上就是软硬件系统的全部细节,欢迎拍砖!