c#串口調試助手、上位機
- 簡介
- 此串口調試助手是在windows下開發的,工具是Visual Studio 2017開發的,串口調試助手支持600~~115200波特率,端口號是CMO1~~CMO9,支持數據位、停止位、奇偶位,支持數據的接收與發送,接收方式十六進制、字符的選擇,對接收端數據的清空,並能對溫度傳感器數據進行接收與繪折線圖,並且能設置正常溫度現實的區間,及時判斷溫度是否顯示正常與否,並能對曲線的顯示進行刷新重新繪製。
- 實驗問題與修正
- 在這篇博客中我會把我所遇到的問題、自己寫的代碼、與解決的方法分享出來
- 先上一下實驗圖片
串口參數介紹
- 簡介
- 典型地,串口用於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文件
using System.IO;
using System.IO.Ports;
- 圖標
屬性 | 介紹 |
---|---|
BaudRate | 波特率 |
DataBits | 數據位 |
Parity | 奇偶校驗位 |
PortName | 端口號 |
StopBits | 停止位 |
ByteToRead | 獲取輸入緩衝區的 |
IsOpen | 獲取是否開啓串口 |
- Serialport通信模塊的事件
在這裏我們只用到DataReceived串口接收函數,如需瞭解另外兩個請自己查閱
事件 | 介紹 |
---|---|
DataReceived | 串口接收函數 |
ErrorReceived | 串口數據接收錯誤 |
PinChanged | 串口號發生改變 |
串口窗口介紹
由圖可以看出正常溫度區間設置的是20-25度之間,測試溫度的過程中溫度曲線黃色的爲正常的曲線,紅色的曲線爲溫度異常。溫度刷新,溫度曲線重新讀取畫線
在窗體上添加4個Groupbox控件
第一個:串口設置
第二的:接收數據
第三個:發送數據
第四個:溫度曲線顯示
代碼編寫
- 串口初始化函數
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;
}
}
- 畫座標圖
- 畫座標時用到頭文件
using System.Drawing;
using System.Drawing.Drawing2D;
- 畫線時用到類graphics
graphics是抽象函數不能實例化,並且畫線的時候編輯器是以左上角爲座標原點畫的,想要把它改成左下角,只能自己計算座標點,根據自己的需求把它畫成稱自己想要的,如上圖我把(50,218)這個座標表示成原點
注意:Graphics是在控件Groupbox控件下事件Paint下寫的
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#延時函數
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屬性返回的值是有符號的,所以有一半的值用負數表示!
- 串口寫函數
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("串口未打開");
}
}
- 串口讀函數(如果是溫度傳感器並選擇溫度傳感器畫溫度曲線)
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) + " ");
}
}
想要源代碼的可以關注公衆號回覆:上位機