diff --git a/drivers/usart.c b/drivers/usart.c index 7567c87..e317567 100644 --- a/drivers/usart.c +++ b/drivers/usart.c @@ -8,6 +8,7 @@ //--includes-------------------------------------------------------------------- #include "usart.h" +#include "nvic.h" #include "usart_regs.h" #include "reg.h" @@ -26,15 +27,28 @@ struct CircularBuffer { bool dmaLooped; }; +struct FragmentedBuffer { + uint8_t** buffers; + uint16_t buffer_size; + uint16_t byte_index; + uint8_t buffer_nb; + uint8_t free_buffer_nb; + uint8_t buffer_index; + uint8_t dma_buffer_index; +}; + static void configure_usart(volatile struct USART* regs, enum UsartConfig config); static void configure_baudrate(volatile struct USART* regs, uint32_t clock, uint32_t baudrate); -static uint32_t periph_regs(enum UsartPeriph periph, - volatile struct USART** regs); -static uint32_t read_from_buffer(struct CircularBuffer* buffer, +static uint32_t write_byte(volatile struct USART* regs, uint8_t byte); +static uint32_t write_to_buffer(volatile struct USART* regs, + volatile struct FragmentedBuffer *buffer, enum DmaChannel channel, + uint8_t byte); +static uint32_t read_from_buffer(volatile struct CircularBuffer* buffer, enum DmaChannel channel, uint8_t* byte); +static void usart1_tx_callback(enum DmaIRQSource src); static void usart1_rx_callback(enum DmaIRQSource src); @@ -44,7 +58,8 @@ static volatile struct USART* const usart1 = (struct USART*)USART1_BASE_ADDRESS; static volatile struct USART* const usart2 = (struct USART*)USART2_BASE_ADDRESS; static volatile struct USART* const usart3 = (struct USART*)USART3_BASE_ADDRESS; -static struct CircularBuffer usart1_rx_buffer; +static volatile struct CircularBuffer usart1_rx_buffer; +static volatile struct FragmentedBuffer usart1_tx_buffer; //--public functions------------------------------------------------------------ @@ -60,6 +75,7 @@ void usart_configure(enum UsartPeriph periph, enum UsartConfig config, rcc_enable(RCC_AHB_NONE, RCC_APB1_NONE, RCC_APB2_USART); configure_baudrate(usart1, clocks.apb2_freq, baudrate); configure_usart(usart1, config); + usart1_tx_buffer.buffers = NULL; break; case USART_PERIPH_2: rcc_enable(RCC_AHB_NONE, RCC_APB1_USART2, RCC_APB2_NONE); @@ -79,22 +95,35 @@ void usart_configure(enum UsartPeriph periph, enum UsartConfig config, uint32_t usart_write_byte(enum UsartPeriph periph, uint8_t byte) { volatile struct USART* regs; - if (periph_regs(periph, ®s)) { - return 1; + volatile struct FragmentedBuffer* buffer; + enum DmaChannel dma_channel; + + switch (periph) { + case USART_PERIPH_1: + regs = usart1; + buffer = &usart1_tx_buffer; + dma_channel = DMA_CHANNEL_4; + break; + case USART_PERIPH_2: + case USART_PERIPH_3: + default: + return 1; + break; } - if (regs->SR.TXE) { + if (buffer->buffers) { + return write_to_buffer(regs, buffer, dma_channel, byte); + } else { + while (regs->SR.TXE == 0) {} reg_write(regs->DR, USART_DR_DR, byte); return 0; - } else { - return 1; } } uint32_t usart_read_byte(enum UsartPeriph periph, uint8_t* byte) { volatile struct USART* regs; - struct CircularBuffer* buffer; + volatile struct CircularBuffer* buffer; enum DmaChannel dma_channel; switch (periph) { @@ -122,6 +151,32 @@ uint32_t usart_read_byte(enum UsartPeriph periph, uint8_t* byte) } } +void usart_set_tx_buffer(enum UsartPeriph periph, uint8_t** buffers, + uint16_t buffer_size, uint8_t buffer_nb) +{ + volatile struct FragmentedBuffer* buffer = NULL; + switch (periph) { + case USART_PERIPH_1: + buffer = &usart1_tx_buffer; + break; + case USART_PERIPH_2: + break; + case USART_PERIPH_3: + break; + } + +#warning "check for null ptr" + +// for (uint8_t i = 0; i < sizeof(struct FragmentedBuffer); ++i) +// { +// ((uint8_t*)(&buffer))[i] = 0; +// } + buffer->buffers = buffers; + buffer->buffer_size = buffer_size; + buffer->buffer_nb = buffer_nb; + buffer->free_buffer_nb = buffer_nb; +} + void usart_set_rx_buffer(enum UsartPeriph periph, uint8_t* buffer, uint16_t size) { @@ -148,9 +203,9 @@ void usart_set_rx_buffer(enum UsartPeriph periph, uint8_t* buffer, //--local functions------------------------------------------------------------- -static void configure_usart(volatile struct USART* regs, enum UsartConfig config) +static void configure_usart(volatile struct USART* regs, + enum UsartConfig config) { - usart1_rx_buffer.buffer = NULL; //configure parity switch (config) { @@ -238,27 +293,55 @@ static void configure_baudrate(volatile struct USART* regs, uint32_t clock, reg_write(regs->BRR, USART_BRR_DIV_Fraction, divider & 0xF); } -static uint32_t periph_regs(enum UsartPeriph periph, volatile struct USART** regs) +static uint32_t write_byte(volatile struct USART* regs, uint8_t byte) { - switch (periph) { - case USART_PERIPH_1: - *regs = usart1; - break; - case USART_PERIPH_2: - *regs = usart2; - break; - case USART_PERIPH_3: - *regs = usart3; - break; - default: - return 1; - break; + if (regs->SR.TXE) { + reg_write(regs->DR, USART_DR_DR, byte); + reg_reset(regs->CR3, USART_CR3_DMAT); + reg_set(regs->CR1, USART_CR1_TXEIE); + nvic_enable(NVIC_IRQ_USART1); + return 0; + } else { + return 1; + } +} + +static uint32_t write_to_buffer(volatile struct USART *regs, + volatile struct FragmentedBuffer *buffer, enum DmaChannel channel, + uint8_t byte) +{ + //if the tx register is empty, there is no need to go through the dma + if (!write_byte(regs, byte)) { + //TODO enable IRQ + return 0; } + //if the current buffer is full, we need to switch it with an empty one + if (buffer->byte_index >= buffer->buffer_size) { + + //if all buffer full, simply wait for the DMA to empty one + while (buffer->free_buffer_nb == 0) {} + dma_enter_critical(DMA_PERIPH_1, channel); + + ++buffer->buffer_index; + if (buffer->buffer_index >= buffer->buffer_nb) { + buffer->buffer_index = 0; + } + --buffer->free_buffer_nb; + + buffer->byte_index = 0; + } else { + dma_enter_critical(DMA_PERIPH_1, channel); + } + + buffer->buffers[buffer->buffer_index][buffer->byte_index] = byte; + ++buffer->byte_index; + + dma_exit_critical(DMA_PERIPH_1, channel); return 0; } -static uint32_t read_from_buffer(struct CircularBuffer* buffer, +static uint32_t read_from_buffer(volatile struct CircularBuffer* buffer, enum DmaChannel channel, uint8_t* byte) { uint16_t end = buffer->size - dma_get_remaining(DMA_PERIPH_1, channel); @@ -283,9 +366,65 @@ static uint32_t read_from_buffer(struct CircularBuffer* buffer, //--callbacks------------------------------------------------------------------- +static void usart1_tx_callback(enum DmaIRQSource src) +{ + (void)src; //only transfer complete expected + volatile struct FragmentedBuffer* buffer = &usart1_tx_buffer; + + ++buffer->dma_buffer_index; + if (buffer->dma_buffer_index >= buffer->buffer_nb) { + buffer->dma_buffer_index = 0; + } + ++buffer->free_buffer_nb; + + //no more data to send, stop here + if (buffer->dma_buffer_index == buffer->buffer_index + && buffer->byte_index == 0) { + return; + } + + dma_configure(DMA_PERIPH_1, DMA_CHANNEL_4, + DMA_CONFIG_IRQ_COMPLETE | DMA_CONFIG_FROM_MEM + | DMA_CONFIG_INC_MEM | DMA_CONFIG_PSIZE_8BITS + | DMA_CONFIG_MSIZE_8BITS | DMA_CONFIG_PRIO_LOW, + (void*)&usart1->DR, + (void*)&buffer->buffers[buffer->dma_buffer_index], + buffer->byte_index, + usart1_tx_callback); +} + static void usart1_rx_callback(enum DmaIRQSource src) { (void)src; //only transfer complete expected usart1_rx_buffer.dmaLooped = true; } + +//--ISRs------------------------------------------------------------------------ + +void hdr_usart1(void) +{ + nvic_clear_pending(NVIC_IRQ_USART1); + + volatile struct FragmentedBuffer* buffer = &usart1_tx_buffer; + + //no more data to send, stop here + if (buffer->dma_buffer_index == buffer->buffer_index + && buffer->byte_index == 0) { + return; + } + + reg_set(usart1->CR3, USART_CR3_DMAT); + reg_reset(usart1->CR1, USART_CR1_TXEIE); + nvic_disable(NVIC_IRQ_USART1); + + dma_configure(DMA_PERIPH_1, DMA_CHANNEL_4, + DMA_CONFIG_IRQ_COMPLETE | DMA_CONFIG_FROM_MEM + | DMA_CONFIG_INC_MEM | DMA_CONFIG_PSIZE_8BITS + | DMA_CONFIG_MSIZE_8BITS | DMA_CONFIG_PRIO_LOW, + (void*)&usart1->DR, + (void*)&buffer->buffers[buffer->dma_buffer_index], + buffer->byte_index, + usart1_tx_callback); +} + diff --git a/drivers/usart.h b/drivers/usart.h index ed68b3f..c854c77 100644 --- a/drivers/usart.h +++ b/drivers/usart.h @@ -46,8 +46,8 @@ uint32_t usart_write_byte(enum UsartPeriph periph, uint8_t byte); uint32_t usart_read_byte(enum UsartPeriph periph, uint8_t* byte); -void usart_set_tx_buffer(enum UsartPeriph periph, uint8_t* buffer, - uint16_t size); +void usart_set_tx_buffer(enum UsartPeriph periph, uint8_t** buffers, + uint16_t buffer_size, uint8_t buffer_nb); void usart_set_rx_buffer(enum UsartPeriph periph, uint8_t* buffer, uint16_t size);