C#中SerialPort的使用教程詳解
最近在學習C#的SerialPort ,關于SerialPort 的使用,做如下總結:
1.可以通過函數(shù)System.IO.Ports.SerialPort.GetPortNames() 將獲得系統(tǒng)所有的串口名稱。C#代碼如下:
string[] sPorts = SerialPort.GetPortNames(); foreach(string port in sPorts) { var serialPort = new SerialPort(); serialPort.PortName = port; serialPort.Open(); serialPort.WriteLine("ATI"); // this will ask the port to issue an ident string which you can match against }
2.列出所有的串口:
private void comboBox1_Click(object sender, EventArgs e) { string[] portNamesArray = SerialPort.GetPortNames(); this.comboBox1.Items.Clear(); foreach (var item in portNamesArray) { this.comboBox1.Items.Add(item); } this.comboBox1.Items.Add(""); } private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) { selectedPortName = this.comboBox1.SelectedItem.ToString();//獲取選中的port }
3. 打開/關閉串口:
SerialPort port = new SerialPort(); port.BaudRate = 1200;//波特率 port.PortName = "COM1"; port.Parity = Parity.None;//校驗法:無 port.DataBits = 8;//數(shù)據(jù)位:8 port.StopBits = StopBits.One;//停止位:1 try { port.Open();//打開串口 port.DtrEnable = true;//設置DTR為高電平 port.RtsEnable = true;//設置RTS位高電平 } catch (Exception ex) { //打開串口出錯,顯示錯誤信息 MessageBox.Show(ex.Message); } if (port.IsOpen) { port.Close();//關閉串口 }
4.寫數(shù)據(jù):
函數(shù) | 說明 |
---|---|
void Write(byte[] buffer, int offset, int count); void Write(char[] buffer, int offset, int count); | 寫二進制數(shù)據(jù) |
void Write(string text); | 寫文本數(shù)據(jù) |
void WriteLine(string text); | 寫一行數(shù)據(jù) |
(1)寫二進制數(shù)據(jù):
void Write(byte[] buffer, int offset, int count);和void Write(char[] buffer, int offset, int count);用于寫二進制數(shù)據(jù)。它們的區(qū)別僅僅在于第一個參數(shù)不同:byte[]是無符號的,char[]是有符號的。對于二進制數(shù)據(jù)而言,byte、char沒有實質(zhì)的區(qū)別。
下面的C#代碼,將寫1024個00H:
if (port.IsOpen) { byte[] bt = new byte[1024]; port.Write(bt, 0, bt.Length);//寫1024個00H }
注意:
1、Write函數(shù)是同步的。以上面的代碼為例,1024個00H在發(fā)送完之前,Write函數(shù)是不會返回的。波特率1200,發(fā)送1024個字節(jié)大概要耗時9秒。如果這段代碼在主線程里,那么這9秒內(nèi)整個程序?qū)⑻幱诩偎罓顟B(tài):無法響應用戶的鍵盤、鼠標輸入;
2、WriteTimeout屬性用于控制Write函數(shù)的最長耗時。它的默認值為System.IO.Ports.SerialPort.InfiniteTimeout,也就是-1。其含義為:Write函數(shù)不將所有數(shù)據(jù)寫完絕不返回。可以修改此屬性,如下面的代碼:
if (port.IsOpen) { byte[] bt = new byte[1024]; port.WriteTimeout = 5000;//Write 函數(shù)最多耗時 5秒 port.Write(bt, 0, bt.Length);//寫1024個00H }
上面的代碼中,設置WriteTimeout屬性為5秒。所以Write寫數(shù)據(jù)時最多耗時5秒,超過這個時間未發(fā)的數(shù)據(jù)將被舍棄,Write函數(shù)拋出異常TimeoutException后立即返回。
(2)寫文本數(shù)據(jù)
void Write(string text)的示例:
if (port.IsOpen) { port.Encoding = System.Text.Encoding.GetEncoding(936); port.Write("串行通訊"); }
首先設置代碼頁為936(即GBK碼),Write(string text)函數(shù)根據(jù)代碼頁把字符串"串行通訊"轉換為二進制數(shù)據(jù),如下所示:
字符串 | 串 | 行 | 通 | 訊 |
內(nèi)碼 | B4 AE | D0 D0 | CD A8 | D1 B6 |
然后把二進制數(shù)據(jù)B4 AE D0 D0 CD A8 D1 B6發(fā)送出去。
函數(shù)void WriteLine(string text);等價于void Write(text + NewLine)。參考下面的代碼:
if (port.IsOpen) { port.Encoding = System.Text.Encoding.GetEncoding(936); port.NewLine = "\r\n"; port.WriteLine("串行通訊"); }
代碼port.NewLine = "\r\n";設置行結束符為回車(0DH)換行(0AH)。port.WriteLine("串行通訊");等價于port.Write("串行通訊"+port.NewLine);也就是port.Write("串行通訊\r\n");
最終,發(fā)送出去的二進制數(shù)據(jù)為B4 AE D0 D0 CD A8 D1 B6 0D 0A。
5.讀數(shù)據(jù):
System.IO.Ports.SerialPort用于讀串口數(shù)據(jù)的成員函數(shù)有七個,如下所示:
函數(shù) | 說明 |
---|---|
int ReadByte(); | 讀取一個字節(jié) |
int ReadChar(); | 讀取一個字符 |
int Read(byte[] buffer, int offset, int count); int Read(char[] buffer, int offset, int count); | 讀取二進制數(shù)據(jù) |
string ReadExisting(); | 讀取全部文本 |
string ReadTo(string value); | 讀取文本到某個字符串 |
string ReadLine(); | 讀取一行文本 |
(1)讀二級制
讀取 3 個字節(jié)的串口數(shù)據(jù):
try { byte[] b = new byte[3]; int n = port.Read(b, 0, 3); //返回值是讀取到的字節(jié)數(shù) } catch (Exception ex) { MessageBox.Show(ex.Message); }
注意:
1、Read函數(shù)是同步的。以上面的代碼為例,3個字節(jié)的數(shù)據(jù)被讀取之前,Read函數(shù)是不會返回的。如果這段代碼在主線程里,那么整個程序?qū)⑻幱诩偎罓顟B(tài);
2、ReadTimeout屬性用于控制Read函數(shù)的最長耗時。它的默認值為System.IO.Ports.SerialPort.InfiniteTimeout,也就是-1。其含義為:Read函數(shù)未讀取到串口數(shù)據(jù)之前是不會返回的??梢孕薷拇藢傩?,如下面的代碼:
byte[] b = new byte[3]; port.ReadTimeout = 2000; int n = port.Read(b, 0, 3); //返回值是讀取到的字節(jié)數(shù)
上面的代碼中,設置ReadTimeout屬性為2秒。所以Read函數(shù)讀數(shù)據(jù)時最多耗時2秒。超過這個時間未讀取到數(shù)據(jù),Read函數(shù)將拋出異常TimeoutException,然后返回。
(2) 讀一個字節(jié)
int ReadByte();與int Read(byte[] buffer, int offset, int count);類似,它的特點就是只讀取一個字節(jié)的串口數(shù)據(jù)。
(3) 讀一個字符
int ReadChar();是讀取一個字符,這個稍微復雜些。它可能讀取1~3個字節(jié)的數(shù)據(jù),然后合為一個字符。
如:port.Encoding = System.Text.Encoding.GetEncoding(936);即字符串編碼為GBK。給port發(fā)送"串"的GBK編碼B4 AE。ReadChar首先讀取一個字節(jié)得到B4H。這是一個漢字的區(qū)碼,還得讀取一個字節(jié)得到位碼。最終ReadChar讀取的是B4 AE。ReadChar的返回值是Unicode編碼,即返回前會把GBK編碼B4 AE轉換為Unicode編碼0x4E32。
再如:port.Encoding = System.Text.Encoding.UTF8;即字符串編碼為UTF8。給port發(fā)送"串"的UTF8編碼E4 B8 B2。ReadChar會讀取三個字節(jié)的串口數(shù)據(jù)E4 B8 B2,然后將其轉換為Unicode編碼0x4E32,并返回這個數(shù)值。
(4) 讀全部文本
函數(shù)string ReadExisting();讀取串口輸入緩沖區(qū)中的所有二進制數(shù)據(jù),然后將其轉換為字符串,最后返回字符串。
注意:
1、ReadExisting會立即返回。如果輸入緩沖區(qū)內(nèi)沒有數(shù)據(jù),直接返回長度為零的空字符串;
2、ReadExisting讀取輸入緩沖區(qū)后,有時會留幾個字節(jié)。參考下面的代碼:
port.Encoding = System.Text.Encoding.GetEncoding(936); string s = port.ReadExisting(); int nn = port.BytesToRead; //輸入緩沖區(qū)剩余的字節(jié)數(shù)
"串"、"行"的GBK編碼分別為 B4 AE和D0 D0。
首先發(fā)送 B4 AE D0 給port,運行上述代碼。ReadExisting將獲得B4 AE D0,"B4 AE"會被解釋為"串",D0是漢字的區(qū)碼,所以ReadExisting會將D0保留在輸入緩沖區(qū)內(nèi)。上述代碼的運行結果就是:s為"串",n為1;
然后發(fā)送D0 給port,運行上述代碼。ReadExisting將獲得D0 D0,"D0 D0"會被解釋為"行"。上述代碼的運行結果就是:s為"行",n為0。
(5) 讀文本到某個字符串
函數(shù)string ReadTo(string value);將在串口輸入緩沖區(qū)內(nèi)查找字符串value。找到了,就返回value之前的字符串,同時清除緩沖區(qū)內(nèi)value及其之前的數(shù)據(jù);未找到,就一直等待,直至超時。
(6) 讀一行文本
函數(shù)string ReadLine();等價于ReadTo(NewLine)。使用前,請設置NewLine屬性,指定行結束符。
(7) DataReceived事件
串口輸入緩沖區(qū)獲得新數(shù)據(jù)后,會以DataReceived事件通知System.IO.Ports.SerialPort對象,可以在此時讀取串口數(shù)據(jù)。請參考下面兩段代碼:
port.ReceivedBytesThreshold = 1; port.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(port_DataReceived);
void port_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) { int nRead = port.BytesToRead; if (nRead > 0) { byte[] data = new byte[nRead]; port.Read(data, 0, nRead); } }
port.ReceivedBytesThreshold = 1;的含義:串口輸入緩沖區(qū)獲得新數(shù)據(jù)后,將檢查緩沖區(qū)內(nèi)已有的字節(jié)數(shù),大于等于ReceivedBytesThreshold就會觸發(fā)DataReceived事件。這里設置為1,顯然就是一旦獲得新數(shù)據(jù)后,立即觸發(fā)DataReceived事件。
port.DataReceived+=new System.IO.Ports.SerialDataReceivedEventHandler(port_DataReceived);的含義:對于DataReceived事件,用函數(shù)port_DataReceived進行處理。
回調(diào)函數(shù)port_DataReceived用于響應DataReceived事件,通常在這個函數(shù)里讀取串口數(shù)據(jù)。它的第一個參數(shù)sender就是事件的發(fā)起者。上面的代碼中,sender其實就是port。也就是說:多個串口對象可以共用一個回調(diào)函數(shù),通過sender可以區(qū)分是哪個串口對象。
回調(diào)函數(shù)是被一個多線程調(diào)用的,它不在主線程內(nèi)。所以,不要在這個回調(diào)函數(shù)里直接訪問界面控件。如下面的代碼將將讀取到的串口數(shù)據(jù)轉換為字符串,然后顯示在按鈕Open上。紅色代碼處將產(chǎn)生異常。
void port_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) { int nRead = port.BytesToRead; if (nRead > 0) { byte[] data = new byte[nRead]; port.Read(data, 0, nRead); btnOpen.Text = System.Text.Encoding.Default.GetString(data); } }
可使用Invoke或BeginInvoke改進上面的紅色代碼:
this.Invoke(new MethodInvoker(() => { btnOpen.Text = System.Text.Encoding.Default.GetString(data); }));
BeginInvoke(new Action<string>((x) => { btnOpen.Text = x; }), new Object[] { System.Text.Encoding.Default.GetString(data) });
6.流控制
串行通訊的雙方,如果有一方反應較慢,另一方不管不顧的不停發(fā)送數(shù)據(jù),就可能造成數(shù)據(jù)丟失。為了防止這種情況發(fā)生,需要使用流控制。
流控制也叫握手,System.IO.Ports.SerialPort的Handshake屬性用于設置流控制。它有四種取值:
取值 | 說明 |
System.IO.Ports.Handshake.None | 無 |
System.IO.Ports.Handshake.XOnXOff | 軟件 |
System.IO.Ports.Handshake.RequestToSend | 硬件 |
System.IO.Ports.Handshake.RequestToSendXOnXOff | 硬件和軟件 |
(1) 軟件流控制(XON/XOFF)
串口設備A給串口設備B發(fā)送數(shù)據(jù)。B忙不過來時(B的串口輸入緩沖區(qū)快滿了)會給A發(fā)送字符XOFF(一般為13H),A將暫停發(fā)送數(shù)據(jù);B的串口輸入緩沖區(qū)快空時,會給A發(fā)送字符XON(一般為11H),A將繼續(xù)發(fā)送數(shù)據(jù)。
軟件流控制最大的問題在于:通訊雙方不能傳輸字符XON和XOFF。
(2)硬件流控制(RTS/CTS)
RTS/CTS流控制是硬件流控制的一種,需要按下圖連線:
串口設備A給串口設備B發(fā)送數(shù)據(jù)。B忙不過來時(B的串口輸入緩沖區(qū)快滿了)會設置自己的RTS為低電平,這樣A的CTS也變?yōu)榈碗娖?。A發(fā)現(xiàn)自己的CTS為低電平后,會停止發(fā)送數(shù)據(jù);B的串口輸入緩沖區(qū)快空時,會設置自己的RTS為高電平,這樣A的CTS也變?yōu)楦唠娖健發(fā)現(xiàn)自己的CTS為高電平后,會繼續(xù)發(fā)送數(shù)據(jù)。
相同的道理,DTR/DSR也可以做硬件流控制。
以上就是C#中SerialPort的使用教程詳解的詳細內(nèi)容,更多關于C# SerialPort的資料請關注腳本之家其它相關文章!
相關文章
c# 通過經(jīng)緯度查詢 具體的地址和區(qū)域名稱
最近項目需要通過經(jīng)緯度查詢 具體的地址和區(qū)域名稱,通過查詢網(wǎng)絡資源,發(fā)現(xiàn)提供的大多是得到具體的地址而對區(qū)域或城市名稱的獲取就不是很好把握;在這里自己搞了個,需要的朋友可以參考下2012-11-11C#中實現(xiàn)輸入漢字獲取其拼音(漢字轉拼音)的2種方法
這篇文章主要介紹了C#中實現(xiàn)輸入漢字獲取其拼音(漢字轉拼音)的2種方法,本文分別給出了使用微軟語言包、手動編碼實現(xiàn)兩種實現(xiàn)方式,需要的朋友可以參考下2015-01-01