使用c#進行串口通信的實現(xiàn)示例
一、串口通信協(xié)議
1.串口通信協(xié)議簡介
串口通信(serial communication)是一種設備間非常常用的串行通信方式,大部分電子設備都支持,電子工程師再調試設備時也經常使用該通信方式輸出調試信息。講到某一種通信協(xié)議,離不開的就是物理層,物理層主要表現(xiàn)形式是電平信號的高低幅值,分別代表的狀態(tài)。 串口的物理層有很多標準,主要包含RS232標準,該標準規(guī)定了信號的用途、通信接口以及信號的電平標準;

在上面的通訊方式中,兩個通訊設備的“DB9接口”之間通過串口信號線建立起連接,串口信號線中使用“RS-232標準”傳輸數(shù)據(jù)信號。
由于RS-232電平標準的信號不能直接被控制器直接識別,所以這些信號會經過一個“電平轉換芯片”轉換成控制器能識別的“TTL校準”的電平信號,才能實現(xiàn)通訊。
電平標準 根據(jù)通信使用的電平標準不同,串口通信可以分為TTL標準和RS232標準

我們常見的電子電路中使用的TTL電平標準,理想狀態(tài)下,使用5V表示二級制邏輯1,使用0V表示邏輯0,而為了增加串口通信的遠距離傳輸以及抗干擾能力,它使用-15V表示邏輯1,+15V表示邏輯0,使用RS232與TTL電平標準表示同一個信號時的對比見圖

因為控制器一般使用TTL電平標準,所以常常會使用MA3232芯片對TTL以及RS232電平信號進行互相轉換。
RS-232信號線 在最初的應用中,RS-232串口標準常用于計算機、路由與調制調解器(MODEN,俗稱“貓”)之間的通訊 ,在這種通訊系統(tǒng)中,設備被分為數(shù)據(jù)終端設備DTE(計算機、路由)和數(shù)據(jù)通訊設備DCE(調制調解器)。我們以這種通訊模型講解它們的信號線連接方式及各個信號線的作用。
在舊式的臺式計算機中一般會有RS-232標準的COM口(也稱DB9接口)

其中接線口以針式引出信號線的稱為公頭,以孔式引出信號線的稱為母頭。在計算機中一般引出公頭接口,而在調制調解器設備中引出的一般為母頭,使用上圖中的串口線即可把它與計算機連接起來。通訊時,串口線中傳輸?shù)男盘柧褪鞘褂们懊嬷v解的RS-232標準調制的。
在這種應用場合下,DB9接口中的公頭及母頭的各個引腳的標準信號線接法見圖

上表中的是計算機端的DB9公頭標準接法,由于兩個通訊設備之間的收發(fā)信號(RXD與TXD)應交叉相連,所以調制調解器端的DB9母頭的收發(fā)信號接法一般與公頭的相反,兩個設備之間連接時,只要使用“直通型”的串口線連接起來即可

串口線中的RTS、CTS、DSR、DTR及DCD信號,使用邏輯 1表示信號有效,邏輯0表示信號無效。例如,當計算機端控制DTR信號線表示為邏輯1時,它是為了告知遠端的調制調解器,本機已準備好接收數(shù)據(jù),0則表示還沒準備就緒。
在目前的其它工業(yè)控制使用的串口通訊中,一般只使用RXD、TXD以及GND三條信號線,直接傳輸數(shù)據(jù)信號。而RTS、CTS、DSR、DTR及DCD信號都被裁剪掉了,如果您在前面被這些信號弄得暈頭轉向,那就直接忽略它們吧。
協(xié)議層
串口通訊的數(shù)據(jù)包由發(fā)送設備通過自身的TXD接口傳輸?shù)浇邮赵O備的RXD接口。在串口通訊的協(xié)議層中,規(guī)定了數(shù)據(jù)包的內容,它由啟始位、主體數(shù)據(jù)、校驗位以及停止位組成,通訊雙方的數(shù)據(jù)包格式要約定一致才能正常收發(fā)數(shù)據(jù),其組成見下圖。

波特率
在串口異步通訊中由于沒有時鐘信號(如前面講解的DB9接口中是沒有時鐘信號的),所以兩個通訊設備之間需要約定好波特率,即每個碼元的長度,以便對信號進行解碼常見的波特率為4800、9600、115200等。
- 通訊的起始和停止信號 串口通訊的一個數(shù)據(jù)包從起始信號開始,直到停止信號結束。數(shù)據(jù)包的起始信號由一個邏輯0的數(shù)據(jù)位表示,而數(shù)據(jù)包的停止信號可由0.5、1、1.5或2個邏輯1的數(shù)據(jù)位表示,只要雙方約定一致即可。
- 有效數(shù)據(jù) 在數(shù)據(jù)包的起始位之后緊接著的就是要傳輸?shù)闹黧w數(shù)據(jù)內容,也稱為有效數(shù)據(jù),有效數(shù)據(jù)的長度常被約定為5、6、7或8位長。
- 數(shù)據(jù)校驗 在有效數(shù)據(jù)之后,有一個可選的數(shù)據(jù)校驗位。由于數(shù)據(jù)通信相對更容易受到外部干擾導致傳輸數(shù)據(jù)出現(xiàn)偏差,可以在傳輸過程加上校驗位來解決這個問題。校驗方法有奇校驗(odd)、偶校驗(even)、0校驗(space)、1校驗(mark)以及無校驗(noparity),它們介紹如下:
- 奇校驗要求有效數(shù)據(jù)和校驗位中“1”的個數(shù)為奇數(shù),比如一個8位長的有效數(shù)據(jù)為:01101001,此時總共有4個“1”,為達到奇校驗效果,校驗位為“1”,最后傳輸?shù)臄?shù)據(jù)將是8位的有效數(shù)據(jù)加上1位的校驗位總共9位。
- 偶校驗與奇校驗要求剛好相反,要求幀數(shù)據(jù)和校驗位中“1”的個數(shù)為偶數(shù),比如數(shù)據(jù)幀:11001010,此時數(shù)據(jù)幀“1”的個數(shù)為4個,所以偶校驗位為“0”。
- 0校驗是不管有效數(shù)據(jù)中的內容是什么,校驗位總為“0”,1校驗是校驗位總為“1”。
在無校驗的情況下,數(shù)據(jù)包中不包含校驗位。
二、程序連接串口操作
SerialPort類的常用API:
屬性:

常用方法

常用事件

代碼示例:
using System.IO.Ports;
using System.Text;
namespace SerialPortCommunication
{
class Program
{
private static SerialPort serialPort;
public static void Main()
{
#region 串口通信
ConnectSerialPort("COM2");
ClosePort();
ReciveData();
SendData();
Console.ReadKey();
#endregion
}
/// <summary>
/// 接收串口數(shù)據(jù)
/// </summary>
/// <exception cref="NotImplementedException"></exception>
private static void ReciveData()
{
// 連接串口
var result = ConnectSerialPort("COM2");
while(true)
{
if (result)
{
// BytesToRead 代表串口中的字節(jié)大小
// 串口中的數(shù)據(jù)接收后,數(shù)據(jù)就被移除了。
var buffer = new byte[serialPort.BytesToRead];
// 接收發(fā)送的數(shù)據(jù)
#region 主動接收
serialPort.Read(buffer, 0, buffer.Length);
var resultString = Encoding.UTF8.GetString(buffer, 0, buffer.Length);
Console.WriteLine($"接收到結果: {resultString}");
}
}
serialPort.Close();
#endregion
}
/// <summary>
/// 發(fā)送數(shù)據(jù)的方法
/// </summary>
/// <exception cref="NotImplementedException"></exception>
private static void SendData()
{
// 連接串口
var result = ConnectSerialPort("COM1");
if (result)
{
// 發(fā)送數(shù)據(jù)
//serialPort.Write("Hello SerialPort");
//var buffer = new byte[] { 0x01, 0x02,0x03 };
// 發(fā)送數(shù)據(jù)
var buffer = Encoding.UTF8.GetBytes("中文");
serialPort.Write(buffer, 0, buffer.Length);
//serialPort.WriteBufferSize = 65535; // modbus 250
//serialPort.WriteLine("Don't type chinese");
}
serialPort.Close();
}
/// <summary>
/// 關閉串口的方法
/// </summary>
/// <exception cref="NotImplementedException"></exception>
private static void ClosePort()
{
if (serialPort == null)
{
return;
}
serialPort.Close();
}
/// <summary>
/// 連接串口的方法
/// </summary>
/// <returns></returns>
private static bool ConnectSerialPort(string portName)
{
// 創(chuàng)建串口對象
serialPort = new SerialPort();
// 設置連接串口名稱
serialPort.PortName = portName;
// 設置波特率
serialPort.BaudRate = 9600;
// 設置數(shù)據(jù)位
serialPort.DataBits = 8;
// 設置停止位
serialPort.StopBits = StopBits.One;
// 設置校驗位
serialPort.Parity = Parity.None;
serialPort.DataReceived += SerialPort_DataReceived;
// 打開串口
serialPort.Open();
// 判斷串口是否成功打開
if (serialPort.IsOpen)
{
Console.WriteLine("串口連接成功");
return true;
}
else
{
Console.WriteLine("串口連接失敗");
}
return false;
}
/// <summary>
/// 接收事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <exception cref="NotImplementedException"></exception>
private static void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
var serialPort1 = sender as SerialPort;
var buffer = new byte[serialPort1!.BytesToRead];
serialPort1.Read(buffer, 0, buffer.Length);
var resultString = Encoding.UTF8.GetString(buffer, 0, buffer.Length);
Console.WriteLine($"接收到結果: {resultString}");
}
}
}
到此這篇關于使用c#進行串口通信的實現(xiàn)示例的文章就介紹到這了,更多相關c# 串口通信內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
C#控件編程之顯示信息控件詳解(Label、LinkLabel)
這篇文章主要介紹了C#控件編程之顯示信息控件詳解(Label、LinkLabel),具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2025-04-04

