使用c#進(jìn)行串口通信的實(shí)現(xiàn)示例
一、串口通信協(xié)議
1.串口通信協(xié)議簡介
串口通信(serial communication)
是一種設(shè)備間非常常用的串行通信方式,大部分電子設(shè)備都支持,電子工程師再調(diào)試設(shè)備時(shí)也經(jīng)常使用該通信方式輸出調(diào)試信息。講到某一種通信協(xié)議,離不開的就是物理層,物理層主要表現(xiàn)形式是電平信號(hào)的高低幅值,分別代表的狀態(tài)。 串口的物理層有很多標(biāo)準(zhǔn),主要包含RS232標(biāo)準(zhǔn)
,該標(biāo)準(zhǔn)規(guī)定了信號(hào)的用途、通信接口以及信號(hào)的電平標(biāo)準(zhǔn);
在上面的通訊方式中,兩個(gè)通訊設(shè)備的“DB9接口”之間通過串口信號(hào)線建立起連接,串口信號(hào)線中使用“RS-232標(biāo)準(zhǔn)”傳輸數(shù)據(jù)信號(hào)。
由于RS-232電平標(biāo)準(zhǔn)的信號(hào)不能直接被控制器直接識(shí)別,所以這些信號(hào)會(huì)經(jīng)過一個(gè)“電平轉(zhuǎn)換芯片”轉(zhuǎn)換成控制器能識(shí)別的“TTL校準(zhǔn)”的電平信號(hào),才能實(shí)現(xiàn)通訊。
電平標(biāo)準(zhǔn)
根據(jù)通信使用的電平標(biāo)準(zhǔn)不同,串口通信可以分為TTL標(biāo)準(zhǔn)和RS232標(biāo)準(zhǔn)
我們常見的電子電路中使用的TTL電平標(biāo)準(zhǔn),理想狀態(tài)下,使用5V表示二級(jí)制邏輯1,使用0V表示邏輯0,而為了增加串口通信的遠(yuǎn)距離傳輸以及抗干擾能力,它使用-15V表示邏輯1,+15V表示邏輯0,使用RS232與TTL電平標(biāo)準(zhǔn)表示同一個(gè)信號(hào)時(shí)的對比見圖
因?yàn)榭刂破饕话闶褂肨TL電平標(biāo)準(zhǔn),所以常常會(huì)使用MA3232芯片對TTL以及RS232電平信號(hào)進(jìn)行互相轉(zhuǎn)換。
RS-232信號(hào)線 在最初的應(yīng)用中,RS-232串口標(biāo)準(zhǔn)常用于計(jì)算機(jī)、路由與調(diào)制調(diào)解器(MODEN,俗稱“貓”)之間的通訊 ,在這種通訊系統(tǒng)中,設(shè)備被分為數(shù)據(jù)終端設(shè)備DTE(計(jì)算機(jī)、路由)和數(shù)據(jù)通訊設(shè)備DCE(調(diào)制調(diào)解器)。我們以這種通訊模型講解它們的信號(hào)線連接方式及各個(gè)信號(hào)線的作用。
在舊式的臺(tái)式計(jì)算機(jī)中一般會(huì)有RS-232標(biāo)準(zhǔn)的COM口(也稱DB9接口)
其中接線口以針式引出信號(hào)線的稱為公頭,以孔式引出信號(hào)線的稱為母頭。在計(jì)算機(jī)中一般引出公頭接口,而在調(diào)制調(diào)解器設(shè)備中引出的一般為母頭,使用上圖中的串口線即可把它與計(jì)算機(jī)連接起來。通訊時(shí),串口線中傳輸?shù)男盘?hào)就是使用前面講解的RS-232標(biāo)準(zhǔn)調(diào)制的。
在這種應(yīng)用場合下,DB9接口中的公頭及母頭的各個(gè)引腳的標(biāo)準(zhǔn)信號(hào)線接法見圖
上表中的是計(jì)算機(jī)端的DB9公頭標(biāo)準(zhǔn)接法,由于兩個(gè)通訊設(shè)備之間的收發(fā)信號(hào)(RXD與TXD)應(yīng)交叉相連,所以調(diào)制調(diào)解器端的DB9母頭的收發(fā)信號(hào)接法一般與公頭的相反,兩個(gè)設(shè)備之間連接時(shí),只要使用“直通型”的串口線連接起來即可
串口線中的RTS、CTS、DSR、DTR及DCD信號(hào),使用邏輯 1表示信號(hào)有效,邏輯0表示信號(hào)無效。例如,當(dāng)計(jì)算機(jī)端控制DTR信號(hào)線表示為邏輯1時(shí),它是為了告知遠(yuǎn)端的調(diào)制調(diào)解器,本機(jī)已準(zhǔn)備好接收數(shù)據(jù),0則表示還沒準(zhǔn)備就緒。
在目前的其它工業(yè)控制使用的串口通訊中,一般只使用RXD、TXD以及GND三條信號(hào)線,直接傳輸數(shù)據(jù)信號(hào)。而RTS、CTS、DSR、DTR及DCD信號(hào)都被裁剪掉了,如果您在前面被這些信號(hào)弄得暈頭轉(zhuǎn)向,那就直接忽略它們吧。
協(xié)議層
串口通訊的數(shù)據(jù)包由發(fā)送設(shè)備通過自身的TXD接口傳輸?shù)浇邮赵O(shè)備的RXD接口。在串口通訊的協(xié)議層中,規(guī)定了數(shù)據(jù)包的內(nèi)容,它由啟始位、主體數(shù)據(jù)、校驗(yàn)位以及停止位組成,通訊雙方的數(shù)據(jù)包格式要約定一致才能正常收發(fā)數(shù)據(jù),其組成見下圖。
波特率
在串口異步通訊中由于沒有時(shí)鐘信號(hào)(如前面講解的DB9接口中是沒有時(shí)鐘信號(hào)的),所以兩個(gè)通訊設(shè)備之間需要約定好波特率,即每個(gè)碼元的長度,以便對信號(hào)進(jìn)行解碼常見的波特率為4800、9600、115200等。
- 通訊的起始和停止信號(hào) 串口通訊的一個(gè)數(shù)據(jù)包從起始信號(hào)開始,直到停止信號(hào)結(jié)束。數(shù)據(jù)包的起始信號(hào)由一個(gè)邏輯0的數(shù)據(jù)位表示,而數(shù)據(jù)包的停止信號(hào)可由0.5、1、1.5或2個(gè)邏輯1的數(shù)據(jù)位表示,只要雙方約定一致即可。
- 有效數(shù)據(jù) 在數(shù)據(jù)包的起始位之后緊接著的就是要傳輸?shù)闹黧w數(shù)據(jù)內(nèi)容,也稱為有效數(shù)據(jù),有效數(shù)據(jù)的長度常被約定為5、6、7或8位長。
- 數(shù)據(jù)校驗(yàn) 在有效數(shù)據(jù)之后,有一個(gè)可選的數(shù)據(jù)校驗(yàn)位。由于數(shù)據(jù)通信相對更容易受到外部干擾導(dǎo)致傳輸數(shù)據(jù)出現(xiàn)偏差,可以在傳輸過程加上校驗(yàn)位來解決這個(gè)問題。校驗(yàn)方法有奇校驗(yàn)(odd)、偶校驗(yàn)(even)、0校驗(yàn)(space)、1校驗(yàn)(mark)以及無校驗(yàn)(noparity),它們介紹如下:
- 奇校驗(yàn)要求有效數(shù)據(jù)和校驗(yàn)位中“1”的個(gè)數(shù)為奇數(shù),比如一個(gè)8位長的有效數(shù)據(jù)為:01101001,此時(shí)總共有4個(gè)“1”,為達(dá)到奇校驗(yàn)效果,校驗(yàn)位為“1”,最后傳輸?shù)臄?shù)據(jù)將是8位的有效數(shù)據(jù)加上1位的校驗(yàn)位總共9位。
- 偶校驗(yàn)與奇校驗(yàn)要求剛好相反,要求幀數(shù)據(jù)和校驗(yàn)位中“1”的個(gè)數(shù)為偶數(shù),比如數(shù)據(jù)幀:11001010,此時(shí)數(shù)據(jù)幀“1”的個(gè)數(shù)為4個(gè),所以偶校驗(yàn)位為“0”。
- 0校驗(yàn)是不管有效數(shù)據(jù)中的內(nèi)容是什么,校驗(yàn)位總為“0”,1校驗(yàn)是校驗(yàn)位總為“1”。
在無校驗(yàn)的情況下,數(shù)據(jù)包中不包含校驗(yàn)位。
二、程序連接串口操作
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 主動(dòng)接收 serialPort.Read(buffer, 0, buffer.Length); var resultString = Encoding.UTF8.GetString(buffer, 0, buffer.Length); Console.WriteLine($"接收到結(jié)果: {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> /// 關(guān)閉串口的方法 /// </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(); // 設(shè)置連接串口名稱 serialPort.PortName = portName; // 設(shè)置波特率 serialPort.BaudRate = 9600; // 設(shè)置數(shù)據(jù)位 serialPort.DataBits = 8; // 設(shè)置停止位 serialPort.StopBits = StopBits.One; // 設(shè)置校驗(yàn)位 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($"接收到結(jié)果: {resultString}"); } } }
到此這篇關(guān)于使用c#進(jìn)行串口通信的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)c# 串口通信內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C#控件編程之顯示信息控件詳解(Label、LinkLabel)
這篇文章主要介紹了C#控件編程之顯示信息控件詳解(Label、LinkLabel),具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-04-04C#實(shí)現(xiàn)文件與字符串互轉(zhuǎn)的方法詳解
這篇文章主要為大家詳細(xì)介紹了如何利用C#實(shí)現(xiàn)文件與字符串互轉(zhuǎn)效果,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)C#有一定幫助,需要的可以參考一下2022-08-08C#解決訪問API顯示基礎(chǔ)連接已經(jīng)關(guān)閉的問題
最近在 Web 部署百度 AI 圖像識(shí)別 AipSdk.dll 封裝庫的時(shí)候,在調(diào)用OCR圖像識(shí)別 API 的時(shí)候,顯示為 “ 基礎(chǔ)連接已經(jīng)關(guān)閉: 接收時(shí)發(fā)生錯(cuò)誤,” ,并且運(yùn)行后直接崩潰,所以本文給大家介紹了C#解決訪問API顯示基礎(chǔ)連接已經(jīng)關(guān)閉的問題,需要的朋友可以參考下2024-12-12