Serial communication C-library for STM32 and MSP430G2553 MCUs

1. Introduction

„libserial“ is part of „libemb„. It provides a simple to use API for configuring and using the first USART as an UART on the STM32 or USCI_A UART on the MSP430. The STM32 version of the library depends on „libopencm3“ for accessing the STM32 HW. It has no dependencies to other „libemb“ libraries.

For detailed building instructions see instructions for „libemb

2. Hardware Setup

No special hardware setup is required. Just connect the RX/TX (and GND) lines of your USB to serial converter to the corresponding lines of your MCU.

Note: When using the TI Launchpad, „/dev/ttyACM0“ could be used as serial port at 9600 Bauds, but RX/TX must be crossed on the jumper bridge of the Launchpad.

3. Including libserial Headers

To use „libserial“ functionality add the following include to your sources:

#include <libemb/serial/serial.h>

If you intend to use the ringbuffer that comes with libserial also include:

#include <libemb/serial/serial_rb.h>

4. Linking libserial Library

To link against „libserial“, add the following to your linker options:


5. Initializing the Hardware

Before using the functionality of libserial, the UART of your MCU has to be initialized. This is done by calling:


The serial_init method only takes one parameter: the desired baudrate to operate the UART with. Internally, the above method call does the following:

  • Configure the FIRST UART of your MCU to given baudrate@8,N,1
  • Configure the GPIOs used for RX and TX properly

Note: libserial clearly comes with some limitations. Databits, parity and stopbits are not configurable, you can not select which UART to use (allways the first UART is used). The limitations are due to the fact, that in 99% of all use cases they do not matter (since the defaults are good enough). If there is need, it may be that some of the limitations will be removed in the future.

6. Send/Receive

For sending/receiving a byte to/form the serial line, the following methos are available:

Method Description
serial_send Send one byte non-blocking to the serial line
serial_send_blocking Send one byte blocking to the serial line
serial_recv Receive one byte non-blocking from the serial line
serial_recv_blocking Receive one byte blocking from the serial line

1. Sending Data to the Serial Line

To send a byte non-blocking:


Or blocking:


2. Receiving Data from the Serial Line

To receive a character non-blocking:

unsigned char c = serial_recv();

Note: since the read is non-blocking, it will return imidiately. If there was no data to receive, „c“ will contain whatever currently is stored for that memory address. Thus, using „serial_recv“ is only a good idea when you are sure that there is data to read. E.g. „serial_recv“ comes in handy when using an ISR for receiving.

To receive a character blocking:

unsiged char c = serial_recv_blocking();

7. Using a Ringbuffer for Communication

In many cases when using serial communications, the need for some kind of buffer arises. Thus, libserial comes with a ringbuffer implementation which e. g. could be used when doing serial communications completely interrupt driven.

The ringbuffer implementatino provides the following methods:

Method Description
serial_rb_init Initialize a new ringbuffer
serial_rb_write Write element to a ringbuffer
serial_rb_read Read element from a ringbuffer
serial_rb_full Check if ringbuffer is full
serial_rb_empty Check if ringbuffer ie empty
serial_rb_free Get number of free ringbuffer entries

1. Initialize a Ringbuffer

Before using a ringbuffer you have to initialize it. This is done in the following steps:

1. Define the buffer holding the data

SERIAL_RB_Q rb_buf[64];

2. Define the struct which holds the ringbuffers meta data (read/write position, buffer, …)

serial_rb buf;

3. Initialize the ringbuffer

  • Pass pointer with RBs meta data
  • Pass pointer to buffer
  • Pass size of buffer
serial_rb_init(&srb, &(rb_buf[0]), 64);

2. Write to a Ringbuffer

After a ringbuffer is initialized, data could be written to it. Prior of writing data to a ringbuffer, make sure it is not full already:

1. Check if RB is NOT full

  • Pass pointer to RBs meta data
if(!serial_rb_full(&rb)) {
	// write to RB

2. Write Data to RB

  • Pass in RBs meta data
  • Pass in value to add to the RB
serial_rb_write(&rb, 'A');

3. Read from a Ringbuffer

Before reading from a ringbuffer, check if it is not already empty.

1. Check if RB is NOT empty

if(!serial_rb_empty(&rb)) {
	// read from RB

2. Read Data from RB

unsigned char c = serial_rb_read(&rb);

8. Complete Usage Examples

2 Comments to libserial

  1. paran's Gravatar paran
    28. Oktober 2012 at 21:15 | Permalink

    Hi, I missing purpose of function „serial_rb_full()“ in ring buffer.
    for example:

    client_server_msp430.c :

    interrupt(USCIAB0RX_VECTOR) USCI0RX_ISR(void)
    if (!serial_rb_full(&srx)) {
    serial_rb_write(&srx, UCA0RXBUF);

    What will be if buffer will be full? It is normal behavious that is filled and then Write pointer will be moved from end of RB to start of RB. I dont see in code that „moving“ ? Thanks

Leave a Reply

You must be logged in to post a comment.