转到上位机做开发 VC还是有些东西不太一样 绕了些圈子 在此做一下记录 对一些基本概念做一些说明
项目中要求有多个串口的转发通信 不定时、随机长度帧的帧处理转发等操作
1.初始化及其中涉及到的问题
HCom1ConnWithLcd = CreateFile("COM1",//COM1 GENERIC_READ|GENERIC_WRITE, //允许读和写 0, //独占方式 NULL, OPEN_EXISTING, //打开而不是创建 //FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, //重叠方式 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, NULL); if(HCom1ConnWithLcd == INVALID_HANDLE_VALUE) return false; SetupComm(HCom1ConnWithLcd,SYS_COM_BUF_LEN,SYS_COM_BUF_LEN);//输入输出缓冲区大小设置 PurgeComm(HCom1ConnWithLcd,PURGE_TXCLEAR|PURGE_RXCLEAR);//清除串口缓冲区 COMMTIMEOUTS TimeOuts; //设定读超时----------------------------------------------------------------------------- TimeOuts.ReadIntervalTimeout=0; TimeOuts.ReadTotalTimeoutMultiplier=0; TimeOuts.ReadTotalTimeoutConstant=0; //在读一次输入缓冲区的内容后读操作就立即返回//而不管是否读入了要求的字符。 //设定写超时 TimeOuts.WriteTotalTimeoutMultiplier=100; TimeOuts.WriteTotalTimeoutConstant=500; SetCommTimeouts(HCom1ConnWithLcd,&TimeOuts); //设置超时--------------------------------------------------------- DCB dcb; GetCommState(HCom1ConnWithLcd,&dcb); dcb.BaudRate=115200; //波特率为115200bps dcb.ByteSize=8; //每个字节有8位 dcb.Parity=NOPARITY; //无奇偶校验位 dcb.StopBits=ONESTOPBIT; //1个停止位 SetCommState(HCom1ConnWithLcd,&dcb); return TRUE;
上述代码很常见,但标红的两个点在后续调试的时候困扰了好一会
问题1.关于串口的重叠模式和同步模式
第一次写的时候将串口都声明成了同步模式,然后
SetCommMask(HCom2 , EV_RXCHAR );
每次接收一字节,理论上这样无论帧的长度、发送间隔 怎样变化对上位机来说都没有问题。
但调试时发现无法发送数据,DEBUG跟踪发现,到writefile()函数直接阻塞了,原来是设置成同步模式后调用
WaitCommEvent(HCom2, &dwCommEvent, &m_osReadEx);
因为上面已经设置了 EV_RXCHAR,接收进程WaitCommEvent()函数会一直阻塞句柄,导致写函数无法取得句柄阻塞跳出
于是又重新改写结构将串口声明成异步的 即声明添加 FILE_FLAG_OVERLAPPED, //重叠方式
重叠方式与同步方式的区别就是重叠方式执行发送或写入操作后函数就退出并不阻塞,而同步方式阻塞函数等待IO操作完成
反应在代码上异步会以如下一种形式体现:
bReadStu = Readfile(hanle,lp,读取缓存指针,读取字节数,&读取字节数,OVERLAPPED) if(!bReadStu ){ if(bReadStu ==IO_PENDING) WaitforSingalObject(hEvent , INFINITE ); }
其中hEvent为 OVERLAPPED结构体中的变量由Readfile函数赋值代表IO操作完成
读取字节数,&读取字节数 输入数据的同时又输入引用时因为函数要在操作后清零"读取字节数"变量
这里就是如果不用WaitforSingalObject阻塞,代码继续执行,这就是异步
注意Readfile()函数并不一定要IO_PENDING 也可以直接读取成功返回一个非零值的读取字节数
问题2:为什么接收数据不完整
声明为异步模式后读写操作都正常了,但是发现接收的数据总是会丢字节。
因为正确接收后基本会有一个
PurgeComm(HCom2, PURGE_RXABORT|PURGE_RXCLEAR);
操作,来清除缓存,如果一字节一接收一处理,在接收到第一个字节处理结束后PurgeComm,就有可能清除在接收字节过程中接收到的其他字节,如果不清那缓存总有用完的时候,"接收队列溢出"错误就会出现。清就伴随了永远可能丢失下一个字节的问题。
修改了接收模式,近可能让缓存接受到完整的一帧后利用帧的间隔来处理,数据完整性大大提高。但这个问题其实没有解决,在帧数据长度,发送时间随机的情况下无法判断下一帧什么时间到来,什么是一帧的结束。
有人会说帧是有结构的当然可以判断帧的开始结束,问题是缓存不会判断这个,他们只是接收、发送。解析的事情还是在程序里自己做的。更不要说还会有非正常的数据错误,这会引出要不要使用校验的问题。原本的下位机是没有起用校验的,也没什么问题,但连续长帧在经过几个串口转发后就会出现一定错误率。本例中下位机程序也是我写的,所以可以更容易两边更改来互相迁就。
让帧之间尽量有一个时间间隔,1ms,2ms的都可以。这样给出时间让帧解析以减少丢数据的发生。
问题3:为什么Readfile()不阻塞
bReadStu = Readfile(hanle,lp,读取缓存指针,读取字节数,&读取字节数,OVERLAPPED)
if(!bReadStu )
{ if(bReadStu ==IO_PENDING)
WaitforSingalObject(hEvent , INFINITE );
}
运行到图示位置无论有没有数据都不阻塞
这是因为超时没有设置好。
TimeOuts.ReadIntervalTimeout=MAXWORDS;
TimeOuts.ReadTotalTimeoutMultiplier=0;
imeOuts.ReadTotalTimeoutConstant=0;
//在读一次输入缓冲区的内容后读操作就立即返回//而不管是否读入了要求的字符。
ReadIntervalTimeout参数是读入一个字节后与下一个字节到来的间隙,如果过了这个间隙就要超时。超时了函数是要返回的,返回就是这个信号量不在pending.
修改ReadIntervalTimeout=0;取消这个间隔判断。
这里有个重要的概念就是各种超时发生时,PENDING操作都会返回
问题4:各种崩溃情况
基本都是0xcxxxxxxx在0x0xxxxxxxx出发生了读写冲突。好好检查数组越界的问题,一般发生错误弹窗的时候会中断下来,就是发生越界的点。看一下调用堆栈,函数局部变量的数据,可以很容发现问题。不行就排除法加大缓存容量。