c# 模擬串口通信 SerialPort的實(shí)現(xiàn)示例
一、前導(dǎo)知識(shí)
串行口是計(jì)算機(jī)的標(biāo)準(zhǔn)接口,現(xiàn)在的PC機(jī)(個(gè)人電腦)一般至少有兩個(gè)串行口COM1和COM2。串行口應(yīng)用廣泛,在數(shù)據(jù)通信、計(jì)算機(jī)網(wǎng)絡(luò)以及分布式工業(yè)控制系統(tǒng)中,經(jīng)常采用串行通信來(lái)交換數(shù)據(jù)和信息
電氣標(biāo)準(zhǔn)及協(xié)議來(lái)分包括RS-232-C、RS-422、RS485、USB(Universal Serial Bus)等
實(shí)現(xiàn)串口通信的必要設(shè)置
串口通信最重要的參數(shù)是波特率、數(shù)據(jù)位、停止位和奇偶校驗(yàn)。
對(duì)于兩個(gè)進(jìn)行通行的端口,這些參數(shù)必須匹配:
波特率
這是一個(gè)衡量通信速度的參數(shù)。它表示**每秒鐘傳送的bit的個(gè)數(shù)**。例如300波特表示每秒鐘發(fā)送300個(gè)bit,波特率和距離成反比。高波特率常常用于放置的很近的儀器間的通信,典型的例子就是GPIB設(shè)備的通信
數(shù)據(jù)位
這是衡量通信中實(shí)際數(shù)據(jù)位的參數(shù)。當(dāng)計(jì)算機(jī)發(fā)送一個(gè)信息包,實(shí)際的數(shù)據(jù)不會(huì)是8位的,標(biāo)準(zhǔn)的值是5、7和8位,如何設(shè)置取決于你想傳送的信息。比如,標(biāo)準(zhǔn)的ASCII碼是0~127(7位)。擴(kuò)展的ASCII碼是0~255(8位)。如果數(shù)據(jù)使用簡(jiǎn)單的文本(標(biāo)準(zhǔn) ASCII碼),那么每個(gè)數(shù)據(jù)包使用7位數(shù)據(jù)。每個(gè)包是指一個(gè)字節(jié),包括開始/停止位,數(shù)據(jù)位和奇偶校驗(yàn)位。由于實(shí)際數(shù)據(jù)位取決于通信協(xié)議的選取,術(shù)語(yǔ)“包”指任何通信的情況。
停止位
用于表示單個(gè)包的最后一位。典型的值為1,1.5和2位。由于數(shù)據(jù)是在傳輸線上定時(shí)的,并且每一個(gè)設(shè)備有其自己的時(shí)鐘,很可能在通信中兩臺(tái)設(shè)備間出現(xiàn)了小小的不同步。因此停止位不僅僅是表示傳輸?shù)慕Y(jié)束,并且提供計(jì)算機(jī)校正時(shí)鐘同步的機(jī)會(huì)。適用于停止位的位數(shù)越多,不同時(shí)鐘同步的容忍程度越大,但是數(shù)據(jù)傳輸率同時(shí)也越慢
奇偶校驗(yàn)位
在串口通信中一種簡(jiǎn)單的檢錯(cuò)方式。有四種檢錯(cuò)方式:偶、奇、高和低。當(dāng)然沒(méi)有校驗(yàn)位也是可以的。對(duì)于偶和奇校驗(yàn)的情況,串口會(huì)設(shè)置校驗(yàn)位(數(shù)據(jù)位后面的一位),用一個(gè)值確保傳輸?shù)臄?shù)據(jù)有偶個(gè)或者奇?zhèn)€邏輯高位。例如,如果數(shù)據(jù)是011,那么對(duì)于偶校驗(yàn),校驗(yàn)位為0,保證邏輯高的位數(shù)是偶數(shù)個(gè)。如果是奇校驗(yàn),校驗(yàn)位位1,這樣就有3個(gè)邏輯高位。高位和低位不真正的檢查數(shù)據(jù),簡(jiǎn)單置位邏輯高或者邏輯低校驗(yàn)。這樣使得接收設(shè)備能夠知道一個(gè)位的狀態(tài),有機(jī)會(huì)判斷是否有噪聲干擾了通信或者是否傳輸和接收數(shù)據(jù)是否不同步
二、實(shí)驗(yàn)
我們將通過(guò)模擬串口通信,在pc機(jī)上進(jìn)行兩個(gè)串口(COM1、COM2)的交互
需要用到的軟件:
Launch Virtual Serial Port Driver Pro:虛擬串口。使用它來(lái)模擬兩個(gè)串口的連接
繪制窗口

代碼實(shí)現(xiàn)
1.使用SerialPort控制串口
private SerialPort sp1 = new SerialPort();
2.打開串口
private void button2_Click(object sender, EventArgs e)
{
if (!sp1.IsOpen)
{
try
{
//串口號(hào)
sp1.PortName = "COM1";
//波特率
sp1.BaudRate = 115200;
//數(shù)據(jù)位
sp1.DataBits = 8;
//停止位
sp1.StopBits = StopBits.One;
//奇偶校驗(yàn)位
sp1.Parity = Parity.Even;
//DataReceived事件發(fā)送前,內(nèi)部緩沖區(qū)里的字符數(shù)
sp1.ReceivedBytesThreshold = 1;
sp1.RtsEnable = true; sp1.DtrEnable = true; sp1.ReadTimeout = 3000;
// Control.CheckForIllegalCrossThreadCalls = false;
//表示將處理 System.IO.Ports.SerialPort 對(duì)象的數(shù)據(jù)接收事件的方法。
sp1.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(sp1_DataReceived_1);
//打開串口
sp1.Open();
MessageBox.Show("COM1打開成功!");
}
catch (Exception ex)
{
MessageBox.Show("COM1打開失?。?);
}
}
else
{
MessageBox.Show("COM1打開成功!");
}
}3.關(guān)閉串口
private void button3_Click(object sender, EventArgs e)
{
if (sp1.IsOpen)
{
sp1.Close();
MessageBox.Show("COM1關(guān)閉成功!");
}
}串口2的打開和關(guān)閉同理串口1實(shí)現(xiàn)
4.發(fā)送
private void button1_Click(object sender, EventArgs e)
{
if (sp1.IsOpen)
{
if (!string.IsNullOrEmpty(this.textBox1.Text))
{
sp1.WriteLine(this.textBox1.Text+"\r\n");
}
else
{
MessageBox.Show("發(fā)送數(shù)據(jù)為空");
}
}
else
{
MessageBox.Show("COM1未打開!");
}
}5.接收
StringBuilder builder1 = new StringBuilder();
//在接收到了ReceivedBytesThreshold設(shè)置的字符個(gè)數(shù)或接收到了文件結(jié)束字符并將其放入了輸入緩沖區(qū)時(shí)被觸發(fā)
public void sp1_DataReceived_1(object sender, SerialDataReceivedEventArgs e)
{
Console.WriteLine("接收中...");
int n = sp1.BytesToRead; //先記錄下來(lái),避免某種原因,人為的原因,操作幾次之間時(shí)間長(zhǎng),緩存不一致
byte[] buf = new byte[n]; //聲明一個(gè)臨時(shí)數(shù)組存儲(chǔ)當(dāng)前來(lái)的串口數(shù)據(jù)
sp1.Read(buf, 0, n); //讀取緩沖數(shù)據(jù)
builder1.Remove(0, builder1.Length); //清除字符串構(gòu)造器的內(nèi)容
builder1.Append(Encoding.ASCII.GetString(buf));
string comdata = builder1.ToString();
Console.WriteLine("data: + " + comdata);
this.Invoke(settextevent,comdata);
}這里僅僅實(shí)現(xiàn)了一般的接收方式,并不嚴(yán)謹(jǐn)和健壯
測(cè)試
使用軟件模擬串口連接

打開兩個(gè)程序

在一程序中打開串口1,在二程序中打開串口2,發(fā)送消息
在一程序中輸入字符"hello,HanHanCheng!",發(fā)現(xiàn)在二程序中接收到,同樣,在二程序中輸入,在一中也能收到

三、總結(jié)
1.由于是異步線程接收,在接收中需要使用委托來(lái)跨線程調(diào)用組件
public delegate void settext(string text);
public event settext settextevent;
public void set(string text)
{
this.textBox2.Text = text;
}
//再注冊(cè)
settextevent += set;2.DataReceived事件觸發(fā)條件需要注意,可能在實(shí)現(xiàn)時(shí),無(wú)法觸發(fā)導(dǎo)致無(wú)法接收。
觸發(fā)條件是:在接收到了ReceivedBytesThreshold設(shè)置的字符個(gè)數(shù)或接收到了文件結(jié)束字符并將其放入了輸入緩沖區(qū)時(shí)被觸發(fā)
四、附件完整代碼
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;
namespace Training_USBCOM
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
settextevent += set;
}
private SerialPort sp1 = new SerialPort();
StringBuilder builder = new StringBuilder();
private void button1_Click(object sender, EventArgs e)
{
if (sp1.IsOpen)
{
if (!string.IsNullOrEmpty(this.textBox1.Text))
{
sp1.WriteLine(this.textBox1.Text+"\r\n");
}
else
{
MessageBox.Show("發(fā)送數(shù)據(jù)為空");
}
}
else
{
MessageBox.Show("COM1未打開!");
}
}
public delegate void settext(string text);
public event settext settextevent;
public void set(string text)
{
this.textBox2.Text = text;
}
StringBuilder builder1 = new StringBuilder();
//在接收到了ReceivedBytesThreshold設(shè)置的字符個(gè)數(shù)或接收到了文件結(jié)束字符并將其放入了輸入緩沖區(qū)時(shí)被觸發(fā)
public void sp1_DataReceived_1(object sender, SerialDataReceivedEventArgs e)
{
Console.WriteLine("接收中...");
int n = sp1.BytesToRead; //先記錄下來(lái),避免某種原因,人為的原因,操作幾次之間時(shí)間長(zhǎng),緩存不一致
byte[] buf = new byte[n]; //聲明一個(gè)臨時(shí)數(shù)組存儲(chǔ)當(dāng)前來(lái)的串口數(shù)據(jù)
sp1.Read(buf, 0, n); //讀取緩沖數(shù)據(jù)
builder1.Remove(0, builder1.Length); //清除字符串構(gòu)造器的內(nèi)容
builder1.Append(Encoding.ASCII.GetString(buf));
string comdata = builder1.ToString();
Console.WriteLine("data: + " + comdata);
this.Invoke(settextevent,comdata);
}
private void button2_Click(object sender, EventArgs e)
{
if (!sp1.IsOpen)
{
try
{
//串口號(hào)
sp1.PortName = "COM1";
//波特率
sp1.BaudRate = 115200;
//數(shù)據(jù)位
sp1.DataBits = 8;
//停止位
sp1.StopBits = StopBits.One;
//奇偶校驗(yàn)位
sp1.Parity = Parity.Even;
//DataReceived事件發(fā)送前,內(nèi)部緩沖區(qū)里的字符數(shù)
sp1.ReceivedBytesThreshold = 1;
sp1.RtsEnable = true; sp1.DtrEnable = true; sp1.ReadTimeout = 3000;
// Control.CheckForIllegalCrossThreadCalls = false;
//表示將處理 System.IO.Ports.SerialPort 對(duì)象的數(shù)據(jù)接收事件的方法。
sp1.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(sp1_DataReceived_1);
//打開串口
sp1.Open();
MessageBox.Show("COM1打開成功!");
}
catch (Exception ex)
{
MessageBox.Show("COM1打開失?。?);
}
}
else
{
MessageBox.Show("COM1打開成功!");
}
}
private void button3_Click(object sender, EventArgs e)
{
if (sp1.IsOpen)
{
sp1.Close();
MessageBox.Show("COM1關(guān)閉成功!");
}
}
private void button5_Click(object sender, EventArgs e)
{
if (!sp1.IsOpen)
{
try
{
//串口號(hào)
sp1.PortName = "COM2";
//波特率
sp1.BaudRate = 115200;
//數(shù)據(jù)位
sp1.DataBits = 8;
//停止位
sp1.StopBits = StopBits.One;
//奇偶校驗(yàn)位
sp1.Parity = Parity.Even;
sp1.ReceivedBytesThreshold = 1;
sp1.RtsEnable = true; sp1.DtrEnable = true; sp1.ReadTimeout = 3000;
Control.CheckForIllegalCrossThreadCalls = false;
//表示將處理 System.IO.Ports.SerialPort 對(duì)象的數(shù)據(jù)接收事件的方法。
sp1.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(sp1_DataReceived_1);
//打開串口
sp1.Open();
MessageBox.Show("COM2打開成功!");
}
catch (Exception ex)
{
MessageBox.Show("COM2打開失?。?);
}
}
else
{
MessageBox.Show("COM2打開成功!");
}
}
private void button4_Click(object sender, EventArgs e)
{
if (sp1.IsOpen)
{
sp1.Close();
MessageBox.Show("COM2關(guān)閉成功!");
}
}
}
}到此這篇關(guān)于c# 模擬串口通信 SerialPort的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)c# 模
相關(guān)文章
C#實(shí)現(xiàn)的ZPL條碼打印類完整實(shí)例
這篇文章主要介紹了C#實(shí)現(xiàn)的ZPL條碼打印類,結(jié)合實(shí)例形式詳細(xì)分析了C#實(shí)現(xiàn)條碼打印的原理與使用方法,代碼注釋中備有詳盡的說(shuō)明,便于理解使用,需要的朋友可以參考下2016-06-06
C#實(shí)現(xiàn)簡(jiǎn)單過(guò)濾非法字符實(shí)例
這篇文章主要介紹了C#實(shí)現(xiàn)簡(jiǎn)單過(guò)濾非法字符的方法,涉及C#針對(duì)字符串遍歷與判斷的相關(guān)技巧,非常簡(jiǎn)單實(shí)用,需要的朋友可以參考下2015-11-11
Qt之調(diào)用C#的動(dòng)態(tài)庫(kù)的解決方法
這篇文章給大家介紹了Qt之調(diào)用C#的動(dòng)態(tài)庫(kù)的解決方法,環(huán)境使用的是VS2019+Qt5.12,感興趣的朋友一起看看吧2021-10-10
利用C#實(shí)現(xiàn)SSLSocket加密通訊的方法詳解
這篇文章主要給大家介紹了關(guān)于如何利用C#實(shí)現(xiàn)SSLSocket加密通訊的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07

