From ccf36ac400e7f978ff53f25442d2599476ab761d Mon Sep 17 00:00:00 2001 From: Steins7 Date: Wed, 3 Apr 2024 22:03:15 +0200 Subject: [PATCH 1/9] Simplify dma buffers and adjust usart The dma buffer should be services that are used on top of peripherals. As such, the usart driver should'nt directly use them, this is up to the user. The multi-buffer has also been simplified since I was not satisfied with the previous implementation --- drv/dma.h | 7 ++ drv/dma_mbuf.c | 143 ---------------------------- drv/usart.c | 203 ++++++++++++++++------------------------ drv/usart.h | 9 +- {drv => srv}/dma_cbuf.c | 17 ++-- {drv => srv}/dma_cbuf.h | 14 ++- srv/dma_mbuf.c | 99 ++++++++++++++++++++ {drv => srv}/dma_mbuf.h | 28 +++--- 8 files changed, 218 insertions(+), 302 deletions(-) delete mode 100644 drv/dma_mbuf.c rename {drv => srv}/dma_cbuf.c (85%) rename {drv => srv}/dma_cbuf.h (78%) create mode 100644 srv/dma_mbuf.c rename {drv => srv}/dma_mbuf.h (68%) diff --git a/drv/dma.h b/drv/dma.h index 309f797..89d9e00 100644 --- a/drv/dma.h +++ b/drv/dma.h @@ -77,6 +77,13 @@ enum DmaIRQSource { typedef void (*DmaCallback)(enum DmaIRQSource, volatile void* param); +struct DmaParam { + void* periph; + enum DmaConfig config; //DMA config, must correspond to peripheral + enum DmaPeriph dma; //DMA peripheral, must correspond to peripheral + enum DmaChannel channel; //DMA channel, must correspond to peripheral +}; + //--functions------------------------------------------------------------------- diff --git a/drv/dma_mbuf.c b/drv/dma_mbuf.c deleted file mode 100644 index 8d10b27..0000000 --- a/drv/dma_mbuf.c +++ /dev/null @@ -1,143 +0,0 @@ -/** @file dma_mbuf.h - * Module handling Direct Memory Access controller's multibuffer system - * - * The module provides a convenient tool to send data to a peripheral in a - * buffered, low-altency, low-cpu usage, non-blocking way - */ - -//--includes-------------------------------------------------------------------- - -#include "dma_mbuf.h" - - -//--local definitions----------------------------------------------------------- - -#define DMA_CONFIG (DMA_CONFIG_IRQ_COMPLETE | DMA_CONFIG_FROM_MEM \ - | DMA_CONFIG_INC_MEM | DMA_CONFIG_PSIZE_8BITS \ - | DMA_CONFIG_MSIZE_8BITS) - -static void mbuf_callback(enum DmaIRQSource src, volatile void* param); - -//take last 2 bytes of a buffer and assemble them store the current byte index -#define byte_index (((uint16_t**)buffer->buffers) \ - [buffer->buffer_index][buffer->buffer_size/2]) -#define dma_byte_index (((uint16_t**)buffer->buffers) \ - [buffer->dma_buffer_index][buffer->buffer_size/2]) - - -//--local variables------------------------------------------------------------- - -//--public functions------------------------------------------------------------ - -void dma_mbuf_configure(volatile struct DmaMultiBuffer* buffer, void** buffers, - volatile void* dest, uint16_t buffer_size, uint8_t buffer_nb, - enum DmaPeriph dma, enum DmaChannel channel, enum DmaConfig priority) -{ - #warning "check for null ptr" - - buffer->buffers = buffers; - buffer->dest = dest; - - buffer->buffer_size = buffer_size - 2; - - buffer->buffer_nb = buffer_nb; - buffer->free_buffer_nb = buffer_nb - 1; - buffer->buffer_index = 0; - buffer->dma_buffer_index = 0; - - buffer->dma = dma; - buffer->channel = channel; - buffer->config = DMA_CONFIG | priority; - - for (uint8_t i=0; ibuffer_nb; ++i) { - for (uint16_t j=0; jbuffer_size + 2; ++j) { - ((uint8_t**)buffer->buffers)[i][j] = 0; - } - } -} - -uint32_t dma_mbuf_write_byte(volatile struct DmaMultiBuffer* buffer, - uint8_t byte) -{ - dma_enter_critical(buffer->dma, buffer->channel); - - //if the current buffer is full, we need to switch it with an empty one - volatile uint16_t index = byte_index; - if (byte_index >= buffer->buffer_size) { - - //if all buffer full, give up - if (buffer->free_buffer_nb == 0) { - dma_exit_critical(buffer->dma, buffer->channel); - return 1; - } - - ++buffer->buffer_index; - if (buffer->buffer_index >= buffer->buffer_nb) { - buffer->buffer_index = 0; - } - --buffer->free_buffer_nb; - - byte_index = 0; - } - - //write the byte - uint8_t** buffers = (uint8_t**)buffer->buffers; - buffers[buffer->buffer_index][byte_index] = byte; - ++byte_index; - ++index; - - dma_exit_critical(buffer->dma, buffer->channel); - return 0; -} - -void dma_mbuf_refresh(volatile struct DmaMultiBuffer* buffer) -{ - //no more data to send, stop here - if (buffer->dma_buffer_index == buffer->buffer_index - && byte_index == 0) { - return; - } - - //else start a new transfer - dma_configure(buffer->dma, buffer->channel, buffer->config, buffer->dest, - buffer->buffers[buffer->dma_buffer_index], - dma_byte_index, mbuf_callback, buffer); - - //if the newly transfering buffer was being written to, switch the current - //buffer. Since we just ended a transfer, the next buffer should be empty - if (buffer->dma_buffer_index == buffer->buffer_index) - { - ++buffer->buffer_index; - if (buffer->buffer_index >= buffer->buffer_nb) { - buffer->buffer_index = 0; - } - --buffer->free_buffer_nb; - - byte_index = 0; - } - - return; -} - - -//--local functions------------------------------------------------------------- - -/** - * Callback called on DMA TX tranfert's completion. Checks for any remaining - * data to send. If any, starts a new transfer, else stop the DMA - */ -static void mbuf_callback(enum DmaIRQSource src, volatile void* param) -{ - (void)src; //only transfer complete expected - volatile struct DmaMultiBuffer* buffer = param; - - //increment DMA's buffer since the last once has already been sent - ++buffer->dma_buffer_index; - if (buffer->dma_buffer_index >= buffer->buffer_nb) { - buffer->dma_buffer_index = 0; - } - ++buffer->free_buffer_nb; - - dma_mbuf_refresh(param); -} - diff --git a/drv/usart.c b/drv/usart.c index ae53d66..ae1a928 100644 --- a/drv/usart.c +++ b/drv/usart.c @@ -14,8 +14,6 @@ #include "rcc.h" #include "dma.h" -#include "dma_mbuf.h" -#include "dma_cbuf.h" #include "stddef.h" @@ -27,6 +25,8 @@ static void configure_usart(volatile struct USART* regs, static void configure_baudrate(volatile struct USART* regs, uint32_t clock, uint32_t baudrate); +#define DMA_CONFIG (DMA_CONFIG_PSIZE_8BITS) + //--local variables------------------------------------------------------------- @@ -34,12 +34,47 @@ 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 volatile struct DmaCircBuffer usart1_rx_buffer; -static volatile struct DmaMultiBuffer usart1_tx_buffer; -static volatile struct DmaCircBuffer usart2_rx_buffer; -static volatile struct DmaMultiBuffer usart2_tx_buffer; -static volatile struct DmaCircBuffer usart3_rx_buffer; -static volatile struct DmaMultiBuffer usart3_tx_buffer; +static const struct DmaParam usart1_rx_param = { + (void*)&usart1->DR, + DMA_CONFIG, + DMA_PERIPH_1, + DMA_CHANNEL_5, +}; + +static const struct DmaParam usart2_rx_param = { + (void*)&usart2->DR, + DMA_CONFIG, + DMA_PERIPH_1, + DMA_CHANNEL_6, +}; + +static const struct DmaParam usart3_rx_param = { + (void*)&usart3->DR, + DMA_CONFIG, + DMA_PERIPH_1, + DMA_CHANNEL_3, +}; + +static const struct DmaParam usart1_tx_param = { + (void*)&usart1->DR, + DMA_CONFIG, + DMA_PERIPH_1, + DMA_CHANNEL_4, +}; + +static const struct DmaParam usart2_tx_param = { + (void*)&usart2->DR, + DMA_CONFIG, + DMA_PERIPH_1, + DMA_CHANNEL_7, +}; + +static const struct DmaParam usart3_tx_param = { + (void*)&usart3->DR, + DMA_CONFIG, + DMA_PERIPH_1, + DMA_CHANNEL_2, +}; //--public functions------------------------------------------------------------ @@ -55,22 +90,16 @@ 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; - usart1_rx_buffer.buffer = NULL; break; case USART_PERIPH_2: rcc_enable(RCC_AHB_NONE, RCC_APB1_USART2, RCC_APB2_NONE); configure_baudrate(usart2, clocks.apb1_freq, baudrate); configure_usart(usart2, config); - usart2_tx_buffer.buffers = NULL; - usart2_rx_buffer.buffer = NULL; break; case USART_PERIPH_3: rcc_enable(RCC_AHB_NONE, RCC_APB1_USART3, RCC_APB2_NONE); configure_baudrate(usart3, clocks.apb1_freq, baudrate); configure_usart(usart3, config); - usart3_tx_buffer.buffers = NULL; - usart3_rx_buffer.buffer = NULL; break; default: break; @@ -80,134 +109,104 @@ void usart_configure(enum UsartPeriph periph, enum UsartConfig config, uint32_t usart_write_byte(enum UsartPeriph periph, uint8_t byte) { volatile struct USART* regs; - volatile struct DmaMultiBuffer* buffer; - enum NvicIrq irq; switch (periph) { case USART_PERIPH_1: regs = usart1; - buffer = &usart1_tx_buffer; - irq = NVIC_IRQ_USART1; break; case USART_PERIPH_2: regs = usart2; - buffer = &usart2_tx_buffer; - irq = NVIC_IRQ_USART2; break; case USART_PERIPH_3: regs = usart3; - buffer = &usart3_tx_buffer; - irq = NVIC_IRQ_USART3; break; default: return 1; break; } - if (buffer->buffers) { - //if the tx register is empty, there is no need to go through the dma - if (regs->SR.TC) { - reg_write(regs->DR, USART_DR_DR, byte); - //enable IRQ, disable DMA - reg_reset(regs->CR3, USART_CR3_DMAT); - reg_set(regs->CR1, USART_CR1_TXEIE); - nvic_enable(irq); - return 0; - } else { - return dma_mbuf_write_byte(buffer, byte); - } + //only write data if the tx register it empty, give up otherwise + if (regs->SR.TXE) { + reg_write(regs->DR, USART_DR_DR, byte); + return 0; } else { - //only write data if the tx register it empty, give up otherwise - if (regs->SR.TXE) { - reg_write(regs->DR, USART_DR_DR, byte); - return 0; - } else { - return 1; - } + return 1; } } uint32_t usart_read_byte(enum UsartPeriph periph, uint8_t* byte) { volatile struct USART* regs; - volatile struct DmaCircBuffer* buffer; switch (periph) { case USART_PERIPH_1: regs = usart1; - buffer = &usart1_rx_buffer; break; case USART_PERIPH_2: regs = usart2; - buffer = &usart2_rx_buffer; break; case USART_PERIPH_3: regs = usart3; - buffer = &usart3_rx_buffer; break; default: return 1; break; } - if (buffer->buffer) { - return dma_cbuf_read_byte(buffer, byte); + if (regs->SR.RXNE) { + *byte = regs->DR.DR; + return 0; } else { - if (regs->SR.RXNE) { - *byte = regs->DR.DR; - return 0; - } else { - return 1; - } + return 1; } } -void usart_set_tx_buffer(enum UsartPeriph periph, uint8_t** buffers, - uint16_t buffer_size, uint8_t buffer_nb, enum DmaConfig priority) +const struct DmaParam* usart_configure_rx_dma(enum UsartPeriph periph) { - switch (periph) { - case USART_PERIPH_1: - dma_mbuf_configure(&usart1_tx_buffer, (void**)buffers, &usart1->DR, - buffer_size, buffer_nb, DMA_PERIPH_1, DMA_CHANNEL_4, - priority); - break; - case USART_PERIPH_2: - dma_mbuf_configure(&usart2_tx_buffer, (void**)buffers, &usart2->DR, - buffer_size, buffer_nb, DMA_PERIPH_1, DMA_CHANNEL_7, - priority); - break; - case USART_PERIPH_3: - dma_mbuf_configure(&usart3_tx_buffer, (void**)buffers, &usart3->DR, - buffer_size, buffer_nb, DMA_PERIPH_1, DMA_CHANNEL_2, - priority); - break; - } -} + const struct DmaParam* param; -void usart_set_rx_buffer(enum UsartPeriph periph, uint8_t* buffer, - uint16_t size, enum DmaConfig priority) -{ switch (periph) { case USART_PERIPH_1: - dma_cbuf_configure(&usart1_rx_buffer, buffer, (void*)&usart1->DR, - size, DMA_PERIPH_1, DMA_CHANNEL_5, priority); + param = &usart1_rx_param; reg_set(usart1->CR3, USART_CR3_DMAR); break; case USART_PERIPH_2: - dma_cbuf_configure(&usart2_rx_buffer, buffer, (void*)&usart2->DR, - size, DMA_PERIPH_1, DMA_CHANNEL_6, priority); + param = &usart2_rx_param; reg_set(usart2->CR3, USART_CR3_DMAR); break; - case USART_PERIPH_3: - dma_cbuf_configure(&usart3_rx_buffer, buffer, (void*)&usart3->DR, - size, DMA_PERIPH_1, DMA_CHANNEL_3, priority); + param = &usart3_rx_param; reg_set(usart3->CR3, USART_CR3_DMAR); - break; + default: + return nullptr; } + + return param; } +const struct DmaParam* usart_configure_tx_dma(enum UsartPeriph periph) +{ + const struct DmaParam* param; + + switch (periph) { + case USART_PERIPH_1: + param = &usart1_tx_param; + break; + case USART_PERIPH_2: + param = &usart2_tx_param; + break; + case USART_PERIPH_3: + param = &usart3_tx_param; + break; + default: + return nullptr; + } + + return param; +} + + //--local functions------------------------------------------------------------- /** @@ -309,39 +308,3 @@ static void configure_baudrate(volatile struct USART* regs, uint32_t clock, reg_write(regs->BRR, USART_BRR_DIV_Fraction, divider & 0xF); } - -//--ISRs------------------------------------------------------------------------ - -void hdr_usart1(void) -{ - //disable the interrupt. It will be reenabled on a write if needed - nvic_clear_pending(NVIC_IRQ_USART1); - nvic_disable(NVIC_IRQ_USART1); - reg_reset(usart1->CR1, USART_CR1_TXEIE); - reg_set(usart1->CR3, USART_CR3_DMAT); - - dma_mbuf_refresh(&usart1_tx_buffer); -} - -void hdr_usart2(void) -{ - //disable the interrupt. It will be reenabled on a write if needed - nvic_clear_pending(NVIC_IRQ_USART2); - nvic_disable(NVIC_IRQ_USART2); - reg_reset(usart2->CR1, USART_CR1_TXEIE); - reg_set(usart2->CR3, USART_CR3_DMAT); - - dma_mbuf_refresh(&usart2_tx_buffer); -} - -void hdr_usart3(void) -{ - //disable the interrupt. It will be reenabled on a write if needed - nvic_clear_pending(NVIC_IRQ_USART3); - nvic_disable(NVIC_IRQ_USART3); - reg_reset(usart3->CR1, USART_CR1_TXEIE); - reg_set(usart3->CR3, USART_CR3_DMAT); - - dma_mbuf_refresh(&usart3_tx_buffer); -} - diff --git a/drv/usart.h b/drv/usart.h index 8739e63..9189d01 100644 --- a/drv/usart.h +++ b/drv/usart.h @@ -41,18 +41,13 @@ enum UsartConfig { void usart_configure(enum UsartPeriph periph, enum UsartConfig config, uint32_t baudrate); - void usart_reset(enum UsartPeriph periph); 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** buffers, - uint16_t buffer_size, uint8_t buffer_nb, enum DmaConfig priority); - -void usart_set_rx_buffer(enum UsartPeriph periph, uint8_t* buffer, - uint16_t size, enum DmaConfig priority); +const struct DmaParam* usart_configure_rx_dma(enum UsartPeriph periph); +const struct DmaParam* usart_configure_tx_dma(enum UsartPeriph periph); #endif //_USART_H_ diff --git a/drv/dma_cbuf.c b/srv/dma_cbuf.c similarity index 85% rename from drv/dma_cbuf.c rename to srv/dma_cbuf.c index f324d20..e66b0e1 100644 --- a/drv/dma_cbuf.c +++ b/srv/dma_cbuf.c @@ -24,24 +24,23 @@ static void cbuf_callback(enum DmaIRQSource src, volatile void* param); //--public functions------------------------------------------------------------ void dma_cbuf_configure(volatile struct DmaCircBuffer* buffer, - volatile void* raw_buffer, volatile void* src, uint16_t buffer_size, - enum DmaPeriph dma, enum DmaChannel channel, enum DmaConfig priority) + const struct DmaParam* param, enum DmaConfig priority, + volatile void* raw_buffer, uint16_t buffer_size) { #warning "check for null ptr" buffer->buffer = raw_buffer; - buffer->src = src; + + buffer->param = param; + buffer->priority = priority; buffer->size = buffer_size; buffer->begin = 0; - buffer->dma = dma; - buffer->channel = channel; - buffer->config = DMA_CONFIG | priority; - buffer->dma_looped = false; - dma_configure(buffer->dma, buffer->channel, buffer->config, buffer->src, + dma_configure(buffer->param->dma, buffer->param->channel, + buffer->param->config, buffer->param->periph, buffer->buffer, buffer->size, cbuf_callback, buffer); } @@ -50,7 +49,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->dma, buffer->channel); + - dma_get_remaining(buffer->param->dma, buffer->param->channel); //check for bytes to read and overflow if ((end > buffer->begin) && buffer->dma_looped) { diff --git a/drv/dma_cbuf.h b/srv/dma_cbuf.h similarity index 78% rename from drv/dma_cbuf.h rename to srv/dma_cbuf.h index 4221f14..606829e 100644 --- a/drv/dma_cbuf.h +++ b/srv/dma_cbuf.h @@ -10,7 +10,7 @@ //--includes-------------------------------------------------------------------- -#include "dma.h" +#include "../drv/dma.h" //--type definitions------------------------------------------------------------ @@ -22,15 +22,13 @@ */ struct DmaCircBuffer { volatile void* buffer; //the buffer to use as a circular buffer - volatile void* src; //source peripheral register + + 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; //DMA peripheral, must correspond to peripheral - enum DmaChannel channel; //DMA channel, must correspond to peripheral - enum DmaConfig config; //DMA config, must correspond to peripheral - bool dma_looped; //whether the DMA looped or not (buffer overflow) }; @@ -47,8 +45,8 @@ struct DmaCircBuffer { * will stay running until manually stopped */ void dma_cbuf_configure(volatile struct DmaCircBuffer* buffer, - volatile void* raw_buffer, volatile void* src, uint16_t buffer_size, - enum DmaPeriph dma, enum DmaChannel channel, enum DmaConfig priority); + const struct DmaParam* param, enum DmaConfig priority, + volatile void* raw_buffer, uint16_t buffer_size); uint32_t dma_cbuf_read_byte(volatile struct DmaCircBuffer* buffer, uint8_t* byte); diff --git a/srv/dma_mbuf.c b/srv/dma_mbuf.c new file mode 100644 index 0000000..dc6a876 --- /dev/null +++ b/srv/dma_mbuf.c @@ -0,0 +1,99 @@ +/** @file dma_mbuf.h + * Module handling Direct Memory Access controller's multibuffer system + * + * The module provides a convenient tool to send data to a peripheral in a + * buffered, low-altency, low-cpu usage, non-blocking way + */ + +//--includes-------------------------------------------------------------------- + +#include "dma_mbuf.h" + + +//--local definitions----------------------------------------------------------- + +#define DMA_CONFIG (DMA_CONFIG_IRQ_COMPLETE | DMA_CONFIG_FROM_MEM \ + | DMA_CONFIG_INC_MEM | DMA_CONFIG_PSIZE_8BITS \ + | DMA_CONFIG_MSIZE_8BITS) + +static void mbuf_callback(enum DmaIRQSource src, volatile void* param); + + +//--local variables------------------------------------------------------------- + +//--public functions------------------------------------------------------------ + +void dma_mbuf_configure(volatile struct DmaMultiBuffer* buffer, + const struct DmaParam* param, enum DmaConfig priority, + void* raw_buffer, uint16_t buffer_size) +{ + #warning "check for null ptr" + + 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_running = false; +} + +uint32_t dma_mbuf_write_byte(volatile struct DmaMultiBuffer* buffer, + uint8_t byte) +{ + //if the current buffer is full, give up + if (buffer->byte_index >= buffer->buffer_size) { + return 1; + } + + //write the byte + buffer->raw_buffer[buffer->buffer_index * buffer->buffer_size + + buffer->byte_index] = byte; + ++buffer->byte_index; + return 0; +} + +void dma_mbuf_switch(volatile struct DmaMultiBuffer* buffer) +{ + //if transfer already in progress or no data to send, don't start the + //transfer + if (buffer->dma_running || buffer->byte_index == 0) { + return; + } + + //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; + + buffer->byte_index = 0; + ++buffer->buffer_index; + if (buffer->buffer_index > 1) { + buffer->buffer_index = 0; + } + + return; +} + + +//--local functions------------------------------------------------------------- + +/** + * Callback called on DMA TX tranfert's completion. Checks for any remaining + * data to send. If any, starts a new transfer, else stop the DMA + */ +static void mbuf_callback(enum DmaIRQSource src, volatile void* param) +{ + (void)src; //only transfer complete expected + volatile struct DmaMultiBuffer* buffer = param; + + buffer->dma_running = false; +} + diff --git a/drv/dma_mbuf.h b/srv/dma_mbuf.h similarity index 68% rename from drv/dma_mbuf.h rename to srv/dma_mbuf.h index 4ab6952..966c5c1 100644 --- a/drv/dma_mbuf.h +++ b/srv/dma_mbuf.h @@ -10,7 +10,7 @@ //--includes-------------------------------------------------------------------- -#include "dma.h" +#include "../drv/dma.h" //--type definitions------------------------------------------------------------ @@ -21,19 +21,17 @@ * sizes */ struct DmaMultiBuffer { - void** buffers; //list of buffers to write to - volatile void* dest; //destination peripheral register + uint8_t* raw_buffer; - uint16_t buffer_size; //size of a single buffer + const struct DmaParam* param; + enum DmaConfig priority; - uint8_t buffer_nb; //total number of buffers - uint8_t free_buffer_nb; //number of buffers not currently used - uint8_t buffer_index; //index of the current buffer - uint8_t dma_buffer_index; //index of the DMA's current buffer + uint16_t buffer_size; - enum DmaPeriph dma; //DMA peripheral, must correspond to peripheral - enum DmaChannel channel; //DMA channel, must correspond to peripheral - enum DmaConfig config; //DMA config, must correspond to peripheral + uint8_t byte_index; + uint8_t buffer_index; + + bool dma_running; }; @@ -51,9 +49,9 @@ struct DmaMultiBuffer { * filling up the buffer. Transfers will then automatically be started as long * as there are bytes in the buffer. See the usart module for an usage exemple */ -void dma_mbuf_configure(volatile struct DmaMultiBuffer* buffer, void** buffers, - volatile void* dest, uint16_t buffer_size, uint8_t buffer_nb, - enum DmaPeriph dma, enum DmaChannel channel, enum DmaConfig priority); +void dma_mbuf_configure(volatile struct DmaMultiBuffer* buffer, + const struct DmaParam* param, enum DmaConfig priority, + void* raw_buffer, uint16_t buffer_size); /** * Write the given byte to the given buffer. Returns 0 if the write operation @@ -71,7 +69,7 @@ uint32_t dma_mbuf_write_byte(volatile struct DmaMultiBuffer* buffer, * transfer will be handled automatically until there are no bytes left in the * buffer */ -void dma_mbuf_refresh(volatile struct DmaMultiBuffer* buffer); +void dma_mbuf_switch(volatile struct DmaMultiBuffer* buffer); #endif //_DMA_MBUF_H_ -- 2.45.2 From 7a19ae9223f53aec5780d66f265e5efcc9ea5773 Mon Sep 17 00:00:00 2001 From: Steins7 Date: Sat, 6 Apr 2024 16:28:08 +0200 Subject: [PATCH 2/9] Clean minor issues in startup.s --- startup.s | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/startup.s b/startup.s index 8f48739..a664898 100644 --- a/startup.s +++ b/startup.s @@ -4,7 +4,7 @@ * This startup file provides a reset handler to setup the basics for c to run * (stack pointer, static data, ...) as well as the vector table and the * differents interrupt handlers that go with it. By default, all handlers use - * the default handler wich is simply and infinite loop + * the default handler wich is simply an infinite loop */ .syntax unified @@ -25,7 +25,7 @@ */ .section .text.hdr_reset .weak hdr_reset - .type hdr_reset, %function + .type hdr_reset, %function hdr_reset: ldr r0, = _estack mov sp, r0 @@ -75,19 +75,19 @@ bss_init_end: */ .section .text.hdr_default, "ax", %progbits .weak hdr_default -hdr_default: + hdr_default: b hdr_default .size hdr_default, .-hdr_default -//--Vector table---------------------------------------------------------------- + //--Vector table---------------------------------------------------------------- .section .vector_table, "a", %progbits .type vector_table, %object .size vector_table, .-vector_table -vector_table: + vector_table: .word _estack .word hdr_reset .word hdr_nmi @@ -165,7 +165,7 @@ vector_table: .word hdr_dma2_channel3 .word hdr_dma2_channel4_5 -//--Weak definitions------------------------------------------------------------ + //--Weak definitions------------------------------------------------------------ .weak hdr_nmi .thumb_set hdr_nmi, hdr_default -- 2.45.2 From fd33003e26f23f6609d20d3371f2b618cddd7aab Mon Sep 17 00:00:00 2001 From: Steins7 Date: Sat, 6 Apr 2024 16:29:45 +0200 Subject: [PATCH 3/9] 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; }; -- 2.45.2 From 5e961b15fc28d1529029bd142d824d64e8100af7 Mon Sep 17 00:00:00 2001 From: Steins7 Date: Sat, 6 Apr 2024 16:55:31 +0200 Subject: [PATCH 4/9] Document dma_mbuf service --- srv/dma_mbuf.c | 27 ++++++++++++++++---------- srv/dma_mbuf.h | 51 +++++++++++++++++++++++++------------------------- 2 files changed, 43 insertions(+), 35 deletions(-) diff --git a/srv/dma_mbuf.c b/srv/dma_mbuf.c index 1ceaad0..4f2255c 100644 --- a/srv/dma_mbuf.c +++ b/srv/dma_mbuf.c @@ -12,9 +12,12 @@ //--local definitions----------------------------------------------------------- +/** + * DMA configuration to be used for the buffer. Additionnal configuration may be + * added by the peripherals used + */ #define DMA_CONFIG (DMA_CONFIG_IRQ_COMPLETE | DMA_CONFIG_FROM_MEM \ - | DMA_CONFIG_INC_MEM | DMA_CONFIG_PSIZE_8BITS \ - | DMA_CONFIG_MSIZE_8BITS) + | DMA_CONFIG_INC_MEM | DMA_CONFIG_MSIZE_8BITS) static void mbuf_callback(enum DmaIRQSource src, volatile void* param); @@ -60,12 +63,16 @@ uint32_t dma_mbuf_write_byte(volatile struct DmaMultiBuffer* buffer, return 0; } -void dma_mbuf_switch(volatile struct DmaMultiBuffer* buffer) +uint32_t dma_mbuf_switch(volatile struct DmaMultiBuffer* buffer) { - //if transfer already in progress or no data to send, don't start the - //transfer - if (buffer->dma_running || buffer->byte_index == 0) { - return; + //no data to send, stop here + if (buffer->byte_index == 0) { + return 0; + } + + //dma already running, give up + if (buffer->dma_running) { + return 1; } //start a new transfer @@ -80,15 +87,15 @@ void dma_mbuf_switch(volatile struct DmaMultiBuffer* buffer) buffer->buffer_index = 0; } - return; + return 0; } //--local functions------------------------------------------------------------- /** - * Callback called on DMA TX tranfert's completion. Checks for any remaining - * data to send. If any, starts a new transfer, else stop the DMA + * Callback called on DMA TX tranfert's completion. Stops the DMA and notifies + * the buffer that the transfer is done. */ static void mbuf_callback(enum DmaIRQSource src, volatile void* param) { diff --git a/srv/dma_mbuf.h b/srv/dma_mbuf.h index cf44d0d..b5f930c 100644 --- a/srv/dma_mbuf.h +++ b/srv/dma_mbuf.h @@ -2,7 +2,7 @@ * Module handling Direct Memory Access controller's multibuffer system * * The module provides a convenient tool to send data to a peripheral in a - * buffered, low-altency, low-cpu usage, non-blocking way + * buffered, low-latency, low-cpu usage, non-blocking way */ #ifndef _DMA_MBUF_H_ @@ -17,36 +17,38 @@ /** * Struct used by the multibuffer system. This system allows bufferized writes - * to a peripheral with no latency, minimal cpu usage and customisable buffer - * sizes + * to a peripheral with controlled latency, minimal cpu usage and customisable + * buffer size */ struct DmaMultiBuffer { - uint8_t* raw_buffer; + uint8_t* raw_buffer; //the buffer to use as a multi-buffer - uint16_t buffer_size; + uint16_t buffer_size; //the size of the buffer - uint8_t byte_index; - uint8_t buffer_index; - enum DmaPeriph dma; - enum DmaChannel channel; + uint8_t byte_index; //index of the next byte to be written + uint8_t buffer_index; //index of the buffer being written to + enum DmaPeriph dma; //DMA peripheral used for transfers + enum DmaChannel channel; //DMA channel used for transfers - bool dma_running; + bool dma_running; //whether the dam is currently running or not }; //--functions------------------------------------------------------------------- /** - * Configure a DMA multibuffer for a single DMA channel. A list of buffers is - * used to allow concurent write and DMA tranfers to the specified destination - * wich must be a peripheral. The DMA's priority is also given as parameters. - * The peripheral's specific configuration must be handled separately. 2 bytes - * are reserved in each buffer for index storage. + * Configure a DMA multibuffer for a single DMA channel. The given buffer is + * divided into 2 to allow one part to be written to while the other is being + * transfered by the DMA to a peripheral. + * The peripheral's specific configuration must be handled separately. + * Each peripheral's driver is responsible for providing the DMA configuration + * for said peripheral, though it can be manually given too. Please note that + * multiple peripherals may share the same DMA channel, which will cause issue + * when used with this buffer. * - * This system needs to be started manually: dma_mbuf_refresh() should be called - * whenever a DMA transfer can be started. This can be done manually after - * filling up the buffer. Transfers will then automatically be started as long - * as there are bytes in the buffer. See the usart module for an usage exemple + * This buffer can be written to at any given time using dma_mbuf_write_byte(), + * but its content are only written to the corresponding peripheral when + * dma_mbuf_switch() is called. */ void dma_mbuf_configure(volatile struct DmaMultiBuffer* buffer, const struct DmaParam* param, enum DmaConfig priority, @@ -57,18 +59,17 @@ void dma_mbuf_configure(volatile struct DmaMultiBuffer* buffer, * was successfull, 1 otherwise. The function is non-blocking. * * Note: calling this function will not trigger a DMA transfer, see - * dma_mbuf_refresh() to do that + * dma_mbuf_switch() to do that */ uint32_t dma_mbuf_write_byte(volatile struct DmaMultiBuffer* buffer, uint8_t byte); /** - * Refresh the buffer state, checking for bytes to send and triggering a DMA - * transfer if necessary. Should be called for the first transfer, any remaining - * transfer will be handled automatically until there are no bytes left in the - * buffer + * Switch the buffers, triggering a DMA transfer while allowing writes to + * continue. This function may fail if a DMA transfer is already running, + * returning 1 */ -void dma_mbuf_switch(volatile struct DmaMultiBuffer* buffer); +uint32_t dma_mbuf_switch(volatile struct DmaMultiBuffer* buffer); #endif //_DMA_MBUF_H_ -- 2.45.2 From a1ae9042394f84f3c36c61db49b5556fac241d79 Mon Sep 17 00:00:00 2001 From: Steins7 Date: Sat, 6 Apr 2024 16:59:57 +0200 Subject: [PATCH 5/9] Document dma_cbuf service --- srv/dma_cbuf.c | 6 +++++- srv/dma_cbuf.h | 14 +++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/srv/dma_cbuf.c b/srv/dma_cbuf.c index a9277f6..22f1fe4 100644 --- a/srv/dma_cbuf.c +++ b/srv/dma_cbuf.c @@ -12,9 +12,13 @@ //--local definitions----------------------------------------------------------- +/** + * DMA configuration to be used for the buffer. Additionnal configuration may be + * added by the peripherals used + */ #define DMA_CONFIG (DMA_CONFIG_IRQ_COMPLETE | DMA_CONFIG_FROM_PERIPH \ | DMA_CONFIG_CIRCULAR | DMA_CONFIG_INC_MEM \ - | DMA_CONFIG_PSIZE_8BITS | DMA_CONFIG_MSIZE_8BITS) + | DMA_CONFIG_MSIZE_8BITS) static void cbuf_callback(enum DmaIRQSource src, volatile void* param); diff --git a/srv/dma_cbuf.h b/srv/dma_cbuf.h index 7a05bbd..cd21f49 100644 --- a/srv/dma_cbuf.h +++ b/srv/dma_cbuf.h @@ -26,8 +26,8 @@ struct DmaCircBuffer { 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; + enum DmaPeriph dma; //DMA peripheral used for transfers + enum DmaChannel channel; //DMA channel used for transfers bool dma_looped; //whether the DMA looped or not (buffer overflow) }; @@ -38,8 +38,12 @@ struct DmaCircBuffer { /** * Configure a DMA circular buffer for a single DMA channel. A standard buffer * is used a base to construct a circular buffer, supplied in data by DMA - * tranfers from a peripheral. The DMA's priority is given as parameters. + * tranfers from a peripheral. * The peripheral's specific configuration must be handled separately. + * Each peripheral's driver is responsible for providing the DMA configuration + * for said peripheral, though it can be manually given too. Please note that + * multiple peripherals may share the same DMA channel, which will cause issue + * when used with this buffer. * * Once this function is called, the system will start running right away and * will stay running until manually stopped @@ -48,6 +52,10 @@ void dma_cbuf_configure(volatile struct DmaCircBuffer* buffer, const struct DmaParam* param, enum DmaConfig priority, volatile void* raw_buffer, uint16_t buffer_size); +/** + * Reads a bytes from the buffer, returning if the read operation was + * successfull, 1 otherwise. This function is non-blocking. + */ uint32_t dma_cbuf_read_byte(volatile struct DmaCircBuffer* buffer, uint8_t* byte); -- 2.45.2 From 85e7ad5ef1bcac5ce79f59477f85337ddde082f4 Mon Sep 17 00:00:00 2001 From: Steins7 Date: Sat, 6 Apr 2024 17:26:56 +0200 Subject: [PATCH 6/9] Document DMA driver --- drv/dma.c | 66 ++++++++++++++++++++++++++---------------------- drv/dma.h | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 107 insertions(+), 34 deletions(-) diff --git a/drv/dma.c b/drv/dma.c index 7fd4085..7d68b09 100644 --- a/drv/dma.c +++ b/drv/dma.c @@ -95,21 +95,6 @@ void dma_reset(enum DmaPeriph dma, enum DmaChannel channel) regs->CPAR = 0; } -void dma_exit_critical(enum DmaPeriph dma, enum DmaChannel channel) -{ - switch (dma) { - case DMA_PERIPH_1: - nvic_enable(NVIC_IRQ_DMA1_CHANNEL1 + channel); - break; - case DMA_PERIPH_2: - nvic_enable(NVIC_IRQ_DMA2_CHANNEL1 + channel); - break; - default: - return; - break; - } -} - void dma_start(enum DmaPeriph dma, enum DmaChannel channel, volatile void* mem, uint16_t size) { @@ -132,21 +117,6 @@ void dma_start(enum DmaPeriph dma, enum DmaChannel channel, } } -void dma_enter_critical(enum DmaPeriph dma, enum DmaChannel channel) -{ - switch (dma) { - case DMA_PERIPH_1: - nvic_disable(NVIC_IRQ_DMA1_CHANNEL1 + channel); - break; - case DMA_PERIPH_2: - nvic_disable(NVIC_IRQ_DMA2_CHANNEL1 + channel); - break; - default: - return; - break; - } -} - void dma_stop(enum DmaPeriph dma, enum DmaChannel channel) { switch (dma) { @@ -168,6 +138,36 @@ void dma_stop(enum DmaPeriph dma, enum DmaChannel channel) } } +void dma_enter_critical(enum DmaPeriph dma, enum DmaChannel channel) +{ + switch (dma) { + case DMA_PERIPH_1: + nvic_disable(NVIC_IRQ_DMA1_CHANNEL1 + channel); + break; + case DMA_PERIPH_2: + nvic_disable(NVIC_IRQ_DMA2_CHANNEL1 + channel); + break; + default: + return; + break; + } +} + +void dma_exit_critical(enum DmaPeriph dma, enum DmaChannel channel) +{ + switch (dma) { + case DMA_PERIPH_1: + nvic_enable(NVIC_IRQ_DMA1_CHANNEL1 + channel); + break; + case DMA_PERIPH_2: + nvic_enable(NVIC_IRQ_DMA2_CHANNEL1 + channel); + break; + default: + return; + break; + } +} + uint16_t dma_get_remaining(enum DmaPeriph dma, enum DmaChannel channel) { switch (dma) { @@ -185,6 +185,9 @@ uint16_t dma_get_remaining(enum DmaPeriph dma, enum DmaChannel channel) //--local functions------------------------------------------------------------- +/** + * Applies the given configuration mask to the given DMA channel + */ static void configure_dma(volatile struct DMA* dma, enum DmaChannel channel, enum DmaConfig config_mask, volatile void* periph) { @@ -195,6 +198,9 @@ static void configure_dma(volatile struct DMA* dma, enum DmaChannel channel, regs->CPAR = (uint32_t)periph; } +/** + * Starts the given DMA channel using the given parameters + */ static void start_dma(volatile struct DMA* dma, enum DmaChannel channel, volatile void* mem, uint16_t size) { diff --git a/drv/dma.h b/drv/dma.h index 1e38987..5367530 100644 --- a/drv/dma.h +++ b/drv/dma.h @@ -15,11 +15,19 @@ //--type definitions------------------------------------------------------------ +/** + * Available DMA peripherals. Note that some of these peripherals may not be + * available on all chips + */ enum DmaPeriph { DMA_PERIPH_1, DMA_PERIPH_2, }; +/** + * Available DMA channels. Note that some of these channels may not be + * available on all chips and all DMA peripheral + */ enum DmaChannel { DMA_CHANNEL_1 = 0, DMA_CHANNEL_2, @@ -30,6 +38,9 @@ enum DmaChannel { DMA_CHANNEL_7, //not available for DMA 2 }; +/** + * Configuration options for memory-to-peripheral transfers + */ enum DmaConfig { DMA_CONFIG_IRQ_COMPLETE = (0x1 << 1), DMA_CONFIG_IRQ_HALF = (0x1 << 2), @@ -51,6 +62,9 @@ enum DmaConfig { DMA_CONFIG_PRIO_VHIGH = (0x3 << 12), }; +/** + * Configuration options for memory-to-memory transfers + */ enum DmaConfigM2M { DMA_CONFIG_M2M_IRQ_COMPLETE = (0x1 << 1), DMA_CONFIG_M2M_IRQ_HALF = (0x1 << 2), @@ -69,14 +83,25 @@ enum DmaConfigM2M { DMA_CONFIG_M2M_PRIO_VHIGH = (0x3 << 12), }; +/** + * Available sources for a DMA IRQ. These sources can be enabled independently + * in the DMA configuration. + */ enum DmaIRQSource { DMA_IRQ_SOURCE_COMPLETE = (0x1 << 1), DMA_IRQ_SOURCE_HALF = (0x1 << 2), DMA_IQR_SOURCE_ERROR = (0x2 << 3), }; +/** + * Prototype of the IRQ callbacks that the applicative code can provide + */ typedef void (*DmaCallback)(enum DmaIRQSource, volatile void* param); +/** + * Generic struct used to share DAM configs between peripheral drivers and + * services providing DMA interfaces + */ struct DmaParam { void* periph; enum DmaConfig config; //DMA config, must correspond to peripheral @@ -87,25 +112,67 @@ struct DmaParam { //--functions------------------------------------------------------------------- +/** + * Configures the given DMA channel with the provided configuration, to perform + * a transfer to or from the given peripheral register. The specified callback, + * if any, will be called on the enabled IRQ sources, with the specified + * parameter, if any. + * + * This function doesn't initiate transfers, use dma_start() for that + */ void dma_configure(enum DmaPeriph dma, enum DmaChannel channel, enum DmaConfig config_mask, volatile void* periph, DmaCallback callback, volatile void* cb_param); +/** + * Unimplemented + */ void dma_configure_mem2mem(enum DmaPeriph dma, enum DmaChannel channel, enum DmaConfigM2M config_mask, const void* src, void* dest, uint16_t size, DmaCallback callback, void* cb_param); +/** + * Resets the given DMA channel, restoring the default configuration and + * disabling it + */ void dma_reset(enum DmaPeriph dma, enum DmaChannel channel); -void dma_exit_critical(enum DmaPeriph dma, enum DmaChannel channel); - +/** + * Initiate a transfer on the given DMA channel, to or from the given memory + * address, and of the specified size. + * + * Should only be used after the channel has been configured through + * dma_configure() + * All transfers started must be eventually stopped, or the channel reset, for + * proper IRQ behavior. + */ 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); - +/** + * Stops a transfer on the given DMA channel. If the transfer has already + * ended, properly shutdown the channel. Configuration is not lost after calling + * this function and dma_start() may be called again + */ void dma_stop(enum DmaPeriph dma, enum DmaChannel channel); +/** + * Enters a DMA critical section, disabling the given channel's IRQs until + * dma_exit_critical() is called + */ +void dma_enter_critical(enum DmaPeriph dma, enum DmaChannel channel); + +/** + * Exists a DMA critical section previously entered through + * dma_enter_critical(). Reenables the given channel's IRQs + */ +void dma_exit_critical(enum DmaPeriph dma, enum DmaChannel channel); + +/** + * Returns the remaining number of bytes to be transmitted while a transfer is + * running. When no transfer is running, returns the number of bytes to transfer + * next + */ uint16_t dma_get_remaining(enum DmaPeriph dma, enum DmaChannel channe); -- 2.45.2 From c6da4e11d8fed20836f242962d7cc1dfc28f1795 Mon Sep 17 00:00:00 2001 From: Steins7 Date: Sat, 6 Apr 2024 22:19:19 +0200 Subject: [PATCH 7/9] Document USART driver --- drv/usart.c | 57 +++++++++++++++++++++++++-------------------------- drv/usart.h | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 86 insertions(+), 30 deletions(-) diff --git a/drv/usart.c b/drv/usart.c index 578b93b..35bc779 100644 --- a/drv/usart.c +++ b/drv/usart.c @@ -8,7 +8,6 @@ //--includes-------------------------------------------------------------------- #include "usart.h" -#include "nvic.h" #include "usart_regs.h" #include "reg.h" @@ -106,6 +105,33 @@ void usart_configure(enum UsartPeriph periph, enum UsartConfig config, } } +uint32_t usart_read_byte(enum UsartPeriph periph, uint8_t* byte) +{ + volatile struct USART* regs; + + 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.RXNE) { + *byte = regs->DR.DR; + return 0; + } else { + return 1; + } +} + uint32_t usart_write_byte(enum UsartPeriph periph, uint8_t byte) { volatile struct USART* regs; @@ -134,33 +160,6 @@ uint32_t usart_write_byte(enum UsartPeriph periph, uint8_t byte) } } -uint32_t usart_read_byte(enum UsartPeriph periph, uint8_t* byte) -{ - volatile struct USART* regs; - - 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.RXNE) { - *byte = regs->DR.DR; - return 0; - } else { - return 1; - } -} - const struct DmaParam* usart_configure_rx_dma(enum UsartPeriph periph) { const struct DmaParam* param; @@ -295,7 +294,7 @@ static void configure_usart(volatile struct USART* regs, /** * Configure the given registers with the given baudrate. Baudrate is dependant - * on the peripheric's clock and may not be exact due to precision errors (see + * on the peripheral's clock and may not be exact due to precision errors (see * table 192 in documentation) */ static void configure_baudrate(volatile struct USART* regs, uint32_t clock, diff --git a/drv/usart.h b/drv/usart.h index 9189d01..960bf47 100644 --- a/drv/usart.h +++ b/drv/usart.h @@ -17,12 +17,19 @@ //--type definitions------------------------------------------------------------ +/** + * Available USART peripherals. Note that some of these peripherals may not be + * available on all chips + */ enum UsartPeriph { USART_PERIPH_1, USART_PERIPH_2, USART_PERIPH_3, }; +/** + * Available configuration options + */ enum UsartConfig { USART_CONFIG_7E1, USART_CONFIG_7E2, @@ -39,14 +46,64 @@ enum UsartConfig { //--functions------------------------------------------------------------------- +/** + * Configures the given USART peripheral using the provided condiguration + * options and baudrate. The baudrate may be any value supported by the + * peripheral, though some may not be exact due to precision errors (see + * table 192 in documentation). The baudrate is dependant on the peripheral's + * clock and changes to the later after this function has been called will cause + * the effective baudrate to change + * + * This function doesn't configure the required ports. This should be done using + * the gpio driver: + * - Tx port should be using GPIO_CONFIG_OUT_ALT_PUSH_PULL with + * the appropriate output speed based on the baurate (see GpioMode) + * - Rx port should be using GPIO_CONFIG_IN_FLOATING in input mode + * Both ports do not need to be configured if not used (e.g. if only using Tx, + * the Rx port can be left unconfigured) + */ void usart_configure(enum UsartPeriph periph, enum UsartConfig config, uint32_t baudrate); + +/** + * Resets the given USART peripheral, applying the default configuration and + * disabling it + */ void usart_reset(enum UsartPeriph periph); -uint32_t usart_write_byte(enum UsartPeriph periph, uint8_t byte); +/** + * Reads a single byte to the given USART peripheral, returning 0 if + * successfull, 1 otherwise. + * + * The Rx port must be configured for this function to ever return successfully + */ uint32_t usart_read_byte(enum UsartPeriph periph, uint8_t* byte); +/** + * Writes a single byte to the given USART peripheral, returning 0 if + * successfull, 1 otherwise. + * + * The Tx port must be configured for this function to do anything, though the + * function would still return 0 + */ +uint32_t usart_write_byte(enum UsartPeriph periph, uint8_t byte); + +/** + * Configures the given USART peripheral for DMA Rx operations, returning the + * corresponding DMA parameters to be used. + * + * The DMA must be configured separately using the DMA driver or an existing + * service + */ const struct DmaParam* usart_configure_rx_dma(enum UsartPeriph periph); + +/** + * Configures the given USART peripheral for DMA Rx operations, returning the + * corresponding DMA parameters to be used. + * + * The DMA must be configured separately using the DMA driver or an existing + * service + */ const struct DmaParam* usart_configure_tx_dma(enum UsartPeriph periph); -- 2.45.2 From 4eec301d17de001d8d75b20adfbcb05d088aff34 Mon Sep 17 00:00:00 2001 From: Steins7 Date: Sat, 6 Apr 2024 22:56:28 +0200 Subject: [PATCH 8/9] Optimize USART driver's code size Using tables reduces code size while also improving lisibility. That is a win-win --- drv/usart.c | 183 +++++++++++++++++++--------------------------------- 1 file changed, 67 insertions(+), 116 deletions(-) diff --git a/drv/usart.c b/drv/usart.c index 35bc779..25167a2 100644 --- a/drv/usart.c +++ b/drv/usart.c @@ -29,50 +29,52 @@ static void configure_baudrate(volatile struct USART* regs, uint32_t clock, //--local variables------------------------------------------------------------- -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 const struct DmaParam usart1_rx_param = { - (void*)&usart1->DR, - DMA_CONFIG, - DMA_PERIPH_1, - DMA_CHANNEL_5, +static volatile struct USART* const usarts[] = { + (struct USART*)USART1_BASE_ADDRESS, + (struct USART*)USART2_BASE_ADDRESS, + (struct USART*)USART3_BASE_ADDRESS, }; -static const struct DmaParam usart2_rx_param = { - (void*)&usart2->DR, - DMA_CONFIG, - DMA_PERIPH_1, - DMA_CHANNEL_6, +static const struct DmaParam usarts_rx_param[] = { + { + (void*)&usarts[USART_PERIPH_1]->DR, + DMA_CONFIG, + DMA_PERIPH_1, + DMA_CHANNEL_5, + }, + { + (void*)&usarts[USART_PERIPH_2]->DR, + DMA_CONFIG, + DMA_PERIPH_1, + DMA_CHANNEL_6, + }, + { + (void*)&usarts[USART_PERIPH_3]->DR, + DMA_CONFIG, + DMA_PERIPH_1, + DMA_CHANNEL_3, + }, }; -static const struct DmaParam usart3_rx_param = { - (void*)&usart3->DR, - DMA_CONFIG, - DMA_PERIPH_1, - DMA_CHANNEL_3, -}; - -static const struct DmaParam usart1_tx_param = { - (void*)&usart1->DR, - DMA_CONFIG, - DMA_PERIPH_1, - DMA_CHANNEL_4, -}; - -static const struct DmaParam usart2_tx_param = { - (void*)&usart2->DR, - DMA_CONFIG, - DMA_PERIPH_1, - DMA_CHANNEL_7, -}; - -static const struct DmaParam usart3_tx_param = { - (void*)&usart3->DR, - DMA_CONFIG, - DMA_PERIPH_1, - DMA_CHANNEL_2, +static const struct DmaParam usarts_tx_param[] = { + { + (void*)&usarts[USART_PERIPH_1]->DR, + DMA_CONFIG, + DMA_PERIPH_1, + DMA_CHANNEL_4, + }, + { + (void*)&usarts[USART_PERIPH_2]->DR, + DMA_CONFIG, + DMA_PERIPH_1, + DMA_CHANNEL_7, + }, + { + (void*)&usarts[USART_PERIPH_3]->DR, + DMA_CONFIG, + DMA_PERIPH_1, + DMA_CHANNEL_2, + }, }; @@ -87,18 +89,21 @@ void usart_configure(enum UsartPeriph periph, enum UsartConfig config, switch (periph) { case USART_PERIPH_1: rcc_enable(RCC_AHB_NONE, RCC_APB1_NONE, RCC_APB2_USART); - configure_baudrate(usart1, clocks.apb2_freq, baudrate); - configure_usart(usart1, config); + configure_baudrate(usarts[USART_PERIPH_1], clocks.apb2_freq, + baudrate); + configure_usart(usarts[USART_PERIPH_1], config); break; case USART_PERIPH_2: rcc_enable(RCC_AHB_NONE, RCC_APB1_USART2, RCC_APB2_NONE); - configure_baudrate(usart2, clocks.apb1_freq, baudrate); - configure_usart(usart2, config); + configure_baudrate(usarts[USART_PERIPH_2], clocks.apb1_freq, + baudrate); + configure_usart(usarts[USART_PERIPH_2], config); break; case USART_PERIPH_3: rcc_enable(RCC_AHB_NONE, RCC_APB1_USART3, RCC_APB2_NONE); - configure_baudrate(usart3, clocks.apb1_freq, baudrate); - configure_usart(usart3, config); + configure_baudrate(usarts[USART_PERIPH_3], clocks.apb1_freq, + baudrate); + configure_usart(usarts[USART_PERIPH_3], config); break; default: break; @@ -107,25 +112,12 @@ void usart_configure(enum UsartPeriph periph, enum UsartConfig config, uint32_t usart_read_byte(enum UsartPeriph periph, uint8_t* byte) { - volatile struct USART* regs; - - 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 (periph > USART_PERIPH_3) { + return 1; } - if (regs->SR.RXNE) { - *byte = regs->DR.DR; + if (usarts[periph]->SR.RXNE) { + *byte = usarts[periph]->DR.DR; return 0; } else { return 1; @@ -134,26 +126,13 @@ uint32_t usart_read_byte(enum UsartPeriph periph, uint8_t* byte) uint32_t usart_write_byte(enum UsartPeriph periph, uint8_t byte) { - volatile struct USART* regs; - - 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 (periph > USART_PERIPH_3) { + return 1; } //only write data if the tx register it empty, give up otherwise - if (regs->SR.TXE) { - reg_write(regs->DR, USART_DR_DR, byte); + if (usarts[periph]->SR.TXE) { + reg_write(usarts[periph]->DR, USART_DR_DR, byte); return 0; } else { return 1; @@ -162,50 +141,22 @@ uint32_t usart_write_byte(enum UsartPeriph periph, uint8_t byte) const struct DmaParam* usart_configure_rx_dma(enum UsartPeriph periph) { - const struct DmaParam* param; - - switch (periph) { - case USART_PERIPH_1: - param = &usart1_rx_param; - reg_set(usart1->CR3, USART_CR3_DMAR); - break; - case USART_PERIPH_2: - param = &usart2_rx_param; - reg_set(usart2->CR3, USART_CR3_DMAR); - break; - case USART_PERIPH_3: - param = &usart3_rx_param; - reg_set(usart3->CR3, USART_CR3_DMAR); - break; - default: - return nullptr; + if (periph > USART_PERIPH_3) { + return nullptr; } - return param; + reg_set(usarts[periph]->CR3, USART_CR3_DMAR); + return &usarts_rx_param[periph]; } const struct DmaParam* usart_configure_tx_dma(enum UsartPeriph periph) { - const struct DmaParam* param; - - 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; + if (periph > USART_PERIPH_3) { + return nullptr; } - return param; + reg_set(usarts[periph]->CR3, USART_CR3_DMAT); + return &usarts_tx_param[periph]; } -- 2.45.2 From cf077d2e51c624e76fceca0520154b5a1f48b592 Mon Sep 17 00:00:00 2001 From: Steins7 Date: Sat, 6 Apr 2024 23:02:08 +0200 Subject: [PATCH 9/9] Fix unwanted changes --- startup.s | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/startup.s b/startup.s index a664898..cbc3e23 100644 --- a/startup.s +++ b/startup.s @@ -75,19 +75,19 @@ bss_init_end: */ .section .text.hdr_default, "ax", %progbits .weak hdr_default - hdr_default: +hdr_default: b hdr_default .size hdr_default, .-hdr_default - //--Vector table---------------------------------------------------------------- +//--Vector table---------------------------------------------------------------- .section .vector_table, "a", %progbits .type vector_table, %object .size vector_table, .-vector_table - vector_table: +vector_table: .word _estack .word hdr_reset .word hdr_nmi @@ -165,7 +165,7 @@ bss_init_end: .word hdr_dma2_channel3 .word hdr_dma2_channel4_5 - //--Weak definitions------------------------------------------------------------ +//--Weak definitions------------------------------------------------------------ .weak hdr_nmi .thumb_set hdr_nmi, hdr_default -- 2.45.2