Brief description
This library was created as a result of professional activities aimed at developing software for another project on a microcontroller It consists of 2 header files and 2 C files:
- uart.h
- uart.c
- RingFIFO.h
- RingFIFO.c
- Runs on stm32f103xx microcontrollers;
- Allows data exchange via UART1, 2, 3, 4. Data exchange through UART5 is not yet implemented (was not necessary, but this will be corrected );
- Ability to reinitialize UART directly during code execution;
- The ability to engage and customize the length of the FIFO-buffer on the receiver and transmitter;
- Code sections not used in this project will not be compiled into the project (implemented via #ifdef);
- Uses only CMSIS for operation;
- Not overloaded with unnecessary code (if possible) and requires not very many resources (reasonable price for usability).
Code: Select all
#define UART1_ENABLE
#define UART2_ENABLE
#define UART3_ENABLE
//#define UART4_ENABLE
Next comes the following:
Code: Select all
#ifdef UART1_ENABLE
#define UART1_USE_RING_BUFF
#define UART1_TXBUFF_LENGHT 16
#define UART1_RXBUFF_LENGHT 16
#endif
#ifdef UART2_ENABLE
#define UART2_USE_RING_BUFF
#define UART2_TXBUFF_LENGHT 16
#define UART2_RXBUFF_LENGHT 16
#endif
#ifdef UART3_ENABLE
#define UART3_USE_RING_BUFF
#define UART3_TXBUFF_LENGHT 16
#define UART3_RXBUFF_LENGHT 16
#endif
#ifdef UART4_ENABLE
#define UART4_USE_RING_BUFF
#define UART4_TXBUFF_LENGHT 16
#define UART4_RXBUFF_LENGHT 16
#endif
If we want to use the ring buffer, we need to uncomment #define UARTn_USE_RING_BUFF, n is the number of UART-a. Further, you can configure the length of the ring buffer for the receiver and transmitter through #define UARTn_TXBUFF_LENGHT and #define UARTn_RXBUFF_LENGHT, in this example, the lengths of the buffers are 16 bytes.
Then comes the initialization structure:
Code: Select all
typedef struct
{
uint32_t bus_freq; // uart bus frequency
uint32_t baud; //baud rate
uint8_t data_bits; //number of data bits (8, 9)
uint8_t stop_bits; //number of stop bits (1 or 2)
uint8_t parity; // parity check (0 - none, 1 - even, 2 - odd)
} UARTInitStructure_t;
Code: Select all
//UART initialization
//
//id - port number
//init - UART initialization structure
//
//Return: 0 - success, -1 - initialization error
int16_t UART_Init(uint8_t id, const UARTInitStructure_t *init);
//Write a character into the transmitter buffer
//
//id - port number
//s - symbol to be sent
//
//Retrun: s - success, -1 - error, buffer overflowed
int16_t UART_PutC(uint8_t id, char c);
//Read the symbol from the receiver buffer
//
//id - port number
//
//Return: -1 - no data to read, 0..255 - read character
int16_t UART_GetC(uint8_t id);
//Get the number of unread bytes in the buffer of the receiver
int16_t UART_BytesToRead(uint8_t id);
//Get the number of bytes not yet sent in the transmitter buffer
int16_t UART_BytesToWrite(uint8_t id);
//Clear the receiver buffer
void UART_ReadBuffClear(uint8_t id);
//Clear the transmitter buffer
void UART_WriteBuffClear(uint8_t id);
Next are the functions of working with ring buffers.
UART_BytesToRead() - returns the number of bytes in the receiver buffer, which can be read by the function UART_GetC().
UART_BytesToWrite() - with this function you can get the number of bytes in the UART transmitter buffer.
UART_ReadBuffClear() and UART_WriteBuffClear() - clear the buffer of the receiver and transmitter respectively.
Internal device
Full description of the library I will not spend a full description of the library, I will stop only on the function of UART initialization. It is organized as follows:
Code: Select all
int16_t UART_Init(uint8_t id, const UARTInitStructure_t *init)
{
if(id == 1)
{
....
return 0;
}
if(id == 2)
{
....
return 0;
}
if(id == 3)
{
....
return 0;
}
....
return -1;
}
Code: Select all
#ifdef UART1_ENABLE
/*
PORTS:
DEFAULT REMAP.
TX1 PA9 PB6
RX1 PA10 PB7
BUS:
APB2
*/
if(id == 1)
{
//Turn on UART module clocking
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
//Customize ports
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
//configure TX (PA9)
TxPinInit(&GPIOA->CRH,
GPIO_CRH_MODE9_Pos,
GPIO_CRH_CNF9_Pos);
//setup RX (PA10)
RxPinInit(&GPIOA->CRH,
&GPIOA->BSRR,
GPIO_CRH_MODE10_Pos,
GPIO_CRH_CNF10_Pos,
10);
//Module reset
RCC->APB2RSTR |= RCC_APB2RSTR_USART1RST;
asm("nop");
asm("nop");
asm("nop");
RCC->APB2RSTR &= ~RCC_APB2RSTR_USART1RST;
asm("nop");
asm("nop");
asm("nop");
//Initialization of the main registers of UART
if(_uart_init(USART1, init) < 0)
return -1;
#ifdef UART1_USE_RING_BUFF
RingBuffInit(&tx_fifo1, tx_buff1, UART1_TXBUFF_LENGHT);
RingBuffInit(&rx_fifo1, rx_buff1, UART1_RXBUFF_LENGHT);
RXNEIEnable(USART1);
NVIC_EnableIRQ(USART1_IRQn);
#endif
//Start UART
_uart_en(USART1);
return 0;
}
#endif
Code: Select all
//Turn on the clocking of the UART module
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
Code: Select all
//Set up the ports
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
//configure TX (PA9)
TxPinInit(&GPIOA->CRH,
GPIO_CRH_MODE9_Pos,
GPIO_CRH_CNF9_Pos);
//setup RX (PA10)
RxPinInit(&GPIOA->CRH,
&GPIOA->BSRR,
GPIO_CRH_MODE10_Pos,
GPIO_CRH_CNF10_Pos,
10);
Next is the initialization of the UART:
Code: Select all
//Reset the module
RCC->APB2RSR |= RCC_APB2RSTR_USART1RST;
asm("nop");
asm("nop");
asm("nop");
RCC->APB2RSTR &= ~RCC_APB2RSTR_USART1RST;
asm("nop");
asm("nop");
asm("nop");
//Initialization of the main registers of UART
if(_uart_init(USART1, init) < 0)
return -1;
After that we call the _uart_init() function, and in case of failure we exit with the return code -1.
Then we initialize the ring buffer, if it is used for this UART:
Code: Select all
#ifdef UART1_USE_RING_BUFF
RingBuffInit(&tx_fifo1, tx_buff1, UART1_TXBUFF_LENGHT);
RingBuffInit(&rx_fifo1, rx_buff1, UART1_RXBUFF_LENGHT);
RXNEIEnable(USART1);
NVIC_EnableIRQ(USART1_IRQn);
#endif
Code: Select all
//Start UART
_uart_en(USART1);
return 0;
Initially this library was written to work on stm32f103c8 microcontroller, then it was finalized on stm32f103ve, and for writing this article the project for stm32f103c8 was created again, and it didn't cause any problems. Most likely, there will be no problems when porting this library to any stm32f103 microcontroller.
So, let's move on to the code. Here is main.c:
Code: Select all
#include <stdint.h>
#include "clock.h"
#include "uart.h"
static const UARTInitStructure_t UARTInitStr =
{
.bus_freq = 36000000,
.baud = 19200,
.data_bits = 8,
.stop_bits = 1,
.parity = 0,
};
void main()
{
int16_t c;
ClockInit();
UART_Init(1, &UARTInitStr);
UART_ReadBuffClear(1);
UART_WriteBuffClear(1);
for(;;)
{
c = UART_GetC(1);
if(c != -1)
UART_PutC(1, (char)c);
}
}
- bus_freq - the frequency of the bus, which is connected to the module UART, in this case 36MHz
- baud - data transfer rate
- data_bits - the number of data bits, 8 or 9
- stop_bits - number of stop bits, 1 or 2
- parity - parity control, 0 - none, 1 - even, 2 - odd.
Let's go to main(). First initialize the clock generator with ClockInit() to the maximum frequency of operation. After that initialize UART1:
UART_Init(1, &UARTInitStr);
As arguments we pass the number of the UART and the initialization structure.
After that, we clear the buffers of the receiver and transmitter (it is not necessary), and in an infinite loop poll the receiver UART with the function UART_GetC(). If any character arrives, the function will return a value other than -1, which we immediately send back using UART_PutC(). Here is the whole example.