这段时间用Moxa DA660(WinCE5.0平台)测试16口同时下发数据,发现由于该硬件设备的CPU主频仅有260M赫兹,大于10口同时下发数据就会造成发送延迟,导致下发失败。前次用.net的SerialPort类实现了一个PPC红外口读写数据的小程序(其实就是串口操作),发现该程序在接收大量的数据时,很容易发生崩溃,并且该错误信息,程序本身无法捕捉(用EVC开发的程序就没有这种情况),所以就有了一探SerialPort类的冲动。
用.Net Reflector工具(该工具在《程序员》杂志4月刊有介绍)很容易就可以看到微软.net框架集SerialPort的实现源码,下面从构造函数开始谈起(注:精简框架下的system.dll反射后竟然看不到相关代码,看来微软对精简集进行了加密,只能看非精简框架集的system.dll,其实现我想应该差不太多,但是Wince平台仅能实现同步读写)。
1、通信参数的默认值
this.baudRate = 9600; //波特率
this.dataBits = 8; //数据位
this.stopBits = StopBits.One; //停止位
this.portName = "COM1";//串口号
this.readTimeout = -1; //读超时
this.writeTimeout = -1; //写超时
this.receivedBytesThreshold = 1; //触发事件前接收缓冲区的数据个数
this.parityReplace = 0x3f; //数据校验失败,该数据的替换字符
this.newLine = "/n"; //换行符
this.readBufferSize = 4096; //读缓冲区大小
this.writeBufferSize = 2048; //写缓冲区大小
2、看看微软的代码如何枚举本机串口号(也是通过注册表方式)
public static string[] GetPortNames()
{
RegistryKey localMachine = null;
RegistryKey key2 = null;
string[] textArray = null;
//这里有个断言,判断该注册表项是否存在
new RegistryPermission(RegistryPermissionAccess.Read, @"HKEY_LOCAL_MACHINE/HARDWARE/DEVICEMAP/SERIALCOMM").Assert();
try
{
localMachine = Registry.LocalMachine;
key2 = localMachine.OpenSubKey(@"HARDWARE/DEVICEMAP/SERIALCOMM", false);
if (key2 != null)
{
string[] valueNames = key2.GetValueNames();
textArray = new string[valueNames.Length];
for (int i = 0; i Length; i++)
{
textArray[i] = (string) key2.GetValue(valueNames[i]);
}
}
}
finally
{
if (localMachine != null)
{
localMachine.Close();
}
if (key2 != null)
{
key2.Close();
}
CodeAccessPermission.RevertAssert();
}
if (textArray == null)
{
textArray = new string[0];
}
return textArray;
}
3、核心读代码
//如果在超时时间内没有获取指定的数据个数就会抛出异常,这种设计方式我不大习惯,如果超时直接返回-1或0即可,没有必要抛出异常。
private int InternalRead(char[] buffer, int offset, int count, int timeout, bool countMultiByteCharsAsOne)
{
if (count == 0)
{
return 0;
}
int tickCount = Environment.TickCount;
int additionalByteLength = this.internalSerialStream.BytesToRead;
this.MaybeResizeBuffer(additionalByteLength);
//用到了流的串口读写,看来还需要继续跟踪
this.readLen += this.internalSerialStream.Read(this.inBuffer, this.readLen, additionalByteLength);
if (this.decoder.GetCharCount(this.inBuffer, this.readPos, this.CachedBytesToRead) > 0)
{
return this.ReadBufferIntoChars(buffer, offset, count, countMultiByteCharsAsOne);
}
if (timeout == 0)
{
throw new TimeoutException();
}
int maxByteCount = this.Encoding.GetMaxByteCount(count);
while (true)
{
this.MaybeResizeBuffer(maxByteCount);
this.readLen += this.internalSerialStream.Read(this.inBuffer, this.readLen, maxByteCount);
int num4 = this.ReadBufferIntoChars(buffer, offset, count, countMultiByteCharsAsOne);
//只要获取了数据,就返回数据接收的个数
if (num4 > 0)
{
return num4;
}
//这里可以看出timeout设为-1的用意了
if ((timeout != -1) && ((timeout - (Environment.TickCount - tickCount)) 0))
{
throw new TimeoutException();
}
}
}
看该函数的构造函数中的代码:
SafeFileHandle hFile = UnsafeNativeMethods.CreateFile(@"//./" + portName, -1073741824, 0, IntPtr.Zero, 3, dwFlagsAndAttributes, IntPtr.Zero);
注意串口文件名为
@"//./" + portName,
@//./表示串口超过9的函数就必须添加该字符串,否则打开串口失败("$device//COM"
+ PortNo.ToString() + "/0"(从这里明白了,为什么当初我把串口名称设为
@“
//./COM" + PortNo.ToString()+”:”也一样失败了,因为它已经预先添加了)。
if (readTimeout == 0)
{
this.commTimeouts.ReadTotalTimeoutConstant = 0;
this.commTimeouts.ReadTotalTimeoutMultiplier = 0;
this.commTimeouts.ReadIntervalTimeout = -1;
}
else if (readTimeout == -1)
{
this.commTimeouts.ReadTotalTimeoutConstant = -2;
this.commTimeouts.ReadTotalTimeoutMultiplier = -1;
this.commTimeouts.ReadIntervalTimeout = -1;
}
else
{
this.commTimeouts.ReadTotalTimeoutConstant = readTimeout;
this.commTimeouts.ReadTotalTimeoutMultiplier = -1;
this.commTimeouts.ReadIntervalTimeout = -1;
}
从以上代码可以看出,它的超时仅仅是总超时时间,不能设置单个字节之间的超时时间。
这是读操作中的一段代码:
int num = 0;
if (this.isAsync)
{
IAsyncResult asyncResult = this.BeginReadCore(array, offset, count, null, null);
num = this.EndRead(asyncResult);
}
Else //对我们来说,仅关心同步操作即可
{
int hr;
num = this.ReadFileNative(array, offset, count, null, out hr);
if (num == -1)
{
InternalResources.WinIOError();
}
}
if (num == 0)
{
throw new TimeoutException();
}
这是读写操作的函数,看给直接用API读没有什么区别:
fixed (byte* numRef = bytes)//用到了指针
{
if (this.isAsync)
{
num = UnsafeNativeMethods.ReadFile(this._handle, numRef + offset, count, IntPtr.Zero, overlapped);
}
else
{
num = UnsafeNativeMethods.ReadFile(this._handle, numRef + offset, count, out numBytesRead, IntPtr.Zero);
}
}
注:写代码与上面的类似
从以上代码可以粗浅的看出(SerialPort类->SerialStream类->UnsafeNativeMethods类->API函数),采用SerialPort读写串口是很繁琐的,并且效率较低,有时间可以根据这写代码写一个紧凑有效的串口读写类(有兴趣的朋友也可以实现,到时候可别忘了发给我一份:)。
<iframe align="center" marginwidth="0" marginheight="0" src="http://www.zealware.com/csdnblog.html" frameborder="0" width="728" scrolling="no" height="90"></iframe>
相关推荐
VB.NET之SerialPort编程实例源码 直接使用
serialport讲述属性和事件,以及接收和发送方法
vb.net + SerialPort串口通讯,.net SerialPort控件,源码文件
利用vb.net 实现串口数据采集,给出了2中源代码
源代码的节选,有编程的注释,利用SerialPort进行读取串口操作
VB2008中使用SerialPort控件详解,从接收到发送。估计你看了就会用这个了,其实VB2008很简单哦~
基于C++.Net的SerialPort串口组件的PC机双串口互相通信winform程序
C#.NET SerialPort控件实现
详细介绍com口、usb口编程,适用Rs-232、485、wireless,附有源程序代码。
此实例为VS2008环境下 VB.NET 编写的 SerialPort通信为16进制,可以改为字符串 内含不同种类的2种时间延时方法及如何取得串口号等。
VS6.0版本用MSComm控件进行串口通信,.net之后新增SerialPort控件。做串口通信的可以看看。
http://topic.csdn.net/u/20100802/21/1d98bc48-f8ae-4d77-85f5-bab30c429e9e.html 这个帖子问题的范例
使用SerialPort在WPF中进行串口通信的实例,实现了本机发送,本机接收,既可以同步接收又可以异步接收
.NET SerialPort源代码示例(VB.NET C#)自己最近写的串口通信代码
关于VS.Net2005中串口操作说明(C#)(SerialPort组件) -ZT
vb.net编写的SerialPort类串口通信程序,在vs2010上编译通过。
基于VS2010平台下VC++使用SerialPort类完成了简单的串口通信,可以选择串口号
SerialPort串口通信 vb .net c#
对操作无线通信模块M1206的主要AT命令格式及功能进行了描述,针对短信传输采用的PDU数据格式单元及编码方式进行了阐述,重点论述了在C#语言中,使用SerialPort类发送短信的程序设计思想和实现方法。在网络应用程序...
asp.net串口编程教程serialport,希望能给开发串口的朋友提供帮助。