周末在家进行实验,打算写个VS2008串口发送数据的小程序。结果万万没想到麻烦一大堆。
目标:用VS2008发送串口数据。
为了实现这个目标,需要有能发送和接受串口数据的设备,我有一只USB转串口线,又找到了一个USB转串口的小板子。
下面的这个小卡在之前的《使用 Arduino 打造一个硬件的Watchdog》出场过。
问题是这两个哥们连不上啊~当然对于每个都分别测试过 loop back.唯独连在一起的时候工作不正常。
串口公头定义如下
4 数据终端准备好(DTR)
5 信号地线(SG)
6 数据准备好(DSR)
7 请求发送(RTS)
8 清除发送(CTS)
9 振铃指示(RI)
插上之后发现两个都是 ch341A:
他们之间无法正常通信(当然 TXD TXD GND 已经连接好),乱码,严重的乱码,发送 01 接收到的是 7F,尝试使用Putty和WCH自己的工具都是不行的,波特率之类的设定经过一遍一遍的检查,确定都已经设置为相同了。真是万万没想到啊!
本打算放弃了,忽然想起来前一段买了一个 USB转IIC的小卡,可以当作串口用,也是CH340系列的芯片。再找出我珍藏多年的25米串口线,连在一起。
测试他和我那个USB转串口线,通过Putty可以通讯,一切正常。
终于开始调试程序,发现程序运行时出现错误,GetLastError = 2 意思是“ERROR_FILE_NOT_FOUND 2 (0x2) The system cannot find the file specified.” 【参考2】,但是实验 COM6是可以的。搜索资料,找到【参考3】,原来是我用的下面这样的形式,只给COM1-9来用.......
hComm = CreateFile( "com6", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
要想打开COM10及其以后的必须使用\\\\.\\COM27这样的形式。
CreateFile(
"\\\\.\\COM27", // address of name of the communications device
fdwAccess, // access (read-write) mode
0, // share mode
NULL, // address of security descriptor
OPEN_EXISTING, // how to create
0, // file attributes
NULL // handle of file with attributes to copy
);
万万没想到啊,还有这样的限制。继续下面的调试。
继续测试发现我的程序这边发送,那边可以收到了,但是只能收到前面几个字节。我的程序端显示发送的字符串数量是正确的,但是接收到的字数却是少了一截。
实验发现如果升高波特率比如使用 9600反倒没问题。于是怀疑这个问题和超时相关,查看COMMTIMEOUTS相关的资料。
// COMMTIMEOUTS对象
COMMTIMEOUTS comTimeOut;
// 接收时,两字符间最大的时延
comTimeOut.ReadIntervalTimeout = 0;
// 读取每字节的超时
comTimeOut.ReadTotalTimeoutMultiplier = 0;
// 读串口数据的固定超时
// 总超时= ReadTotalTimeoutMultiplier * 字节数+ ReadTotalTimeoutConstant
comTimeOut.ReadTotalTimeoutConstant = 0;
// 写每字节的超时
comTimeOut.WriteTotalTimeoutMultiplier = 0;
// 写串口数据的固定超时
comTimeOut.WriteTotalTimeoutConstant = 0;
// 将超时参数写入设备控制
SetCommTimeouts(hCom,&comTimeOut);
上面写为0表示会无限等待下去。但是仍然不好用。
调试又发现如果在WriteFile的时候断点调试,是可以收到的。但是我在他前面加入一个 sheep之后仍然现象相同。
最终,万万没想到,真相只有一个:我在最后设定了关闭串口 CloseHandle(hCom) ,但是如果这个时候没有完成发送就被截断(按道理,应该是同步操作,但是不知道为什么,这里不会等待完全发送)。对于我程序来说解决方法就是把关闭串口放在 pause 的后面………
// ComPortSend.cpp : main project file. //http://forums.codeguru.com/showthread.php?442634-Serial-port-program-in-c-language #include "stdafx.h" #include <windows.h> #include <stdio.h> #include <conio.h> int main(int argc, char *argv[]) { DCB dcb; HANDLE hCom; BOOL fSuccess; TCHAR SPort[]=L"\\\\.\\COM27"; char txchar[]="This is a test from www.lab-z.com!"; DWORD iBytesWritten; hCom = CreateFile(SPort, GENERIC_WRITE, 0, // must be opened with exclusive-access NULL, // no security attributes OPEN_EXISTING, // must use OPEN_EXISTING 0, // not overlapped I/O NULL // hTemplate must be NULL for comm devices ); if (hCom == INVALID_HANDLE_VALUE) { // Handle the error. printf ("CreateFile failed with error %d.\n", GetLastError()); return (1); } SetupComm(hCom, 32, 32);//输入输出缓冲区大小都是1024个字节 // Build on the current configuration, and skip setting the size // of the input and output buffers with SetupComm. fSuccess = GetCommState(hCom, &dcb); if (!fSuccess) { // Handle the error. printf ("GetCommState failed with error %d.\n", GetLastError()); return (2); } // Fill in DCB: 300 bps, 8 data bits, no parity, and 1 stop bit. dcb.BaudRate = CBR_300; // set the baud rate dcb.ByteSize = 8; // data size, xmit, and rcv dcb.Parity = NOPARITY; // no parity bit dcb.StopBits = ONESTOPBIT; // one stop bit fSuccess = SetCommState(hCom, &dcb); if (!fSuccess) { // Handle the error. printf ("SetCommState failed with error %d.\n", GetLastError()); return (3); } WriteFile(hCom, &txchar, strlen(txchar), &iBytesWritten,NULL); printf ("Serial port %s successfully reconfigured. \n", "COM27"); system("PAUSE"); CloseHandle(hCom); return (0); }
终于我的Putty在另外一端能够接收到完整的数据了。
代码下载 ComPortSend
后记:从我目前的认知来看,USB转串口质量最好的是 FT232 ,然后是 PL2xx(严重的问题是它没有 Win8的驱动) ,CH34x系列只能排在最后。如果成本压力不是很大,有机会应该优先考虑Ft232。
参考:
1. http://www.elecfans.com/yuanqijian/jiekou/200801247510.html
2. http://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx System Error Codes (0-499)
3. http://bbs.csdn.net/topics/80130516 用CreateFile打开串口设备,出现问题?
4. http://www.lab-z.com/%E4%BD%BF%E7%94%A8-arduino-%E6%89%93%E9%80%A0%E4%B8%80%E4%B8%AA%E7%A1%AC%E4%BB%B6%E7%9A%84watchdog/ 使用 Arduino 打造一个硬件的Watchdog