一、通讯基础理论
1.1 基本概念
通讯发生在两端或多端之间,分为发送方和接收方
C# 中通讯主要分为两大块:
串口通讯:有距离限制,速度较慢
网络通讯:传输距离远,速度快,重要且难度较大
1.2 通讯协议
通讯双方必须遵循约定的数据格式(数据报文 / 数据帧),这些约定的规则称为通讯协议。
| 通讯类型 | 常见协议 |
|---|---|
| 串口通讯 | RS232、RS485、Modbus(通用)、西门子 S7 协议等 |
| 网络通讯 | TCP、UDP、HTTP、HTTPS、STMP 等 |
在网络通讯中:
一端称为服务器(Server / 主机 Host)
另一端称为客户端(Client / 从机 Slave)
一个服务器可以连接多个客户端
二、串口通讯详解
2.1 串口基本概念
英文:serial port,也称串行通信接口或 COM 接口
用于在两个设备之间通过专门设计的线路传输数据
实现计算机与外部设备的可靠点对点(P2P/E2E)连接
常见设备:打印机、调制解调器、传感器等
2.2 串行通讯原理
串行数据传输:通过单个线路按顺序发送比特信息
并行数据传输:使用多个信道同时传输
单位换算:8bit = 1 字节,1024 位 = 128 字节
2.3 通信模式
单工模式:只能单向传输
半双工模式:可以双向传输,但不能同时进行
全双工模式:可以同时双向传输(串口通讯属于此类)
2.4 串口通讯数据格式(重点)
一个完整的串口数据格式包括:
起始位(1bit)+ 数据位(8bit)+ 奇偶校验位(1bit)+ 停止位(1bit)
关键参数:
波特率:
传输速度,单位 bps(每秒比特数)
常用值:600、1200、2400、4800、9600、19200、38400bps
数据位数:
接收和发送的数据字节数
常用 7 位或 8 位,默认使用 8 位
校验位:
保证数据准确性的校验机制
类型:
奇校验(Odd):使总位数为奇数
偶校验(Even):使总位数为偶数
无校验(None):不进行校验
标记校验(Mark):校验位固定为 1
空格校验(Space):校验位固定为 0
停止位:
数据传输完成的标志位
通常为 1 位
三、C# 串口通讯编程实例
3.1 串口操作类(SerialPort)常用成员
属性:
PortName:端口名称(如 "COM1")
BaudRate:波特率
DataBits:数据位
StopBits:停止位
Parity:校验位
IsOpen:串口是否已打开
方法:
Open ():打开串口
Close ():关闭串口
Write ()/WriteLine ():发送数据
Read ()/ReadLine ()/ReadExisting ():读取数据
事件:
- DataReceived:数据接收事件
3.2 基本串口通讯示例
cs
using System;
using System.IO.Ports;
using System.Windows.Forms;
namespace SerialCommunicationDemo
{
public partial class MainForm : Form
{
// 创建串口对象
private SerialPort _serialPort = new SerialPort();
public MainForm()
{
InitializeComponent();
InitializeSerialPort();
LoadPortNames();
}
// 初始化串口参数
private void InitializeSerialPort()
{
// 默认参数设置
_serialPort.BaudRate = 9600; // 波特率
_serialPort.DataBits = 8; // 数据位
_serialPort.StopBits = StopBits.One; // 停止位
_serialPort.Parity = Parity.None; // 校验位
_serialPort.ReadTimeout = 500; // 读取超时
_serialPort.WriteTimeout = 500; // 写入超时
// 绑定数据接收事件
_serialPort.DataReceived += SerialPort_DataReceived;
}
// 加载可用串口列表
private void LoadPortNames()
{
string[] portNames = SerialPort.GetPortNames();
cmbPort.Items.AddRange(portNames);
if (portNames.Length > 0)
cmbPort.SelectedIndex = 0;
}
// 打开/关闭串口按钮
private void btnOpenClose_Click(object sender, EventArgs e)
{
if (!_serialPort.IsOpen)
{
try
{
// 设置端口名称
_serialPort.PortName = cmbPort.SelectedItem.ToString();
// 打开串口
_serialPort.Open();
// 更新UI状态
btnOpenClose.Text = "关闭串口";
cmbPort.Enabled = false;
txtLog.AppendText("串口已打开" + Environment.NewLine);
}
catch (Exception ex)
{
MessageBox.Show("打开串口失败:" + ex.Message);
}
}
else
{
// 关闭串口
_serialPort.Close();
// 更新UI状态
btnOpenClose.Text = "打开串口";
cmbPort.Enabled = true;
txtLog.AppendText("串口已关闭" + Environment.NewLine);
}
}
// 发送数据按钮
private void btnSend_Click(object sender, EventArgs e)
{
if (!_serialPort.IsOpen)
{
MessageBox.Show("请先打开串口");
return;
}
try
{
string dataToSend = txtSendData.Text;
if (!string.IsNullOrEmpty(dataToSend))
{
// 发送数据
_serialPort.WriteLine(dataToSend);
txtLog.AppendText("发送:" + dataToSend + Environment.NewLine);
txtSendData.Clear();
}
}
catch (Exception ex)
{
MessageBox.Show("发送数据失败:" + ex.Message);
}
}
// 数据接收事件处理
private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
// 读取接收到的数据
string receivedData = _serialPort.ReadLine();
// 跨线程更新UI
this.Invoke(new Action(() =>
{
txtLog.AppendText("接收:" + receivedData + Environment.NewLine);
}));
}
catch (Exception ex)
{
this.Invoke(new Action(() =>
{
txtLog.AppendText("接收数据错误:" + ex.Message + Environment.NewLine);
}));
}
}
// 窗体关闭时确保串口已关闭
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (_serialPort.IsOpen)
_serialPort.Close();
}
}
}
串口初始化:
设置了常用的串口参数(波特率 9600、数据位 8 等)
绑定了数据接收事件,用于处理异步接收的数据
核心功能:
打开 / 关闭串口
发送数据到串口设备
接收来自串口设备的数据
显示通讯日志
注意事项:
操作串口前必须确保端口已正确打开
数据接收事件在非 UI 线程触发,更新 UI 时需要使用 Invoke
程序退出前应关闭串口,释放资源
四、开发与调试工具
虚拟串口工具:
- Virtual Serial Port Driver:创建虚拟串口对,用于调试
串口调试助手:
XCOM V2.0:常用的串口调试工具
其他可选:SSCOM、友善串口调试助手等
使用技巧:
先用虚拟串口和调试助手测试通讯逻辑
确保通讯双方参数(波特率、校验位等)完全一致
遇到问题时,先检查硬件连接和参数设置
五、常见问题与解决
无法打开串口:
检查端口是否被其他程序占用
确认端口名称是否正确
检查设备驱动是否安装
数据传输错误:
确认双方波特率、校验位等参数一致
检查物理连接是否稳定
尝试降低波特率测试
接收数据不完整:
可能是缓冲区设置问题
考虑增加接收超时时间
实现数据帧校验机制
通过以上内容,你可以掌握 C# 串口通讯的基本理论和编程实现方法。在实际应用中,还需要根据具体的硬件设备和通讯协议进行适当调整。
一、通讯基础理论
1.1 基本概念
通讯发生在两端或多端之间,分为发送方和接收方
C# 中通讯主要分为两大块:
串口通讯:有距离限制,速度较慢
网络通讯:传输距离远,速度快,重要且难度较大
1.2 通讯协议
通讯双方必须遵循约定的数据格式(数据报文 / 数据帧),这些约定的规则称为通讯协议。
| 通讯类型 | 常见协议 |
|---|---|
| 串口通讯 | RS232、RS485、Modbus(通用)、西门子 S7 协议等 |
| 网络通讯 | TCP、UDP、HTTP、HTTPS、STMP 等 |
在网络通讯中:
一端称为服务器(Server / 主机 Host)
另一端称为客户端(Client / 从机 Slave)
一个服务器可以连接多个客户端
二、串口通讯详解
2.1 串口基本概念
英文:serial port,也称串行通信接口或 COM 接口
用于在两个设备之间通过专门设计的线路传输数据
实现计算机与外部设备的可靠点对点(P2P/E2E)连接
常见设备:打印机、调制解调器、传感器等
2.2 串行通讯原理
串行数据传输:通过单个线路按顺序发送比特信息
并行数据传输:使用多个信道同时传输
单位换算:8bit = 1 字节,1024 位 = 128 字节
2.3 通信模式
单工模式:只能单向传输
半双工模式:可以双向传输,但不能同时进行
全双工模式:可以同时双向传输(串口通讯属于此类)
2.4 串口通讯数据格式(重点)
一个完整的串口数据格式包括:
起始位(1bit)+ 数据位(8bit)+ 奇偶校验位(1bit)+ 停止位(1bit)
关键参数:
波特率:
传输速度,单位 bps(每秒比特数)
常用值:600、1200、2400、4800、9600、19200、38400bps
数据位数:
接收和发送的数据字节数
常用 7 位或 8 位,默认使用 8 位
校验位:
保证数据准确性的校验机制
类型:
奇校验(Odd):使总位数为奇数
偶校验(Even):使总位数为偶数
无校验(None):不进行校验
标记校验(Mark):校验位固定为 1
空格校验(Space):校验位固定为 0
停止位:
数据传输完成的标志位
通常为 1 位
三、C# 串口通讯编程实例
3.1 串口操作类(SerialPort)常用成员
属性:
PortName:端口名称(如 "COM1")
BaudRate:波特率
DataBits:数据位
StopBits:停止位
Parity:校验位
IsOpen:串口是否已打开
方法:
Open ():打开串口
Close ():关闭串口
Write ()/WriteLine ():发送数据
Read ()/ReadLine ()/ReadExisting ():读取数据
事件:
- DataReceived:数据接收事件
3.2 基本串口通讯示例
cs
using System;
using System.IO.Ports;
using System.Windows.Forms;
namespace SerialCommunicationDemo
{
public partial class MainForm : Form
{
// 创建串口对象
private SerialPort _serialPort = new SerialPort();
public MainForm()
{
InitializeComponent();
InitializeSerialPort();
LoadPortNames();
}
// 初始化串口参数
private void InitializeSerialPort()
{
// 默认参数设置
_serialPort.BaudRate = 9600; // 波特率
_serialPort.DataBits = 8; // 数据位
_serialPort.StopBits = StopBits.One; // 停止位
_serialPort.Parity = Parity.None; // 校验位
_serialPort.ReadTimeout = 500; // 读取超时
_serialPort.WriteTimeout = 500; // 写入超时
// 绑定数据接收事件
_serialPort.DataReceived += SerialPort_DataReceived;
}
// 加载可用串口列表
private void LoadPortNames()
{
string[] portNames = SerialPort.GetPortNames();
cmbPort.Items.AddRange(portNames);
if (portNames.Length > 0)
cmbPort.SelectedIndex = 0;
}
// 打开/关闭串口按钮
private void btnOpenClose_Click(object sender, EventArgs e)
{
if (!_serialPort.IsOpen)
{
try
{
// 设置端口名称
_serialPort.PortName = cmbPort.SelectedItem.ToString();
// 打开串口
_serialPort.Open();
// 更新UI状态
btnOpenClose.Text = "关闭串口";
cmbPort.Enabled = false;
txtLog.AppendText("串口已打开" + Environment.NewLine);
}
catch (Exception ex)
{
MessageBox.Show("打开串口失败:" + ex.Message);
}
}
else
{
// 关闭串口
_serialPort.Close();
// 更新UI状态
btnOpenClose.Text = "打开串口";
cmbPort.Enabled = true;
txtLog.AppendText("串口已关闭" + Environment.NewLine);
}
}
// 发送数据按钮
private void btnSend_Click(object sender, EventArgs e)
{
if (!_serialPort.IsOpen)
{
MessageBox.Show("请先打开串口");
return;
}
try
{
string dataToSend = txtSendData.Text;
if (!string.IsNullOrEmpty(dataToSend))
{
// 发送数据
_serialPort.WriteLine(dataToSend);
txtLog.AppendText("发送:" + dataToSend + Environment.NewLine);
txtSendData.Clear();
}
}
catch (Exception ex)
{
MessageBox.Show("发送数据失败:" + ex.Message);
}
}
// 数据接收事件处理
private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
// 读取接收到的数据
string receivedData = _serialPort.ReadLine();
// 跨线程更新UI
this.Invoke(new Action(() =>
{
txtLog.AppendText("接收:" + receivedData + Environment.NewLine);
}));
}
catch (Exception ex)
{
this.Invoke(new Action(() =>
{
txtLog.AppendText("接收数据错误:" + ex.Message + Environment.NewLine);
}));
}
}
// 窗体关闭时确保串口已关闭
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (_serialPort.IsOpen)
_serialPort.Close();
}
}
}
串口初始化:
设置了常用的串口参数(波特率 9600、数据位 8 等)
绑定了数据接收事件,用于处理异步接收的数据
核心功能:
打开 / 关闭串口
发送数据到串口设备
接收来自串口设备的数据
显示通讯日志
注意事项:
操作串口前必须确保端口已正确打开
数据接收事件在非 UI 线程触发,更新 UI 时需要使用 Invoke
程序退出前应关闭串口,释放资源
四、开发与调试工具
虚拟串口工具:
- Virtual Serial Port Driver:创建虚拟串口对,用于调试
串口调试助手:
XCOM V2.0:常用的串口调试工具
其他可选:SSCOM、友善串口调试助手等
使用技巧:
先用虚拟串口和调试助手测试通讯逻辑
确保通讯双方参数(波特率、校验位等)完全一致
遇到问题时,先检查硬件连接和参数设置
五、常见问题与解决
无法打开串口:
检查端口是否被其他程序占用
确认端口名称是否正确
检查设备驱动是否安装
数据传输错误:
确认双方波特率、校验位等参数一致
检查物理连接是否稳定
尝试降低波特率测试
接收数据不完整:
可能是缓冲区设置问题
考虑增加接收超时时间
实现数据帧校验机制
通过以上内容,你可以掌握 C# 串口通讯的基本理论和编程实现方法。在实际应用中,还需要根据具体的硬件设备和通讯协议进行适当调整。