如何修复rs232串口的超时?

时间:2014-04-17 15:34:31

标签: c++ linux serial-port serial-communication

从串口读取时出现问题。 问题是,对于从rs232端口读取的最后2个字节(CRC字节),读取将等待,直到timeval中设置的超时结束,然后返回。在rs485上,使用与读取相同的方法,read返回正常。我在debbuging时看到的是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

正如您所看到的,当可用字节数等于要读取的字节数时,它会在读取函数中等待,直到超时然后返回。

3 个答案:

答案 0 :(得分:2)

您使用的是哪些硬件?真正的串口或USB适配器?配置了一些FTDI usb到串行适配器,因此它们分批在usb上发送字节。当端口完全加载时,这会加速传输,但它们的行为就像你在一次处理几个字节时提到的那样。

答案 1 :(得分:1)

我设法解决了这个问题。这是一个简单的解决方案。

我不得不替换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等待直到超时,即使它读取了某些内容。如果在端口上有某些东西,它正在检查while的第二部分并退出它。

答案 2 :(得分:0)

设置VMIN的非零值,以便至少一旦收到很多字符就可以满足读取。

这意味着:

  • 如果没有数据被缓冲,则呼叫将等待某些数据的超时

  • 如果至少缓冲了VMIN字符,则呼叫将立即返回,而不等待超时。