对于串口工具想必大家也不陌生,比如sscom、putty、xshell等,做嵌入式的都会用到串口工具进行调试,8位的51单片机、32位的STM32以及更高级的linux开发。串口对于开发调试是必不可少。
不过有时候你会觉得用别人的串口工具会感觉到很痛苦。太多限制,太局限性,比如要抓取调试的LOG的某个字段,要实时监控数据,要对接收到的数据进行解析,用sscom、putty、xshell等上位机都无法实现自己想要的功能。所以我们要定制画一款属于自己的上位机,来实现自己想要的功能,想怎么搞就怎么搞。
我有用过C#、QT、python等开发过串口上位机,不过我感觉C#是比较简单的。下面介绍的串口开发就是采用C#开发的。
首先,开发上位机要先设计好UI,C#的UI很简单设计,拖拉控件,拼凑一下就编程一个属于自己的上位机界面。下图是我简单设计的界面
功能实现:这个功能的是实现,其实就是实现四个按钮的功能
其中:
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;
}
{
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 发送数据:数据发送演示,接受端采用sscom软件,通过自制上位机将数据“Rice DIY”发送到sscom上位机。
串口还可以做很多东西,大家还可以在上面的例程中扩展自己想要的功能。以下软件经供参考,大家发挥自己的想象力。
1 《在线调试软件》—图像、参数:这款软件的开发,是当时在做飞思卡尔智能车,做的是摄像头双车。目的①要验证图像处理算法是否正确,所以你看到两幅图像,左边是没有处理过得图像,右边是处理过的图像。通过上位机一目了然。目的②因为车载跑,你不能一直跟着车跑,所以电脑端通过蓝牙转串口接收车上蓝牙发送的参数,电脑也可以给车发送参数。实现了远程控制。
2 BatteryMonitoring:这款软件的开发,是为了监控电池的实时状态,电池的充电、放电是一个非常漫长的过程,大容量的电池,时间就越长。如果靠人为去监控电池的实时状态,可能几天几夜都可能不用睡觉。所以就开发了这个软件监控。通过下位机实时发数据上位机,进行实时监控。里面也集成了一些算法,来实现电池曲线的拟合,补偿。
本文作者:饭仔DIY,工作之余喜欢电子DIY,开源分享是我写文章的动力,互相学习才能让自己不断强大。
欢迎广大电子发烧友们投稿,投稿邮箱:liuyong@elecfans.com