1

我正在尝试从 Adafruit 的 Ultimate GPS 模块中读取 GPS NMEA 语句。我在树莓派上使用 C++ 来读取模块的串口连接

这是我的读取功能:

int Linuxutils::readFromSerialPort(int fd, int bufferSize) {

    /*
    Reading data from a port is a little trickier. When you operate the port in raw data mode,
    each read(2) system call will return however many characters are actually available in the
    serial input buffers. If no characters are available, the call will block (wait) until
    characters come in, an interval timer expires, or an error occurs. The read function can be
    made to return immediately by doing the following:
    fcntl(fd, F_SETFL, FNDELAY);
    The NDELAY option causes the read function to return 0 if no characters are available on the port.
    */

    // Check the file descriptor
    if ( !checkFileDecriptorIsValid(fd) ) {
        fprintf(stderr, "Could not read from serial port - it is not a valid file descriptor!\n");
        return -1;
    }

    // Now, let's wait for an input from the serial port.
    fcntl(fd, F_SETFL, 0); // block until data comes in

    // Now read the data
    int absoluteMax = bufferSize*2;
    char *buffer = (char*) malloc(sizeof(char) * bufferSize); // allocate buffer.
    int rcount = 0;
    int length = 0;

    // Read in each newline
    FILE* fdF = fdopen(fd, "r");
    int ch = getc(fdF);
    while ( (ch != '\n') ) { // Check for end of file or newline

        // Reached end of file
        if ( ch == EOF ) {
            printf("ERROR: EOF!");
            continue;
        }

        // Expand by reallocating if necessary
        if( rcount == absoluteMax ) { // time to expand ?
          absoluteMax *= 2; // expand to double the current size of anything similar.
          rcount = 0; // Re-init count
          buffer = (char*)realloc(buffer, absoluteMax); // Re-allocate memory.
        }

        // Read from stream
        ch = getc(fdF);

        // Stuff in buffer
        buffer[length] = ch;

        // Increment counters
        length++;
        rcount++;

    }

    // Don't care if we return 0 chars read
    if ( rcount == 0 ) {
        return 0;
    }

    // Stick
    buffer[rcount] = '\0';

    // Print results
    printf("Received ( %d bytes ): %s\n", rcount,buffer);

    // Return bytes read
    return rcount;

}

所以我有点像你在下面看到的句子,问题是我得到了一个完整句子的这些“重复”部分,如下所示:

Received ( 15 bytes ): M,-31.4,M,,*61

这是完整的东西:

Received ( 72 bytes ): GPGGA,182452.000,4456.2019,N,09337.0243,W,1,8,1.19,292.6,M,-31.4,M,,*61

Received ( 56 bytes ): GPGSA,A,3,17,07,28,26,08,11,01,09,,,,,1.49,1.19,0.91*00

Received ( 15 bytes ): M,-31.4,M,,*61

Received ( 72 bytes ): GPGGA,182453.000,4456.2019,N,09337.0242,W,1,8,1.19,292.6,M,-31.4,M,,*61

Received ( 56 bytes ): GPGSA,A,3,17,07,28,26,08,11,01,09,,,,,1.49,1.19,0.91*00

Received ( 15 bytes ): M,-31.4,M,,*61

Received ( 72 bytes ): GPGGA,182456.000,4456.2022,N,09337.0241,W,1,8,1.21,292.6,M,-31.4,M,,*64

Received ( 56 bytes ): GPGSA,A,3,17,07,28,26,08,11,01,09,,,,,2.45,1.21,2.13*0C

Received ( 70 bytes ): GPRMC,182456.000,A,4456.2022,N,09337.0241,W,0.40,183.74,110813,,,A*7F

Received ( 37 bytes ): GPVTG,183.74,T,,M,0.40,N,0.73,K,A*34

Received ( 70 bytes ): GPRMC,182453.000,A,4456.2019,N,09337.0242,W,0.29,183.74,110813,,,A*7E

Received ( 37 bytes ): GPVTG,183.74,T,,M,0.29,N,0.55,K,A*3F

Received ( 32 bytes ): 242,W,0.29,183.74,110813,,,A*7E

Received ( 70 bytes ): GPRMC,182452.000,A,4456.2019,N,09337.0243,W,0.33,183.74,110813,,,A*75

为什么我会收到重复的句子,我该如何解决?我尝试刷新串行端口缓冲区,但事情变得非常丑陋!谢谢。

4

2 回答 2

1

我不确定我是否理解您的确切问题。该功能存在一些问题,但可能会解释各种错误。

线条

int absoluteMax = bufferSize*2;
char *buffer = (char*) malloc(sizeof(char) * bufferSize); // allocate buffer.

似乎错了。您将通过比较读取的字符数来决定何时增加缓冲区,absoluteMax因此这需要与分配的缓冲区大小相匹配。在重新分配之前,您当前正在写入超出分配内存的末尾。这会导致未定义的行为。如果你很幸运,你的应用程序会崩溃,如果你不走运,一切似乎可以正常工作,但你会丢失你读取的数据的后半部分,因为只有写入你拥有的内存的数据会被移动realloc(如果它重新定位您的堆单元)。

此外,您不应该从malloc(or realloc) 转换返回值,并且可以依赖sizeof(char)为 1。

while您丢失了读取的第一个字符(在循环之前读取的字符)。这是故意的吗?

当你重新分配时buffer,你不应该重置rcount。这会导致与上述相同的错误,您将buffer在再次重新分配之前写入超出末尾的内容。同样,这样做的影响是不确定的,但可能包括损失部分输出。

与您当前关注的错误无关,但值得注意的是您泄漏bufferfdF. 在退出函数之前,您应该分别free和它们。fclose

以下(未经测试的)版本应该可以解决这些问题

int Linuxutils::readFromSerialPort(int fd, int bufferSize)
{
    if ( !checkFileDecriptorIsValid(fd) ) {
        fprintf(stderr, "Could not read from serial port - it is not a valid file descriptor!\n");
        return -1;
    }

    fcntl(fd, F_SETFL, 0); // block until data comes in
    int absoluteMax = bufferSize;
    char *buffer = malloc(bufferSize);
    int rcount = 0;
    int length = 0;

    // Read in each newline
    FILE* fdF = fdopen(fd, "r");
    int ch = getc(fdF);
    for (;;) {
        int ch = getc(fdF);
        if (ch == '\n') {
            break;
        }
        if (ch == EOF) { // Reached end of file
            printf("ERROR: EOF!\n");
            break;
        }
        if (length+1 >= absoluteMax) {
            absoluteMax *= 2;
            char* tmp = realloc(buffer, absoluteMax);
            if (tmp == NULL) {
                printf("ERROR: OOM\n");
                goto cleanup;
            }
            buffer = tmp;
        }
        buffer[length++] = ch;
    }

    if (length == 0) {
        return 0;
    }
    buffer[length] = '\0';

    // Print results
    printf("Received ( %d bytes ): %s\n", rcount,buffer);

cleanup:
    free(buffer);
    fclose(fdH);
    return length;
}
于 2013-08-11T18:54:29.170 回答
1

也许您可以尝试在读取之前刷新串行端口缓冲区,如此链接所示?

我还会考虑在每次调用 Linuxutils::readFromSerialPort 时不要重新打开串行端口 - 您可以保持文件描述符打开以供进一步阅读(无论如何调用被阻塞,因此从调用者的角度来看没有任何变化)。

于 2013-08-11T19:27:08.993 回答