- Linux Serial Ports Using C/C++
- Overview
- Everything Is A File
- Basic Setup In C
- Configuration Setup
- Control Modes (c_cflags)
- PARENB (Parity)
- CSTOPB (Num. Stop Bits)
- Number Of Bits Per Byte
- Flow Control (CRTSCTS)
- CREAD and CLOCAL
- Local Modes (c_lflag)
- Disabling Canonical Mode
- Disable Signal Chars
- Input Modes (c_iflag)
- Software Flow Control (IXOFF, IXON, IXANY)
- Disabling Special Handling Of Bytes On Receive
- Output Modes (c_oflag)
- VMIN and VTIME (c_cc)
- Baud Rate
- Custom Baud Rates
- Saving termios
- Reading And Writing
- Writing
- Reading
- Closing
- Full Example (Standard Baud Rates)
- Issues With Getty
- Exclusive Access
- Changing Terminal Settings Are System Wide
- Serial Port Programming on Linux
- Sourcecodes
Linux Serial Ports Using C/C++
Article by:Geoffrey Hunter
Date Published: | June 24, 2017 |
Last Modified: | November 24, 2020 |
Overview
Unluckily, using serial ports in Linux is not the easiest thing in the world. When dealing with the termios.h header, there are many finicky settings buried within multiple bytes worth of bitfields. This page is an attempt to help explain these settings and show you how to configure a serial port in Linux correctly.
Everything Is A File
In typical UNIX style, serial ports are represented by files within the operating system. These files usually pop-up in /dev/ , and begin with the name tty* .
Common names are:
- /dev/ttyACM0 — ACM stands for the ACM modem on the USB bus. Arduino UNOs (and similar) will appear using this name.
- /dev/ttyPS0 — Xilinx Zynq FPGAs running a Yocto-based Linux build will use this name for the default serial port that Getty connects to.
- /dev/ttyS0 — Standard COM ports will have this name. These are less common these days with newer desktops and laptops not having actual COM ports.
- /dev/ttyUSB0 — Most USB-to-serial cables will show up using a file named like this.
- /dev/pts/0 — A pseudo terminal. These can be generated with socat .
A listing of the /dev/ directory in Linux with a connected Arduino. The Arduino serial port is present as /dev/ttyACM0.
To write to a serial port, you write to the file. To read from a serial port, you read from the file. Of course, this allows you to send/receive data, but how do you set the serial port parameters such as baud rate, parity, e.t.c? This is set by a special tty configuration struct .
Basic Setup In C
First we want to include a few things:
Then we want to open the serial port device (which appears as a file under /dev/ ), saving the file descriptor that is returned by open() :
One of the common errors you might see here is errno = 2 , and strerror(errno) returns No such file or directory . Make sure you have the right path to the device and that the device exists!
Another common error you might get here is errno = 13 , which is Permission denied . This usually happens because the current user is not part of the dialout group. Add the current user to the dialout group with:
You must log out and back in before these group changes come into effect.
At this point we could technically read and write to the serial port, but it will likely not work, because the default configuration settings are not designed for serial port use. So now we will set the configuration correctly.
When modifying any configuration value, it is best practice to only modify the bit you are interested in, and leave all other bits of the field untouched. This is why you will see below the use of &= or |= , and never = when setting bits.
Configuration Setup
We need access to the termios struct in order to configure the serial port. We will create a new termios struct, and then write the existing configuration of the serial port to it using tcgetattr() , before modifying the parameters as needed and saving the settings with tcsetattr() .
We can now change tty ’s settings as needed, as shown in the following sections. Before we get onto that, here is the definition of the termios struct if you’re interested (pulled from termbits.h ):
Control Modes (c_cflags)
The c_cflag member of the termios struct contains control parameter fields.
PARENB (Parity)
If this bit is set, generation and detection of the parity bit is enabled. Most serial communications do not use a parity bit, so if you are unsure, clear this bit.
CSTOPB (Num. Stop Bits)
If this bit is set, two stop bits are used. If this is cleared, only one stop bit is used. Most serial communications only use one stop bit.
Number Of Bits Per Byte
The CS fields set how many data bits are transmitted per byte across the serial port. The most common setting here is 8 ( CS8 ). Definitely use this if you are unsure, I have never used a serial port before which didn’t use 8 (but they do exist). You must clear all of the size bits before setting any of them with &=
Flow Control (CRTSCTS)
If the CRTSCTS field is set, hardware RTS/CTS flow control is enabled. The most common setting here is to disable it. Enabling this when it should be disabled can result in your serial port receiving no data, as the sender will buffer it indefinitely, waiting for you to be “ready”.
CREAD and CLOCAL
Setting CLOCAL disables modem-specific signal lines such as carrier detect. It also prevents the controlling process from getting sent a SIGHUP signal when a modem disconnect is detected, which is usually a good thing here. Setting CLOCAL allows us to read data (we definitely want that!).
Local Modes (c_lflag)
Disabling Canonical Mode
UNIX systems provide two basic modes of input, canonical and non-canonical mode. In canonical mode, input is processed when a new line character is received. The receiving application receives that data line-by-line. This is usually undesirable when dealing with a serial port, and so we normally want to disable canonical mode.
Canonical mode is disabled with:
Also, in canonical mode, some characters such as backspace are treated specially, and are used to edit the current line of text (erase). Again, we don’t want this feature if processing raw serial data, as it will cause particular bytes to go missing!
If this bit is set, sent characters will be echoed back. Because we disabled canonical mode, I don’t think these bits actually do anything, but it doesn’t harm to disable them just in case!
Disable Signal Chars
When the ISIG bit is set, INTR , QUIT and SUSP characters are interpreted. We don’t want this with a serial port, so clear this bit:
Input Modes (c_iflag)
The c_iflag member of the termios struct contains low-level settings for input processing. The c_iflag member is an int .
Software Flow Control (IXOFF, IXON, IXANY)
Clearing IXOFF , IXON and IXANY disables software flow control, which we don’t want:
Disabling Special Handling Of Bytes On Receive
Clearing all of the following bits disables any special handling of the bytes as they are received by the serial port, before they are passed to the application. We just want the raw data thanks!
Output Modes (c_oflag)
The c_oflag member of the termios struct contains low-level settings for output processing. When configuring a serial port, we want to disable any special handling of output chars/bytes, so do the following:
Both OXTABS and ONOEOT are not defined in Linux. Linux however does have the XTABS field which seems to be related. When compiling for Linux, I just exclude these two fields and the serial port still works fine.
VMIN and VTIME (c_cc)
VMIN and VTIME are a source of confusion for many programmers when trying to configure a serial port in Linux.
An important point to note is that VTIME means slightly different things depending on what VMIN is. When VMIN is 0, VTIME specifies a time-out from the start of the read() call. But when VMIN is > 0, VTIME specifies the time-out from the start of the first received character.
Let’s explore the different combinations:
VMIN = 0, VTIME = 0: No blocking, return immediately with what is available
VMIN > 0, VTIME = 0: This will make read() always wait for bytes (exactly how many is determined by VMIN ), so read() could block indefinitely.
VMIN = 0, VTIME > 0: This is a blocking read of any number of chars with a maximum timeout (given by VTIME ). read() will block until either any amount of data is available, or the timeout occurs. This happens to be my favourite mode (and the one I use the most).
VMIN > 0, VTIME > 0: Block until either VMIN characters have been received, or VTIME after first character has elapsed. Note that the timeout for VTIME does not begin until the first character is received.
VMIN and VTIME are both defined as the type cc_t , which I have always seen be an alias for unsigned char (1 byte). This puts an upper limit on the number of VMIN characters to be 255 and the maximum timeout of 25.5 seconds (255 deciseconds).
“Returning as soon as any data is received” does not mean you will only get 1 byte at a time. Depending on the OS latency, serial port speed, hardware buffers and many other things you have no direct control over, you may receive any number of bytes.
For example, if we wanted to wait for up to 1s, returning as soon as any data was received, we could use:
Baud Rate
Rather than use bit fields as with all the other settings, the serial port baud rate is set by calling the functions cfsetispeed() and cfsetospeed() , passing in a pointer to your tty struct and a enum :
If you want to remain UNIX compliant, the baud rate must be chosen from one of the following:
Some implementation of Linux provide a helper function cfsetspeed() which sets both the input and output speeds at the same time:
Custom Baud Rates
As you are now fully aware that configuring a Linux serial port is no trivial matter, you’re probably unfazed to learn that setting custom baud rates is just as difficult. There is no portable way of doing this, so be prepared to experiment with the following code examples to find out what works on your target system.
GNU/Linux Method
If you are compiling with the GNU C library, you can forgo the standard enumerations above just specify an integer baud rate directly to cfsetispeed() and cfsetospeed() , e.g.:
termios2 Method
This method relied on using a termios2 struct, which is like a termios struct but with sightly more functionality. I’m unsure on exactly what UNIX systems termios2 is defined on, but if it is, it is usually defined in termbits.h (it was on the Xubuntu 18.04 with GCC system I was doing these tests on):
Which is very similar to plain old termios , except with the addition of the c_ispeed and c_ospeed . We can use these to directly set a custom baud rate! We can pretty much set everything other than the baud rate in exactly the same manner as we could for termios , except for the reading/writing of the terminal attributes to and from the file descriptor — instead of using tcgetattr() and tcsetattr() we have to use ioctl() .
Let’s first update our includes, we have to remove termios.h and add the following:
Please read the comment above about BOTHER . Perhaps on your system this method will work!
Saving termios
After changing these settings, we can save the tty termios struct with tcsetattr() :
Reading And Writing
Now that we have opened and configured the serial port, we can read and write to it!
Writing
Writing to the Linux serial port is done through the write() function. We use the serial_port file descriptor which was returned from the call to open() above.
Reading
Reading is done through the read() function. You have to provide a buffer for Linux to write the data into.
Closing
This is a simple as:
Full Example (Standard Baud Rates)
Issues With Getty
Getty can cause issues with serial communication if it is trying to manage the same tty device that you are attempting to perform serial communications with.
To Stop Getty:
Getty can be hard to stop, as by default if you try and kill the process, a new process will start up immediately.
These instructions apply to older versions of Linux, and/or embedded Linux.
- Load /etc/inittab in your favourite text editor.
- Comment out any lines involving getty and your tty device.
- Save and close the file.
- Run the command
$ init q to reload the /etc/inittab file.
Exclusive Access
It can be prudent to try and prevent other processes from reading/writing to the serial port at the same time you are.
One way to accomplish this is with the flock() system call:
Changing Terminal Settings Are System Wide
Although getting and setting terminal settings are done with a file descriptor, the settings apply to the terminal device itself and will effect all other system applications that are using or going to use the terminal. This also means that terminal setting changes are persistant after the file descriptor is closed, and even after the application that changed the settings is terminated 1 .
Источник
Serial Port Programming on Linux
This is a tutorial on how to program the Serial Ports on your Linux box.Serial Ports are nice little interfaces on the PC which helps you to interface your embedded system projects using a minimum number of wires.In this tutorial we will write a small program that will send and receive ASCII characters to a microcontroller development board.
One problem with the traditional serial ports is that they are now legacy hardware and are being phased out by the PC manufacturers and most laptops have USB ports only,this problem can be easily solved by using a variety of USB to Serial Converters available in the market,eg USB2SERIAL V2.0.
Please note that in this tutorial I am using a USB to Serial Converter based on FT232 from FTDI. You can use any FT232 based board ( other USB converter boards based on different chip sets should work,but i have not tested it ) or you can buy the one I am using here.
If you have traditional D subminiature (DB9) Serial Ports,just identify the relevant pins (RXD, TXD,RTS,DTR and Ground) and continue with the tutorial.
In this tutorial we are going to use the C language to program the serial port on a Linux Operating system(Ubuntu/LinuxMint).The compiler used is gcc which is freely available along with the OS.
Sourcecodes
- Please note that the source codes on the website show only the relevant sections to highlight the process of programming the serial port.
- Contact Us to Enquire about the full Source codes
Serial Ports under Linux
Traditional hardware serial ports under Linux are named as ttyS* where * can be 1,2,3. etc for eg ttyS1,ttyS2,ttyS23 . etc.They are similar to COM1,COM2 etc under a windows box.
While
USB to Serial Converter based serial ports are usually named as ttyUSB* where * can be 1,2,3. etc for eg ttyUSB0,ttyUSB1 etc .
In Linux the hardware components like serial ports are treated like files and are grouped together in the /dev folder in the file system.if you navigate into the /dev folder and list the files using ls command you can see the files corresponding to various hardware devices.
Now you can see a large number of serial ports listed in the /dev directory like ttyS28,ttyS23 etc.Now to identify which of the ttyS* corrosponds to your hardware serial port run the following command on the terminal.
dmesg | grep ttyS
if you are getting an output like this
[ 37.531286] serial8250: ttyS0 at I/O 0x3f8 (irq = 4) is a 16550A
it means that ttyS0 is your hardware serial port connected to your computer .
Now if you dont have any hardware serial ports ( like in my case ) the command will not print anything,like this.
Now to check for any USB to serial converter use dmesg | grep tty command.
You can see that FTDI based USB to Serial Converter is attached to ttyUSB0.Now you can use the ttyUSB0 to communicate with your serial converter .
Another place where you can see the attached serial devices is the /dev/serial/by-id folder.
Programming the Serial Port
In this tutorial i am going to use C language to program the Serial port,compiler used is GCC.
If you are interested to know more about the internals of the serial port you can refer “The Serial Programming Guide for POSIX Operating Systems” written by Michael R.Sweet.
To perform serial I/O under linux we are going to use the termios API.termios API supports two modes of accessing the serial ports.
1. Cannonical Mode
2. NonCannonical Mode
Cannonical mode is the default mode and is used for accessing terminals and stuff.For our tutorial we are going to use the second mode called the non cannonical mode.More details about the difference between the modes are available in the above book .
Opening and Closing the Serial Port
Opening a serial port in Linux is accomplished by using the open() system call and closing the serial port is done using the close() system call.
The open() system call takes two arguments ,name of the file to be opened (here serial port ) and the various parameters associated with it.It returns a -1 on failure and a positive integer on success.
int fd ;
fd = open( » /dev/ttyUSB0 «,O_RDWR | O_NOCTTY ) ;
here /dev/ttyUSB0 is the name of the serial port you are using (here FTDI based USB2SERIAL Converter board),If you are using a hardware serial port it will be some thing like /dev/ttyS1 or /dev/ttyS2.
gcc -o serial serial.c Now save the file and compile it using GCC at the terminal.
Please make sure that you are using the correct serial port name in the open() command.In the above code it is ttyUSB0 , it may be different in your system.
Another important fact regarding the Linux system is that Linux will only allow you to access the hardware resources from root account.So you will have to execute the resulting binary as root ,just use the sudo command.
After the code is compiled successfully,execute the binary as root by issuing the following command.Enter the password for root when prompted.
sudo ./serial
Please make sure your hardware is connected before executing the binary, in case you are using a USB to serial converter.
Configuring the termios structure
After opening and closing the serial port successfully ,its time to configure the correct mode,baudrate,data format,number of start/stop bits etc .In Linux it is done by a structure called termios.
And this is how the structure looks like
struct termios
<
tcflag_t c_iflag ; /* input mode flags */
tcflag_t c_oflag ; /* output mode flags */
tcflag_t c_cflag ; /* control mode flags */
tcflag_t c_lflag ; /* local mode flags */
cc_t c_line ; /* line discipline */
cc_t c_cc[NCCS] ; /* control characters */
>;
Now first thing to do is to declare a structure of type termios
struct termios SerialPortSettings ;
Now to configure the termios structure according to our needs we use two functions,tcgetattr() and tcsetattr().
tcgetattr() is used to get the current settings of the serial port to the structure we just declared.It takes two arguments, the file descriptor fd corresponding to the serial port we have opened and the address of the structure we just declared.
After getting the current settings we are going to change it suit our needs.We are going to configure the serial port to transmit at 9600 baudrate , 8 bits, No parity ,1 Stop bit ,the 8N1 configuration.
Setting the Baudrate
In Linux ,there is an option to set different read and write speeds but it is recommended to set the same speed for both read and write.
The Baudrate is set using two functions,
cfsetispeed() for setting the input speed or read speed and
cfsetospeed() for setting the output speed or write speed.
Now let’s set the speed for both reading and writing at 9600 bps
you can use other standard baudrates like 4800,19200,38400 etc all Baudrate constants have to be prefixed with a ‘B’ like B4800 , B19200 etc .
Please note that the MSP430 microcontroller code provided (microcontroller side) uses 9600bps 8N1 format for communicating with the PC.
Configuring data format,Start/Stop bits ,Parity
The control flags ( c_cflag ) of the termios structure configures the data format (8bit or 7 bits for data),Parity(Even,Odd,None) and the number of start and stop bits to use while communicating.
Configuring these information involves setting and clearing individual bits of the control flags.It is advisable to check out “Bit manipulation in C language” in google/wikipedia,if you are not familiar.
Configuring other Options
Turn off hardware based flow control (RTS/CTS).
Turn on the receiver of the serial port (CREAD),other wise reading from the serial port will not work.
SerialPortSettings.c_cflag |= CREAD | CLOCAL ;
Turn off software based flow control (XON/XOFF).
( IXON | IXOFF | IXANY );
Setting the mode of operation,the default mode of operation of serial port in Linux is the Cannonical mode.For Serial communications with outside devices like serial modems,mice etc NON Cannonical mode is recommended.
( ICANON | ECHO | ECHOE | ISIG );
More information about the functions and constants discussed above can be found in the termios manual page.You can easly access them by typing
man termios on your terminal.
The man pages are also available online at www.man7.org.
After you have configured all of the required bitfields of the terminos structure.You can use the tcsetattr() function to set them.tcsetattr() takes three arguments,the first and last arguments are same as in the tcgetattr()function.The second one TCSANOW tells to make the changes now without waiting.
tcsetattr ( fd , TCSANOW , &SerialPortSettings )
Hardware Connections
Now we will create a small serial link between a microcontroller board and a PC running Linux OS to test out reading and writing from a PC serial port .
Microcontroller used here is MSP430G2553 from Texas instruments on Launchpad development board.Please note that you can use any microcontroller like 8051 or Atmel AVR on the embedded system side.Since this is an article about setting up and configuring the serial port on Linux,the microcontroller side code is explained here.
The PC is connected to the microcontroller board using a null modem cable.The RX of the PC (serial port) is connected to the TX of the Microcontroller and vice versa.The Grounds of PC and microcontroller are connected together.
Here my PC do not have a hardware DB9 serial port(9 pin),So i am using a FTDI based USB to Serial converter called USB2SERIAL which converts the USB signals to TTL compatible serial outputs (RXD and TXD). you can directly connect the outputs of USB2SERIAL to MSP430 microcontroller as shown in the below block diagram. USB2SERIAL also offers selectable 3V/5V TTL outputs for interfacing to 3.3V logic microcontrollers like MSP430.
Please noter that if you are using a DB9 RS232 SerialPort of your PC ,you will have to build a RS232 signal level converter at the microcontroller side to decode the RS232 signal.Connecting the RS232 Lines from the PC directly to microcontroller UART directly will damage the chip.
Writing data into Serial Port
Writing data to serial port is accomplished using the write() system call.
Reading Data from the Serial Port
Reading data from the serial port is accomplished by using the read() system call.
In the zip file you can find “SerialPort_read.c” which contains the complete program.Compile the program using gcc and run it.Remember to use sudo so that your executable runs as root.
After providing the root password,the program will execute and wait for a character to be send by the microcontroller.
You can now reset the MSP430 microcontroller connected to the USB2SERIAL converter to transmit the string .
Program receives the “Hello from MSP430” string send by the microcontroller and displays it on the terminal.
Источник