上位机软件(C/Python/Java等)
直播中

李勇俊

7年用户 1489经验值
私信 关注
[经验]

【微信精选】教你如何拥有自己的“串口”上位机

对于串口工具想必大家也不陌生,比如sscom、putty、xshell等,做嵌入式的都会用到串口工具进行调试,8位的51单片机、32位的STM32以及更高级的linux开发。串口对于开发调试是必不可少。

不过有时候你会觉得用别人的串口工具会感觉到很痛苦。太多限制,太局限性,比如要抓取调试的LOG的某个字段,要实时监控数据,要对接收到的数据进行解析,用sscom、putty、xshell等上位机都无法实现自己想要的功能。所以我们要定制画一款属于自己的上位机,来实现自己想要的功能,想怎么搞就怎么搞。

我有用过C#、QT、python等开发过串口上位机,不过我感觉C#是比较简单的。下面介绍的串口开发就是采用C#开发的。

本上位机的源码在作者的github上:git@github.com:RiceChen/SerialDemo.git

首先,开发上位机要先设计好UI,C#的UI很简单设计,拖拉控件,拼凑一下就编程一个属于自己的上位机界面。下图是我简单设计的界面

1.png

功能实现:这个功能的是实现,其实就是实现四个按钮的功能

其中:
scan按钮:自动搜索串口号,省去了通过设备管理器查找串口号

open按钮:打开or关闭串口。

clear按钮:清除接收数据的界面。

send按钮:将发送界面的数据,通过串口发送出去。

1 添加下面三个命名空间:

其中IO 和 IO.Ports 命名空间是提供关于串口的API,Threading命名空间是提供线程接口,这个软件的线程使用在接收数据中。


using System.IO;
using System.IO.Ports;
using System.Threading;

2 添加串口对象,面向对象的语言一切皆对象,这里就是将串口抽象成一个对象,然后接下来的操作都是操作这个对象实体。

定义一个串口接收线程(串口数据接受,可以通过两种方式:定时器or线程,本软件采用线程的方法)

定义一个是否存在串口的标志

       public SerialPort MySerialPort = new SerialPort();
       Thread serialPort_Thread;
       bool comExistence = false;

3 Scan按钮的实现:自动扫描串口实现原理:定义一个临时串口实体sp,然后遍历尝试打开串口sp.Open(),如果打开失败会出现异常,所以必须要使用try{}catch{}将异常抛出。

如果打开成功,则会先关闭串口(原因:这里尝试打开串口是使用默认参数,所以需要先关闭串口,然后由我们设置好参数在手动打开串口),然后将串口号回显到界面中。我这里设置的最大串口号是30,可以根据自己的需求去更改。

       private void scan_Click(object sender, EventArgs e)
       {
           port.Clear();
           for (int i = 1; i <= 30; i++)
           {
               try
               {
                   SerialPort sp = new SerialPort("COM" + i.ToString());
                   sp.Open();
                   sp.Close();
                   port.Text = "COM" + i.ToString();
                   comExistence = true;
               }
               catch (Exception)
               {
                   continue;
               }
           }
           if (comExistence == false)
           {
               port.Text = "COM";
               MessageBox.Show("Unused serial!");
           }
       }

4 open按钮,主要是设置参数,比如串口号、波特率、数据位、停止位、奇偶校验位。我这里使用的方法,是通过判断界面中的控件选择的内容,然后设置对应的值。

然后调用串口打开API函数Open(),打开串口,如果打开成功,则会启动数据接受线程(首先是为串口对象定义实体:serialPort_Thread = new Thread(new ThreadStart(this.readPort)); 然后打开线程,通过调用API函数Start())。相反,当关闭串口时,调用串口关闭API接口Close()则会将数据接受线程关闭,调用API接口Abort()。

其中:设置参数,只要将参数赋值给对应的参数,下面是参数对应的变量

串口号:MySerialPort.PortName
波特率:MySerialPort.BaudRate
数据位:MySerialPort.DataBits
停止位:MySerialPort.StopBits
奇偶校验位:MySerialPort.Parity

       private void open_Click(object sender, EventArgs e)
       {
           try
           {
               if (open.Text == "open")
               {
                   if (comExistence == true)
                   {
                       MySerialPort.Close();
                       /* port */
                       MySerialPort.PortName = port.Text;
                       /* baud rate */
                       MySerialPort.BaudRate = Convert.ToInt32(baud.Text.Trim());
                       /* data bit */
                       if (data_bit.Text == "5")
                           MySerialPort.DataBits = (byte)5;
                       else if (data_bit.Text == "6")
                           MySerialPort.DataBits = (byte)6;
                       else if (data_bit.Text == "7")
                           MySerialPort.DataBits = (byte)7;
                       else
                           MySerialPort.DataBits = (byte)8;
                       /* stop bit */
                       if (stop_bit.Text == "2")
                           MySerialPort.StopBits = StopBits.Two;
                       else if (stop_bit.Text == "1.5")
                           MySerialPort.StopBits = StopBits.OnePointFive;
                       else
                           MySerialPort.StopBits = StopBits.One;
                       /* parity */
                       if (parity.Text == "Even")
                           MySerialPort.Parity = Parity.Even;
                       else if (parity.Text == "Odd")
                           MySerialPort.Parity = Parity.Odd;
                       else
                           MySerialPort.Parity = Parity.None;
                       MySerialPort.Open();

                       if (MySerialPort.IsOpen)
                       {
                           open.Text = "Close";
                           port.Enabled = false;
                           baud.Enabled = false;
                           data_bit.Enabled = false;
                           stop_bit.Enabled = false;
                           parity.Enabled = false;
                           scan.Enabled = false;
                           serialPort_Thread = new Thread(new ThreadStart(this.readPort));
                           serialPort_Thread.Start();//start receive date thread
                       }
                   }
               }
               else
               {
                   open.Text = "open";
                   port.Enabled = true;
                   baud.Enabled = true;
                   data_bit.Enabled = true;
                   stop_bit.Enabled = true;
                   parity.Enabled = true;
                   scan.Enabled = true;
                   comExistence = false;
                   port.Text = "COM";
                   MySerialPort.Close();
                   serialPort_Thread.Abort();
               }
           }
           catch (Exception ex)
           {
               MessageBox.Show(ex.Message);
               open.Text = "Open";
           }
       }

5 clear按钮:清空接收数据控件内容,比较简单,调用API函数Clear()。

       private void clear_Click(object sender, EventArgs e)
       {
           receive_box.Clear();
       }

6 send按钮:本文章的例子是将发送控件的内容通过串口发送出去。这里直接调用API -WriteLine(),实现数据的发送。

       private void send_Click(object sender, EventArgs e)
       {
           if (MySerialPort.IsOpen)
           {
               MySerialPort.WriteLine(send_box.Text);
           }
       }

7 接收数据线程的服务函数:串口接收数据,也是比较简单,通过调用API-ReadExisting(),其实还可以通过其他API读取数据。其中注意的是,数据要显示在界面,线程内的内容要显示在界面的控件需要采用委托的形式。使用的格式:this.Invoke((EventHandler)(delegate{}));

       private void readPort()
       {
           string rxBuffer = "";

           while (MySerialPort.IsOpen)
           {
               try
               {
                   rxBuffer = MySerialPort.ReadExisting();
                   this.Invoke((EventHandler)(delegate
                   {
                       receive_box.Text += rxBuffer;
                   }));
               }
               catch
               {
                   rxBuffer = "";
               }
           }
       }

功能测试:

1 数据接受:数据接受演示,发送端采用stm32发送内容“RICE DIY”,自制上位机接受控件将接受到的数据显示出来。

2.png

2 发送数据:数据发送演示,接受端采用sscom软件,通过自制上位机将数据“Rice DIY”发送到sscom上位机。

3.png

串口还可以做很多东西,大家还可以在上面的例程中扩展自己想要的功能。以下软件经供参考,大家发挥自己的想象力。

1 《在线调试软件》—图像、参数:这款软件的开发,是当时在做飞思卡尔智能车,做的是摄像头双车。目的①要验证图像处理算法是否正确,所以你看到两幅图像,左边是没有处理过得图像,右边是处理过的图像。通过上位机一目了然。目的②因为车载跑,你不能一直跟着车跑,所以电脑端通过蓝牙转串口接收车上蓝牙发送的参数,电脑也可以给车发送参数。实现了远程控制。

4.png


2 BatteryMonitoring:这款软件的开发,是为了监控电池的实时状态,电池的充电、放电是一个非常漫长的过程,大容量的电池,时间就越长。如果靠人为去监控电池的实时状态,可能几天几夜都可能不用睡觉。所以就开发了这个软件监控。通过下位机实时发数据上位机,进行实时监控。里面也集成了一些算法,来实现电池曲线的拟合,补偿。


5.png

本文作者:饭仔DIY,工作之余喜欢电子DIY,开源分享是我写文章的动力,互相学习才能让自己不断强大。

欢迎广大电子发烧友们投稿,投稿邮箱:liuyong@elecfans.com


更多回帖

发帖
×
20
完善资料,
赚取积分