简体   繁体   中英

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. On rs485 , with the same method used for reading, read returns fine. What I have seen while debbuging is that on rs485 there is an extra byte with value FF after the 2 CRC bytes. I can't find another difference between the 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.

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); is setting bytesAvail to 2, but read waits until timeout to read them.

Edit 2:

Here is a log with a timeout set to 25 seconds so you can get an idea of what's the problem:

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.

And here is one with a timeout set to around 30 ms ( I can't remember the exact value):

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. 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? Some FTDI usb to serial adapters are configured so they send the bytes over usb in batches. 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)) .

If the serial buffer was empty it was waiting in select until timeout, even if it read something. If there was someting on the port it was checking the second part of the while and exiting it.

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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