How to set up serial communication on Linux correctly

I am trying to read and write data from an FPGA board. The board itself is equipped with a driver that creates the ttyUSB0 terminal device whenever the board is connected. The FPGA has an asynchronous receiver and transmitter, and they seem to work.

However, the problem seems to be on C side. I used some test vectors to check if the FPGA outputs the correct information. I noticed a few things:

  • The device sometimes does not open correctly
  • Terminal attributes can sometimes not be restored or set.
  • Reading is sometimes not blocked and does not return the correct value.

The following describes how to configure the terminal descriptor and file parameters. Most of this was taken from here: http://slackware.osuosl.org/slackware-3.3/docs/mini/Serial-Port-Programming

Any advice or comments on why the program may be unsuccessful will be very helpful.

#include <stdio.h>   // Standard input/output definitions
#include <string.h>  // String function definitions
#include <unistd.h>  // UNIX standard function definitions
#include <fcntl.h>   // File control definitions
#include <errno.h>   // Error number definitions
#include <termios.h> // POSIX terminal control definitions

int open_port(void){

    int fd;    // File descriptor for the port
    fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY);

    if (fd == -1){
        fprintf(stderr, "open_port: Unable to open /dev/ttyUSB0 %s\n",strerror(errno));
        exit(EXIT_FAILURE);
    }

    return (fd);
}

int main(void){

    int fd = 0;              // File descriptor
    struct termios options;  // Terminal options

    fd = open_port();    // Open tty device for RD and WR

    fcntl(fd, F_SETFL);            // Configure port reading
    tcgetattr(fd, &options);       // Get the current options for the port
    cfsetispeed(&options, B230400);    // Set the baud rates to 230400
    cfsetospeed(&options, B230400);

    options.c_cflag |= (CLOCAL | CREAD);    // Enable the receiver and set local mode
    options.c_cflag &= ~PARENB;             // No parity bit
    options.c_cflag &= ~CSTOPB;             // 1 stop bit
    options.c_cflag &= ~CSIZE;              // Mask data size
    options.c_cflag |=  CS8;                // Select 8 data bits
    options.c_cflag &= ~CRTSCTS;            // Disable hardware flow control  

    // Enable data to be processed as raw input
    options.c_lflag &= ~(ICANON | ECHO | ISIG);

    // Set the new attributes
    tcsetattr(fd, TCSANOW, &options);

    ////////////////////////////////////
    // Simple read and write code here//
    ////////////////////////////////////

    // Close file descriptor & exit
    close(fd)
    return EXIT_SUCCESS
}  

UPDATE I changed my code based on the first answer. This is what I have now:

#include <errno.h>      // Error number definitions
#include <stdint.h>     // C99 fixed data types
#include <stdio.h>      // Standard input/output definitions
#include <stdlib.h>     // C standard library
#include <string.h>     // String function definitions
#include <unistd.h>     // UNIX standard function definitions
#include <fcntl.h>      // File control definitions
#include <termios.h>    // POSIX terminal control definitions

// Open usb-serial port for reading & writing
int open_port(void){

    int fd;    // File descriptor for the port
    fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY);

    if (fd == -1){
        fprintf(stderr, "open_port: Unable to open /dev/ttyUSB0 %s\n",strerror(errno));
        exit(EXIT_FAILURE);
    }

    return fd;
}

int main(void){

    int              fd = 0;     // File descriptor
    struct termios   options;    // Terminal options
    int              rc;         // Return value

    fd = open_port();            // Open tty device for RD and WR

    // Get the current options for the port
    if((rc = tcgetattr(fd, &options)) < 0){
        fprintf(stderr, "failed to get attr: %d, %s\n", fd, strerror(errno));
        exit(EXIT_FAILURE);
    }

    // Set the baud rates to 230400
    cfsetispeed(&options, B230400);

    // Set the baud rates to 230400
    cfsetospeed(&options, B230400);

    cfmakeraw(&options);
    options.c_cflag |= (CLOCAL | CREAD);   // Enable the receiver and set local mode
    options.c_cflag &= ~CSTOPB;            // 1 stop bit
    options.c_cflag &= ~CRTSCTS;           // Disable hardware flow control
    options.c_cc[VMIN]  = 1;
    options.c_cc[VTIME] = 2;

    // Set the new attributes
    if((rc = tcsetattr(fd, TCSANOW, &options)) < 0){
        fprintf(stderr, "failed to set attr: %d, %s\n", fd, strerror(errno));
        exit(EXIT_FAILURE);
    }

    ////////////////////////////////
        // Simple Read/Write Code Here//
        ////////////////////////////////

    // Close file descriptor & exit
    close(fd);
    return EXIT_SUCCESS;
} 

To clarify, the receiver and transmitter use 8 data bits, 1 stop bit and a parity bit.

+5
source share
1 answer

I prefer the Serial Programming Guide for POSIX operating systems .

fcntl(mainfd, F_SETFL), (F_GETFL ).

cfmakeraw , :

options->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
        | INLCR | IGNCR | ICRNL | IXON);
options->c_oflag &= ~OPOST; 

options.c_cc[VMIN]  = 1;
options.c_cc[VTIME] = 2;

1 2 - .

.

rc = tcgetattr(mainfd, &options);
if (rc < 0) {
    printf("failed to get attr: %d, %s\n", mainfd, strerror(errno));
    exit (-3);
}

(, 115200 9600).

+5

All Articles