简体   繁体   English

如何修复rs232串口的超时?

[英]How to fix timeout on rs232 serial port?

I have a problem when reading from the serial port. 从串口读取时出现问题。 The problem is that for the last 2 bytes ( CRC bytes ) read from an rs232 port the read waits until the timeout set in timeval ends, then returns. 问题是,对于从rs232端口读取的最后2个字节(CRC字节),读取将等待,直到timeval设置的超时结束,然后返回。 On rs485 , with the same method used for reading, read returns fine. rs485 ,使用与读取相同的方法,read返回正常。 What I have seen while debbuging is that on rs485 there is an extra byte with value FF after the 2 CRC bytes. 我在debbuging时看到的是,在rs485上,在2个CRC字节之后有一个额外的字节值为FF I can't find another difference between the 2. 我找不到2之间的另一个区别。

Here are the relevant parts of code: 以下是代码的相关部分:

setting the port 设置端口

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;

}

opening the port 打开港口

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;

}

The wait frame where data is interpreted and read in chunks. 等待框架,其中数据以块的形式被解释和读取。

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;
}

And where the reading is done. 阅读完成的地方。

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;
}

If I set the timeout to 100 ms, then it waits 100 ms to read the 2 bytes, if I set it to 10 ms, then it waits 10 ms to read the 2 bytes. 如果我将超时设置为100毫秒,则等待100毫秒来读取2个字节,如果我将其设置为10毫秒,则等待10毫秒来读取2个字节。

I tried changing the port settings, but with no success. 我尝试更改端口设置,但没有成功。

I have been trying to solve this for days but with no success. 我一直试图解决这个问题,但没有成功。

Edit: I should add that ioctl(fdPort, FIONREAD, &bytesAvail); 编辑:我应该添加ioctl(fdPort, FIONREAD, &bytesAvail); is setting bytesAvail to 2, but read waits until timeout to read them. bytesAvail设置为2,但是read等待直到超时才能读取它们。

Edit 2: 编辑2:

Here is a log with a timeout set to 25 seconds so you can get an idea of what's the problem: 这是一个超时设置为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

The one with the problems is the PID board ( on a rs232), the others are on the same rs485. 有问题的是PID板(在rs232上),其他的在同一个rs485上。

And here is one with a timeout set to around 30 ms ( I can't remember the exact value): 这里有一个超时设置为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

Edit: I still can't figure out what is wrong. 编辑:我仍然无法弄清楚出了什么问题。 If I remove the extra byte on rs485 it is the same. 如果我删除rs485上的额外字节,它是相同的。 Here is another log: 这是另一个日志:

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

As you can see when the number of available bytes is equal with the number of bytes to read it waits in the read function until timeout and then returns. 正如您所看到的,当可用字节数等于要读取的字节数时,它会在读取函数中等待,直到超时然后返回。

What hardware are you using? 你用的是什么硬件? Real serial port or usb adapter? 真正的串口或USB适配器? Some FTDI usb to serial adapters are configured so they send the bytes over usb in batches. 配置了一些FTDI usb到串行适配器,因此它们分批在usb上发送字节。 That speeds up the transfer when the port is fully loaded but they behave like you mentioned when dealing with few bytes at a time. 当端口完全加载时,它会加速传输,但它们的行为与您在处理几个字节时一样。

I managed to fix it. 我设法修复它。 It was a simple fix. 这是一个简单的修复。

I had to replace while(((test = select(this->fdPort + 1, &readFd, NULL, NULL, &timeout)) == 1) && (tempReceivedDataBytes == -1)) with while( (tempReceivedDataBytes == -1) && ((test = select(this->fdPort + 1, &readFd, NULL, NULL, &timeout)) == 1)) . 我必须替换while(((test = select(this->fdPort + 1, &readFd, NULL, NULL, &timeout)) == 1) && (tempReceivedDataBytes == -1)) with while( (tempReceivedDataBytes == -1) && ((test = select(this->fdPort + 1, &readFd, NULL, NULL, &timeout)) == 1))

If the serial buffer was empty it was waiting in select until timeout, even if it read something. 如果串行缓冲区为空,则它会在select中等待,直到超时,即使它读取了某些内容。 If there was someting on the port it was checking the second part of the while and exiting it. 如果在端口上有某些东西,它正在检查while的第二部分并退出它。

Set a non-zero value of VMIN, so that the read can be satisfied as soon as at least that many characters have been received. 设置VMIN的非零值,以便一旦接收到至少那么多字符就可以满足读取。

This would mean that: 这意味着:

  • If no data has been buffered, the call will wait up to timeout for some data 如果没有缓冲数据,则对于某些数据,呼叫将等待超时

  • If at least VMIN characters have been buffered, the call will return immediately with those, without waiting for the timeout. 如果至少缓冲了VMIN字符,则调用将立即返回,而不等待超时。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM