Preface
Previously, we used Demo to test the serial port's sending and receiving. In order to use the serial port conveniently later, we need to do some encapsulation to realize the serial port's sending and receiving interface.
Design ideas
The core idea is to design a FIFO ring buffer, write data into the FIFO during the serial port receive interrupt, and discard it when it is full.
The user receives the API to query the FIFO. If there is data, it will be read out. If there is no data, it will wait.
Both interrupts and user APIs operate FIFO, so FIFO needs to be protected from critical section operations.
The sending method is similar, but for simplicity, the query method is used for sending.
designing process
data structure
#include <stdint.h>
The ring buffer data structure is as follows
typedef struct
{
uint32_t datalen_u32;
uint32_t maxlen_u32;
uint32_t in_u32;
uint32_t out_u32;
uint8_t* buffer_pu8;
}ring_buffer_t;
Timing buffer stores received data
uint8_t uart_ring_buffer[128];
Instantiate a buffer
ring_buffer_t s_ring_buffer_t=
{
.datalen_u32 = 0,
.maxlen_u32 = sizeof(uart_ring_buffer),
.in_u32 = 0,
.out_u32 = 0,
.buffer_pu8 = uart_ring_buffer,
};
Critical Section Processing
The critical section is implemented by disabling interrupts when operating FIFO in Rx API
#include "bsp_api.h"
#define Alloc_Critical() FSP_CRITICAL_SECTION_DEFINE
#define Enter_Critical() FSP_CRITICAL_SECTION_ENTER
#define Exit_Critical() FSP_CRITICAL_SECTION_EXIT
Receive interrupt processing
void uart_rx_handler(const uint8_t *buffer, uint32_t length)
{
uint32_t i;
for(i=0;i<length; i++)
{
if(s_ring_buffer_t.datalen_u32 < s_ring_buffer_t.maxlen_u32)
{
s_ring_buffer_t.buffer_pu8[s_ring_buffer_t.in_u32] = buffer[i];
s_ring_buffer_t.datalen_u32++;
s_ring_buffer_t.in_u32++;
s_ring_buffer_t.in_u32 %= s_ring_buffer_t.maxlen_u32;
}
else
{
/* full */
break;
}
}
}
Uart_ep.c
#include "uart.h"
void user_uart_callback(uart_callback_args_t *p_args)
{
/* Logged the event in global variable */
g_uart_event = (uint8_t)p_args->event;
if(UART_EVENT_RX_CHAR == p_args->event)
{
uint8_t tmp = (uint8_t ) p_args->data;
uart_rx_handler(&tmp, 1);
}
}
Receiving API
uint32_t uart_read(uint8_t *buff, uint32_t len)
{
uint32_t readlen = 0;
//uint32_t mask;
if(s_ring_buffer_t.datalen_u32 == 0)
{
return 0;
}
Alloc_Critical();
Enter_Critical();
uint32_t i;
for(i=0;i<len;i++)
{
if(s_ring_buffer_t.datalen_u32 > 0)
{
buff[i] = s_ring_buffer_t.buffer_pu8[s_ring_buffer_t.out_u32];
s_ring_buffer_t.datalen_u32--;
s_ring_buffer_t.out_u32++;
s_ring_buffer_t.out_u32 %= s_ring_buffer_t.maxlen_u32;
readlen++;
}
else
{
break;
}
}
Exit_Critical();
return readlen;
}
Send API
int uart_write(uint8_t *buff, uint32_t len)
{
uint32_t i;
for(i=0; i<len ;i++)
{
uart_sendbyte(buff[i]);
}
return 0;
}
uart_sendbyte is implemented in uart_ep.c
void uart_sendbyte(uint8_t ch)
{
uint8_t tmp = ch;
fsp_err_t err = FSP_SUCCESS;
uint32_t local_timeout = (DATA_LENGTH * UINT16_MAX);
/* Reset callback capture variable */
g_uart_event = RESET_VALUE;
/* Writing to terminal */
err = R_SCI_UART_Write (&g_uart_ctrl, &tmp, 1);
if (FSP_SUCCESS != err)
{
return;
}
/* Check for event transfer complete */
while ((UART_EVENT_TX_COMPLETE != g_uart_event) && (--local_timeout))
{
/* Check if any error event occurred */
if (UART_ERROR_EVENTS == g_uart_event)
{
return;
}
}
return;
}
Serial port initialization
Use the original uart_initialize
test
uint8_t buffer[128];
for(;;)
{
uint32_t len=0;
if((len = uart_read(buffer, sizeof(buffer))) >0)
{
uart_write(buffer, len);
}
}
The host computer sends and the development board returns the data as it is. If the sent and received data are consistent, it means the function is OK.
Code
uart.c
#include <stdint.h>
#include "uart.h"
#include "bsp_api.h"
#define Alloc_Critical() FSP_CRITICAL_SECTION_DEFINE
#define Enter_Critical() FSP_CRITICAL_SECTION_ENTER
#define Exit_Critical() FSP_CRITICAL_SECTION_EXIT
typedef struct
{
uint32_t datalen_u32;
uint32_t maxlen_u32;
uint32_t in_u32;
uint32_t out_u32;
uint8_t* buffer_pu8;
}ring_buffer_t;
uint8_t uart_ring_buffer[128];
ring_buffer_t s_ring_buffer_t=
{
.datalen_u32 = 0,
.maxlen_u32 = sizeof(uart_ring_buffer),
.in_u32 = 0,
.out_u32 = 0,
.buffer_pu8 = uart_ring_buffer,
};
void uart_rx_handler(const uint8_t *buffer, uint32_t length)
{
uint32_t i;
for(i=0;i<length; i++)
{
if(s_ring_buffer_t.datalen_u32 < s_ring_buffer_t.maxlen_u32)
{
s_ring_buffer_t.buffer_pu8[s_ring_buffer_t.in_u32] = buffer;
s_ring_buffer_t.datalen_u32++;
s_ring_buffer_t.in_u32++;
s_ring_buffer_t.in_u32 %= s_ring_buffer_t.maxlen_u32;
}
else
{
/* full */
break;
}
}
}
uint32_t uart_read(uint8_t *buff, uint32_t len)
{
uint32_t readlen = 0;
//uint32_t mask;
if(s_ring_buffer_t.datalen_u32 == 0)
{
return 0;
}
Alloc_Critical();
Enter_Critical();
uint32_t i;
for(i=0;i<len;i++)
{
if(s_ring_buffer_t.datalen_u32 > 0)
{
buff = s_ring_buffer_t.buffer_pu8[s_ring_buffer_t.out_u32];
s_ring_buffer_t.datalen_u32--;
s_ring_buffer_t.out_u32++;
s_ring_buffer_t.out_u32 %= s_ring_buffer_t.maxlen_u32;
readlen++;
}
else
{
break;
}
}
Exit_Critical();
return readlen;
}
int uart_write(uint8_t *buff, uint32_t len)
{
uint32_t i;
for(i=0; i<len ;i++)
{
uart_sendbyte(buff);
}
return 0;
}
uart.h
#ifndef UART_H
#define UART_H
#include <stdint.h>
void uart_rx_handler(const uint8_t *buffer, uint32_t length);
uint32_t uart_read(uint8_t *buff, uint32_t len);
int uart_write(uint8_t *buff, uint32_t len);
extern void uart_sendbyte(uint8_t ch);
#endif
Summarize
Relying on the underlying serial port driver, a serial port driver interface based on a circular buffer FIFO is implemented to facilitate use by the subsequent application layer.
Thanks to the official complete driver and Demo, it can be implemented quickly.