Winform:记录下串口调试
记录下串口通讯
因为FX的SerialPort不好用,所以也自已试着去调试了串口,可是一调就不开心起来了。本来串口就一个读,一个写,就是这样的事,可不知为什么调试起来有时老回读不到数据。最后还是用了VB的MSCom才搞定,尽管搞定了,可还是不知道问题出在那,在这里给自已标志一下吧。
http://www.vckbase.com/document/viewdoc/?id=1734
从VCKBase里就找到一篇很好的例子,直接通过API操作串口的。
1. CreateFile打开串口一下,
2. 配置一下DCB。DCB结构包含了诸如波特率、数据位数、奇偶校验和停止位数等信息。在查询或配置串口的属性时,都要用DCB结构来作为缓冲区。
[StructLayout(LayoutKind.Sequential)]
public struct DCB
{
//taken from c struct in platform sdk
public int DCBlength; // sizeof(DCB)
public int BaudRate; // 指定当前波特率 current baud rate
// these are the c struct bit fields, bit twiddle flag to set
public int fBinary; // 指定是否允许二进制模式,在windows95中必须主TRUE binary mode, no EOF check
public int fParity; // 指定是否允许奇偶校验 enable parity checking
public int fOutxCtsFlow; // 指定CTS是否用于检测发送控制,当为TRUE是CTS为OFF,发送将被挂起。 CTS output flow control
public int fOutxDsrFlow; // 指定CTS是否用于检测发送控制 DSR output flow control
public int fDtrControl; // DTR_CONTROL_DISABLE值将DTR置为OFF, DTR_CONTROL_ENABLE值将DTR置为ON, DTR_CONTROL_HANDSHAKE允许DTR"握手" DTR flow control type
public int fDsrSensitivity; // 当该值为TRUE时DSR为OFF时接收的字节被忽略 DSR sensitivity
public int fTXContinueOnXoff; // 指定当接收缓冲区已满,并且驱动程序已经发送出XoffChar字符时发送是否停止。TRUE时,在接收缓冲区接收到缓冲区已满的字节XoffLim且驱动程序已经发送出XoffChar字符中止接收字节之后,发送继续进行。 FALSE时,在接收缓冲区接收到代表缓冲区已空的字节XonChar且驱动程序已经发送出恢复发送的XonChar之后,发送继续进行。XOFF continues Tx
public int fOutX; // TRUE时,接收到XoffChar之后便停止发送接收到XonChar之后将重新开始 XON/XOFF out flow control
public int fInX; // TRUE时,接收缓冲区接收到代表缓冲区满的XoffLim之后,XoffChar发送出去接收缓冲区接收到代表缓冲区空的XonLim之后,XonChar发送出去 XON/XOFF in flow control
public int fErrorChar; // 该值为TRUE且fParity为TRUE时,用ErrorChar 成员指定的字符代替奇偶校验错误的接收字符 enable error replacement
public int fNull; // eTRUE时,接收时去掉空(0值)字节 enable null stripping
public int fRtsControl; // RTS flow control
/*RTS_CONTROL_DISABLE时,RTS置为OFF
RTS_CONTROL_ENABLE时, RTS置为ON
RTS_CONTROL_HANDSHAKE时,
当接收缓冲区小于半满时RTS为ON
当接收缓冲区超过四分之三满时RTS为OFF
RTS_CONTROL_TOGGLE时,
当接收缓冲区仍有剩余字节时RTS为ON ,否则缺省为OFF*/
public int fAbortOnError; // TRUE时,有错误发生时中止读和写操作 abort on error
public int fDummy2; // 未使用 reserved
public uint flags;
public ushort wReserved; // 未使用,必须为0 not currently used
public ushort XonLim; // 指定在XON字符发送这前接收缓冲区中可允许的最小字节数 transmit XON threshold
public ushort XoffLim; // 指定在XOFF字符发送这前接收缓冲区中可允许的最小字节数 transmit XOFF threshold
public byte ByteSize; // 指定端口当前使用的数据位 number of bits/byte, 4-8
public byte Parity; // 指定端口当前使用的奇偶校验方法,可能为:EVENPARITY,MARKPARITY,NOPARITY,ODDPARITY 0-4=no,odd,even,mark,space
public byte StopBits; // 指定端口当前使用的停止位数,可能为:ONESTOPBIT,ONE5STOPBITS,TWOSTOPBITS 0,1,2 = 1, 1.5, 2
public char XonChar; // 指定用于发送和接收字符XON的值 Tx and Rx XON character
public char XoffChar; // 指定用于发送和接收字符XOFF值 Tx and Rx XOFF character
public char ErrorChar; // 本字符用来代替接收到的奇偶校验发生错误时的值 error replacement character
public char EofChar; // 当没有使用二进制模式时,本字符可用来指示数据的结束 end of input character
public char EvtChar; // 当接收到此字符时,会产生一个事件 received event character
public ushort wReserved1; // 未使用 reserved; do not use
}
3. 设置I/O缓冲区的大小和超时。Windows用I/O缓冲区来暂存串口输入和输出的数据。如果通信的速率较高,则应该设置较大的缓冲区。调用SetupComm函数可以设置串行口的输入和输出缓冲区的大小。
BOOL SetupComm(
HANDLE hFile, // 通信设备的句柄
DWORD dwInQueue, // 输入缓冲区的大小(字节数)
DWORD dwOutQueue // 输出缓冲区的大小(字节数)
);
4. 读:ReadFile
5. 写: WriteFile
注:ReadFile和WriteFile函数是同步还是异步由CreateFile函数决定,如果在调用CreateFile创建句柄时指定了 FILE_FLAG_OVERLAPPED标志,那么调用ReadFile和WriteFile对该句柄进行的操作就应该是重叠的;如果未指定重叠标志, 则读写操作应该是同步的。ReadFile和WriteFile函数的同步或者异步应该和CreateFile函数相一致。
6. 关闭串口:BOOL
CloseHandle(
HANDLE hObject; //handle to object to close
);
7. 清空缓冲区:PurgeComm
PurgeComm(hCom, PURGE_TXABORT|
PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);
Value | Description |
PURGE_TXABORT | Terminates outstanding overlapped write operations and returns immediately, even if the write operations have not been completed. |
PURGE_RXABORT | Terminates outstanding overlapped read operations and returns immediately, even if the read operations have not been completed. |
PURGE_TXCLEAR | Clears the output buffer (if the device driver has one). |
PURGE_RXCLEAR | Clears the input buffer (if the device driver has one). |
8. 清除串口错误
在使用ReadFile 函数进行读操作前,应先使用ClearCommError函数清除错误。ClearCommError函数的原型如下:
BOOL ClearCommError(
HANDLE hFile, // 串口句柄
LPDWORD lpErrors, // 指向接收错误码的变量
LPCOMSTAT lpStat // 指向通讯状态缓冲区
);
而在网上也有对这一段代码的C#版的封装,搜下CommPort就知道了。
可不知为什么,有时串口断开后再连接进去,有时数据老写不进去,用串口监控工具(Serial Port Monitor)看才知道。这是一个很好用的工具,把串口操作的每一步都打印出来了。最近因为进度的问题没法深究下去,用上了非常好用的MSCom了,哈哈
http://www.yes-tele.com/mscomm.html
在这里提供了这个控件很好的详解。
http://hi.baidu.com/wolfwhite/blog/item/5ec742164bb4604820a4e9b1.html
http://topic.csdn.net/t/20061227/22/5260726.html
这里也是。。。。关于这个就不想多说了,太经典的一个了。。。。
最后记下CRC校验,本来是自已专业课教的,可毕业两年就给忘光了,说不清楚了,大概就是这样
发送方:发出的传输字段为: 1 0 1 1 0 0 1 1 0 10
信息字段 校验字段
接收方:使用相同的生成码进行校验:接收到的字段/生成码(二进制除法)
如果能够除尽,则正确,
public class CRC
{
// Fields
private static byte[] ArrayCRCHigh = new byte[] {
0, 0xc1, 0x81, 0x40, 1, 0xc0, 0x80, 0x41, 1, 0xc0, 0x80, 0x41, 0, 0xc1, 0x81, 0x40,
1, 0xc0, 0x80, 0x41, 0, 0xc1, 0x81, 0x40, 0, 0xc1, 0x81, 0x40, 1, 0xc0, 0x80, 0x41,
1, 0xc0, 0x80, 0x41, 0, 0xc1, 0x81, 0x40, 0, 0xc1, 0x81, 0x40, 1, 0xc0, 0x80, 0x41,
0, 0xc1, 0x81, 0x40, 1, 0xc0, 0x80, 0x41, 1, 0xc0, 0x80, 0x41, 0, 0xc1, 0x81, 0x40,
1, 0xc0, 0x80, 0x41, 0, 0xc1, 0x81, 0x40, 0, 0xc1, 0x81, 0x40, 1, 0xc0, 0x80, 0x41,
0, 0xc1, 0x81, 0x40, 1, 0xc0, 0x80, 0x41, 1, 0xc0, 0x80, 0x41, 0, 0xc1, 0x81, 0x40,
0, 0xc1, 0x81, 0x40, 1, 0xc0, 0x80, 0x41, 1, 0xc0, 0x80, 0x41, 0, 0xc1, 0x81, 0x40,
1, 0xc0, 0x80, 0x41, 0, 0xc1, 0x81, 0x40, 0, 0xc1, 0x81, 0x40, 1, 0xc0, 0x80, 0x41,
1, 0xc0, 0x80, 0x41, 0, 0xc1, 0x81, 0x40, 0, 0xc1, 0x81, 0x40, 1, 0xc0, 0x80, 0x41,
0, 0xc1, 0x81, 0x40, 1, 0xc0, 0x80, 0x41, 1, 0xc0, 0x80, 0x41, 0, 0xc1, 0x81, 0x40,
0, 0xc1, 0x81, 0x40, 1, 0xc0, 0x80, 0x41, 1, 0xc0, 0x80, 0x41, 0, 0xc1, 0x81, 0x40,
1, 0xc0, 0x80, 0x41, 0, 0xc1, 0x81, 0x40, 0, 0xc1, 0x81, 0x40, 1, 0xc0, 0x80, 0x41,
0, 0xc1, 0x81, 0x40, 1, 0xc0, 0x80, 0x41, 1, 0xc0, 0x80, 0x41, 0, 0xc1, 0x81, 0x40,
1, 0xc0, 0x80, 0x41, 0, 0xc1, 0x81, 0x40, 0, 0xc1, 0x81, 0x40, 1, 0xc0, 0x80, 0x41,
1, 0xc0, 0x80, 0x41, 0, 0xc1, 0x81, 0x40, 0, 0xc1, 0x81, 0x40, 1, 0xc0, 0x80, 0x41,
0, 0xc1, 0x81, 0x40, 1, 0xc0, 0x80, 0x41, 1, 0xc0, 0x80, 0x41, 0, 0xc1, 0x81, 0x40
};
private static byte[] checkCRCLow = new byte[] {
0, 0xc0, 0xc1, 1, 0xc3, 3, 2, 0xc2, 0xc6, 6, 7, 0xc7, 5, 0xc5, 0xc4, 4,
0xcc, 12, 13, 0xcd, 15, 0xcf, 0xce, 14, 10, 0xca, 0xcb, 11, 0xc9, 9, 8, 200,
0xd8, 0x18, 0x19, 0xd9, 0x1b, 0xdb, 0xda, 0x1a, 30, 0xde, 0xdf, 0x1f, 0xdd, 0x1d, 0x1c, 220,
20, 0xd4, 0xd5, 0x15, 0xd7, 0x17, 0x16, 0xd6, 210, 0x12, 0x13, 0xd3, 0x11, 0xd1, 0xd0, 0x10,
240, 0x30, 0x31, 0xf1, 0x33, 0xf3, 0xf2, 50, 0x36, 0xf6, 0xf7, 0x37, 0xf5, 0x35, 0x34, 0xf4,
60, 0xfc, 0xfd, 0x3d, 0xff, 0x3f, 0x3e, 0xfe, 250, 0x3a, 0x3b, 0xfb, 0x39, 0xf9, 0xf8, 0x38,
40, 0xe8, 0xe9, 0x29, 0xeb, 0x2b, 0x2a, 0xea, 0xee, 0x2e, 0x2f, 0xef, 0x2d, 0xed, 0xec, 0x2c,
0xe4, 0x24, 0x25, 0xe5, 0x27, 0xe7, 230, 0x26, 0x22, 0xe2, 0xe3, 0x23, 0xe1, 0x21, 0x20, 0xe0,
160, 0x60, 0x61, 0xa1, 0x63, 0xa3, 0xa2, 0x62, 0x66, 0xa6, 0xa7, 0x67, 0xa5, 0x65, 100, 0xa4,
0x6c, 0xac, 0xad, 0x6d, 0xaf, 0x6f, 110, 0xae, 170, 0x6a, 0x6b, 0xab, 0x69, 0xa9, 0xa8, 0x68,
120, 0xb8, 0xb9, 0x79, 0xbb, 0x7b, 0x7a, 0xba, 190, 0x7e, 0x7f, 0xbf, 0x7d, 0xbd, 0xbc, 0x7c,
180, 0x74, 0x75, 0xb5, 0x77, 0xb7, 0xb6, 0x76, 0x72, 0xb2, 0xb3, 0x73, 0xb1, 0x71, 0x70, 0xb0,
80, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 150, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54,
0x9c, 0x5c, 0x5d, 0x9d, 0x5f, 0x9f, 0x9e, 0x5e, 90, 0x9a, 0x9b, 0x5b, 0x99, 0x59, 0x58, 0x98,
0x88, 0x48, 0x49, 0x89, 0x4b, 0x8b, 0x8a, 0x4a, 0x4e, 0x8e, 0x8f, 0x4f, 0x8d, 0x4d, 0x4c, 140,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 70, 0x86, 130, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x40
};
// Methods
public static short CRC16(byte[] data, int arrayLength)
{
byte num = 0xff;
byte num2 = 0xff;
int num4 = 0;
while (arrayLength-- > 0)
{
byte index = (byte)(num ^ data[num4++]);
num = (byte)(num2 ^ ArrayCRCHigh[index]);
num2 = checkCRCLow[index];
}
return (short)((num << 8) | num2);
}
}
回读的时候判断下
if (b.Length == 8)
{
short h = CRC.CRC16(b, 6);
byte hight = (byte)(h >> 8);//取高字节
byte low = (byte)(h & 0xFF);//取低字节
//校验
if (b[6] == hight &&b[7] == low)
{}
else
{
throw new System.IO.IOException("机器未打开");
}
}
感谢这个COM,让我第一阶段如期完成了,真开心。不过留下问题了,以后要多加注意,下次一定调通。