Windows串口参数必须通过SetCommState写入DCB结构体才生效,CreateFile后默认为9600/N/8/1;需先GetCommState读取再修改字段并SetCommState回写,否则隐式字段错误会导致握手失败或通信异常。

Windows下用SetCommState配置DCB结构体
Windows平台必须通过SetCommState函数写入DCB结构体来生效串口参数,直接调用CreateFile打开串口后参数仍是默认值(通常是9600/N/8/1),不手动设置就发不出正确数据。
关键点在于:先用GetCommState读取当前DCB,再改其中字段,最后SetCommState回写。漏掉GetCommState直接memset初始化DCB会导致其他隐式字段(如fBinary、fDtrControl)为0,串口可能打不开或握手失败。
-
dcb.BaudRate = CBR_115200(必须用CBR_*宏,不能填数字115200) -
dcb.ByteSize = 8(数据位,5~8) -
dcb.Parity = NOPARITY(可选ODDPARITY/EVENPARITY) -
dcb.StopBits = ONESTOPBIT(或ONESTOPBIT、TWOSTOPBITS) - 别忘了
dcb.fAbortOnError = FALSE,否则校验错会直接中断通信
Linux下用termios结构体配stty等效参数
Linux没有DCB概念,得用termios结构体配合tcsetattr。和Windows不同,这里波特率不是宏定义,而是用cfsetispeed/cfsetospeed单独设输入输出速率,且支持非标准值(比如B123456需用cfsetspeed或ioctl(TIOCSSERIAL))。
常见坑是忘记清空c_iflag和c_oflag里的默认标志——比如ICRNL会把收到的\r转成\n,导致协议解析错乱;INPCK开启后若设备没发校验位,整帧会被丢弃。
立即学习“C++免费学习笔记(深入)”;
- 数据位:
c_cflag &= ~CSIZE; c_cflag |= CS8(CS5~CS8) - 校验位:
c_cflag &= ~PARENB关校验,或| PARENB | PARODD开奇校验 - 停止位:
c_cflag &= ~CSTOPB(1位),| CSTOPB(2位) - 必须设
c_cflag |= CREAD | CLOCAL,否则无法读写 - 记得
tcflush(fd, TCIOFLUSH)清缓存,避免旧参数残留
CSerialPort类里怎么设参数才不被忽略
很多开发者调了CSerialPort::SetPortParam却没生效,根本原因是:这个函数只保存参数到类成员变量,并不自动调用SetCommState——它只在Open内部触发一次实际配置。所以如果你先Open再SetPortParam,参数根本不会写入硬件。
正确顺序只有两种:SetPortParam → Open,或者Open → Close → SetPortParam → Open。后者代价大但安全,前者更常用。
-
SetPortParam(115200, 8, 'N', 1, FALSE):参数顺序固定,第3个是校验类型字符('N'/'O'/'E'/'M'/'S') - 第5个
FALSE表示不启用XON/XOFF流控,设TRUE但设备不支持会卡死 - 某些老旧版本
CSerialPort不检查StopBits合法性,传3进去也不会报错,但硬件拒绝配置
参数不匹配时最典型的错误现象
参数错一个,通常不是“收不到”,而是“收得乱”:字节粘连、0x00高频出现、固定偏移错位。这是因为起始位/停止位对不齐,采样点漂移,接收端把噪声当数据位解码。
特别注意校验位:如果设备发偶校验,而你设了NOPARITY,Windows下ReadFile可能返回ERROR_INVALID_PARAMETER或直接丢帧;Linux下read()返回正常但stat()显示frame计数上涨,说明物理层已检测到帧错误。
- 波特率差5%以内可能勉强通信,但高负载时丢包率陡增
- 数据位设7但设备发8位,每字节高位恒为0,容易误判为协议头
- 停止位设2但设备只发1位,接收端等不到电平跳变,后续所有字节全偏移
- 用逻辑分析仪看TX线,能直观确认起始位宽度、数据位采样点、停止位电平持续时间
stty -F /dev/ttyUSB0或mode COM3确认系统级当前值,再比对代码所设——多数问题出在“以为设了,其实没写进硬件”。


















