Add ring buffer for uart TX

This commit is contained in:
Mario Hüttel 2020-02-11 22:49:47 +01:00
parent 14ba09a716
commit 673e651910
6 changed files with 185 additions and 12 deletions

View File

@ -14,9 +14,26 @@ struct dma_ring_buffer_to_mem {
size_t element_size;
};
struct dma_ring_buffer_to_periph {
void *src_buffer;
size_t buffer_count;
DMA_Stream_TypeDef *dma;
volatile size_t dma_get_idx_current;
volatile size_t dma_get_idx_future;
volatile size_t sw_put_idx;
uint8_t dma_base_id;
size_t element_size;
};
int dma_ring_buffer_periph_to_mem_initialize(struct dma_ring_buffer_to_mem *dma_buffer, uint8_t base_dma_id, DMA_Stream_TypeDef *dma_stream, size_t buffer_element_count, size_t element_size, void *data_buffer, void *src_reg, uint8_t dma_trigger_channel);
int dma_ring_buffer_periph_to_mem_get_data(struct dma_ring_buffer_to_mem *buff, const void **data_buff, size_t *len);
void dma_ring_buffer_periph_to_mem_stop(struct dma_ring_buffer_to_mem *buff);
int dma_ring_buffer_mem_to_periph_initialize(struct dma_ring_buffer_to_periph *dma_buffer, uint8_t base_dma_id, DMA_Stream_TypeDef *dma_stream, size_t buffer_element_count, size_t element_size, void *data_buffer, uint8_t dma_trigger_channel, void *dest_reg);
int dma_ring_buffer_mem_to_periph_insert_data(struct dma_ring_buffer_to_periph *buff, const void *data_to_insert, size_t count);
void dma_ring_buffer_mem_to_periph_int_callback(struct dma_ring_buffer_to_periph *buff);
void dma_ring_buffer_mem_to_periph_stop(struct dma_ring_buffer_to_periph *buff);
#endif /* __DMA_RING_BUFFER_H__ */

View File

@ -6,7 +6,7 @@
#define UART_RECEIVE_DMA_STREAM DMA2_Stream5
#define UART_SEND_DMA_STREAM
#define UART_SEND_DMA_STREAM DMA2_Stream7
#define UART_PERIPH USART1
#define UART_RCC_MASK RCC_APB2ENR_USART1EN
@ -42,11 +42,11 @@ void uart_send_char(char c);
void uart_send_array(const char *data, uint32_t len);
void uart_send_string(char *string);
void uart_send_string(const char *string);
void uart_send_array_with_dma(char *data, uint32_t len);
void uart_send_array_with_dma(const char *data, uint32_t len);
void uart_send_string_with_dma(char *string);
void uart_send_string_with_dma(const char *string);
int uart_receive_data_with_dma(const char **data, size_t *len);

View File

@ -57,7 +57,6 @@ int main()
shell_handle_input(shell_handle, uart_input, uart_input_len);
}
//systick_wait_ms(300);
}
}

View File

@ -16,8 +16,7 @@ static char history_buffer[1024];
static shellmatta_retCode_t write_shell_callback(const char *data, uint32_t len)
{
uart_send_array(data, len);
uart_send_array_with_dma(data, len);
return SHELLMATTA_OK;
}

View File

@ -1,6 +1,7 @@
#include <uart/dma-ring-buffer.h>
#include <clock-enable-manager.h>
#include <stdbool.h>
#include <string.h>
static int dma_ring_buffer_switch_clock_enable(uint8_t base_dma, bool clk_en)
{
@ -105,3 +106,135 @@ void dma_ring_buffer_periph_to_mem_stop(struct dma_ring_buffer_to_mem *buff)
dma_ring_buffer_switch_clock_enable(buff->base_dma_id, false);
}
int dma_ring_buffer_mem_to_periph_initialize(struct dma_ring_buffer_to_periph *dma_buffer, uint8_t base_dma_id, DMA_Stream_TypeDef *dma_stream, size_t buffer_element_count, size_t element_size, void *data_buffer, uint8_t dma_trigger_channel, void *dest_reg)
{
if (!dma_buffer || !dma_stream || !data_buffer || !dest_reg)
return -1000;
dma_buffer->dma = dma_stream;
dma_buffer->dma_base_id = base_dma_id;
dma_buffer->src_buffer = data_buffer;
dma_buffer->buffer_count = buffer_element_count;
dma_buffer->element_size = element_size;
dma_buffer->sw_put_idx = 0U;
dma_buffer->dma_get_idx_current = 0U;
dma_buffer->dma_get_idx_future = 0U;
dma_ring_buffer_switch_clock_enable(base_dma_id, true);
dma_stream->PAR = (uint32_t)dest_reg;
dma_stream->CR = DMA_SxCR_MINC | DMA_SxCR_TCIE | (dma_trigger_channel<<25) | DMA_SxCR_DIR_0;
return 0;
}
static size_t calculate_ring_buffer_fill_level(size_t buffer_size, size_t get_idx, size_t put_idx)
{
size_t fill_level;
if (put_idx >= get_idx) {
fill_level = (put_idx - get_idx);
} else {
fill_level = buffer_size - get_idx + put_idx;
}
return fill_level;
}
static void queue_or_start_dma_transfer(struct dma_ring_buffer_to_periph *buff)
{
uint32_t dma_transfer_cnt;
if (!buff)
return;
/* Check if DMA is running. Do nothing in this case. Will be stated from interrupt */
if (buff->dma_get_idx_current != buff->dma_get_idx_future)
return;
/* No new data to transfer */
if (buff->sw_put_idx == buff->dma_get_idx_current)
return;
/* Calculate future get idx. Stop at end of buffer to prevent impossible wrap around */
if (buff->sw_put_idx < buff->dma_get_idx_current && buff->sw_put_idx != 0) {
buff->dma_get_idx_future = 0U;
dma_transfer_cnt = buff->buffer_count - buff->dma_get_idx_current;
} else {
buff->dma_get_idx_future = buff->sw_put_idx;
if (buff->sw_put_idx == 0)
dma_transfer_cnt = buff->buffer_count - buff->dma_get_idx_current;
else
dma_transfer_cnt = buff->sw_put_idx - buff->dma_get_idx_current;
}
buff->dma->NDTR = dma_transfer_cnt;
buff->dma->M0AR = (uint32_t)&((char *)buff->src_buffer)[buff->dma_get_idx_current * buff->element_size];
buff->dma->CR |= DMA_SxCR_EN;
}
int dma_ring_buffer_mem_to_periph_insert_data(struct dma_ring_buffer_to_periph *buff, const void *data_to_insert, size_t count)
{
int ret = 0;
size_t free_item_count;
char *insert_ptr;
char *dest_ptr;
void *ptr;
size_t first_round_count;
if (!buff || !data_to_insert || !count)
return -1000;
/* Check if data fits into buffer minus one element. If not: try full-1 buffer and rest
* Buffer is not allowed to be completely full, because I cannot ddifferentiate a full buffer from a completely empty one
*/
if (count >= buff->buffer_count) {
ret = dma_ring_buffer_mem_to_periph_insert_data(buff, data_to_insert, buff->buffer_count - 1);
if (ret)
goto return_retval;
ptr = (void *)(((char *)data_to_insert) + ((buff->buffer_count-1) * buff->element_size));
ret = dma_ring_buffer_mem_to_periph_insert_data(buff, ptr, count - buff->buffer_count + 1);
goto return_retval;
}
/* Wait for buffer to be able to handle input */
do {
free_item_count = buff->buffer_count - calculate_ring_buffer_fill_level(buff->buffer_count, buff->dma_get_idx_current, buff->sw_put_idx);
} while (free_item_count < count+1);
/* Fillup buffer (max is buffer end, wrap around afterwards) */
insert_ptr = (char *)data_to_insert;
dest_ptr = &((char *)buff->src_buffer)[buff->sw_put_idx * buff->element_size];
if (buff->buffer_count - buff->sw_put_idx >= count) {
memcpy(dest_ptr, insert_ptr, buff->element_size * count);
buff->sw_put_idx += count;
if(buff->sw_put_idx >= buff->buffer_count)
buff->sw_put_idx = 0;
} else {
first_round_count = buff->element_size * (buff->buffer_count - buff->sw_put_idx);
memcpy(dest_ptr, insert_ptr, first_round_count);
insert_ptr += first_round_count;
memcpy(buff->src_buffer, insert_ptr, count - first_round_count);
buff->sw_put_idx = count - first_round_count;
}
queue_or_start_dma_transfer(buff);
return_retval:
return ret;
}
void dma_ring_buffer_mem_to_periph_int_callback(struct dma_ring_buffer_to_periph *buff)
{
/* update current get index */
buff->dma_get_idx_current = buff->dma_get_idx_future;
queue_or_start_dma_transfer(buff);
}
void dma_ring_buffer_mem_to_periph_stop(struct dma_ring_buffer_to_periph *buff)
{
buff->dma->CR = 0;
dma_ring_buffer_switch_clock_enable(buff->dma_base_id, false);
memset(buff, 0, sizeof(struct dma_ring_buffer_to_periph));
}

View File

@ -13,10 +13,12 @@
#include <clock-enable-manager.h>
#include <stm32-gpio-macros.h>
#include <uart/dma-ring-buffer.h>
#include <string.h>
static struct dma_ring_buffer_to_mem ring_buff_rx;
static struct dma_ring_buffer_to_periph ring_buff_tx;
static char uart_rx_buffer[64];
static char uart_tx_buffer[256];
#ifdef DEBUGBUILD
static inline void uart_gpio_config()
@ -41,6 +43,10 @@ void uart_init_with_dma()
dma_ring_buffer_periph_to_mem_initialize(&ring_buff_rx, 2, UART_RECEIVE_DMA_STREAM, sizeof(uart_rx_buffer), 1U,
uart_rx_buffer, (char *)&UART_PERIPH->DR, 4);
dma_ring_buffer_mem_to_periph_initialize(&ring_buff_tx, 2, UART_SEND_DMA_STREAM, sizeof(uart_tx_buffer), 1U,
uart_tx_buffer, 4U, (void *)&UART_PERIPH->DR);
NVIC_EnableIRQ(DMA2_Stream7_IRQn);
}
void uart_disable()
@ -49,7 +55,7 @@ void uart_disable()
UART_PERIPH->CR2 = 0;
UART_PERIPH->CR3 = 0;
dma_ring_buffer_periph_to_mem_stop(&ring_buff_rx);
dma_ring_buffer_mem_to_periph_stop(&ring_buff_tx);
rcc_manager_disable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(UART_PORT_RCC_MASK));
rcc_manager_disable_clock(&RCC->APB2ENR, BITMASK_TO_BITNO(UART_RCC_MASK));
}
@ -68,7 +74,7 @@ void uart_send_array(const char *data, uint32_t len)
uart_send_char(data[i]);
}
void uart_send_string(char *string)
void uart_send_string(const char *string)
{
int i;
@ -76,11 +82,30 @@ void uart_send_string(char *string)
uart_send_char(string[i]);
}
void uart_send_array_with_dma(char *data, uint32_t len);
void uart_send_array_with_dma(const char *data, uint32_t len)
{
dma_ring_buffer_mem_to_periph_insert_data(&ring_buff_tx, data, len);
}
void uart_send_string_with_dma(char *string);
void uart_send_string_with_dma(const char *string)
{
size_t len;
len = strlen(string);
uart_send_array_with_dma(string, (uint32_t)len);
}
int uart_receive_data_with_dma(const char **data, size_t *len)
{
return dma_ring_buffer_periph_to_mem_get_data(&ring_buff_rx, (const void **)data, len);
}
void DMA2_Stream7_IRQHandler()
{
uint32_t hisr = DMA2->HISR;
DMA2->HIFCR = hisr;
if (hisr & DMA_HISR_TCIF7) {
dma_ring_buffer_mem_to_periph_int_callback(&ring_buff_tx);
}
}