USART (JSAPI_USART)

USART (Universal Synchronous/Asynchronous Receiver Transmitter), commonly known as a “serial port”, is a common method to connect devices across a distance. RS-232 is one of the most popular UART protocols. Devices on both ends of a UART link can initiate communication, and there is no master or slave in the communication. The term asynchronous as used in the name refers to the fact that the data is clocked out of the sending device asynchronously with the receiving device, and there is no common clock signal between them. Both ends of the communication must either agree on the communication rate (commonly called baud-rate), or the receiving device may be able to deduce the baud rate from the data stream.

In modern MCUs, the UART peripheral unit usually also can handle synchronous protocols such as SPI (see below), hence the name USART with the term “synchronous” added. The JSAPI_USART class currently does not support synchronous mode, but likely may in the future.

A UART uses two signal lines, TX (Transmit) and RX (Receive), and two optional handshaking lines, CTS (Clear to Send) and RTS (Request to Send):

https://lh4.googleusercontent.com/jJiUFszx8-kw8b43i1zt-wDsisC4ZcqgRgxkLfdx9LYcsWAUPrppwrsO9EYQaZh6mK9MigGAktwFqd1Q8eiKuNFTi0tKvprNagcUmx8sO1KUHdOwIZReFoDaBz9Mv7fX7DbFTT4

The TX of one device is connected to the RX of the other device, and the CTS from one is connected to the RTS of the other. The notation nCTS and nRTS with the ‘n’ prefix means that the signal is active low. If the optional CTS/RTS hardware handshaking is used:

·         If a device is ready to receive a character, it asserts its RTS line by making it active low.

·         If a device wants to send a character, it checks its CTS line, which is connected to the RTS of the other device. If it is low, it can then transmit.

UART protocol formats the data into frames, with each frame carrying 7 to 9 bits of data, plus overhead. The communicating devices must agree on these protocol characteristics beforehand:

·         The baud rate - the number of bits per second. The stated baud rate is for the whole frame, including transmission of the stop bits and parity bit.

·         The number of data bits, which can range from 7 to 9 bits

·         The number of stop bits, from 0, 1, 1.5 to 2

·         The parity bit, which can be none, odd, or even. Parity is used for error detection.

VCOM and Windows

Besides communication between two embedded devices, UART is also a common way to connect an embedded device to a PC. Doing serial I/O is fairly easy and straight forward, usually involving a call to open the serial port, followed by reading and writing to the port using the equivalence of C’s getchar and putchar.

Modern PCs eschew real serial ports, however, replacing them with USB ports. In response, today’s embedded devices often use a serial-to-USB converter; e.g.: a FTDI FT232 chip, so that on the embedded side the same simple serial I/O programming can be used. The serial-to-USB converter converts the serial data to USB data. On the Windows side, a driver provides a VCOM (Virtual Com Port) interface to the Windows program. The net effect is that both the embedded firmware and the Windows PC program can continue to communicate using the simple serial protocol while having the convenience of being able to connect via a USB cable.

Serial-to-USB conversion can be done in different ways. For example, the ST-Nucleo and ST-DISCO boards use the built-in ST-LINK debugger chip to also handle serial-to-USB conversion.

Interrupt-Driven I/O

In non-interrupt mode (polled mode), writing a character (using putchar) to the UART requires any character previously written to the UART output register to actually be transferred out before the new character can proceed. This could take a long time in MCU time. For example, with 9600 baud, it takes almost 1 millisecond (or approximately 80000+ instructions for an ARM MCU running at 84 MHz) to transmit one character. With polled mode, the user program does nothing else during this period, wasting CPU cycles. Some UART units include hardware FIFO (First In First Out) buffers (that is, bytes are retrieved from the buffer in the same order as they were received), so that multiple characters can be written to the UART, minimizing the wait time of the embedded firmware. However, the size of the hardware FIFO is device-dependent, and not under firmware control.

Software FIFO buffers are used with JumpStart API’s USART interrupt-driven mode. These buffers operate independently with any hardware FIFO that the UART hardware may have. You can use buffers you declare yourself, or call the SetIntrMode function to use the built-in buffers, which are 12 bytes each. There is no reason to use your own buffers unless you want buffers that are larger than the default 12 bytes, as the internal buffers are always allocated even when they not being used, and there is no performance advantage gained by using smaller buffers.

The putchar output function places the characters in the transmit buffer. This allows the putchar function to quickly return to the calling function without waiting for the slow UART hardware to finish. JumpStart API sets up an interrupt handler to run whenever the UART hardware can transmit a character. Whenever it runs, it removes a character from the transmit buffer and writes it to the UART.

In the case where the transmit buffer is full when putchar is called, the function waits until there is space available, i.e. it waits until a UART transmit interrupt occurs and the interrupt handler is run to remove a character from the buffer. The size of the buffer and the user code determine how often this happens.

If putchar is called from an interrupt handler and the transmit buffer is full, putchar temporarily switches to polled mode to wait for the UART hardware to be available. This is necessary because if the calling interrupt handler has an interrupt priority equal to or higher than the UART interrupt priority, the UART interrupt will never occur, and putchar will hang.

Interrupt-driven input works similarly: whenever a character is available at the UART hardware, it is put into the receiver buffer. When the user program calls getchar, if there is a character available in the receive buffer, it is removed from the buffer and returned to the user. Otherwise, getchar returns -1.

If the receiver buffer is full when there is a character available at the hardware, the character is read and dropped, as it is uncertain when the user code will perform a read and free up space in the receive buffer. There is no indication for this error condition.

Object

extern JSAPI_USART usart1, usart2, usart3, usart4, usart5;

Enum

enum FLOW_CONTROL {

    FC_NONE,      

    FC_HARDWARE,  

    FC_SOFTWARE

};  

Associate GPIO pins with USART functions

void SetPins(

    JSAPI_GPIO *tx_port,

    unsigned tx_pin_no,

    unsigned tx_af,

    JSAPI_GPIO *rx_port,

    unsigned rx_pin_no,

    unsigned rx_af

);

Make a USART unit.

void MakeUSART(

    unsigned baud,

    unsigned bits,

    unsigned stop,

    enum FLOW_CONTROL fc

);

Control the buffered state of the stdin and stdout. This is only useful when printf/scanf redirection is used

int ControlStreamBuffering(

    FILE * stream,

    bool isBuffered

);

Enable interrupt-driven IO

int SetIntrBuffers(

    unsigned char *txbuff,

    unsigned txlen,

    unsigned char *rxbuff,

    unsigned rxlen,

    int priority

);

Enable interrupt-driven IO using built-in buffers of 12 bytes each for input and output

int SetIntrMode(int priority);

Associate a read filter with interrupt-driven IO

int SetReadFilter(

    int (*readHandler)(unsigned char c),

    int *pbytes

);

Set the BAUD rate of the USART

void SetBaudrate(unsigned baud);

Write data to USART

int putchar(int ch);

Read a byte

int getchar(void);

In interrupt-driven IO, check to see if data is available for reading

int kbhit(void);

Enable USART

void Enable(void);

Disable USART

void Disable(void);

 

How to Use

To use the JumpStart USART API with a JSAPI_USART object (e.g. usart1):

1.       Call SetPins to associate GPIO pins with the USART.

2.       Call MakeUSART to specify the USART protocol parameters.

3.       Call JSAPI_RetargetConsole with the USART object if you want to redirect printf/putchar to the USART’s output.

4.       Call SetIntrMode or SetIntrBuffers if you want to use interrupt-driven I/O.

5.       Use printf/puts/putchar to write to the USART port.

6.       Use gets/getchar to read from the USART port.

Example

This simple example implements an “echo” program where each character typed (and received) will be echoed back to the terminal window. The UART is set to use interrupt-driven mode, so the function kbhit is used to check if a character is available.

    usart2.SetPins(&porta, 2, 7, &porta, 3, 7);

    usart2.MakeUSART(9600, 8, 1, FC_NONE);

    //

    // In order to use the compiler standard input and output

    // functions the I/O must be retargetted to your hardware.

    //

    // Here we retarget USART2 to be the standard Input/Output.

    //

    // Functions like "printf" now use USART2

    //

    JSAPI_RetargetConsole(&usart2);

    usart2.SetIntrMode();

   

    while (1)

        {

        if (usart2.kbhit())

            putchar(getchar());

        asm(“wfi”);

        }

 

Note the use of the WFI [3] assembly instruction to pause the CPU. Otherwise, the CPU would just continue to “busy-check” if a key has been entered. Busy-checking defeats the purpose of using interrupt-driven I/O, so while this is a trivial example and it makes no difference to use the WFI instruction, it is still a good coding programming practice to follow.