Working with linux serial port in C, Not able to get full data

后端 未结 4 1097
刺人心
刺人心 2020-12-07 05:59

I am working with Linux Serial port written in C. Below is my UART settings

 int fd;
 struct termios tty_attributes;
 fd = open(comport, O_RDWR | O_NOCTTY |         


        
相关标签:
4条回答
  • 2020-12-07 06:28

    Looking at The Man

    RETURN VALUE

    On success, the number of bytes read is returned (zero indicates end of file), and the file position is advanced by this number. It is not an error if this number is smaller than the number of bytes requested; this may happen for example because fewer bytes are actually available right now (maybe because we were close to end-of- file, or because we are reading from a pipe, or from a terminal), or because read() was interrupted by a signal. See also NOTES.

    Emphasis mine

    So you cannot expect that a whole frame can be retrieved by a single read. You should loop until all expected chars are received, for example:

    int total_rec = 0;
    char temp[SIZE];
    while( total_rec < SIZE )
    {
        read_ret_val = read(fd, temp, SIZE);
        if (read_ret_val != -1)
        {
           if ( (total_rec + read_ret_val) >= SIZE)
           { 
               read_ret_val = SIZE - total_rec;
           }
           memcpy(&frame[total_rec], temp, read_ret_val);
           total_rec += read_ret_val;
        }
        else
        {
           perror("error reading serial line: ");
        }
    }
    
    0 讨论(0)
  • 2020-12-07 06:29

    Try with

        memset(&tty_attributes,0,sizeof(tty_attributes));
        tty_attributes.c_iflag=0;
        tty_attributes.c_oflag=0;
        tty_attributes.c_cflag=CS8|CREAD|CLOCAL;
        tty_attributes.c_lflag=0;
        tty_attributes.c_cc[VMIN]=1;
        tty_attributes.c_cc[VTIME]=5;
    
    0 讨论(0)
  • 2020-12-07 06:33

    Your original code has numerous issues which cause it to "getting first 16 bytes only":

    • The code (as posted) only performs a single read() syscall (rather than continuously loop to read the data from the device).

    • The input is obviously ASCII text delimited into lines terminated with carriage return and line feed, yet your program uses non-canonical mode to read rather than canonical mode. The assumption by @pbn is confirmed by the minicom output.

    • Your program uses the serial terminal in non-blocking mode, rather than blocking mode, and resorts to using select() and usleep() calls to wait for the arrival of data.

    • The termios initialization (besides not being POSIX compliant) has several errors, including improper iflag symbols applied to the cflag member, the character size bits are not cleared with ~CSIZE, and CREAD is not enabled.

    • Your read routine unnecessarily flushes (i.e. discards) all received but unread data prior to the select() call.

    A revised routine for opening and configuring the serial terminal (for blocking canonical mode):

    #define BAUDRATE    B9600
    
    int init_comm_channel(char *comport)
    {   
        struct termios tty_attributes;
        int fd;
    
        fd = open(comport, O_RDWR | O_NOCTTY);
        if (fd < 0) {
            perror("open comport error.\n");
            return (-2);
        }
        if (tcgetattr(fd, &tty_attributes) == -1) {
            perror("tcgetattr termios function error.\n");
            return (-3);
        }
        tty_attributes.c_cflag |= CLOCAL | CREAD;
        tty_attributes.c_cflag &= ~CSIZE;
        tty_attributes.c_cflag |= CS8;         /* 8-bit characters */
        tty_attributes.c_cflag &= ~PARENB;     /* no parity bit */
        tty_attributes.c_cflag &= ~CSTOPB;     /* only need 1 stop bit */
        tty_attributes.c_cflag &= ~CRTSCTS;    /* no hardware flowcontrol */
    
        tty_attributes.c_lflag |= ICANON | ISIG;  /* canonical input */
        tty_attributes.c_lflag &= ~(ECHO | ECHOE | ECHONL | IEXTEN);
    
        tty_attributes.c_iflag &= ~INPCK;
        tty_attributes.c_iflag |= IGNCR;
        tty_attributes.c_iflag &= ~(INLCR | ICRNL | IUCLC | IMAXBEL);
        tty_attributes.c_iflag &= ~(IXON | IXOFF | IXANY);   /* no SW flowcontrol */
    
        tty_attributes.c_oflag &= ~OPOST;
    
        cfsetospeed(&tty_attributes, BAUDRATE);       //setting communication speed and other attributes
        cfsetispeed(&tty_attributes, BAUDRATE);
        tcflush(fd, TCIOFLUSH);
    
        if (tcsetattr(fd, TCSANOW, &tty_attributes) < 0) {
            perror("tcsetattr function error.\n");
            return (-4);
        }
        return fd;
    }
    

    The revised routine for reading a line per syscall:

    #define SIZE    64
    unsigned char frame[SIZE];
    
    char *frame_read(int fd)
    {
        int read_ret_val;
    
        if (fd < 0) {
            printf("Before read over comm channel, channel must be initialize\n");
            exit (EXIT_FAILURE);
        }
        read_ret_val = read(fd, frame, SIZE - 1);
        if (read_ret_val < 0) {
            perror("read");
            exit (EXIT_FAILURE);
        }
        frame[read_ret_val] = 0; /* terminate string */
        return (frame);
    }
    

    A revised main() routine that loops forever:

    int main(int argc, char *argv[])
    {
        int fd;
        char *readb;
        char com_portname[13] = {0};
    
        if (argc > 1)
            strcpy(com_portname, argv[1]);  // give the first port number for GPS receiving
        if ((fd = init_comm_channel(com_portname)) < 0) {
            printf("port is not opened\n");
            exit (EXIT_FAILURE);
        }
        printf("port is open for communication:\n");
        do {
            readb = frame_read(fd);
            while (*readb > 0)
                printf("the data is 0x%x\n", *readb++);
            printf("The line is: %s", frame);
        } while (1);  /* loop for another line */
        close(fd);
    }
    
    0 讨论(0)
  • 2020-12-07 06:50

    Most GPS modules and serial interfaces for devices in general send you data line by line. For this you can use canonical mode which you have explicitly disabled.

    Canonical mode as stated in manual

    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).

    I post code to set serial interface speed and parity with canonical mode enabled:

    int set_interface_attribs(int fd, int speed, int parity) 
    {
      // setup based on stty < /dev/ttyACM0 (cfg.txt) output which
      // worked for ABSniffer in pyserial implementation
      // otherwise module responded only once for every two prompts
      struct termios tty;
      int rc;
      memset(&tty, 0, sizeof tty);
      if (tcgetattr(fd, &tty) != 0) 
      {
        log_info("error from tcgetattr %s\r\n", strerror(errno));
        return -1;
      }
    
      rc = cfsetospeed(&tty, speed);
      if (rc == - 1) return -1;
      rc = cfsetispeed(&tty, speed);
      if (rc == - 1) return -1;
    
      tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;  // 8-bit chars
      // disable IGNBRK for mismatched speed tests; otherwise receive break
      // as \000 chars
      tty.c_cc[VMIN] = 0;   // read doesn't block
      tty.c_cc[VTIME] = 5;  // 0.5 seconds read timeout
    
      tty.c_cflag |= (CLOCAL | CREAD);  // ignore modem controls,
      // enable reading
      tty.c_cflag &= ~(PARENB | PARODD);  // shut off parity
      tty.c_cflag |= parity;
      tty.c_cflag &= ~CSTOPB;
      //    tty.c_iflag |= ICRNL | BRKINT; //ICRNL
      tty.c_iflag |= IGNCR;
      tty.c_cflag &= ~CRTSCTS;
      //    tty.c_oflag |= OPOST | ONLCR;
      // tty.c_iflag |= ISIG | ICANON | IEXTEN;
      tty.c_lflag |= ISIG | IEXTEN | ICANON;
      tty.c_lflag &= ~ECHO;
      tty.c_cc[VEOF] = 0x0;
      tty.c_cc[VEOL] = 0x0;
    
      if (tcsetattr(fd, TCSANOW, &tty) != 0) 
      {
        log_info("error from tcsetattr %s\r\n", strerror(errno));
        return -1;
      }
      return 0;
    }
    

    Here is how you use it:

    rc = set_interface_attribs(fd, B9600, 0);
    

    From now on data should be available line by line. All the errors and possible return values are explained in read manual. Assuming there are no errors, reading a buffer of some arbitrary size should return either EAGAIN (Resource temporarily unavailable) with return code -1 or bytes to the newline character '\n'.

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