编写一个简单的vb.net上位机
- 编程语言:vb.net
- 开发环境:visual studio
- 技术类别:可视化Windows窗体应用
- 主要内容:串口通信
小弟嵌入式菜鸟一枚,从事嵌入式一年了,第一次写blog,不知道怎么下手,写得不好,求不黑,之前一直都在做底层单片机的程序,关于上位机编程其实刚涉足,我看了下网上这方面的免费案例材料好像并不多,有的只是很老vb6.0以前的资料,所以决定给论坛做点贡献,把我了解到的有价值的一些知识点都记录下来,同时和大家分享,共同进步。
上位机是嵌入式人机交互的重要一个环节,编程者可以使用各种不同的方式达到对底层设备的信号控制和数据采集,vb.net提供的一些简单的控件可以帮助我们快速上手一个上位机项目,实现基本功能。下面首先了解一下基本的控件。相应的控件使用方法可以参照visual studiu MSDN官方给出的说明。比如我们做一个简单的测试小工具如该图所示。
前期准备
VB.NET是一门开发语言,我们需要在visual studio编译环境中运行和开发项目,下图所示为visual studio软件截图及下载链接。(具体安装在这里就不详细描述了,自行按照引导“下一步下一步”就行了)。
visual studio下载地址安装完整后,打开软件,文件-新建-项目
弹出的对话框中,找到,已安装-模板-visual basic-windows窗体应用,给自己的项目取个名字。点击确定完成项目创建。
创建一个空白窗体后的界面如图所示,在工具栏中找到你要的控件,点击放置到窗体相应的位置,常用的控件都有。其中和串口通信有关的关键控件是serialport。
我们添加完控件,给每个空间设置好相应的属性,效果如图所示。
双击窗体及每个控件,对其进行相应的事件程序编写。(需要有一定的vb.NET编程基础)
编写程序
创建一个窗体文件和一个模块文件,窗体文件用来放置控件和编写窗体运行的相关事件过程代码,模块文件用来存放公共变量
附上工程代码
'Create By Fgl
'添加串口相关引用和文件读写相关引用
Imports System.IO.Ports
Imports Microsoft.VisualBasic.FileIO
''' <窗体类代码>
''' 包含整个窗体的运行过程和事件
''' </窗体类代码>
Public Class Form_Main
''' <窗体变量的定义>
'''
''' </窗体变量的定义>
Dim Ports As String() '定义端口数组
Dim PortNum As Integer '定义端口数量
Dim HsArray() As HScrollBar = New HScrollBar() {HScrollBar_P0, HScrollBar_V0, HScrollBar_P1, HScrollBar_V1,
HScrollBar_P2, HScrollBar_V2, HScrollBar_P3, HScrollBar_V3, HScrollBar_P4, HScrollBar_V4}
Dim SentToDeviceFlag As Boolean = False
Const HsDataNum = 10 '定义横向框的数量
''' <无阻塞延时函数>
''' 单位1ms
''' </无阻塞延时函数>
''' <param name="Interval"></param>
Public Shared Sub Sleep(ByVal Interval)
Dim __time As DateTime = DateTime.Now
Dim __Span As Int64 = Interval * 10000 '因为时间是以100纳秒为单位。
While (DateTime.Now.Ticks - __time.Ticks < __Span)
Application.DoEvents()
End While
End Sub
''' <消息框输出>
''' 自动加上时间和回车换行
''' </消息框输出>
''' <param name="Msg"></param>
Private Sub GiveMsg(Msg As String)
Dim s As String = String.Format("{0} {1} {2}", Now, Msg, vbCrLf)
TextBox_Msg.AppendText(s)
End Sub
''' <错误信息输出>
''' 自动加上错误信息及时间和回车换行
''' </错误信息输出>
''' <param name="Msg"></param>
Private Sub GiveErrMsg(Msg As String)
TextBox_Msg.AppendText("Err: ")
GiveMsg(Msg)
End Sub
''' <刷新设置参数>
'''
''' </刷新设置参数>
''' <param name="SData"></param>
Private Sub ReFreshSettingData(ByVal SData() As Byte)
For j = 0 To HsDataNum - 1
CType(Panel1.Controls.Item(j), HScrollBar).Value = SData(j)
Next
End Sub
''' <发送当前参数到设备>
'''
''' </发送当前参数到设备>
Private Sub SentCurrentDataToDevice()
Try
SerialPort1.Write(ActionCmd_Current, 0, ActionCmd_Current.Length)
Catch ex As Exception
GiveErrMsg(ex.Message)
End Try
End Sub
''' <设置进度条最大值>
'''
''' </设置进度条最大值>
''' <param name="parent"></param>
Public Sub SetProgressBarMaximum(ByVal parent As Control)
For Each c As Control In parent.Controls
If TypeOf (c) Is ProgressBar Then
CType(c, ProgressBar).Maximum = 255
End If
'MessageBox.Show(c.ToString())
If c.HasChildren Then
'利用递归实现容器子控件的访问
SetProgressBarMaximum(c)
End If
Next
End Sub
''' <设置横向框值域>
'''
''' </设置横向框值域>
''' <param name="parent"></param>
Public Sub SetHScrollBarMaximum(ByVal parent As Control)
For Each c As Control In parent.Controls
If TypeOf (c) Is HScrollBar Then
CType(c, HScrollBar).Maximum = 255
End If
'MessageBox.Show(c.ToString())
If c.HasChildren Then
'利用递归实现容器子控件的访问
SetHScrollBarMaximum(c)
End If
Next
End Sub
''' <窗体事件>
'''
''' </窗体事件>
''' <param name="sender"></param>
''' <param name="e"></param>
Private Sub Form_Main_Load(sender As Object, e As EventArgs) Handles MyBase.Load
GiveMsg("Start App")
Dim FirstOpenFlag As Integer
FileOpen(1, Application.StartupPath & "\FileCmdData.txt", OpenMode.Random)
Try
FileGet(1, FirstOpenFlag, 1)
Catch ex As Exception
GiveErrMsg(ex.Message)
End Try
If Not FirstOpenFlag = 123456 Then '如果是第一次打开
FilePut(1, 123456, 1)
For i = 0 To ActionCmd_Table.GetLength(0) - 1
FilePut(1, ActionCmd_Table(i), i + 2)
Next
End If
For i = 0 To ActionCmd_Table.GetLength(0) - 1
FileGet(1, ActionCmd_Table(i), i + 2)
Next
FileClose({1})
SetProgressBarMaximum(GroupBox_Return) '初始化进度条参数
SetHScrollBarMaximum(GroupBox_Setting) '初始化横向框
Ports = SerialPort.GetPortNames '获取当前存在的端口名
PortNum = Ports.GetLength(0) '获取当前存在的端口数量
For Each Portx As String In Ports
ComboBox_PortNum.Items.Add(Portx) '向复选框里添加可用端口
Next
If PortNum > 0 Then
ComboBox_PortNum.Text = ComboBox_PortNum.Items(1)
End If
For Each str As String In Modual_ActionTable.ActionTable
ComboBox_BasicAction.Items.Add(str) '初始化训练动作组合框内容
Next
ComboBox_BasicAction.Text = ComboBox_BasicAction.Items(0)
For i = 0 To 100
ComboBox_BasicTrainTimes.Items.Add(i) '初始化训练次数组合框内容
Next
ComboBox_BasicTrainTimes.Text = 1
CheckBox_SA.Checked = True
'TextBox_Msg.AppendText(ComboBox_BasicAction.SelectedIndex)
GiveMsg("Init Ok")
End Sub
''' <复位按键的点击事件>
'''
''' </复位按键的点击事件>
''' <param name="sender"></param>
''' <param name="e"></param>
Private Sub Button_Reset_Click(sender As Object, e As EventArgs) Handles Button_Reset.Click
Try
For i = 0 To 11
ActionCmd_Current(i) = ActionCmd_Table(0)(i) '刷新当前电机参数
Next
ReFreshSettingData(ActionCmd_Current) '刷新横向框参数
SentToDeviceFlag = True '发送数据到电机
GiveMsg("复位")
Catch ex As Exception
GiveErrMsg(ex.Message)
End Try
End Sub
''' <打开串口过程>
'''
''' </打开串口过程>
''' <param name="sender"></param>
''' <param name="e"></param>
Private Sub Button_OpenUart_Click(sender As Object, e As EventArgs) Handles Button_OpenUart.Click
Try
If SerialPort1.IsOpen = True Then
SerialPort1.Close()
Button_OpenUart.Text = "打开串口"
Else
SerialPort1.Open()
Button_OpenUart.Text = "关闭串口"
End If
ComboBox_PortNum.Enabled = Not SerialPort1.IsOpen
Catch ex As Exception
GiveErrMsg(ex.Message)
End Try
End Sub
''' <训练按钮单击事件>
'''
''' </训练按钮单击事件>
''' <param name="sender"></param>
''' <param name="e"></param>
Private Sub Button_Train_Click(sender As Object, e As EventArgs) Handles Button_Train.Click
'Try
Dim i As Integer = ComboBox_BasicAction.SelectedIndex
If i < ActionCmd_Table.GetLength(0) Then
For j = 0 To ActionCmd_Current.Length - 1
ActionCmd_Current(j) = ActionCmd_Table(i)(j) '刷新当前电机参数
Next
ReFreshSettingData(ActionCmd_Current) '刷新横向框参数
SentToDeviceFlag = True '发送当前参数到电机
Dim s As String = String.Format("进行{0}训练", ComboBox_BasicAction.Text)
GiveMsg(s)
End If
'Catch ex As Exception
' GiveErrMsg(ex.Message)
'End Try
End Sub
''' <组合框选择事件>
'''
''' </组合框选择事件>
''' <param name="sender"></param>
''' <param name="e"></param>
Private Sub ComboBox_PortNum_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ComboBox_PortNum.SelectedIndexChanged
Try
SerialPort1.PortName = ComboBox_PortNum.Text
Catch ex As Exception
GiveErrMsg(ex.Message)
End Try
End Sub
''' <清空按钮单机事件>
'''
''' </清空按钮单机事件>
''' <param name="sender"></param>
''' <param name="e"></param>
Private Sub Button_Clear_Click(sender As Object, e As EventArgs) Handles Button_Clear.Click
TextBox_Msg.Clear()
End Sub
''' <横向框滚动事件>
'''
''' </横向框滚动事件>
''' <param name="sender"></param>
''' <param name="e"></param>
Private Sub HScrollBar_PX_Scroll(sender As Object, e As ScrollEventArgs) Handles HScrollBar_P0.Scroll,
HScrollBar_P1.Scroll, HScrollBar_P2.Scroll, HScrollBar_P3.Scroll, HScrollBar_P4.Scroll, HScrollBar_PA.Scroll
Try
For i = 0 To HsDataNum - 1
ActionCmd_Current(i) = CType(Panel1.Controls.Item(i), HScrollBar).Value
Next
SentToDeviceFlag = True
Catch ex As Exception
GiveErrMsg(ex.Message)
End Try
End Sub
''' <定时器扫描过程>
''' 在定时器中扫描发送标志位并对待发送的内容进行发送
''' </定时器扫描过程>
''' <param name="sender"></param>
''' <param name="e"></param>
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
If SentToDeviceFlag = True Then
SentToDeviceFlag = False
SentCurrentDataToDevice()
End If
End Sub
''' <总体滚动条滚动事件>
'''
''' </总体滚动条滚动事件>
''' <param name="sender"></param>
''' <param name="e"></param>
Private Sub HScrollBar_PA_Scroll(sender As Object, e As ScrollEventArgs) Handles HScrollBar_PA.Scroll
Try
For i = 0 To 4
If CType(Panel2.Controls.Item(i), CheckBox).Checked = True Then
CType(Panel1.Controls.Item(i * 2 + 1), HScrollBar).Value = HScrollBar_PA.Value
End If
Next
Catch ex As Exception
GiveErrMsg(ex.Message)
End Try
End Sub
''' <全选复选框改变事件>
'''
''' </全选复选框改变事件>
''' <param name="sender"></param>
''' <param name="e"></param>
Private Sub CheckBox_SA_CheckedChanged(sender As Object, e As EventArgs) Handles CheckBox_SA.CheckedChanged
If CheckBox_SA.CheckState = CheckState.Checked Then
For Each c As CheckBox In Panel2.Controls
c.Checked = True
Next
End If
If CheckBox_SA.CheckState = CheckState.Unchecked Then
For Each c As CheckBox In Panel2.Controls
c.Checked = False
Next
End If
End Sub
''' <复选框改变事件>
'''
''' </复选框改变事件>
''' <param name="sender"></param>
''' <param name="e"></param>
Private Sub CheckBox_S0_CheckedChanged(sender As Object, e As EventArgs) Handles CheckBox_S0.CheckedChanged _
, CheckBox_S1.CheckedChanged, CheckBox_S2.CheckedChanged, CheckBox_S3.CheckedChanged, CheckBox_S4.CheckedChanged
If CheckBox_S0.Checked = True And CheckBox_S1.Checked = True And CheckBox_S2.Checked = True And
CheckBox_S3.Checked = True And CheckBox_S4.Checked = True Then
CheckBox_SA.CheckState = CheckState.Checked
ElseIf CheckBox_S0.Checked = False And CheckBox_S1.Checked = False And CheckBox_S2.Checked = False And
CheckBox_S3.Checked = False And CheckBox_S4.Checked = False Then
CheckBox_SA.Checked = CheckState.Unchecked
Else
CheckBox_SA.CheckState = CheckState.Indeterminate
End If
End Sub
''' <标定按钮单击事件>
'''
''' </标定按钮单击事件>
''' <param name="sender"></param>
''' <param name="e"></param>
Private Sub Button_Sign_Click(sender As Object, e As EventArgs) Handles Button_Sign.Click
For i = 0 To 11
ActionCmd_Table(ComboBox_BasicAction.SelectedIndex)(i) = ActionCmd_Current(i)
Next
FileOpen(1, Application.StartupPath & "\FileCmdData.txt", OpenMode.Random)
For i = 0 To ActionCmd_Table.GetLength(0) - 1
FilePut(1, ActionCmd_Table(i), i + 2)
Next
FileClose({1})
Dim s As String = String.Format("{0}手势位置标定成功", ComboBox_BasicAction.Text)
GiveMsg(s)
End Sub
''' <保存历史按钮单击事件>
'''
''' </保存历史按钮单击事件>
''' <param name="sender"></param>
''' <param name="e"></param>
Private Sub Button_SaveHistory_Click(sender As Object, e As EventArgs) Handles Button_SaveHistory.Click
SaveFileDialog1.Filter = "txt files (*.txt)|*.txt"
Dim s As String = String.Format("History")
SaveFileDialog1.FileName = s
If SaveFileDialog1.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
'如果确定保存
My.Computer.FileSystem.WriteAllText(SaveFileDialog1.FileName, TextBox_Msg.Text, True)
My.Computer.FileSystem.WriteAllText(SaveFileDialog1.FileName, ActionCmd_Table.ToString, True)
End If
End Sub
End Class
Module Modual_ActionTable
Public ActionTable() As String = {"张开", "握拳", "食指对指", "中指对指", "无名指对指", "小拇指对指",
"数字1", "数字2", "数字3", "数字4", "数字5", "数字6", "数字7", "数字8", "数字9"}
Public ActionCmd_Reset() As Byte = {&HFF, &H00, &HFF, &H00, &HFF, &H00, &HFF, &H00, &HFF, &H00, &HFF, &H00, &HFF, &H00}
Public ActionCmd_Fist() As Byte = {&HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF}
Public ActionCmd_IndexToThumb() As Byte = {&HFF, &HFF, &HFF, &HFF, &HFF, &H00, &HFF, &H00, &HFF, &H00, &HFF, &H00, &HFF, &H00}
Public ActionCmd_MiddleToThumb() As Byte = {&HFF, &HFF, &HFF, &H00, &HFF, &HFF, &HFF, &H00, &HFF, &H00, &HFF, &H00, &HFF, &H00}
Public ActionCmd_RingToThumb() As Byte = {&HFF, &HFF, &HFF, &H00, &HFF, &H00, &HFF, &HFF, &HFF, &H00, &HFF, &H00, &HFF, &H00}
Public ActionCmd_LittleToThumb() As Byte = {&HFF, &HFF, &HFF, &H00, &HFF, &H00, &HFF, &H00, &HFF, &HFF, &HFF, &H00, &HFF, &H00}
Public ActionCmd_Current() As Byte = {&HFF, &H00, &HFF, &H00, &HFF, &H00, &HFF, &H00, &HFF, &H00, &HFF, &H00, &HFF, &H00}
Public ActionCmd_Table()() As Byte = {ActionCmd_Reset, ActionCmd_Fist, ActionCmd_IndexToThumb, ActionCmd_MiddleToThumb,
ActionCmd_RingToThumb, ActionCmd_LittleToThumb}
End Module
OK,运行一下,完成。
哦,对了,如果要移植copy此模板,记得控件的名字要和代码中一致。
看似复杂的上位机工程,其实代码也就短短几百行。关于指令解析部分,下一篇再写吧。
第一次写blog可能难免会有犯些基本错误,若有什么问题好的建议随时和我交流。