Sunday, March 4, 2018

UART library for EFM8BB1 Busy Bee microcontrollers


This UART library is made for EFM8 microcontrollers. It uses circular buffers for transmission and reception of data. This way large amounts of data can be sent or received with only 5 or 10 bytes per buffer, thus saving memory.
Uses 8 data bits, 1 stop bit, no parity. Supported baud rates: 9600, 14400, 19200, 38400, 57600, 115200, 128000, 256000.



On EFM8BB10F8G-A device, the UART pins are on port 0 and pin 4 is TX and pin 5 is used for RX.

EFM8BB1 SOIC16 pinout


How to use the UART library

Includes 

Include the library uart header file and SFR declarations for your specific microcontroller. Optionally you might want to include the globals header that holds the CPU frequency from here globals header for EFM8BB1.


main.c
//=================================================================================
// Includes
//=================================================================================
#include <SI_EFM8BB1_Register_Enums.h> // SFR declarations
#include "uart.h"

If you don't use the globals header you must define the CPU frequency used for baud rate calculation in the main.c file.

main.c
//=============================================================================
// Defines
//=============================================================================
// CPU FREQUENCY IN Hz
#define F_CPU   24500000

In the UART library file there are two array buffers used to store data that is received and transmitted. These buffers are by default stored in XRAM and are set to 40 bytes for TX and 80 for RX and seems to be enough when using 115200 baud rate and a 49 MHz CPU. In case of a lower CPU speed if you notice any loss of data, try reducing the baud rate to 9600 and/or increase the size of the buffers.

uart.h
//-----------------------------------------------------------------------------
// Defines
//-----------------------------------------------------------------------------
#define UART_TX_BUFFER_SIZE 40 // Max 256 Bytes
#define UART_RX_BUFFER_SIZE 80 // Max 256 Bytes

Setup

In your main function run the setup function, only once and pass the desired baud rate. The function will configure Timer 1 used by the UART, calculate the prescaler depending on the CPU frequency, will setup the crossbar for UART and enable UART interrupts.
UART_Begin(BAUD_RATE);

Functions

UART_Send()

Send data over UART TX line. If the previous data is still being transmitted, the function will wait until the ISR finishes transmission. The string must be null terminated. If it's a string array a character must be reserved for the null when declaring the array size.
void UART_Send(const char *s)
 

UART_SendSize()

Same as UART_Send() but with an extra argument that is the number of bytes to be transmitted. This way an array that includes 0's can be transmitted. In the case of UART_Send() the function searches for a null terminator inside the string and 0 is null in ASCII and any data after 0 was not transmitted.
void UART_SendSize(const char *s, uint8_t *size)
 

 UART_Available()

Returns true if  there are new unread bytes and false if otherwise. timeout is a boolean parameter - true or false. If true, the function will wait a few milliseconds for the UART to have time receiving first byte. See the bellow examples on how it can be used.
bool UART_Available(bool timeout)
 

UART_ReadByte() 

Returns a received byte. On each reading it will return the next consecutive received byte until no more bytes are available. Note that the incoming data must be parsed as fast as possible because after the receive buffer will be full it will be rewritten from the beginning. The function is taking into account the circular buffer and will return the bytes in order.
When no new byte is available, it will return null '\0'.

Important: this function must be called only after UART_Available(). See the examples bellow.

For parsing, trimming and collecting large amount of data, I made a function for the ESP8266 library that does just that. It takes two marker words and puts the data between them in an array that the user provides. Also there is an option for passing a few keywords and it will return true if one or all keywords are found.
uint8_t UART_ReadByte(void)
 

UART_Read()

If you need to access the receive buffer, then this function returns the address of the UART receive buffer to access the received data.
const char *UART_Read(void)

UART_End()

Disables UART interrupts and Timer 1.
void UART_End(void)

Example

To test this example you need a serial terminal like Termite Terminal. Make sure you set the baud rate in the terminal similar to the one in the code. Comment out one of the method, don't use them at the same time or might interfere with each other.

//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------
#include <SI_EFM8BB1_Register_Enums.h> // SFR declarations
#include "globals.h"
#include "delay.h"
#include "uart.h"

//-----------------------------------------------------------------------------
// Defines
//-----------------------------------------------------------------------------
#define BAUD_RATE                       115200
#define RECEIVED_DATA_ARRAY_SIZE        80 // size of array where to store the incoming data

//-----------------------------------------------------------------------------
// Global CONSTANTS
//-----------------------------------------------------------------------------
//SI_SBIT(LED, SFR_P0, 0);   // LED on Port 0: Bit 0

///-----------------------------------------------------------------------------
// Function PROTOTYPES
//-----------------------------------------------------------------------------
void Port_Init(void);

//-----------------------------------------------------------------------------
// SiLabs_Startup() Routine
// ----------------------------------------------------------------------------
// This function is called immediately after reset, before the initialization
// code is run in SILABS_STARTUP.A51 (which runs before main() ). This is a
// useful place to disable the watchdog timer, which is enable by default
// and may trigger before main() in some instances.
//-----------------------------------------------------------------------------
void SiLabs_Startup(void){
 // Disable Watchdog with key sequence
 WDTCN = 0xDE; // First key
 WDTCN = 0xAD; // Second key
}


//-----------------------------------------------------------------------------
// main() Routine
// ----------------------------------------------------------------------------
// Note: the software watchdog timer is not disabled by default in this
// example, so a long-running program will reset periodically unless
// the timer is disabled or your program periodically writes to it.
//-----------------------------------------------------------------------------
int main(void){
 uint8_t i = 0;
 // Must be initiated with null values
 uint8_t xdata receivedData[RECEIVED_DATA_ARRAY_SIZE] = {'\0'};
 
 Port_Init();
 UART_Begin(BAUD_RATE);
 
 UART_Send("Send some text to the terminal\n");


 // Receiving data by passing true to UART_Available. If we were to use false as an argument then the
 // function will return false and skip the while loop because the UART is slower than the CPU. That's why we need a timeout of a few ms
 while(UART_Available(true)){
  receivedData[i] = UART_ReadByte();
  i++;
  if(i > RECEIVED_DATA_ARRAY_SIZE - 2){ // subtract 1 for the null character and 1 because of the index
   // Add null terminator
   receivedData[i] = '\0';
   i = 0;

   // Send the buffer contents over UART
   UART_Send(receivedData);

   // The buffer is full, exit the while loop
   break;
  }
 }

 while(1){
  // Since the function is in a loop here we can pass a false parameter indicating no timeout is necessary
  if(UART_Available(false)){
   receivedData[i] = UART_ReadByte();
   // .... as above
  }
 }
}


//-----------------------------------------------------------------------------
// Functions
//-----------------------------------------------------------------------------
void Port_Init(void){
 // -------------------------------------------------------------
 // 4. Enable crossbar and weak pull-ups
 // -------------------------------------------------------------
 XBR2 |= 0x40;

 // SYSCLK - CPU speed
 CLKSEL = CLKSEL_CLKSL__HFOSC | CLKSEL_CLKDIV__SYSCLK_DIV_1;
}




Download 

Version 2.2


No comments:

Post a Comment