From fd33003e26f23f6609d20d3371f2b618cddd7aab Mon Sep 17 00:00:00 2001 From: Steins7 Date: Sat, 6 Apr 2024 16:29:45 +0200 Subject: [PATCH] Rework DMA buffer to work properly DMA buffers now work reliably with USART. The DMA api has changed to make it more efficient code-size-wise. This may, however, complicate things when implementating powersaving features --- drv/dma.c | 43 +++++++++++++++++++++++++------------------ drv/dma.h | 9 +++++---- drv/usart.c | 3 +++ srv/dma_cbuf.c | 16 ++++++++-------- srv/dma_cbuf.h | 6 +++--- srv/dma_mbuf.c | 19 ++++++++++--------- srv/dma_mbuf.h | 5 ++--- 7 files changed, 56 insertions(+), 45 deletions(-) diff --git a/drv/dma.c b/drv/dma.c index f13f9f1..7fd4085 100644 --- a/drv/dma.c +++ b/drv/dma.c @@ -19,9 +19,9 @@ //--local definitions----------------------------------------------------------- static void configure_dma(volatile struct DMA* dma, enum DmaChannel channel, - enum DmaConfig config_mask, volatile void* periph, volatile void* mem, - uint16_t size); - + enum DmaConfig config_mask, volatile void* periph); +static void start_dma(volatile struct DMA* dma, enum DmaChannel channel, + volatile void* mem, uint16_t size); //--local variables------------------------------------------------------------- @@ -36,8 +36,8 @@ static volatile void* dma2_cb_params[5]; //--public functions------------------------------------------------------------ void dma_configure(enum DmaPeriph dma, enum DmaChannel channel, - enum DmaConfig config_mask, volatile void* periph, volatile void* mem, - uint16_t size, DmaCallback callback, volatile void* cb_param) + enum DmaConfig config_mask, volatile void* periph, + DmaCallback callback, volatile void* cb_param) { //reset peripheral first, to ensure proper configuration dma_reset(dma, channel); @@ -45,7 +45,7 @@ void dma_configure(enum DmaPeriph dma, enum DmaChannel channel, switch (dma) { case DMA_PERIPH_1: rcc_enable(RCC_AHB_DMA1, RCC_APB1_NONE, RCC_APB2_NONE); - configure_dma(dma1, channel, config_mask, periph, mem, size); + configure_dma(dma1, channel, config_mask, periph); if (callback) { dma1_callbacks[channel] = callback; dma1_cb_params[channel] = cb_param; @@ -54,7 +54,7 @@ void dma_configure(enum DmaPeriph dma, enum DmaChannel channel, break; case DMA_PERIPH_2: rcc_enable(RCC_AHB_DMA2, RCC_APB1_NONE, RCC_APB2_NONE); - configure_dma(dma2, channel, config_mask, periph, mem, size); + configure_dma(dma2, channel, config_mask, periph); if (callback) { dma2_callbacks[channel] = callback; dma2_cb_params[channel] = cb_param; @@ -110,20 +110,21 @@ void dma_exit_critical(enum DmaPeriph dma, enum DmaChannel channel) } } -void dma_enable(enum DmaPeriph dma, enum DmaChannel channel) +void dma_start(enum DmaPeriph dma, enum DmaChannel channel, + volatile void* mem, uint16_t size) { switch (dma) { case DMA_PERIPH_1: - reg_set(dma1->CHANNELS[channel].CCR, DMA_CCR_EN); if (dma1_callbacks[channel]) { nvic_enable(NVIC_IRQ_DMA1_CHANNEL1 + channel); } + start_dma(dma1, channel, mem, size); break; case DMA_PERIPH_2: - reg_set(dma2->CHANNELS[channel].CCR, DMA_CCR_EN); if (dma2_callbacks[channel]) { nvic_enable(NVIC_IRQ_DMA2_CHANNEL1 + channel); } + start_dma(dma2, channel, mem, size); break; default: return; @@ -146,7 +147,7 @@ void dma_enter_critical(enum DmaPeriph dma, enum DmaChannel channel) } } -void dma_disable(enum DmaPeriph dma, enum DmaChannel channel) +void dma_stop(enum DmaPeriph dma, enum DmaChannel channel) { switch (dma) { case DMA_PERIPH_1: @@ -185,21 +186,27 @@ uint16_t dma_get_remaining(enum DmaPeriph dma, enum DmaChannel channel) //--local functions------------------------------------------------------------- static void configure_dma(volatile struct DMA* dma, enum DmaChannel channel, - enum DmaConfig config_mask, volatile void* periph, volatile void* mem, - uint16_t size) + enum DmaConfig config_mask, volatile void* periph) { volatile struct DMA_CHANNEL* regs = &dma->CHANNELS[channel]; //registers should already be at reset value, apply new config regs->CCR.word = config_mask; - reg_write(regs->CNDTR, DMA_CNDTR_NDT, size); regs->CPAR = (uint32_t)periph; - regs->CMAR = (uint32_t)mem; - - //only enable channel when everything is configured - reg_set(regs->CCR, DMA_CCR_EN); } +static void start_dma(volatile struct DMA* dma, enum DmaChannel channel, + volatile void* mem, uint16_t size) +{ + volatile struct DMA_CHANNEL* regs = &dma->CHANNELS[channel]; + + //registers should already be configured, apply transfer config + reg_write(regs->CNDTR, DMA_CNDTR_NDT, size); + regs->CMAR = (uint32_t)mem; + + //only start transfer when everything is configured + reg_set(regs->CCR, DMA_CCR_EN); +} //--ISRs------------------------------------------------------------------------ diff --git a/drv/dma.h b/drv/dma.h index 89d9e00..1e38987 100644 --- a/drv/dma.h +++ b/drv/dma.h @@ -88,8 +88,8 @@ struct DmaParam { //--functions------------------------------------------------------------------- void dma_configure(enum DmaPeriph dma, enum DmaChannel channel, - enum DmaConfig config_mask, volatile void* periph, volatile void* mem, - uint16_t size, DmaCallback callback, volatile void* cb_param); + enum DmaConfig config_mask, volatile void* periph, + DmaCallback callback, volatile void* cb_param); void dma_configure_mem2mem(enum DmaPeriph dma, enum DmaChannel channel, enum DmaConfigM2M config_mask, const void* src, void* dest, @@ -99,11 +99,12 @@ void dma_reset(enum DmaPeriph dma, enum DmaChannel channel); void dma_exit_critical(enum DmaPeriph dma, enum DmaChannel channel); -void dma_enable(enum DmaPeriph dma, enum DmaChannel channel); +void dma_start(enum DmaPeriph dma, enum DmaChannel channel, + volatile void* mem, uint16_t size); void dma_enter_critical(enum DmaPeriph dma, enum DmaChannel channel); -void dma_disable(enum DmaPeriph dma, enum DmaChannel channel); +void dma_stop(enum DmaPeriph dma, enum DmaChannel channel); uint16_t dma_get_remaining(enum DmaPeriph dma, enum DmaChannel channe); diff --git a/drv/usart.c b/drv/usart.c index ae1a928..578b93b 100644 --- a/drv/usart.c +++ b/drv/usart.c @@ -192,12 +192,15 @@ const struct DmaParam* usart_configure_tx_dma(enum UsartPeriph periph) switch (periph) { case USART_PERIPH_1: param = &usart1_tx_param; + reg_set(usart1->CR3, USART_CR3_DMAT); break; case USART_PERIPH_2: param = &usart2_tx_param; + reg_set(usart2->CR3, USART_CR3_DMAT); break; case USART_PERIPH_3: param = &usart3_tx_param; + reg_set(usart3->CR3, USART_CR3_DMAT); break; default: return nullptr; diff --git a/srv/dma_cbuf.c b/srv/dma_cbuf.c index e66b0e1..a9277f6 100644 --- a/srv/dma_cbuf.c +++ b/srv/dma_cbuf.c @@ -31,17 +31,18 @@ void dma_cbuf_configure(volatile struct DmaCircBuffer* buffer, buffer->buffer = raw_buffer; - buffer->param = param; - buffer->priority = priority; - buffer->size = buffer_size; buffer->begin = 0; + buffer->dma = param->dma; + buffer->channel = param->channel; + buffer->dma_looped = false; - dma_configure(buffer->param->dma, buffer->param->channel, - buffer->param->config, buffer->param->periph, - buffer->buffer, buffer->size, cbuf_callback, buffer); + dma_configure(buffer->dma, buffer->channel, + DMA_CONFIG | param->config | priority, + param->periph, cbuf_callback, buffer); + dma_start(buffer->dma, buffer->channel, buffer->buffer, buffer->size); } uint32_t dma_cbuf_read_byte(volatile struct DmaCircBuffer* buffer, @@ -49,7 +50,7 @@ uint32_t dma_cbuf_read_byte(volatile struct DmaCircBuffer* buffer, { //retreive the current end of the buffer based on the DMA's progress uint16_t end = buffer->size - - dma_get_remaining(buffer->param->dma, buffer->param->channel); + - dma_get_remaining(buffer->dma, buffer->channel); //check for bytes to read and overflow if ((end > buffer->begin) && buffer->dma_looped) { @@ -85,4 +86,3 @@ static void cbuf_callback(enum DmaIRQSource src, volatile void* param) buffer->dma_looped = true; } - diff --git a/srv/dma_cbuf.h b/srv/dma_cbuf.h index 606829e..7a05bbd 100644 --- a/srv/dma_cbuf.h +++ b/srv/dma_cbuf.h @@ -23,12 +23,12 @@ struct DmaCircBuffer { volatile void* buffer; //the buffer to use as a circular buffer - const struct DmaParam* param; - enum DmaConfig priority; //DMA config, must correspond to peripheral - uint16_t size; //the size of the buffer uint16_t begin; //pointer to the current begin of the buffer + enum DmaPeriph dma; + enum DmaChannel channel; + bool dma_looped; //whether the DMA looped or not (buffer overflow) }; diff --git a/srv/dma_mbuf.c b/srv/dma_mbuf.c index dc6a876..1ceaad0 100644 --- a/srv/dma_mbuf.c +++ b/srv/dma_mbuf.c @@ -31,15 +31,18 @@ void dma_mbuf_configure(volatile struct DmaMultiBuffer* buffer, buffer->raw_buffer = raw_buffer; - buffer->param = param; - buffer->priority = priority; - buffer->buffer_size = buffer_size / 2; buffer->byte_index = 0; buffer->buffer_index = 0; + buffer->dma = param->dma; + buffer->channel = param->channel; buffer->dma_running = false; + + dma_configure(buffer->dma, buffer->channel, + DMA_CONFIG | param->config | priority, + param->periph, mbuf_callback, buffer); } uint32_t dma_mbuf_write_byte(volatile struct DmaMultiBuffer* buffer, @@ -66,12 +69,10 @@ void dma_mbuf_switch(volatile struct DmaMultiBuffer* buffer) } //start a new transfer - dma_configure(buffer->param->dma, buffer->param->channel, - DMA_CONFIG | buffer->param->config | buffer->priority, - buffer->param->periph, - &buffer->raw_buffer[buffer->buffer_index * buffer->buffer_size], - buffer->byte_index, mbuf_callback, buffer); buffer->dma_running = true; + dma_start(buffer->dma, buffer->channel, + &buffer->raw_buffer[buffer->buffer_index * buffer->buffer_size], + buffer->byte_index); buffer->byte_index = 0; ++buffer->buffer_index; @@ -93,7 +94,7 @@ static void mbuf_callback(enum DmaIRQSource src, volatile void* param) { (void)src; //only transfer complete expected volatile struct DmaMultiBuffer* buffer = param; - + dma_stop(buffer->dma, buffer->channel); buffer->dma_running = false; } diff --git a/srv/dma_mbuf.h b/srv/dma_mbuf.h index 966c5c1..cf44d0d 100644 --- a/srv/dma_mbuf.h +++ b/srv/dma_mbuf.h @@ -23,13 +23,12 @@ struct DmaMultiBuffer { uint8_t* raw_buffer; - const struct DmaParam* param; - enum DmaConfig priority; - uint16_t buffer_size; uint8_t byte_index; uint8_t buffer_index; + enum DmaPeriph dma; + enum DmaChannel channel; bool dma_running; };