最近一直在学习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 }
程序运行效果:
以上就是软硬件系统的全部细节,欢迎拍砖!