串口调试助手基于微软的.Net技术与串口设备进行通信,主要用于串口设备调试。软件界面简洁,代码开源。
因项目需要调试串口设备,但是试用了网上众多的串口调试助手后,虽然其标榜“好用”,“免费”,但使用过程中还是发现这些串口助手或多或少都有一些问题:
- 无法自动识别已有的串口,不支持COM4以上串口。
- 界面古老,有广告,且界面逻辑不清。
- 无源代码,无法真正进行调试。
其中有些问题已经严重影响到使用体验,像不支持COM4以上的串口导致根本不能工作。数据发送过程不透明,不知道具体发送的数据是什么。为了不再忍受各种老掉牙的软件,自己随手写一个简单的串口调试助手,主要实现以下功能:
- 自动查找并列出已有的串口。
- 界面清爽。
- 能发送String类型和Hex类型,能够在两者之间相互转换。
- 可以附加2字节的CRC16校验码到发送内容的结尾,校验码由发送内容计算而来(需要在Hex模式下选中CheckBox)。
- 当收到数据时可以自动回复发送窗口内容。
- 代码开源,所有发送过程对用户透明,方便调试。
- 基于Windows,无附加依赖库。
编程语言使用C#,基于微软.Net Framework 4.0 Client Profile。没有使用其他版本进行测试,因此使用其他版本可能导致运行不正确。如果电脑没有安装,可以点此下载。以下是软件界面截图:
使用说明:
1、在Windows系统中,回车键是以"\r\n"表示,如果在发送文本框中输入了回车键,在String模式下发送的数据将包括 0x0D 0x0A 两个字节。在Hex模式下,将无法输入回车键,如果需要回车,用0x0D 0x0A代替。
2、Hex模式下,直接输入16进制数,输入的格式为: FF-34-56-90
CODE:https://github.com/wenhuix/COMDBG,为Visual Studio 2010工程,可以被更高版本的VS打开。
EXE: COMDBG.exe
软件采用MVC设计模式,其架构如下图所示:
Model类主要是跟串口相关的一些操作,包括打开、关闭串口,接收和发送串口数据。在该类中,定义了事件,当接收到串口的数据时,如果有函数注册到了该事件上,则该函数就会被调用,并通过预先定义的参数类,将接收到的数据传递过去。该类需要注意以下几点:
(1) 打开串口时,当Handshake设置为None时,需要设置串口的 RtsEnable = true 和 DtrEnable = true。
(2) 根据MSDN上关于SerialPort这个类的表述,只有SerailPort的公共静态类型的成员是线程安全的,而 SerialPort.Read() 和 SerialPort.Write() 并不是静态方法,因此,需要加入同步锁,以防止在读/写串口的过程中被其他线程中断出现读写错误。
(3) 关闭串口时,要在非当前线程下进行,要防止死锁事件的发生。
//申明委托函数和传递参数的类
public delegate void SerialPortEventHandler(Object sender, SerialPortEventArgs e);
public class SerialPortEventArgs : EventArgs
{
public bool isOpend = false;
public Byte[] receivedBytes = null;
}
public class ComModel
{
private SerialPort sp = new SerialPort();
private Object thisLock = new Object();
//申明事件
public event SerialPortEventHandler comReceiveDataEvent = null;
//串口收到数据是通过中断来实现的,因此在打开串口的同时,要注册数据处理函数:
sp.DataReceived += new SerialDataReceivedEventHandler(DataReceived);
//当串口收到数据时,会自动调用该函数:
private void DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (sp.BytesToRead <= 0)
{
return;
}
//加锁,保证线程安全
lock (thisLock)
{
int len = sp.BytesToRead;
Byte[] data = new Byte[len];
try
{
sp.Read(data, 0, len);
}
catch (System.Exception)
{
//catch read exception
}
SerialPortEventArgs args = new SerialPortEventArgs();
args.receivedBytes = data;
//用事件通知界面
if (comReceiveDataEvent != null)
{
comReceiveDataEvent.Invoke(this, args);
}
}
}
//向串口设备发送数据
public bool Send(Byte[] bytes)
{
...
try
{
sp.Write(bytes, 0, bytes.Length);
}
catch (System.Exception)
{
return false; //write failed
}
return true; //write successfully
}
//打开串口
public void Open()
{
...
if (handshake == "None")
{
sp.RtsEnable = true;
sp.DtrEnable = true;
}
...
}
//关闭串口
public void Close()
{
Thread closeThread = new Thread(new ThreadStart(CloseSpThread));
closeThread.Start();
}
// Close serial port thread
private void CloseSpThread()
{
...
try
{
sp.Close();
}
catch (Exception)
{
...
}
...
}
}
Form类是与界面相关的代码,是Model的观察者,在该类里会实现接口View中更新界面的函数,当该函数注册到Model中并被调用时会更新界面。该类需要注意以下问题:
黄金定律:永远不要在非UI的线程内更新UI部件
public interface IView
{
void SetController(IController controller);
...
//Serial port receive data event
void ComReceiveDataEvent(Object sender, SerialPortEventArgs e);
...
}
public partial class MainForm : Form, IView
{
private IController controller;
...
//设置控制器
public void SetController(IController controller)
{
this.controller = controller;
}
//收到串口数据后,更新界面
public void ComReceiveDataEvent(Object sender, SerialPortEventArgs e)
{
if (this.InvokeRequired)
{
Invoke(new Action<Object, SerialPortEventArgs>(ComReceiveDataEvent),
sender, e);
return;
}
receivetbx.AppendText(Encoding.Default.GetString(e.receivedBytes));
}
//点击发送按钮时,调用Controller发送数据
private void sendbtn_Click(object sender, EventArgs e)
{
String sendText = sendtbx.Text;
...
//send String to serial port
bool flag = controller.SendDataToCom(sendText);
...
}
}
Controller类是Form类和Model类的桥梁,在该Controller中,将界面上的更新函数注册到Model中。一般来说Controller比较复杂的一部分,但在这个程序中比较简单,没有数据库相关的操作。
public class IController
{
ComModel comModel = new ComModel();
IView view;
public IController(IView view)
{
this.view = view;
view.SetController(this);
//将页面上的更新界面函数注册到Model的事件
comModel.comReceiveDataEvent
+= new SerialPortEventHandler(view.ComReceiveDataEvent);
}
//通过Model发送字符到串口
public bool SendDataToCom(String str)
{
if (str != null && str != "")
{
return comModel.Send(Encoding.Default.GetBytes(str));
}
return true;
}
}
[1] Serial Comms in C# for Beginners. CodeProject 2013.
[2] Your first program using MVC pattern with C#/WinForms. CodeProje 2013.
Last update: 2014-11-13