reading from serial port in c breaks up lines

后端 未结 2 1470
眼角桃花
眼角桃花 2021-01-26 13:37

I\'m trying to write a little program in C that will read from the serial port using the select command so that it blocks and waits for input. It\'s working, except it keeps br

相关标签:
2条回答
  • 2021-01-26 14:19

    The problem is that you are reading an arbitrary number of bytes, and then outputing them separated by a newline:

    read(fileDescriptor, buf, 1000);
    printf("%s\n", buf);
    

    You opened the descriptor O_NONBLOCK and I'm not sure your fcntl call is sufficient to clear it. The result is that read pulls out however many characters happen to be buffered that that moment, and then you print them followed by a newline.

    You probably do not want to read in blocking mode, as then it may not return until 1000 characters are read. This may be closer to what you want:

    amt = read(fileDescriptor, buf, 1000);
    if (amt > 0)
        write(1,buff,amt);
    else
        break;
    

    Of course, there should be a lot more error handling.

    0 讨论(0)
  • 2021-01-26 14:26

    it keeps breaking up lines and I have no idea why.

    If you wants to read lines from the serial terminal, then you have to configure it to do so.
    Instead you have configured it to be in non-canonical and non-blocking mode.
    The code does not match your stated intentions at all.

    Quoting from the Linux termios man page:

    In canonical mode:
    Input is made available line by line. An input line is available when one of the line delimiters is typed (NL, EOL, EOL2; or EOF at the start of line). Except in the case of EOF, the line delimiter is included in the buffer returned by read(2).

    The code is clearly commented that it is using non-canonical mode (i.e. the wrong mode):

    // Set raw input (non-canonical) mode, with reads blocking until either
    // a single character has been received or a one second timeout expires.
    // See tcsetattr(4) ("man 4 tcsetattr") and termios(4) ("man 4 termios")
    // for details.
    
    cfmakeraw(&options);
    options.c_cc[VMIN] = 1;
    options.c_cc[VTIME] = 5;
    

    You need to remove these lines to get canonical mode and read lines instead of raw bytes.

    If you expect the read() to return complete lines, then the program will have to wait for input. That means that you need blocking I/O.

    // The O_NONBLOCK flag also causes subsequent I/O on the device to
    // be non-blocking.
    // See open(2) ("man 2 open") for details.
    fileDescriptor = open(deviceFilePath, O_RDWR | O_NOCTTY | O_NONBLOCK);
    

    The O_NONBLOCK option needs to be removed from the open() syscall.

    In spite of what at least three commenters have written, a Linux serial terminal can be configured to read lines. You are using a real operating system, and not running bare-metal on a microprocessor. All you have to do is activate the line discipline to scan the characters received by the serial terminal.
    Full details for programming canonical mode can be found in Serial Programming Guide for POSIX Operating Systems and the termios man page.

    There are also several issues with your code that should be corrected:

    • Instead of memset(&options, 0, sizeof(options)) the code should be calling tcgetattr() to properly initialize the structure. This can be a serious issue for canonical input, as the existing code will have zeroed out all of the control code specifications instead of having proper definitions.
    • Instead of direct assignments, the code should be performing bit-wise operations (in order to preserve existing settings). See Setting Terminal Modes Properly.
    • The read(fileDescriptor, buf, 1000) statement needs to be expanded to handle possible errors and to deal with the received data.
      • the return code from the read() syscall needs to be checked for any error conditions.
      • when no error is detected, then the return code indicates the number of bytes returned in the buffer. Note that the input will not be terminated by a null byte, so string operations should not be applied to the buffer until a null is appended.

    The read code should something like:

     rc = read(fileDescriptor, buf, sizeof(buf) - 1);
     if (rc < 0) {
         /* handle error condition */
     } else {
         buf[rc] = '\0';
         printf("%s", buf);
     }
    

    Since buf[] is allocated for 1000 bytes, the read() request can return a line up to 999 characters long.

    0 讨论(0)
提交回复
热议问题