室内系的上位机Maker(一)
<p>在单片机项目的开发中,有时需要将模拟量数据通过单片机采集成数字量后发送到PC/手机上进行处理和展现,或者有使用PC/手机向单片机发送指令的需求,这时候就需要进行上下位机软件开发。上位机软件主要包括Windows上位机和Android上位机,在Windows平台中可以使用C#和Qt进行开发,使用串口传输数据,而Android平台中使用Java开发,使用蓝牙/WIFI来传输数据。这里先介绍在Visual Studio中使用C#进行上位机软件开发。</p><p>本篇面向主要学习嵌入式开发的同学,能自己手搓一个上位机软件就可,本人也没学C#开发嘻嘻</p><p>本项目中绘图部分得到了http://blog.fcxl9876.xin/ 博主的大力支持,之后这个博客也会更新图标控件chart的使用方法和示例,需要在自己的上位机软件中画统计图的一定要去关注:<a href="http://blog.fcxl9876.xin/index.php/archives/91/" style="white-space: normal;">http://blog.fcxl9876.xin/index.php/archives/91/</a></p><h2>安装VS 2019</h2><p><a href="https://visualstudio.microsoft.com/zh-hans/downloads/">https://visualstudio.microsoft.com/zh-hans/downloads/</a></p><p>进入这个页面找到社区版免费下载即可</p><p><img src="{#ZC_BLOG_HOST#}zb_users/upload/2020/08/5f2fb9281f6e4919287549.png" alt=""/></p><p>安装时需要注意在选择工作负载时勾选 <span style="color:#5b9bd5"><em>使用C++的桌面开发</em></span>和<span style="color:#5b9bd5"><em>Visual Studio扩展开发</em></span></p><p>这里我已经安装过就没有附图了…</p><p>之后可以修改安装路径,然后就可以进行安装了</p><h2>新建工程</h2><p>打开VS2019之后在欢迎页或文件菜单-新建中找到创建新项目选项,这里选择Windows窗体应用。</p><p><img src="{#ZC_BLOG_HOST#}zb_users/upload/2020/08/5f2fb929b4d15418362104.png" alt=""/></p><p>点击下一步后可以命名项目名称,选择存储路径,以及在框架选择中选择.NET版本。根据资料Windows7默认带有.NET 3.5,鉴于如今大部分操作系统是win7或10,因此这里选择4.5。</p><p><img src="{#ZC_BLOG_HOST#}zb_users/upload/2020/08/5f2fb92b9f15b513193593.png" alt=""/></p><p>点击创建后就完成了新建工程。</p><h3>认识窗体和控件</h3><p>进入到我们刚刚建立的工程,可以看到如下界面</p><p><img src="{#ZC_BLOG_HOST#}zb_users/upload/2020/08/5f2fb92bd01e7649914641.png" alt=""/></p><p>注意将界面左边的工具箱面板、右上的解决方案资源管理器面板调到显示状态,如果不慎关掉可以在视图菜单中重新设置。</p><p>而在标签栏中我们可以看到两个标签,分别是Form.cs和Form.cs设计,各自是本工程的后台代码和前台图形编辑页面。另外,可以在右上角的资源管理器中找到Form.Designer.cs,这里是图像界面的代码定义。</p><p>接下来可以罗列一下一个串口助手软件需要的界面功能:在串口列表选择需要打开的串口、波特率、数据位等串口通信参数设置、打开/关闭串口按钮、串口接收信息显示区、串口发送文本框和按钮。</p><p style="margin-left: 18pt">Ⅰ 绘制串口参数设置区</p><p style="margin-left: 18pt">首先在左边的工具箱中找到Label控件,它可以在窗体中显示一些固定文本</p><p style="margin-left: 18pt"><img src="{#ZC_BLOG_HOST#}zb_users/upload/2020/08/5f2fb92c03dfd644375899.png" alt=""/></p><p style="margin-left: 18pt">之后将其拖到窗体中。</p><p style="margin-left: 18pt"><img src="{#ZC_BLOG_HOST#}zb_users/upload/2020/08/5f2fb92c2e7fc932616804.png" alt=""/></p><p style="margin-left: 18pt">可以看到窗体中出现了label1字样。之后我们需要更改其显示的文字内容。在右下角属性面板中找到Text项即可编辑。顺带,属性面板许多项的含义都在下方有解释,如果想对控件外观做更改可以先在属性面板寻找相关项,找不到的话再去搜索如何用代码实现。</p><p style="margin-left: 18pt"><img src="{#ZC_BLOG_HOST#}zb_users/upload/2020/08/5f2fb92c59f96920867861.png" alt=""/></p><p style="margin-left: 18pt">这里需要显示的文本为 串口:</p><p style="margin-left: 18pt">之后在加入一个下拉列表控件(ComboBox),以供我们选择使用哪个串口。这里必须了解一个属性中的项,叫做DropDownStyle</p><p style="margin-left: 18pt"><img src="{#ZC_BLOG_HOST#}zb_users/upload/2020/08/5f2fb92c811b1440015234.png" alt=""/></p><p style="margin-left: 18pt">当使用默认的DropDown时,用户不光可以下拉选择列表中的项目,也可以在控件中直接输入文本,本例中这个下拉列表做选择串口之用,因此采用DropDownList,即只能在列表中选择所使用的串口。</p><p style="margin-left: 18pt">同理,加入波特率选择栏和打开串口(按钮控件Button)按钮。</p><p style="margin-left: 18pt"><img src="{#ZC_BLOG_HOST#}zb_users/upload/2020/08/5f2fb92ca9228238264643.png" alt=""/></p><p style="margin-left: 18pt">此时已经有了基本的串口参数选择,至于之后的数据位校验位停止位,只要单片机那边不做特殊处理,使用默认值就好了,大家是学嵌入式的,.NET这边能省点就省点吧。</p><p style="margin-left: 18pt">之后需要添加文本框,也就是接收到的文本显示区域。这里使用TextBox控件。</p><p style="margin-left: 18pt"><img src="{#ZC_BLOG_HOST#}zb_users/upload/2020/08/5f2fb92cd21d3582945615.png" alt=""/></p><p style="margin-left: 18pt">将其拖到窗体中,需要注意的是为了配合串口发来数据多行、自动刷新的特性,我们需要对其属性进行两项修改:使其成为多行显示的文本框——Multiline属性项设置为true,</p><p style="margin-left: 18pt">加上垂直滚动条——ScrollBars项设置为Vertical。之后,为了方便编程,我们将这个文本框的Name修改为textBox_receive。之后编写代码时一定要注意这个文本框的名字已经变更。</p><p style="margin-left: 18pt"><img src="{#ZC_BLOG_HOST#}zb_users/upload/2020/08/5f2fb92d07075766570244.png" alt=""/></p><p style="margin-left: 18pt">最后需要加入最重要的串口组件以及为它注册事件。串口组件为我们提供了一系列的属性和"函数",使得我们能非常方便的开发串口相关功能。在工具箱中搜索SerialPort,将其拖放到窗体中可以看到窗体下方出现了serialPort1。</p><p style="margin-left: 18pt"><img src="{#ZC_BLOG_HOST#}zb_users/upload/2020/08/5f2fb92d316d5149942855.png" alt=""/></p><p style="margin-left: 18pt">之后选中它,在属性面板中点击小闪电,切换到事件,双击DataReceived即可发现跳转到了数据接收事件的代码部分,每次串口接收到数据就会执行事件中的代码。</p><p style="margin-left: 18pt"><img src="{#ZC_BLOG_HOST#}zb_users/upload/2020/08/5f2fb92d5ec72931117468.png" alt=""/></p><p>回到之前的串口参数,我们会发现目前串口和波特率下拉栏中并没有内容,由于每台计算机开启的串口不一定相同,这里需要我们利用代码来给串口列表加入可用串口。</p><p>双击窗体Form1的空白部分,可以看到跳转到了Form1_Load事件,这是窗体加载事件,当窗体出现时(程序启动时)会执行。向其加入以下代码:</p><p><span style="color:black; font-family:新宋体; font-size:9pt"><span style="color:green"></span></span></p><pre class="prism-highlight prism-language-csharp">//获取可用串口添加到comboBox1
comboBox1.Items.AddRange(System.IO.Ports.SerialPort.GetPortNames());</pre><p><span style="color:black; font-family:新宋体; font-size:9pt"></span><br/></p><p><span style="color:black; font-family:新宋体; font-size:9pt">这一行解决了可用串口有哪些的问题。再添加以下代码:</span></p><p><span style="color:black; font-family:宋体; font-size:9pt"><span style="color:blue"></span></span></p><pre class="prism-highlight prism-language-csharp">int i;
//单个添加for (i = 300; i <= 38400; i = i*2)
{
comboBox2.Items.Add(i.ToString()); //添加波特率列表
}
//批量添加波特率列表
string[] baud = { "43000","56000","57600","115200","128000","230400","256000","460800" };
comboBox2.Items.AddRange(baud);</pre><p><span style="color:black; font-family:宋体; font-size:9pt"></span><br/></p><p> </p><p>这一部分分成两步添加了常见的波特率,300~38400比较有规律,使用for来生成并添加,43000及之后的手动用comboBox2.Items.AddRange(baud);进行添加。</p><p>此外,我们还可以给这两个下拉框设置默认的文本,如果将其设置成自己电脑常用的串口号和自己单片机所使用的波特率,每次开启软件就不需要手动设置了。</p><p><span style="color:black; font-family:新宋体; font-size:9pt"><span style="color:green"></span></span></p><pre class="prism-highlight prism-language-csharp">//设置默认值
comboBox1.Text = "COM12";
comboBox2.Text = "9600";</pre><p><span style="color:black"><span style="font-family:新宋体; font-size:9pt"><span style="color:#a31515"><span style="color:black"></span></span></span></span><br/></p><p>之后就可以点击启动来看看我们的ui有没有正确被加载啦。</p><p><img src="{#ZC_BLOG_HOST#}zb_users/upload/2020/08/5f2fb92d8647d384354656.png" alt=""/></p><h2>后台搭建</h2><p>我们使用串口助手时,往往设置好串口通信参数后会点击打开串口按钮,此时我们还没有给这个按钮添加功能。双击打开串口按钮,会跳转到button1_Click事件。当我们打开串口时如果遇到了一些已有功能无法处理的意外,我们希望软件能通知用户发生了异常,因此需要try..catch机制。先放上单机按钮的代码,如下:</p><p><span style="color:black; font-family:新宋体; font-size:9pt"><span style="color:blue"></span></span></p><pre class="prism-highlight prism-language-csharp">privatevoid button1_Click(object sender, EventArgs e)
{
try
{
//将可能产生异常的代码放置在try块中
//根据当前串口属性来判断是否打开
if (serialPort1.IsOpen)//判断串口开启状态
{
//串口已经处于打开状态
serialPort1.Close(); //关闭串口
button1.Text = "打开串口";//将按钮文本修改为打开串口
button1.BackColor = Color.ForestGreen;//按钮变为绿色
comboBox1.Enabled = true;//允许修改 串口 下拉栏
comboBox2.Enabled = true;//运行修改 波特率 下拉栏
textBox_receive.Text = ""; //清空接收区
}
else
{
//串口已经处于关闭状态,则设置好串口属性后打开
comboBox1.Enabled = false;//不允许修改 串口 下拉栏
comboBox2.Enabled = false; //不运行修改 波特率 下拉栏
serialPort1.PortName = comboBox1.Text;//需要使用的串口号是串口下拉栏中的文本内容
serialPort1.BaudRate = Convert.ToInt32(comboBox2.Text);//需要使用的波特率是波特率下拉栏中的文本
serialPort1.DataBits = Convert.ToInt16(8);//停止位手动设置为8
serialPort1.Parity = System.IO.Ports.Parity.None;//校验位手动设置为None
serialPort1.StopBits = System.IO.Ports.StopBits.One;//停止位手动设置为1
serialPort1.Open(); //打开串口
button1.Text = "关闭串口";
button1.BackColor = Color.Firebrick;
}
}
catch (Exception ex)
{
//捕获可能发生的异常并进行处理
//捕获到异常,创建一个新的对象,之前的不可以再用
serialPort1 = new System.IO.Ports.SerialPort();
//刷新COM口选项
comboBox1.Items.Clear();
comboBox1.Items.AddRange(System.IO.Ports.SerialPort.GetPortNames());
//响铃并显示异常给用户
System.Media.SystemSounds.Beep.Play();
button1.Text = "打开串口";
button1.BackColor = Color.ForestGreen;
MessageBox.Show(ex.Message);
comboBox1.Enabled = true;
comboBox2.Enabled = true;
}
}</pre><p><span style="color:black; font-family:新宋体; font-size:9pt"></span><br/></p><p>几乎每行都有注释,不做其它说明了。</p><p>在连接了串口设备的情况下,程序已经可以正常打开串口了,但此时文本框中并不会显示经由串口收到的信息,因为我们还没对串口数据接收事件进行修改。找到private void serialPort1_DataReceived事件并添加代码:</p><pre class="prism-highlight prism-language-csharp">try
{
//因为要访问UI资源,所以需要使用invoke方式同步ui
this.Invoke((EventHandler)(delegate
{
//在文本框中增加收到的文本
textBox_receive.AppendText(serialPort1.ReadExisting());
}
)
);
}
catch (Exception ex)
{
//响铃并显示异常给用户
System.Media.SystemSounds.Beep.Play();
MessageBox.Show(ex.Message);
}</pre><p><br/></p><p><br/></p><p> </p><p>至此,一个具有接受串口数据功能上位机软件就已经编写好了,我们可以给单片机烧写上串口发送程序,注意单片机程序中的波特率设置,以及数据位校验位停止位,就可以连接到电脑上给刚刚制作的程序发送数据了。调试时使用工具栏中的启动<img src="{#ZC_BLOG_HOST#}zb_users/upload/2020/08/5f2fb92dad777193295211.png" alt=""/>按钮,使用时,在菜单栏生成菜单-生成(项目名)即可。</p><p><img src="{#ZC_BLOG_HOST#}zb_users/upload/2020/08/5f2fb92dd4875383851482.png" alt=""/></p><p>在之后的文章还将介绍如何分割串口数据文本,利用数据绘制折线图,先给成品图:</p><p><img src="{#ZC_BLOG_HOST#}zb_users/upload/2020/08/5f2fb92e0cee7304962204.png" alt=""/></p><p>之后还有计划写写如何给本软件添加串口发送功能以及如何使用Qt制作串口助手。</p><p>本项目中绘图部分得到了http://blog.fcxl9876.xin/ 博主的大力支持,之后这个博客也会更新图标控件chart的使用方法和示例,需要在自己的上位机软件中画统计图的一定要去关注:<a href="http://blog.fcxl9876.xin/index.php/archives/91/">http://blog.fcxl9876.xin/index.php/archives/91/</a></p><p>本教程基于<a href="https://blog.csdn.net/Mculover666">https://blog.csdn.net/Mculover666</a> 的教程编写,本文基本上就是我理解的此博主的一系列教程的成果,内容还有缩水,因此阅读大佬的教学:</p><p><a href="https://blog.csdn.net/Mculover666/article/details/80650067">https://blog.csdn.net/Mculover666/article/details/80650067</a></p><p>来学习也是很好的。</p><p>另附一个完整的串口助手github项目链接:</p><p><a href="https://github.com/Mculover666/SerialAssistant">https://github.com/Mculover666/SerialAssistant</a></p><p> </p><p> </p><p>为什么使用这个标题?Hanser的奶粉味 室内系的TrackMaker 太上头了啊哈哈哈哈</p>
Q.E.D.