C# SerialPort自定义串口DCB
译自Change DCB fields from SerialPort instance C#
C# SerialPort自定义串口DCB
DCB(Device Control Block)在C++ 里面是用bitfield(位域)表示的,C#没有bitfield,但有一个枚举位标志。C#有自己的方法来设置怎么存取DCB,而且“SerialStream”里确实也有“SetDcbFlag”方法(该方法接收2个int参数)。在研究了.Net源码后,我创建了一组常量用来等效原来的DCB int字段和他们的新int代码,我暂时还没有整理出所有位标志,不过你可以在DCB文档里面查阅它们。因为这是一个Internal方法只对.Net内部程序集可见,所以我在获取该方法时用到了System.Reflection(反射)。
下面就是代码,整体上是一个.Net 2.0+SerialPort的扩展类。扩展类中有三个方法,SetField(string name, object value)方法用来设置所有不是以'f'开头的位,SetFlag(int Flag, int Value)方法用来处理以'f'开头的位(我提供了一个针对Flag参数的const列表),最后UpdateComm()方法用来在你更改DCB后刷新串口连接,它本来是SetField方法的一部分,但是如果设置DCB时每次都调用它,那花的时间就有点长了。
注意:
确保开启串口连接后,再使用这些方法。
用法:
SerialPort COM = new SerialPort("COM7");
COM.Open();
COM.DiscardInBuffer();
COM.DiscardOutBuffer();
COM.SetFlag(FBINARY, );
COM.SetFlag(FPARITY, );
COM.SetFlag(FDTRCONTROL, 0x00);
COM.SetFlag(FRTSCONTROL, 0x01);
COM.SetField("BaudRate", (UInt32));
COM.SetField("StopBits", (byte));
COM.SetField("ByteSize", (byte));
COM.SetField("Parity", (byte));
COM.SetField("XonChar", (byte)0x11);
COM.SetField("XoffChar", (byte)0x13);
COM.SetField("EvtChar", (byte)0x1A);
COM.SetField("XonLim", (ushort));
COM.SetField("XoffLim", (ushort));
COM.UpdateComm();
/* Do Stuff */
COM.Close();
常量:
internal const int FBINARY = ;
internal const int FPARITY = ;
internal const int FOUTXCTSFLOW = ;
internal const int FOUTXDSRFLOW = ;
internal const int FDTRCONTROL = ;
internal const int FDSRSENSITIVITY = ;
internal const int FTXCONTINUEONXOFF = ;
internal const int FOUTX = ;
internal const int FINX = ;
internal const int FERRORCHAR = ;
internal const int FNULL = ;
internal const int FRTSCONTROL = ;
internal const int FABORTONOERROR = ;
internal const int FDUMMY2 = ;
最后,扩展类
internal static class SerialPortExtensions
{
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
public static void SetField(this SerialPort port, string field, object value)
{
if (port == null)
throw new NullReferenceException();
if (port.BaseStream == null)
throw new InvalidOperationException("Cannot change fields until after the port has been opened.");
try
{
object baseStream = port.BaseStream;
Type baseStreamType = baseStream.GetType();
FieldInfo dcbFieldInfo = baseStreamType.GetField("dcb", BindingFlags.NonPublic | BindingFlags.Instance);
object dcbValue = dcbFieldInfo.GetValue(baseStream);
Type dcbType = dcbValue.GetType();
dcbType.GetField(field).SetValue(dcbValue, value);
dcbFieldInfo.SetValue(baseStream, dcbValue);
}
catch (SecurityException) { throw; }
catch (OutOfMemoryException) { throw; }
catch (Win32Exception) { throw; }
catch (Exception)
{
throw;
}
}
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
public static void SetFlag(this SerialPort port, int flag, int value)
{
object BaseStream = port.BaseStream;
Type SerialStream = BaseStream.GetType();
SerialStream.GetMethod("SetDcbFlag", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(BaseStream, new object[] { flag, value });
}
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
public static void UpdateComm(this SerialPort port)
{
object baseStream = port.BaseStream;
Type baseStreamType = baseStream.GetType();
FieldInfo dcbFieldInfo = baseStreamType.GetField("dcb", BindingFlags.NonPublic | BindingFlags.Instance);
object dcbValue = dcbFieldInfo.GetValue(baseStream);
SafeFileHandle portFileHandle = (SafeFileHandle)baseStreamType.GetField("_handle", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(baseStream);
IntPtr hGlobal = Marshal.AllocHGlobal(Marshal.SizeOf(dcbValue));
try
{
Marshal.StructureToPtr(dcbValue, hGlobal, false);
if (!SetCommState(portFileHandle, hGlobal))
throw new Win32Exception(Marshal.GetLastWin32Error());
}
finally
{
if (hGlobal != IntPtr.Zero)
Marshal.FreeHGlobal(hGlobal);
}
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool SetCommState(SafeFileHandle hFile, IntPtr lpDCB);
}
Change DCB fields from SerialPort instance C
The DCB struct requires a C++ class known as a bitfield, which C# does not have, so instead they have a field called "Flags" stored as a UInt32.They have their own melarchy set up there as to how it's stored and read from, but they did put a method in the SerialStream called SetDcbFlag which accepts two ints.After digging through the .net source a bit, I managed to come up with a set of constants equating to the original DCB Fields and their new int code, I haven't yet made a list of values for these flags, but those can be easily found in the DCB Documentation.I used system.reflection to gain access to the method as it was an internal method only to be used by internal .NET source.
So here it is, the code, it's an extension class for the SerialPort class which is shipped stock with .NET 2.0+. My extension class adds three methods, SetField(string name, object value) which will set any of the fields that aren't prefixed with "f", SetFlag(int Flag, int Value) which will take care of those fields prefixed with "f" (I'll provide a list of constants for use with the Flag parameter), and finally UpdateComm() this will update the serial connection once you have changed all of your values, it was originally part of the SetField method, but it takes slightly longer to complete things if it's calling that every single time during initialization.
NOTE:
The serial port must be opened before using any of these methods!
Usage:
略
Constants:
略
And finally, the extension class:
略
PS:中文DCB结构详解表
MSDN的DCB文档和这个内容基本相同
| 成员 | 取值 | 说明 |
|---|---|---|
| DCBlength | DCB结构大小,即sizeof(DCB),在调用SetCommState来更新DCB前必须作设置 | |
| BaudRate | 指定当前采用的波特率,应与所连接的通讯设备相匹配 | |
| fBinary | 指定是否允许二进制模式。Win32 API不支持非二进制模式传输,应设置为true | |
| fParity | 指定奇偶校验是否允许,在为true时具体采用何种校验看Parity 设置 | |
| Parity | 指定端口数据传输的校验方法。以下是可取值及其意义: | |
| EVENPARITY | 偶校验(2) | |
| MARKPARITY | 标记校验,所发信息帧第9位恒为1(3) | |
| NOPARITY | 无校验(0) | |
| ODDPARITY | 奇校验(1) | |
| StopBits | 指定端口当前使用的停止位数,可取值: | |
| ONESTOPBIT | 1停止位(0) | |
| ONE5STOPBITS | 1.5停止位(1) | |
| TWOSTOPBITS | 2停止位(2) | |
| fErrorChar | 该值为TRUE,则用ErrorChar指定的字符代替奇偶校验错误的接收字符 | |
| ErrorChar | 指定ErrorChar字符(代替接收到的奇偶校验发生错误时的字节) | |
| EvtChar | 当接收到此字符时,会产生一个EV_RXFLAG事件,如果用SetCommMask函数中指定了EV_RXFLAG ,则可用WaitCommEvent 来监测该事件 | |
| EofChar | 指定用于标示数据结束的字符 | |
| fNull | 为TRUE时,接收时自动去掉空(0值)字节 | |
| fAbortOnError | 读写操作发生错误时是否取消操作。若设置为true,则当发生读写错误时,将取消所有读写操作(错误状态置为ERROR_IO_ABORTED),直到调用ClearCommError函数后才能重新进行通讯操作 | |
| fOutxCtsFlow | 是否监控CTS(clear-to-send)信号来做输出流控。当设置为true时:若CTS为低电平,则数据发送将被挂起,直至CTS变为高。CTS的信号一般由DCE(通常是一个Modem)来控制,而DTE(通常是计算机)发送数据时监测CTS信号。也就是说DCE通过把CTS置高来表明自己可以接收数据了 | |
| fRtsControl | 设置RTS (request-to-send)流控,若为0则缺省取值 RTS_CONTROL_HANDSHAKE。以下是可取值及其意义: | |
| RTS_CONTROL_DISABLE | 打开设备时置RTS信号为低电平,应用程序可通过调用EscapeCommFunction函数来改变RTS线电平状态 | |
| RTS_CONTROL_ENABLE | 打开设备时置RTS信号为高电平,应用程序可通过调用EscapeCommFunction函数来改变RTS线电平状态 | |
| RTS_CONTROL_HANDSHAKE | 允许RTS信号握手,此时应用程序不能调用EscapeCommFunction函数。当输入缓冲区已经有足够空间接收数据时,驱动程序置RTS为高以便允许DCE来发送;反之置RTS为低以阻止DCE发送数据。 | |
| RTS_CONTROL_TOGGLE | 有字节要发送时RTS变高,当所有缓冲字节已经被发送完毕后,RTS变低。此时应用程序不能调用EscapeCommFunction函数。该值在Windows 95系统被忽略 | |
| fOutxDsrFlow | 是否监控DSR (data-set-ready) 信号来做输出流控。当设置为true时:若DSR为低电平,则数据发送将被挂起,直至DSR变为高。DSR的信号一般由DCE来控制 | |
| fDtrControl | DTR (data-terminal-ready)流控,可取值如下: | |
| DTR_CONTROL_DISABLE | 打开设备时置DTR信号为低电平,应用程序可通过调用EscapeCommFunction函数来改变DTR线电平状态 | |
| DTR_CONTROL_ENABLE | 打开设备时置DTR信号为高电平,应用程序可通过调用EscapeCommFunction函数来改变DTR线电平状态 | |
| DTR_CONTROL_HANDSHAKE | 允许DTR信号握手,此时应用程序不能调用EscapeCommFunction函数 | |
| fDsrSensitivity | 通讯设备是否对DSR信号敏感。若设置为TRUE,则当DSR为低时将会忽略所有接收的字节 | |
| fTXContinueOnXoff | 当输入缓冲区满且驱动程序已发出XOFF字符时,是否停止发送。当为TRUE时,XOFF被发送后发送仍然会继续;为FALSE时,发送停止,直至输入缓冲区有XonLim字节的空余空间、驱动程序已发送XON字符之后发送继续。 | |
| fOutX | XON/XOFF 流量控制在发送时是否可用。如果为TRUE, 当 XOFF 值被收到的时候,发送停止;当 XON 值被收到的时候,发送继续 | |
| fInX | XON/XOFF 流量控制在接收时是否可用。如果为TRUE, 当 输入缓冲区已接收满XoffLim 字节时,发送XOFF字符;当输入缓冲区已经有XonLim 字节的空余容量时,发送XON字符 | |
| XonLim | 在XON字符发送前接收缓冲区内可允许的最小字节数 | |
| XoffLim | 在XOFF字符发送前接收缓冲区内可允许的最大字节数 | |
| XonChar | 指定XON字符 | |
| XoffChar | 指定XOFF字符 | |
| fDummy2 | 保留,未启用 | |
| wReserved | 未启用,必须设置为0 | |
| wReserved1 | 保留,未启用 |
C# SerialPort自定义串口DCB的更多相关文章
- C#用SerialPort实现串口通讯
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...
- Nodejs 使用 SerialPort 调用串口
工作经常使用串口读写数据,electron 想要替代原来的客户端,串口成了必须要突破的障碍. get --> https://github.com/EmergingTechnologyAdvi ...
- C#SerialPort实现串口控制继电器
最近做了一个小系统,麻雀虽小五脏俱全呀,用到各种线程控制,串口控制等技术.其中串口控制最麻烦,因为继电器的响应很快,根据不同的转接口,返回的数据质量是不一样的,所以不能直接wirte,然后马上read ...
- C#SerialPort如何读取串口数据并显示在TextBox上
SerialPort中串口数据的读取与写入有较大的不同.由于串口不知道数据何时到达,因此有两种方法可以实现串口数据的读取.一.线程实时读串口:二.事件触发方式实现. 由于线程实时读串口的效率不是十分高 ...
- c#实现串口操作 SerialPort
命名空间:using System.IO.Ports;该类提供了同步 I/O 和事件驱动的 I/O.对管脚和中断状态的访问以及对串行驱动程序属性的访问. 操作类声明: SerialPort sp = ...
- System.IO.Ports.SerialPort串口通信接收完整数据
C#中使用System.IO.Ports.SerialPort进行串口通信网上资料也很多,但都没有提及一些细节: 比如 串口有时候并不会一次性把你想要的数据全部传输给你,可能会分为1次,2次,3次分别 ...
- C#串口操作类,包括串口读写操作
串口进行操作的类,其中包括写和读操作,类可设置串口参数.设置接收函数.打开串口资源.关闭串口资源,操作完成后,一定要关闭串口.接收串口数据事件.接收数据出错事件.获取当前全部串口.把字节型转换成十六进 ...
- .Net Core跨平台应用研究-CustomSerialPort(增强型跨平台串口类库)
.Net Core跨平台应用研究-CustomSerialPort -增强型跨平台串口类库 摘要 在使用SerialPort进行串口协议解析过程中,经常遇到接收单帧协议数据串口接收事件多次触发,协议解 ...
- .Net Core 跨平台应用使用串口、串口通信 ,可能出现的问题、更简洁的实现方法
前些天在学习在 .NET Core下,跨平台使用串口通讯,有一篇文章说到在Linux/物联网下,实现通讯. 主要问题出现在以下两个类库 SerialPortStream flyfire.CustomS ...
随机推荐
- ES6的Symbol
let s = Symbol(); alert(typeof(s)); // Symbol函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时,比 ...
- 自己动手实践 spring retry 重试框架
前序 马上过年了,预祝大家,新年快乐,少写bug 什么是spring retry? spring retry是从spring batch独立出来的一个能功能,主要实现了重试和熔断. 什么时候用? 远程 ...
- 简单DNA序列组装(非循环子图)
生物信息学原理作业第四弹:DNA序列组装(非循环子图) 原理:生物信息学(孙啸) 大致思想: 1. 这个算法理解细节理解比较困难,建议看孙啸的生物信息学相关章节. 2. 算法要求所有序列覆盖整个目标D ...
- CentOS 7 搭建基于携程Apollo(阿波罗)配置中心单机模式
Apollo(阿波罗)是携程框架部门研发的配置管理平台,能够集中化管理应用不同环境.不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限.流程治理等特性.服务端基于Spring Boot ...
- 如何在Python中从零开始实现随机森林
欢迎大家前往云+社区,获取更多腾讯海量技术实践干货哦~ 决策树可能会受到高度变异的影响,使得结果对所使用的特定测试数据而言变得脆弱. 根据您的测试数据样本构建多个模型(称为套袋)可以减少这种差异,但是 ...
- LVS结合keepalived配置测试
LVS/DR + keepalived配置 注意:前面虽然我们已经配置过一些操作,但是下面我们使用keepaliave操作和之前的操作是有些冲突的,所以若是之前配置过DR,请首先做如下操作: 三 ...
- win7连接共享打印机
1. 保证目标电脑启用共享.打印机驱动安装正常 2. 目标电脑进入"设备和打印机" 3. 右键要共享的打印机 - 打印机属性 -共享此打印机 4. 其他电脑打印时,选择其他打印机, ...
- hibernate之实体@onetomany和@manytoone双向注解(转)
下面是User类: @onetomany @Entity @Table(name="user") public class User implements Serializable ...
- php 链接mysql的三种方式对比
PHP连接Mysql的三种方式: 1.原生的连接方式 原生的连接方式是面向过程的写法 <?php $host = 'localhost'; $database = 'test'; $usern ...
- java-redis字符类数据操作示例(一)
对于大部分程序猿来讲,学习新知识重在编码实践,于我也是这样.现在初识redis,一直看文章难免感觉是浮光掠影,印象不深.所以间隙中,将自己的测试代码整理成博客,旨在加深记忆并提醒自己对待编程要用心沉下 ...