译自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的更多相关文章

  1. C#用SerialPort实现串口通讯

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  2. Nodejs 使用 SerialPort 调用串口

    工作经常使用串口读写数据,electron 想要替代原来的客户端,串口成了必须要突破的障碍. get -->  https://github.com/EmergingTechnologyAdvi ...

  3. C#SerialPort实现串口控制继电器

    最近做了一个小系统,麻雀虽小五脏俱全呀,用到各种线程控制,串口控制等技术.其中串口控制最麻烦,因为继电器的响应很快,根据不同的转接口,返回的数据质量是不一样的,所以不能直接wirte,然后马上read ...

  4. C#SerialPort如何读取串口数据并显示在TextBox上

    SerialPort中串口数据的读取与写入有较大的不同.由于串口不知道数据何时到达,因此有两种方法可以实现串口数据的读取.一.线程实时读串口:二.事件触发方式实现. 由于线程实时读串口的效率不是十分高 ...

  5. c#实现串口操作 SerialPort

    命名空间:using System.IO.Ports;该类提供了同步 I/O 和事件驱动的 I/O.对管脚和中断状态的访问以及对串行驱动程序属性的访问. 操作类声明: SerialPort sp = ...

  6. System.IO.Ports.SerialPort串口通信接收完整数据

    C#中使用System.IO.Ports.SerialPort进行串口通信网上资料也很多,但都没有提及一些细节: 比如 串口有时候并不会一次性把你想要的数据全部传输给你,可能会分为1次,2次,3次分别 ...

  7. C#串口操作类,包括串口读写操作

    串口进行操作的类,其中包括写和读操作,类可设置串口参数.设置接收函数.打开串口资源.关闭串口资源,操作完成后,一定要关闭串口.接收串口数据事件.接收数据出错事件.获取当前全部串口.把字节型转换成十六进 ...

  8. .Net Core跨平台应用研究-CustomSerialPort(增强型跨平台串口类库)

    .Net Core跨平台应用研究-CustomSerialPort -增强型跨平台串口类库 摘要 在使用SerialPort进行串口协议解析过程中,经常遇到接收单帧协议数据串口接收事件多次触发,协议解 ...

  9. .Net Core 跨平台应用使用串口、串口通信 ,可能出现的问题、更简洁的实现方法

    前些天在学习在 .NET Core下,跨平台使用串口通讯,有一篇文章说到在Linux/物联网下,实现通讯. 主要问题出现在以下两个类库 SerialPortStream flyfire.CustomS ...

随机推荐

  1. BZOJ 1856: [Scoi2010]字符串 [Catalan数]

    1856: [Scoi2010]字符串 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 1418  Solved: 790[Submit][Status][ ...

  2. 简单DNA序列组装(非循环子图)

    生物信息学原理作业第四弹:DNA序列组装(非循环子图) 原理:生物信息学(孙啸) 大致思想: 1. 这个算法理解细节理解比较困难,建议看孙啸的生物信息学相关章节. 2. 算法要求所有序列覆盖整个目标D ...

  3. create-react-app搭建的项目中添加bootstrap

    react-bootstrap是一个非常受欢迎的针对react封装过的bootstrap,它本身不包含css,所以也是需要使用bootstrap原生库. 在create-react-app建的项目目录 ...

  4. php + 和 array_merge的区别

    (1)对于+,当key相同时,都是舍弃后面的结果: array_merge ,当key相同时,key是字符,则后面的覆盖前面的:key是数字,则不发生覆盖,会重新建立数组索引. $arr1 = arr ...

  5. PyCharm安装及使用

    搭建环境 1.win10_X64,其他Win版本也可以. 2.PyCharm版本:Professional-2016.2.3. 搭建准备 1.到PyCharm官网下载PyCharm安装包.   Dow ...

  6. Linux-安装FFmpeg

    FFmpeg官网:http://www.ffmpeg.org 官网介绍 FFmpeg is the leading multimedia framework, able to decode, enco ...

  7. 关于目前自己iOS项目使用的第三方开源库

    1.AFNetworking 目前比较推荐的iOS网络请求组件,默认网络请求是异步,通过block回调的方式对返回数据进行处理. 2.FMDB 对sqlite数据库操作进行了封装,demo也比较简单. ...

  8. 【NOIP2015】字串

    [NOIP2015]字串 标签: DP NOIP Description 有两个仅包含小写英文字母的字符串 A 和 B.现在要从字符串 A 中取出 k 个互不重叠的非空子串,然后把这 k 个子串按照其 ...

  9. mysql数据库外部无法访问

    有以下两种情况: 1.mysql未分配访问权限 格式:grant 权限 on 数据库名.表名 用户@登录主机 identified by "用户密码"; grant select, ...

  10. 【学习笔记】Spring中的BeanFactory和ApplicationContext 以及 Bean的生命周期(Y2-3-2)

    BeanFactory和ApplicationContext Spring的IoC容器就是一个实现了BeanFactory接口的可实例化类. Spring提供了两种不同的容器: 一种是最基本的Bean ...