STM8 – How I do it

Since I’m coming a little more from the hardware-side of those projects, I prefer data sheets and low-level documentation. When tinkering with microcontrollers, I often had the feeling, that I spent too much time “reverse-engineering” overpowered abstraction layers, bringing together the code and what was really going on on the machine. I then decided to code the stuff as they call it “bare-metal”. The disadvantage is clear: the code may not be as re-usable as it could be. On the other side, I like to use the low-level user manuals directly for programming, because this kind of documentation is very reliable and comprehensive. Additionally, if coding some “non-standard” usage of peripherals, one would fall back to “register level” easily; so why not only code like this when tinkering near to the hardware?

On the coding level, when I want to use some peripheral, I define a pointer variable that points to a stuct. The struct represents the registers which are described in the Reference Manual of the µC. The addresses of the peripherals are defines in the header file. This way, I can just copy the information from the Reference Manual and the documentation of the manufacturer can directly be found in my code.

The following is an example of “bare-metal” definitions for the UART-Peripheral of my STM8L in a header file (stm8l15x_usart.h):

#ifndef __STM8L15x_USART_H
#define __STM8L15x_USART_H

#include "STM8L15x.h"

#define USART1_OFFS (USART_t*)0x005230
#define USART2_OFFS (USART_t*)0x0053E0
#define USART3_OFFS (USART_t*)0x0053F0

#define QQ_BAUD_16M_115k2 	(uint16_t)0x008B
#define QQ_BAUD_16M_9k6		(uint16_t)0x0683

struct USART_s
{
    volatile uint8_t SR;
    volatile uint8_t DR;
    volatile uint8_t BRR1;
    volatile uint8_t BRR2;
    volatile uint8_t CR1;
    volatile uint8_t CR2;
    volatile uint8_t CR3;
    volatile uint8_t CR4;
    volatile uint8_t CR5;
    volatile uint8_t GTR;
    volatile uint8_t PSCR;
};
typedef volatile struct USART_s USART_t;

void usart_init(uint32_t baudrate);

#endif//__STM8L15x_USART_

The corresponding register set is defined in the STM8L Reference Manual:



USART Peripheral Register Set of STM8L MCUs (Excerpt from Reference Manual)


Depending on the need of initialization functions for the peripheral, I create a c-file (e.g. stm8l15x_usart.c) and link the resulting object file into the project, or not.

#include "STM8L15x_USART.h"

extern USART_t *USART;
extern GPIO_t *PORTG;
extern CLK_t *CLK;


void usart_init(uint32_t baudrate) 
{
    uint16_t baud_div;
	
    CLK->PCKENR3 |= (1<<4/*USART3 Enable*/);

    if(baudrate == 9600)
    {
    	baud_div = QQ_BAUD_16M_9k6;
    }
    else
    {
    	baud_div = QQ_BAUD_16M_115k2;
    }
 
    // Set Baud Rate
    USART->BRR2 =  (baud_div & 0x0F) | ((baud_div >> 8) & 0xF0); 
    // LSB
    USART->BRR1 =  (baud_div >> 4) & 0xFF; // MSB
   

    // Init Control Regs
    USART->CR1 = 0x00; // 8N1
    USART->CR2 = 0x0C; // RX ON, TX ON 
    
    
    PORTG->DDR |= 2;
    PORTG->CR1 |= 2;
}