C#中SerialPort 的使用

最近在学习C#的SerialPort ,关于SerialPort 的使用,做如下总结:

1.可以通过函数System.IO.Ports.SerialPort.GetPortNames() 将获得系统所有的串口名称。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;//数据位: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.写数据:

函数说明

void Write(byte[] buffer, int offset, int count);

void Write(char[] buffer, int offset, int count);

写二进制数据
void Write(string text);写文本数据
void WriteLine(string text); 写一行数据

(1)写二进制数据:

void Write(byte[] buffer, int offset, int count);和void Write(char[] buffer, int offset, int count);用于写二进制数据。它们的区别仅仅在于第一个参数不同:byte[]是无符号的,char[]是有符号的。对于二进制数据而言,byte、char没有实质的区别。

下面的C#代码,将写1024个00H:

if (port.IsOpen)
{byte[] bt = new byte[1024];port.Write(bt, 0, bt.Length);//写1024个00H                
}

注意:

1、Write函数是同步的。以上面的代码为例,1024个00H在发送完之前,Write函数是不会返回的。波特率1200,发送1024个字节大概要耗时9秒。如果这段代码在主线程里,那么这9秒内整个程序将处于假死状态:无法响应用户的键盘、鼠标输入;

2、WriteTimeout属性用于控制Write函数的最长耗时。它的默认值为System.IO.Ports.SerialPort.InfiniteTimeout,也就是-1。其含义为:Write函数不将所有数据写完绝不返回。可以修改此属性,如下面的代码:

if (port.IsOpen)
{byte[] bt = new byte[1024];port.WriteTimeout = 5000;//Write 函数最多耗时 5秒port.Write(bt, 0, bt.Length);//写1024个00H                
}

上面的代码中,设置WriteTimeout属性为5秒。所以Write写数据时最多耗时5秒,超过这个时间未发的数据将被舍弃,Write函数抛出异常TimeoutException后立即返回。

(2)写文本数据

void Write(string text)的示例:

if (port.IsOpen)
{port.Encoding = System.Text.Encoding.GetEncoding(936);port.Write("串行通讯");
}

首先设置代码页为936(即GBK码),Write(string text)函数根据代码页把字符串"串行通讯"转换为二进制数据,如下所示:

字符串

内码

B4 AE

D0 D0

CD A8

D1 B6

然后把二进制数据B4 AE D0 D0 CD A8 D1 B6发送出去。

函数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");

最终,发送出去的二进制数据为B4 AE D0 D0 CD A8 D1 B6 0D 0A。

5.读数据:

System.IO.Ports.SerialPort用于读串口数据的成员函数有七个,如下所示:

函数

说明

int ReadByte();

读取一个字节

int ReadChar(); 

读取一个字符

int Read(byte[] buffer, int offset, int count);

int Read(char[] buffer, int offset, int count);

读取二进制数据

string ReadExisting();

读取全部文本

string ReadTo(string value); 

读取文本到某个字符串

string ReadLine(); 

读取一行文本

(1)读二级制

读取 3 个字节的串口数据:

try
{byte[] b = new byte[3];int n = port.Read(b, 0, 3); //返回值是读取到的字节数
}
catch (Exception ex)
{MessageBox.Show(ex.Message);
}

注意:

1、Read函数是同步的。以上面的代码为例,3个字节的数据被读取之前,Read函数是不会返回的。如果这段代码在主线程里,那么整个程序将处于假死状态;

2、ReadTimeout属性用于控制Read函数的最长耗时。它的默认值为System.IO.Ports.SerialPort.InfiniteTimeout,也就是-1。其含义为:Read函数未读取到串口数据之前是不会返回的。可以修改此属性,如下面的代码:

byte[] b = new byte[3];
port.ReadTimeout = 2000;
int n = port.Read(b, 0, 3); //返回值是读取到的字节数

上面的代码中,设置ReadTimeout属性为2秒。所以Read函数读数据时最多耗时2秒。超过这个时间未读取到数据,Read函数将抛出异常TimeoutException,然后返回。

(2) 读一个字节

int ReadByte();与int Read(byte[] buffer, int offset, int count);类似,它的特点就是只读取一个字节的串口数据。

(3) 读一个字符

int ReadChar();是读取一个字符,这个稍微复杂些。它可能读取1~3个字节的数据,然后合为一个字符。

如:port.Encoding = System.Text.Encoding.GetEncoding(936);即字符串编码为GBK。给port发送"串"的GBK编码B4 AE。ReadChar首先读取一个字节得到B4H。这是一个汉字的区码,还得读取一个字节得到位码。最终ReadChar读取的是B4 AE。ReadChar的返回值是Unicode编码,即返回前会把GBK编码B4 AE转换为Unicode编码0x4E32。

再如:port.Encoding = System.Text.Encoding.UTF8;即字符串编码为UTF8。给port发送"串"的UTF8编码E4 B8 B2。ReadChar会读取三个字节的串口数据E4 B8 B2,然后将其转换为Unicode编码0x4E32,并返回这个数值。

(4) 读全部文本

函数string ReadExisting();读取串口输入缓冲区中的所有二进制数据,然后将其转换为字符串,最后返回字符串。

注意:

1、ReadExisting会立即返回。如果输入缓冲区内没有数据,直接返回长度为零的空字符串;

2、ReadExisting读取输入缓冲区后,有时会留几个字节。参考下面的代码:

port.Encoding = System.Text.Encoding.GetEncoding(936);
string s = port.ReadExisting();
int nn = port.BytesToRead; //输入缓冲区剩余的字节数

"串"、"行"的GBK编码分别为 B4 AE和D0 D0。

首先发送 B4 AE D0 给port,运行上述代码。ReadExisting将获得B4 AE D0,"B4 AE"会被解释为"串",D0是汉字的区码,所以ReadExisting会将D0保留在输入缓冲区内。上述代码的运行结果就是:s为"串",n为1;

然后发送D0 给port,运行上述代码。ReadExisting将获得D0 D0,"D0 D0"会被解释为"行"。上述代码的运行结果就是:s为"行",n为0。

(5) 读文本到某个字符串

函数string ReadTo(string value);将在串口输入缓冲区内查找字符串value。找到了,就返回value之前的字符串,同时清除缓冲区内value及其之前的数据;未找到,就一直等待,直至超时。

(6) 读一行文本

函数string ReadLine();等价于ReadTo(NewLine)。使用前,请设置NewLine属性,指定行结束符。

(7) DataReceived事件

串口输入缓冲区获得新数据后,会以DataReceived事件通知System.IO.Ports.SerialPort对象,可以在此时读取串口数据。请参考下面两段代码:

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;的含义:串口输入缓冲区获得新数据后,将检查缓冲区内已有的字节数,大于等于ReceivedBytesThreshold就会触发DataReceived事件。这里设置为1,显然就是一旦获得新数据后,立即触发DataReceived事件。

port.DataReceived+=new System.IO.Ports.SerialDataReceivedEventHandler(port_DataReceived);的含义:对于DataReceived事件,用函数port_DataReceived进行处理。

回调函数port_DataReceived用于响应DataReceived事件,通常在这个函数里读取串口数据。它的第一个参数sender就是事件的发起者。上面的代码中,sender其实就是port。也就是说:多个串口对象可以共用一个回调函数,通过sender可以区分是哪个串口对象。

回调函数是被一个多线程调用的,它不在主线程内。所以,不要在这个回调函数里直接访问界面控件。如下面的代码将将读取到的串口数据转换为字符串,然后显示在按钮Open上。红色代码处将产生异常。

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.流控制

串行通讯的双方,如果有一方反应较慢,另一方不管不顾的不停发送数据,就可能造成数据丢失。为了防止这种情况发生,需要使用流控制。

流控制也叫握手,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发送数据。B忙不过来时(B的串口输入缓冲区快满了)会给A发送字符XOFF(一般为13H),A将暂停发送数据;B的串口输入缓冲区快空时,会给A发送字符XON(一般为11H),A将继续发送数据。

软件流控制最大的问题在于:通讯双方不能传输字符XON和XOFF。

(2)硬件流控制(RTS/CTS)

RTS/CTS流控制是硬件流控制的一种,需要按下图连线:

串口设备A给串口设备B发送数据。B忙不过来时(B的串口输入缓冲区快满了)会设置自己的RTS为低电平,这样A的CTS也变为低电平。A发现自己的CTS为低电平后,会停止发送数据;B的串口输入缓冲区快空时,会设置自己的RTS为高电平,这样A的CTS也变为高电平。A发现自己的CTS为高电平后,会继续发送数据。

相同的道理,DTR/DSR也可以做硬件流控制。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/35956.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深度学习 Deep Learning 第2章 线性代数

深度学习 第2章 线性代数 线性代数是深度学习的语言。 张量操作是神经网络计算的基石&#xff0c;矩阵乘法是前向传播的核心&#xff0c;范数约束模型复杂度&#xff0c;而生成空间理论揭示模型表达能力的本质。 本章介绍线性代数的基本内容&#xff0c;为进一步学习深度学习做…

EDID读取学习

简介 Video BIOS可以被认为是一个具有独立硬件抽象层的操作系统。它不会阻止或监视操作系统、应用程序或设备驱动程序对硬件的直接访问。虽然不推荐,但一些DOS应用程序确实可以改变基本的硬件设置,而根本不需要通过视频BIOS。大多数现代应用程序和操作系统都避免直接使用硬件…

基于SpringBoot的在线拍卖系统

摘 要 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统&#xff0c;主要的模块包括管理员&#xff1b;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…

Android audio(8)-native音频服务的启动与协作(audiopolicyservice和audioflinger)

音频策略的构建 1、概述 2、AudiopolicyService 2.1 任务 2.2 启动流程 2.2.1 加载audio_policy.conf&#xff08;xml&#xff09;配置文件 2.2.2 初始化各种音频流对应的音量调节点 2.2.3 加载audio policy硬件抽象库 2.2.4设置输出设备 ps:audiopatch流程简介 2.2.5打开输出设…

Springdoc 全部注解一文解释清楚

文章目录 **1. 核心注解****Tag-Class类上** **2. 方法级别注解****Operation-方法描述****ApiResponse 和 ApiResponses-方法的返回结果** **3. 参数相关注解****Parameter-方法参数****Parameters方法参数&#xff08;单个&#xff09;** **4. 实体模型相关注解****Schema-描…

Git的基本指令

一、回滚 1.git init 在项目文件夹中打开bash生成一个.git的子目录&#xff0c;产生一个仓库 2.git status 查看当前目录下的所有文件的状态 3.git add . 将该目录下的所有文件提交到暂存区 4.git add 文件名 将该目录下的指定文件提交到暂存区 5.git commit -m 备注信…

通过qemu仿真树莓派系统调试IoT固件和程序

通过qemu仿真树莓派系统调试IoT固件和程序 本文将介绍如何使用 QEMU 模拟器在 x86 架构的主机上运行 Raspberry Pi OS&#xff08;树莓派操作系统&#xff09;。我们将从下载镜像、提取内核和设备树文件&#xff0c;到启动模拟环境&#xff0c;并进行一些常见的操作&#xff0…

DeepSeek大模型在政务服务领域的应用

DeepSeek大模型作为国产人工智能技术的代表&#xff0c;近年来在政务服务领域的应用呈现多点开花的态势。通过多地实践&#xff0c;该技术不仅显著提升了政务服务的效率与智能化水平&#xff0c;还推动了政府治理模式的创新。以下从技术应用场景、典型案例及发展趋势三个维度进…

【HarmonyOS Next之旅】DevEco Studio使用指南(三)

目录 1 -> 一体化工程迁移 1.1 -> 自动迁移 1.2 -> 手动迁移 1.2.1 -> API 10及以上历史工程迁移 1.2.2 -> API 9历史工程迁移 1 -> 一体化工程迁移 DevEco Studio从 NEXT Developer Beta1版本开始&#xff0c;提供开箱即用的开发体验&#xff0c;将SD…

FPGA中级项目4——DDS实现

FPGA中级项目4——DDS实现 DDS简介 DDS&#xff08;直接数字频率合成器&#xff0c;Direct Digital Frequency Synthesis&#xff09;是一种基于数字信号处理技术的频率合成方法&#xff0c;广泛应用于通信、雷达、仪器仪表等领域。在 FPGA中实现 DDS 具有灵活性高、集成度强、…

SAP的WPS导出找不到路径怎么办;上载报错怎么办

一.打开注册编辑器 二.输入以下地址 计算机\HKEY_CLASSES_ROOT\ExcelWorksheet\Protocol\StdFileEditing\Server 去除掉EXE后面的命令即可 二&#xff1a;WPS上载文件没反应怎么办 如何切换整合模式或多组件模式-WPS学堂 根据官方操作把整合模式改成多组件模式

STM32 DAC详解:从原理到实战输出正弦波

目录 一、DAC基础原理1.1 DAC的作用与特性1.2 DAC功能框图解析 二、DAC配置步骤2.1 硬件配置2.2 初始化结构体详解 三、DAC数据输出与波形生成3.1 数据格式与电压计算3.2 正弦波生成实战3.2.1 生成正弦波数组3.2.2 配置DMA传输3.2.3 定时器触发配置 四、常见问题与优化建议4.1 …

MVC_Publish-Subscriber 模式中的事件处理程序

MVC_Publish-Subscriber 模式中的事件处理程序 MVC 中的事件处理&#xff1a;发布者-订阅者模式 程序启动时&#xff0c;controlRecipes将被传入addHandlerRender; addHandlerRender会侦听事件&#xff08;addEventListener&#xff09;,并使用controlRecipes作为回调&#xf…

网络华为HCIA+HCIP 以太网链路聚合与交换机堆叠、集群

网络可靠性 网络的可靠性指当设备或者链路出现单点或者多点故障时保证网络服务不间断的能力。网络的可靠性可以从单板、设备、链路多个层面实现。 单板可靠性 以S12700E-8为例&#xff0c;设备提供8个线路板槽位、4个交换网板槽位、2个主控板槽位、6个电源模块槽位、4个风扇…

施耐德PLC仿真软件Modbus tcp通讯测试

安装仿真软件&#xff1a;EcoStruxure™ Control Expert - PLC 仿真器 下载地址&#xff1a;https://www.schneider-electric.cn/zh/download/document/EIO0000001719/ 配置CPU&#xff1a; 切换至仿真模式&#xff0c;系统托盘中出现仿真器图标 新建变量test&#xff0c;地址…

LabVIEW棉花穴播器排种自动监测系统

一、项目背景与行业痛点 1. 农业需求驱动 我国棉花主产区&#xff0c;种植面积常年超250万公顷&#xff0c;传统人工播种存在两大核心问题&#xff1a; 效率瓶颈&#xff1a;人均日播种面积不足0.5公顷&#xff0c;难以匹配规模化种植需求&#xff1b; 精度缺陷&#xff1a;人…

2024浙江大学计算机考研上机真题

2024浙江大学计算机考研上机真题 2024浙江大学计算机考研复试上机真题 2024浙江大学计算机考研机试真题 2024浙江大学计算机考研复试机试真题 历年浙江大学计算机复试上机真题 历年浙江大学计算机复试机试真题 2024浙江大学计算机复试上机真题 2024浙江大学计算机复试机试真题 …

GPU视频编解码:Jetson VPI+multimedia_api视频编解码入门(一)

目录 一.Pipline与工具栈 二.硬件设备概况 三.GPU视频编解码框架 四.VPI编译使用实例 五. jetson_multimedia_api编译使用实例 一.Pipline与工具栈 二.硬件设备概况 三.GPU视频编解码框架 jetson设备目前不支持VPF框架&#xff0c;关于VPF的使用我在下节PC段使用X86进行安…

设计模式-对象创建

对象创建 前言1. Factory Method1.1 模式介绍1.2 模式代码1.2.1 问题代码1.2.2 重构代码 1.3 模式类图1.4 要点总结 2. Abstract Factory2.1 模式介绍2.2 模式代码2.2.1 问题代码2.2.2 重构代码 2.3 模式类图2.4 要点总结 3. Prototype3.1 模式介绍3.2 模式代码3.3 模式类图3.4…

【大模型基础_毛玉仁】2.6 非 Transformer 架构

更多内容&#xff1a;XiaoJ的知识星球 目录 2.6 非 Transformer 架构2.6.1 状态空间模型 SSM1&#xff09;SSM&#xff08;State Space Model&#xff09;2&#xff09;RWKV&#xff08;Receptance Weighted Key Value&#xff09;3&#xff09;Mamba 2.6.2 训练时更新TTT(Test…