Friday, March 15, 2024

UART library for Silicon Labs microcontrollers using interrupts

This UART library is made for Silicon Labs microcontrollers that can be used for serial communications.

UART is a type of serial interface, as opposed to a parallel interface. A parallel interface can work at higher speeds but the disadvantage is that it needs multiple input/output lines. Other examples of serial interfaces are SPI and I2C. 

UART library for Silicon Labs microcontrollers using interrupts

Supported Devices

At the moment only following devices are supported. I might add more in the feature. It might work for other similar devices not listed below.

  • C8051F330

 

Contents

 

UART Characteristics

Frame format

A serial frame is composed of a character of data bits with synchronization bits (start and stop bits). 8-Bit UART mode uses a total of 10 bits per data byte: one start bit, eight data bits (LSB first), and one stop bit.

A frame starts with the start bit, followed by 8 data bits (LSB first). When a complete frame is transmitted, it can be directly followed by a new frame, or the communication line can be set to an idle (high) state. The figure below illustrates the frame format:

UART frame format 8-bit
IDLE: No transfers on the communication line (Rx or Tx). An IDLE line must be high.

Start Bit: always low.

(D): Data bits (0 to 7).

Stop Bit: always high.

The Stop bit can be followed by a Start bit (low) for another frame, or Idle (high). All devices connected to the UART bus must use the same frame format and baud rate.

Baud Rate

The baud rate specifies how fast data is sent over a serial line. It's usually expressed in units of bits-per-second (bps). Most commonly used baud rates are 9600 and 115200 bps.

"The UART0 baud rate is generated by Timer 1 in 8-bit auto-reload mode. The TX clock is generated by TL1; the RX clock is generated by a copy of TL1 (shown as RX Timer in Figure 16.2), which is not user- accessible. Both TX and RX Timer overflows are divided by two to generate the TX and RX baud rates. The RX Timer runs when Timer 1 is enabled, and uses the same reload value (TH1). However, an RX Timer reload is forced when a START condition is detected on the RX pin. This allows a receive to begin any time a START is detected, independent of the TX Timer state." from C8051F330 datasheet chapter 16.

Wiring

Lets say you want to interface UART 0 with a device that has this pins: RX and TX. Then you connect RXD0 to TX and TXD0 to RX. Also they must share a common ground.

For most Silicon Labs microcontrollers the TX/RX pins are as follows:

TX0 on P0.4, RX on P0.5

Check the datasheet for your specific device to ensure the pins are correct.

Library Structure

The file uart.h defines some user settings that can be modified accordingly.

TX and RX buffers

Transmitted and Received data are buffered in two circular arrays that by default have a size of 16 bytes each. The size can be from 1 to 255 bytes. Having a larger array can be a bit faster. When using higher baud rates it is recommended a bigger buffer to avoid loosing incoming data and to give more time for the microcontroller to process the data.

#define UART_TX_BUFFER_SIZE	16
#define UART_RX_BUFFER_SIZE	16

CPU clock frequency - SYSCLK

 
#define SYSCLK    24500000UL

SYSCLK defines the processor clock frequency in Hertz and is used to calculate the timeout in some functions. 

SYSCLK is best to be defined inside project properties in Simplicity Studio (or your particular IDE) or a Makefile if custom Makefiles are used. See https://www.programming-electronics-diy.xyz/2024/03/defining-sysclk-or-fcpu-in-simplicity.html.

Using UART library

Initialization function

Sets TX, RX pins and the crossbar. Configures UART and Timer 1. Sets the baud rate, enables UART interrupts and global interrupts.

void UART_init(int32_t sysclk, int32_t baudrate)

Parameters

sysclk

System clock in Hertz, SYSCLK also known as F_CPU.

baudrate

Desired baud rate. Some standard values are: 9600, 14400, 19200, 38400, 57600, 115200, 128000 and 256000.

Usage:

UART_init(SYSCLK, 115200);
 

Send a byte

Puts a byte of data in the transmit buffer and enables the transmit interrupt.

uint8_t UART_send(uint8_t data)

Parameters

 
data

The data byte.

Return: 0 on success, 1 on failure.

Usage:

// Send 'A' ASCII character
UART_send('A');

// Send '3' ASCII symbol
UART_send('3');

// Send number of the '3' ASCII symbol
UART_send(51);

Send a string

Send a null terminated string.

uint8_t UART_sendString(char* s)

Parameters

 
char* s

A string of characters.

Return: 0 on success, 1 on failure.

Usage:

UART_sendString("Sent from UART0");
// Or using the '\n' character to start a new line in a serial terminal.
UART_sendString("Sent from UART0\n");

Send bytes

Send a series of bytes in a buffer.

uint8_t UART_sendBytes(uint8_t* buff, uint8_t length)

Parameters


uint8_t* buff

Pointer to a byte array.

uint8_t length

Length of the array.

Return: 0 on success, 1 on failure.

Usage:

const uint8_t bufferSize = 20;
uint8_t buff[bufferSize];

// bufferSize argument can be smaller to
// send only first few bytes
UART_sendBytes(buff, bufferSize);

Send integer number

Convert an integer number into a string array and send it over UART.

void UART_sendInt(INT_SIZE number)

Parameters


INT_SIZE number

INT_SIZE is defined in the "utils.h" file, and can be int32_t or int64_t


Send float number

Convert a float number into a string array and send it over UART.

void UART_sendFloat(float number, uint8_t decimals)

Parameters


float number

A float number.

uint8_t decimals

Number of digits after the dot.


Send hex numbers

Convert a 1- 2- or 4-byte integer number into hexadecimal value and send it over UART.

void UART_sendHex8(uint8_t value)
void UART_sendHex16(uint16_t value)
void UART_sendHex32(uint32_t value)


Check for new received data

Returns true if new data is available and false otherwise.

bool UART_available(void)


Wait for transmission complete

Waits in a while loop until all bytes in the buffer have been transmitted. Can be used before putting the microcontroller to sleep to ensure all data has been transmitted. Baud rate and SYSCLK are used to calculate the time it takes for the last byte to be transmitted after the interrupt is disabled but the last byte is still transmitted.

void UART_isSending(float sysclk, float baudrate)
 

Parameters

sysclk

CPU clock frequency in Hertz.

baudrate

UART baudrate used in initialization function.


Read a byte

Returns the next received byte or 0 if no new data is available. Should be used only if UART_available() returns true.

uint8_t UART_read(void)


Read bytes

Read received bytes into the provided buffer. The function terminates if the specified length has been read, or it times out (around 0.5s).

uint8_t UART_readBytes(uint8_t* buff, uint8_t length)

Parameters


uint8_t* buff

An array buffer where to put incoming data.

uint8_t length

Length of the array.

Return: the number of characters read.

Usage:

const uint8_t bufferSizeRX = 20;
uint8_t buff[bufferSizeRX];

if(UART_available()){
    // Read serial data
    UART_readBytes(buff, bufferSizeRX);
    // Now 'buff' contains received data
}

Read bytes until

Read received bytes into the provided buffer. The function terminates if the specified length has been read, the termination character has been found or it times out (around 0.5s). The termination character is not included in the buffer.

uint8_t UART_readBytesUntil(char character, uint8_t* buff, uint8_t length)

Parameters


char character

The termination character. When this character is encountered, the function terminates.

uint8_t* buff

An array buffer where to put incoming data.

uint8_t length

Length of the array.

Return: the number of characters read.


Disable UART

Disable UART transmitter, receiver and interrupts.

void UART_end(void)

Flush

Resets buffers to 0.

void UART_flush(void)

Redirect received data

Redirects received data to a user defined function.

void UART_setRXhandler(void (*rx_func)(uint8_t c))

Usage:

void rxHandler(uint8_t c){
	// This function is called inside the RX interrupt
	// and it should not take long time.
	// 'c' is the received byte.
}

UART_setRXhandler(&rxHandler);


Code example

#include "uart.h"
#include "delay.h"
 
int main(void){
    const uint8_t bufferSize = 20;
    char_size_t buff[bufferSize];
    uint8_t bytes_read = 0;
	
    UART_init(SYSCLK, 115200);
    UART_sendString("I'm UART 0\r");
	
    UART_sendString("Hi there!\r");
    _delay_ms(2000);
    UART_sendString("Is this thing working?\r");

    while(1){
        if(UART_available()){
	    // Read serial data in the 'buff' array
	    bytes_read = UART_readBytes(buff, bufferSize);
            UART_sendString("Received "); 
            UART_sendInt(bytes_read);
	    UART_sendString(" bytes\r");
			
	    if(bytes_read == bufferSize){
	        // Print received data to a serial terminal
		UART_sendBytes(buff, bytes_read);
		UART_send('\r');
	    }
			
	} // end if UART_available()
    }
}

Download

uart v1.0

uart Contains uart.h and uart.c
utils v1.0

utils Used by sendInt() and sendFloat()
External Links

Termite terminal Nice serial terminal
Changelog
v1.0 14, March, 2024:
- released

No comments:

Post a Comment