c#串口调试助手、上位机

简介:
此串口调试助手是在windows下开发的,工具是 Visual Studio 2017开发的,串口调试助手支持 600115200波特率,端口号是 CMO1CMO9,支持数据位、停止位、奇偶位,支持数据的接收与发送,接收方式十六进制、字符的选择,对接收端数据的清空,并能对温度传感器数据进行接收与绘折线图,并且能设置正常温度现实的区间,及时判断温度是否显示正常与否,并能对曲线的显示进行刷新重新绘制。

效果图片:

源码文件名:Csha_demo-master

想要源代码的可以关注公众号回复:上位机

串口参数介绍

简介:
典型地,串口用于ASCII码字符的传输。通信使用3根线完成:(1)地线,(2)发送,(3)接收。由于串口通信是异步的,端口能够在一根线上发送数据同时在另一根线上接收数据。

波特率:
这是一个衡量符号传输速率的参数。它表示每秒钟传送的符号的个数。例如300波特表示每秒钟发送300个符号。当我们提到时钟周期时,我们就是指波特率,例如如果协议需要4800波特率,那么时钟是4800Hz。这意味着串口通信在数据线上的采样率为4800Hz。通常电话线的波特率为14400,28800和36600。波特率可以远远大于这些值,但是波特率和距离成反比。高波特率常常用于放置的很近的仪器间的通信,典型的例子就是GPIB设备的通信。

数据位:
数据位表示一组数据实际包含的数据位数。数据位紧跟在起始位之后,是通信中的真正有效信息。数据位的位数由通信双方共同约定,一般可以是6位、7位或8位,比如标准的ASCII码是0—127(7位),扩展的ASCII码是0—255(8位)。传输数据时先传送字符的低位,后传送字符的高位,即低位(LSB)在前,高位(MSB)在后。

奇偶位:
奇偶校验通常用在数据通信中来保证数据的有效性。奇偶校验位是一个表示给定位数的二进制数中 1 的个数是奇数还是偶数的二进制数。奇偶校验位是最简单的错误检测码。

停止位:
用于表示单个包的最后一位。典型的值为1,1.5和2位。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。

串口通信模块(SerialPort)

  • 配置IO文件
    1
    2
    using System.IO;
    using System.IO.Ports;
  • 图标
属性 介绍
BaudRate 波特率
DataBits 数据位
Parity 奇偶校验位
PortName 端口号
StopBits 停止位
ByteToRead 获取输入缓冲区的
IsOpen 获取是否开启串口
  • Serialport 通信模块的事件

在这里我们只用到DataReceived串口接收函数,如需了解另外两个请自己查阅

事件 介绍
DataReceived 串口接收函数
ErrorReceived 串口数据接收错误
PinChanged 串口号发生改变

串口窗口介绍

由图可以看出正常温度区间设置的是20-25度之间,测试温度的过程中温度曲线黄色的为正常的曲线,红色的曲线为温度异常。温度刷新,温度曲线重新读取画线

在窗体上添加4个Groupbox控件:

  • 第一个:串口设置
  • 第二的:接收数据
  • 第三个:发送数据
  • 第四个:温度曲线显示

代码编写

串口初始化函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
private void button_open_Click(object sender, EventArgs e)
{
// if(Button_on == 1)
if (!serialPort1.IsOpen)//如果串口是开
{
serialPort1.PortName = comboBox1.Text;//端口号
serialPort1.BaudRate = Convert.ToInt32(comboBox2.Text, 10);//波特率
float f = Convert.ToSingle(comboBox3.Text.Trim());//trim去除字符串前后的空格
if (f == 0)//设置停止位
serialPort1.StopBits = StopBits.None;
else if (f == 1.5)
serialPort1.StopBits = StopBits.OnePointFive;
else if (f == 1)
serialPort1.StopBits = StopBits.One;
else if (f == 2)
serialPort1.StopBits = StopBits.Two;
else
serialPort1.StopBits = StopBits.One;
//设置数据位
serialPort1.DataBits = Convert.ToInt32(comboBox4.Text.Trim());
//设置奇偶校验位
string s = comboBox5.Text.Trim();
if (s.CompareTo("无") == 0)
serialPort1.Parity = Parity.None;
else if (s.CompareTo("奇校验") == 0)
serialPort1.Parity = Parity.Odd;
else if (s.CompareTo("偶校验") == 0)
serialPort1.Parity = Parity.Even;
else
serialPort1.Parity = Parity.None;
try
{
serialPort1.Open(); //打开串口
button_open.Text = "关闭串口";
comboBox1.Enabled = false;//关闭使能
comboBox2.Enabled = false;
comboBox3.Enabled = false;
comboBox4.Enabled = false;
comboBox5.Enabled = false;
comboBox6.Enabled = false;
comboBox7.Enabled = false;
}
catch
{
MessageBox.Show("串口打开失败!");
}
}
else//如果串口是打开的则将其关闭
{
serialPort1.Close();
button_open.Text = "打开串口";
comboBox1.Enabled = true;//使能配置
comboBox2.Enabled = true;
comboBox3.Enabled = true;
comboBox4.Enabled = true;
comboBox5.Enabled = true;
comboBox6.Enabled = true;
comboBox7.Enabled = true;
}

}

画坐标图

画坐标时用到头文件:

1
2
using System.Drawing;
using System.Drawing.Drawing2D;

画线时用到类graphics

graphics是抽象函数不能实例化,并且画线的时候编辑器是以左上角为坐标原点画的,想要把它改成左下角,只能自己计算坐标点,根据自己的需求把它画成称自己想要的,如上图我把(50,218)这个坐标表示成原点

注意:Graphics是在控件Groupbox控件下事件Paint下写的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
private void groupBox4_Paint(object sender, PaintEventArgs e)
{
FontFamily family = new FontFamily("Arial");//实例化字体的类型
int fontstyle = (int)FontStyle.Italic;//设置字体的样式
GraphicsPath gp = new GraphicsPath();
string Str;
//Draw y纵向轴绘制
for (int i = 0; i <= 14; i++)
{
e.Graphics.DrawLine(TablePen, StartPrint + i * Unit_length, StartPrint - 32, StartPrint + i * Unit_length, 10 * Unit_length + 18);//画线
gp.AddString((i * 10).ToString(), family, fontstyle, 8, new RectangleF(StartPrint + i * Unit_length - 7, 10 * Unit_length + 18 + 4, 400, 50), null);//添加文字
}
gp.AddString("时间(s)", family, fontstyle, 12, new RectangleF(groupBox4.ClientRectangle.Width / 2 , 9 * Unit_length + 55, 400, 50), null);
Draw X 横向轴绘制
for (int i = 0; i <= 10; i++)
{
e.Graphics.DrawLine(TablePen, StartPrint, i * Unit_length + 18, StartPrint + 14 * Unit_length, i * Unit_length + 18);//画线
Str = Convert.ToString((10 - i) * 5);
if (i == 0)
Str = "50";
if (i == 10)
break;
gp.AddString(Str, family, fontstyle, 8, new RectangleF(20, i * Unit_length +16, 400, 50), null);//添加文字
}
gp.AddString("温", family, fontstyle, 12, new RectangleF(0, groupBox4.ClientRectangle.Height / 2 - StartPrint, 400, 50), null);
gp.AddString("度", family, fontstyle, 12, new RectangleF(0, groupBox4.ClientRectangle.Height / 2 + 18 - StartPrint, 400, 50), null);
gp.AddString("°C", family, fontstyle, 12, new RectangleF(0, groupBox4.ClientRectangle.Height / 2 + 32 - StartPrint, 400, 50), null);
e.Graphics.DrawPath(Pens.White, gp);//写文字
}

方法介绍

DrawLine(Pen pen, int x1, int y1, int x2, int y2);

pen:定义线条的宽度、颜色、样式
画线由(x1,y1)向(x2,y2)画

public void AddString(string s, FontFamily family, int style, float emSize, RectangleF layoutRect, StringFormat format);

s:绘制的字符
family:绘制的字体类型
style:字体的样式
emsize:方形块的高度
layoutRect:文本的矩形
format:文本格式设置信息

c#延时函数

1
2
3
4
5
6
7
8
public static void Delay(int milliSecond)
{
int start = Environment.TickCount;
while (Math.Abs(Environment.TickCount - start) < milliSecond)//毫秒
{
Application.DoEvents();//可执行某无聊的操作
}
}

关于Math.Abs():
Environment.TickCount,内部API是用DWORD GetTickCount()来实现的,该属性的值从系统计时器派生,并以 32 位有符号整数的形式存储。因此,如果系统连续运行,TickCount 将在约 24.9 天内从零递增至 Int32. MaxValue ,然后跳至 Int32. MinValue (这是一个负数),再在接下来的 24.9 天内递增至零。DWORD是无符号的,而 Environment.TickCount属性返回的值是有符号的,所以有一半的值用负数表示!

串口写函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private void button_send_Click(object sender, EventArgs e)
{//发送数据
if(serialPort1.IsOpen)
{//如果串口开启
if (textBox_send.Text.Trim() != "")//如果框内不为空则
{
serialPort1.Write(textBox_send.Text.Trim());//写数据
}
else
{
MessageBox.Show("发送框没有数据");
}
}
else
{
MessageBox.Show("串口未打开");
}
}

串口读函数(如果是温度传感器并选择温度传感器画温度曲线)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
private void post_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
float Up, Dw,t;
Graphics graphics = groupBox4.CreateGraphics();
if (!radioButton1.Checked)
{
string str = serialPort1.ReadExisting();//字符串方式读
if(!radioButton3.Checked)
{
//提取字符串中的数字
string result = System.Text.RegularExpressions.Regex.Replace(str, @"[^0-9]+", "");
Console.WriteLine(result);
//设置正常温度上限下限
Dw = Convert.ToSingle(comboBox6.Text);
Up = Convert.ToSingle(comboBox7.Text);
if(Dw<Up)
{
t = Dw;
Dw = Up;
Up = t;
}
dstr2 = Convert.ToSingle(result);//字符串的转换成float
textBox_T.Text = result + "度";
textBox_receive.AppendText(str);
//正常温度的曲线
if(dstr2>=Up&&dstr2<=Dw)
{
graphics.DrawLine(LinesPen, Origin_x + i * Unit_length, dstr1, Origin_x + (i+0.1f) * Unit_length, Origin_y - dstr2 * 4);
}
else//异常温度的曲线
{
graphics.DrawLine(LinesEpen, Origin_x + i * Unit_length, dstr1, Origin_x + (i + 0.1f) * Unit_length, Origin_y - dstr2 * 4);
}
i += 0.1f;
dstr1 = Origin_y - dstr2 * 4;
Delay(1000);//不延时的话不显示
//textBox_T.Clear();无所谓
}
else
{
textBox_receive.AppendText(str);
}
}
else
{
byte data;
data = (byte)serialPort1.ReadByte();
string str = Convert.ToString(data, 16).ToUpper();//
textBox_receive.AppendText("0x" + (str.Length == 1 ? "0" + str : str) + " ");

}
}

相关链接

  1. c#串口调试助手温度曲线的显示-上位机的开发-串口参数介绍

=================我是分割线=================

欢迎到公众号来唠嗑: