如何修复 rs232 串口超时?
Posted
技术标签:
【中文标题】如何修复 rs232 串口超时?【英文标题】:How to fix timeout on rs232 serial port? 【发布时间】:2014-04-17 15:34:30 【问题描述】:从串口读取时出现问题。
问题在于,对于从rs232
端口读取的最后 2 个字节(CRC 字节),读取会一直等到timeval
中设置的超时结束,然后返回。在rs485
上,使用与读取相同的方法,读取返回正常。我在调试时看到的是 rs485
在 2 个 CRC 字节之后有一个额外的字节,其值为 FF
。我找不到 2 之间的另一个区别。
以下是代码的相关部分:
设置端口
bool Serial::setup()
if(!openPort())
return false;
tcgetattr(fdPort, &options);
cfsetispeed(&options, B115200);
cfsetospeed(&options, B115200);
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
options.c_cc[VMIN] = 0;
options.c_cc[VTIME] = 1;
options.c_oflag &= ~(OCRNL | ONLCR | ONLRET |
ONOCR | OFILL | OLCUC | OPOST);
options.c_iflag &= ~(IGNBRK | BRKINT | ICRNL | INLCR |
PARMRK | INPCK | ISTRIP | IXON) ;
options.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG);
if(tcsetattr(this->fdPort, TCSANOW, &options))
return false;
return true;
打开端口
bool Serial::openPort()
this->fdPort = open(this->port, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);
if(fdPort == -1)
return false;
else
//fcntl(fdPort, F_SETFL, 0);
//fcntl(fdPort, F_SETFL, FNDELAY);
int status = 0;
status |= TIOCM_RTS;
ioctl(this->fdPort, TIOCMSET, &status);
return true;
分块解释和读取数据的等待帧。
bool GlobalProtocol::WaitFrame(uint32_t timeOut)
int32_t bytesRec = 0;
int32_t bytesToRec = 1;
int recPhase = GC_PHASE_START;
uint8_t *pData = m_RxBuff;
bool commSuccess = true;
m_LastError = boards::GCL_ERR_OK;
while (readData(pData, bytesToRec, &bytesRec, timeOut))
if (bytesRec)
switch (recPhase)
case GC_PHASE_START:
if (*pData != GC_START_FRAME_BYTE)
continue;
recPhase++;
pData++;
break;
case GC_PHASE_DEST:
if (*pData != m_MasterAddr)
commSuccess = false;
break;
recPhase++;
pData++;
break;
case GC_PHASE_SRC:
recPhase++;
pData++;
break;
case GC_PHASE_LEN_LO:
if (*pData < 2 || *pData > GC_MAX_COMM_DATA_LEN)
commSuccess = false;
break;
recPhase++;
pData++;
break;
case GC_PHASE_LEN_HI:
if (*pData != 0)
commSuccess = false;
break;
recPhase++;
pData++;
bytesToRec = m_RxBuff[GC_PHASE_LEN_LO];
break;
case GC_PHASE_DATA:
if (bytesRec != bytesToRec)
commSuccess = false;
break;
recPhase++;
pData += bytesRec;
bytesToRec = 2;
break;
case GC_PHASE_CRC_LO:
if (bytesRec != bytesToRec)
commSuccess = false;
break;
if (CheckCRC(m_RxBuff, m_RxBuff[GC_PHASE_LEN_LO] + GC_PHASE_DATA + sizeof(uint16_t)))
m_RecAddr = m_RxBuff[GC_PHASE_SRC];
return true;
commSuccess = false;
break;
if (!commSuccess) break;
else break;
m_LastError = boards::GCL_ERR_ANSWERNOTREC;
return false;
以及阅读的地点。
bool Serial::readData(uint8_t *data, uint32_t length, int32_t *receivedDataBytes, int32_t timeoutVal)
int32_t tempReceivedDataBytes = -1;
fd_set readFd;
FD_ZERO(&readFd);
FD_SET(fdPort, &readFd);
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = timeoutVal * 1000;
int test = 0;
uint32_t bytesAvail = 0;
QTime timer;
timer.start();
if(fcntl(fdPort, F_GETFD) != -1)
while(((test = select(this->fdPort + 1, &readFd, NULL, NULL, &timeout)) == 1) && (tempReceivedDataBytes == -1))
ioctl(fdPort, FIONREAD, &bytesAvail);
if(FD_ISSET(fdPort, &readFd) && bytesAvail >= length)
tempReceivedDataBytes = read(fdPort, data, length);
if(timer.elapsed() > (timeoutVal + 5)) //fail-safe
logger[debug_log] << "TIMEOUT" << endl;
break;
if(test == -1)
logger[debug_log]<< strerror(errno) << endl;
if(tempReceivedDataBytes < 0)
return false;
if(tempReceivedDataBytes >= 0)
*receivedDataBytes = tempReceivedDataBytes;
return true;
return false;
如果我将超时设置为 100 毫秒,那么它会等待 100 毫秒来读取 2 个字节,如果我将其设置为 10 毫秒,那么它会等待 10 毫秒来读取 2 个字节。
我尝试更改端口设置,但没有成功。
我已经尝试解决这个问题好几天了,但没有成功。
编辑:
我应该补充一点,ioctl(fdPort, FIONREAD, &bytesAvail);
将 bytesAvail
设置为 2,但 read
等到超时才能读取它们。
编辑 2:
这是一个超时设置为 25 秒的日志,以便您了解问题所在:
04/17/2014 15:51:50:584,Main board start:
04/17/2014 15:51:50:586,1 Received 1 Needed to receive 1
04/17/2014 15:51:50:586,1 Received 1 Needed to receive 1
04/17/2014 15:51:50:586,1 Received 1 Needed to receive 1
04/17/2014 15:51:50:587,1 Received 1 Needed to receive 1
04/17/2014 15:51:50:587,1 Received 1 Needed to receive 1
04/17/2014 15:51:50:597,1 Received 110 Needed to receive 110
04/17/2014 15:51:50:597,1 Received 2 Needed to receive 2
04/17/2014 15:51:50:634,PID board start:
04/17/2014 15:51:50:635,1 Received 1 Needed to receive 1
04/17/2014 15:51:50:635,1 Received 1 Needed to receive 1
04/17/2014 15:51:50:635,1 Received 1 Needed to receive 1
04/17/2014 15:51:50:635,1 Received 1 Needed to receive 1
04/17/2014 15:51:50:635,1 Received 1 Needed to receive 1
04/17/2014 15:51:50:641,1 Received 70 Needed to receive 70
04/17/2014 15:52:15:647,0 Received 2 Needed to receive 2
04/17/2014 15:52:15:647,Set Leds board start:
04/17/2014 15:52:15:649,1 Received 1 Needed to receive 1
04/17/2014 15:52:15:649,1 Received 1 Needed to receive 1
04/17/2014 15:52:15:652,1 Received 1 Needed to receive 1
04/17/2014 15:52:15:652,1 Received 1 Needed to receive 1
04/17/2014 15:52:15:652,1 Received 1 Needed to receive 1
04/17/2014 15:52:15:652,1 Received 2 Needed to receive 2
04/17/2014 15:52:15:652,1 Received 2 Needed to receive 2
04/17/2014 15:52:15:652,Get state board start:
04/17/2014 15:52:15:654,1 Received 1 Needed to receive 1
04/17/2014 15:52:15:654,1 Received 1 Needed to receive 1
04/17/2014 15:52:15:654,1 Received 1 Needed to receive 1
04/17/2014 15:52:15:654,1 Received 1 Needed to receive 1
04/17/2014 15:52:15:655,1 Received 1 Needed to receive 1
04/17/2014 15:52:15:657,1 Received 30 Needed to receive 30
04/17/2014 15:52:15:657,1 Received 2 Needed to receive 2
有问题的是 PID 板(在 rs232 上),其他都在相同的 rs485 上。
这里有一个超时设置为 30 毫秒左右(我不记得确切的值):
04/17/2014 15:08:08:045,Main board start:
04/17/2014 15:08:08:046,1 Received 1 Needed to receive 1
04/17/2014 15:08:08:046,1 Received 1 Needed to receive 1
04/17/2014 15:08:08:046,1 Received 1 Needed to receive 1
04/17/2014 15:08:08:047,1 Received 1 Needed to receive 1
04/17/2014 15:08:08:047,1 Received 1 Needed to receive 1
04/17/2014 15:08:08:056,1 Received 110 Needed to receive 110
04/17/2014 15:08:08:056,1 Received 2 Needed to receive 2
04/17/2014 15:08:08:078,PID board start:
04/17/2014 15:08:08:079,1 Received 1 Needed to receive 1
04/17/2014 15:08:08:079,1 Received 1 Needed to receive 1
04/17/2014 15:08:08:079,1 Received 1 Needed to receive 1
04/17/2014 15:08:08:079,1 Received 1 Needed to receive 1
04/17/2014 15:08:08:079,1 Received 1 Needed to receive 1
04/17/2014 15:08:08:094,1 Received 70 Needed to receive 70
04/17/2014 15:08:08:120,0 Received 2 Needed to receive 2
04/17/2014 15:08:08:120,Set Leds board start:
04/17/2014 15:08:08:122,1 Received 1 Needed to receive 1
04/17/2014 15:08:08:122,1 Received 1 Needed to receive 1
04/17/2014 15:08:08:122,1 Received 1 Needed to receive 1
04/17/2014 15:08:08:122,1 Received 1 Needed to receive 1
04/17/2014 15:08:08:122,1 Received 1 Needed to receive 1
04/17/2014 15:08:08:122,1 Received 2 Needed to receive 2
04/17/2014 15:08:08:122,1 Received 2 Needed to receive 2
04/17/2014 15:08:08:123,Get state board start:
04/17/2014 15:08:08:124,1 Received 1 Needed to receive 1
04/17/2014 15:08:08:124,1 Received 1 Needed to receive 1
04/17/2014 15:08:08:124,1 Received 1 Needed to receive 1
04/17/2014 15:08:08:125,1 Received 1 Needed to receive 1
04/17/2014 15:08:08:125,1 Received 1 Needed to receive 1
04/17/2014 15:08:08:128,1 Received 30 Needed to receive 30
04/17/2014 15:08:08:128,1 Received 2 Needed to receive 2
编辑:我仍然不知道出了什么问题。如果我删除 rs485 上的额外字节,它是相同的。这是另一个日志:
06/24/2014 12:57:01:923,Set Leds board start:
06/24/2014 12:57:06:701,Select value 1 Received: 1 Needed: 1 Bytes available: 9
06/24/2014 12:57:06:701,Select value 1 Received: 1 Needed: 1 Bytes available: 8
06/24/2014 12:57:06:702,Select value 1 Received: 1 Needed: 1 Bytes available: 7
06/24/2014 12:57:06:702,Select value 1 Received: 1 Needed: 1 Bytes available: 6
06/24/2014 12:57:06:702,Select value 1 Received: 1 Needed: 1 Bytes available: 5
06/24/2014 12:57:06:702,Select value 1 Received: 2 Needed: 2 Bytes available: 4
06/24/2014 12:57:06:752,Select value 0 Received: 2 Needed: 2 Bytes available: 2
06/24/2014 12:57:06:752,Get state board start:
06/24/2014 12:57:06:754,Select value 1 Received: 1 Needed: 1 Bytes available: 4
06/24/2014 12:57:06:754,Select value 1 Received: 1 Needed: 1 Bytes available: 3
06/24/2014 12:57:06:754,Select value 1 Received: 1 Needed: 1 Bytes available: 2
06/24/2014 12:57:06:754,Select value 1 Received: 1 Needed: 1 Bytes available: 1
06/24/2014 12:57:06:755,Select value 1 Received: 1 Needed: 1 Bytes available: 10
06/24/2014 12:57:06:758,Select value 1 Received: 30 Needed: 30 Bytes available: 30
06/24/2014 12:57:06:808,Select value 0 Received: 2 Needed: 2 Bytes available: 2
06/24/2014 12:57:06:886,Main board start:
06/24/2014 12:57:06:888,Select value 1 Received: 1 Needed: 1 Bytes available: 3
06/24/2014 12:57:06:888,Select value 1 Received: 1 Needed: 1 Bytes available: 2
06/24/2014 12:57:06:889,Select value 1 Received: 1 Needed: 1 Bytes available: 1
06/24/2014 12:57:06:890,Select value 1 Received: 1 Needed: 1 Bytes available: 23
06/24/2014 12:57:06:890,Select value 1 Received: 1 Needed: 1 Bytes available: 22
06/24/2014 12:57:06:898,Select value 1 Received: 110 Needed: 110 Bytes available: 113
06/24/2014 12:57:06:898,Select value 1 Received: 2 Needed: 2 Bytes available: 3
正如你所看到的,当可用字节数等于要读取的字节数时,它会在 read 函数中等待直到超时,然后返回。
【问题讨论】:
您可以使用硬件 RTS/CTS,但这意味着您的连接器必须针对它进行配置。或者,您可以使用 XON XOFF 来停止和启动数据。 问题不在于数据不存在,问题在于即使数据存在read
也会等到超时才读取。
【参考方案1】:
您使用的是什么硬件?真正的串口还是usb适配器?一些 FTDI usb 到串行适配器的配置使它们通过 usb 批量发送字节。这会在端口满载时加快传输速度,但在一次处理几个字节时它们的行为就像您提到的那样。
【讨论】:
在我的电脑上我有一个 USB 适配器,但在我正在为其制作应用程序的设备上我有一个真正的串行端口,问题是一样的。 我不认为由此引起的延迟真的会在超时的数量级,而是大约 1-2 毫秒... 根据我的经验,它是 >10 毫秒,但你是对的,100 毫秒对于这样的延迟来说可能太多了。【参考方案2】:我设法修复它。这是一个简单的修复。
我不得不替换while(((test = select(this->fdPort + 1, &readFd, NULL, NULL, &timeout)) == 1) && (tempReceivedDataBytes == -1))
与 while( (tempReceivedDataBytes == -1) && ((test = select(this->fdPort + 1, &readFd, NULL, NULL, &timeout)) == 1))
.
如果串行缓冲区是空的,它会在select
中等待直到超时,即使它读取了一些内容。如果端口上有问题,它会检查第二部分并退出它。
【讨论】:
嗨,Alex,我遇到了类似的问题,我尝试搜索并遇到了您的帖子。我正在使用不同的硬件和软件(Altera Nios II 中的 C 代码将数据发送到串行端口,Matlab 访问串行端口并实时绘制图形)(在我阅读您的线程***.com/questions/29122503/… 之前将我的线程发布在这里),请你告诉我一般算法你做了什么来解决这个问题,以便我可以在我的平台上进行必要的编码?请提前谢谢您【参考方案3】:设置一个非零的 VMIN 值,这样只要至少接收到这么多字符就可以满足读取。
这意味着:
如果没有缓冲数据,调用将等待某些数据超时
如果至少缓冲了 VMIN 个字符,调用将立即返回这些字符,而无需等待超时。
【讨论】:
我都试过VMIN
设置为1和设置为0,问题是一样的。以上是关于如何修复 rs232 串口超时?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用串口助手在PC上直接抄电表?我有USB转串口,232转485头。
如何在 RS232 串口和本地笔记本电脑之间获取命令历史记录?
UART,串口,RS232,RS485等等,之间有啥联系和区别?