/** @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" #include "error.h" //--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_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) { error_assert(buffer != nullptr); error_assert(param != nullptr); error_assert(raw_buffer != nullptr); buffer->raw_buffer = raw_buffer; 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, uint8_t byte) { error_assert(buffer != nullptr); //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; } uint32_t dma_mbuf_switch(volatile struct DmaMultiBuffer* buffer) { error_assert(buffer != nullptr); //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 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; if (buffer->buffer_index > 1) { buffer->buffer_index = 0; } return 0; } //--local functions------------------------------------------------------------- /** * 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) { (void)src; //only transfer complete expected error_assert(param != nullptr); volatile struct DmaMultiBuffer* buffer = param; dma_stop(buffer->dma, buffer->channel); buffer->dma_running = false; }