RS232/RS422/RS485 這個是屬於串列埠(COM Port)的一種接口,
RS232是一對一的通訊,
RS422/RS485是可以一對多的通訊,
在VS中,SerialPort 是內建元件,拉下來就能用了
它位屬於工具箱中元件標籤頁裡
通訊格式可以在元件的屬性設定
常用的是通訊埠、鮑率、資料位元、停止位元、同位元檢查
開啟串列埠:
void Open()
說明:當屬性都設定好了,開啟後就能進行傳輸了。
範例:
private void btn_Open_Click(object sender, EventArgs e)
{
serialPort1.PortName = "COM1";
serialPort1.Open();
}
傳送方法:
void WriteLine(String text)
說明:上面這個方法為ASCII,傳送英數OK,無法送中文,如果傳了會變 "?",且會在字串最後會補上 "\n"。
範例:
private void button1_Click(object sender, EventArgs e)
{
serialPort1.WriteLine(textBox1.Text);
}
void Write(String text)
說明:上面這個方法為ASCII,傳送英數OK,無法送中文,如果傳了會變 "?"。
範例:
private void button1_Click(object sender, EventArgs e)
{
serialPort1.Write(textBox1.Text);
}
Write(byte[] buffer, int offset, int count)
說明:這個方法可傳送中文字。但要注意編碼格式。
因此,透過各種編碼將字串轉Byte[]:
Byte[] buf = Encoding.Unicode.GetByte(String);
Byte[] buf = Encoding.UTF8.GetByte(String);
Byte[] buf = Encoding.Default.GetByte(String);
Default 這個就看系統語言了,就像我們的系統是Big 5。
範例:
private void button1_Click(object sender, EventArgs e)
{
byte[] buf = Encoding.Unicode.GetBytes(textBox1.Text);
serialPort1.Write(buf, 0, buf.Length);
}
其它編碼:
Byte[] buf = Encoding.GetEncoding(int).GetByte(String);
編碼表:
https://docs.microsoft.com/en-us/windows/win32/intl/code-page-identifiers
例如 Big 5 是950。
接收:
int Read(byte[] buffer, int offset, int count)
int Read(char[] buffer, int offset, int count)
說明:此方法會佔執行序,會讀到Timeout為止,丟出例外;如果Timeout為-1,會等到有為止,不丟出例外。
因此,透過各種編碼將Byte[]轉字串:
string msg = Encoding.Unicode.GetString(buf)
string msg = Encoding.UTF8.GetString(buf)
string msg = Encoding.Default.GetString(buf)
範例:
private void button2_Click(object sender, EventArgs e)
{
byte[] buf = new byte[1024];//配置接收的陣列大小
serialPort1.Read(buf, 0, 1024);//讀取資料,會一直等到有資料
string msg = Encoding.Unicode.GetString(buf);//轉成字串
textBox2.AppendText(msg);
}
int ReadByte()
int ReadChar()
說明:這兩個是很像,都是讀取一個變數長度,但是Byte(8 bit) 純資料,Char(16 bit) ,但ReadChar是用ASCII解碼收到的資料。兩個方法都會佔執行序。
範例:
private void button2_Click(object sender, EventArgs e)
{
char ch = (char)serialPort1.ReadChar();
textBox2.AppendText(ch.ToString());
}
String ReadLine()
說明:此方法都會佔執行序,一直讀取,直到遇到"\n"為止,用ASCII解碼收到的資料。
範例:
private void button2_Click(object sender, EventArgs e)
{
String msg = serialPort1.ReadLine();
textBox2.AppendText(msg);
}
String ReadExisting()
說明:讀取全部,不會佔執行序,是用ASCII解碼收到的資料。。
範例:
private void button2_Click(object sender, EventArgs e)
{
string msg = serialPort1.ReadExisting();//讀取全部
textBox2.AppendText(msg);
}
void DiscardInBuffer()
說明:清空輸入佇列資料。
範例:
serialPort1.DiscardInBuffer()
void DiscardOutBuffer()
說明:清空輸出佇列資料。
範例:
serialPort1.DiscardOutBuffer()
using System.IO.Ports;
public Form1()
{
InitializeComponent();
String[] names = SerialPort.GetPortNames();
comboBox1.Items.AddRange(names);
if (names.Length > 0) comboBox1.SelectedIndex = 0;
}
取得目前電腦現有的COM Port (含使用中的)
這個方法比較穩定,就算使用的是USB熱插拔的方式,也能確保資料的正確性。缺點是沒有名稱,得到的就是COM1、COM2 ... 這樣的內容
這個比較要注意的是,先前範例都是用 serialPort1 的物件,而這裡SerialProt 是一個class ,GetPortNames() 是靜態函式。
using Microsoft.Win32;//for RegistryKey
RegistryKey key = Registry.LocalMachine.OpenSubKey(@"Hardware\DeviceMap\SerialComm");
if (key != null)
{
//讀取所有串列通訊的名稱
String[] names = key.GetValueNames();
foreach (String s in names)
{
comboBox1.Items.Add(key.GetValue(s) + " " + s);
}
}
取得註冊在電腦中COM Port的名稱,但這方法有時會發生錯誤,
有時候因為USB太平凡的熱插拔,導致註冊檔與實際不同步
該消失沒消失,該出現沒出現,
當有收到任何資料的時後會觸發DataReceived事件,但不會將資料透過引數傳入。
使用事件處理資料時,要注意跨執行序問題。
錯誤示範:
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
String msg = serialPort1.ReadExisting();
textBox2.Text = msg;
}
示範:
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
String msg = serialPort1.ReadExisting();
this.Invoke((Action)(() =>
{
textBox2.AppendText(msg + "\r\n");
}));
}