Using Serial Ports on GNU/Linux Systems

While for the modern PC user serial ports are just a page on Wikipedia, for the embedded developer a 3 wire UART can be a simple point-to-point bus, or even the only way you have to access the debug data of the system.

Serial ports are almost always used in SoC based designs as the bus to access the bootloader command prompt and the debugging console, and is not uncommon to find SoC with 5 or more serial ports. Also, many peripherals communicate with the main processor via UARTs, such as GPS, GSM modems, Bluetooth radio, field bus devices and general purpose microcontrollers.

On a GNU/Linux system, a serial port is just a character device file, which can be opened, written, read and closed. However, to properly use the device from a C application, you have to use a certain command sequence, which can be quite tricky if you never did it before.

Opening a Serial Port Device

The serial port should be always opened in non-blocking mode, so that the open call do not block if the device was previously misconfigured. After that, the resulting file descriptor can be resetted to blocking mode with an appropriate fcntl.

An example to do that is the following:

int fd;

int open_port (char * path)
{
  int ret;

  fd = open(path, O_RDWR | O_NOCTTY | O_NONBLOCK);
  if (fd < 0) {
    perror("open");
    exit(-1);
  }

  ret = fcntl(fd, F_SETFL, O_RDWR);
  if (ret < 0) {
    perror("fcntl");
    exit(-1);
  }

  return 0;
}

Configuring a serial port

Serial port configuration is handled by the tcsetattr syscall, and can become quite complicated as the function has many flags that you’ll probably never need.

When dealing with 3-wire UART (the one with just tx, rx and reference ground) with standard data encoding (8 bit data, 1 stop bit, no parity), things get simpler, as what you need is just a “raw mode” terminal, described in the manual as:

Input is available character by character, echoing is disabled, and all special processing of terminal input and output characters is disabled.

At this point all you have to do is set the terminal to “raw mode”, set the correct port speed and flush the eventual spurious data still on buffers, such as in:

int config_port (void)
{
  struct termios tp;
  int ret;

  ret = tcgetattr(fd, &tp);
  if (ret < 0)
    perror("tcgetattr");

  cfmakeraw(&tp);

  cfsetspeed(&tp, B38400);

  ret = tcsetattr(fd, TCSANOW, &tp);
  if (ret < 0)
    perror("tcsetattr");

  ret = tcflush(fd, TCIOFLUSH);
  if (ret < 0)
    perror("tcflush");

  return ret;
}

At this point, you are free to write and read to the port using your favorite functions.

Closing a serial port

When you are over, just close the port with the standard close call, as in:

void close_port (void)
{
  close(fd);
}
Advertisement

One Response to Using Serial Ports on GNU/Linux Systems

  1. dalsename says:

    Very instructive

    Thanks!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: