/** @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----------------------------------------------------------- static void mbuf_callback(enum DmaIRQSource src, volatile void* param); //--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 config) { #warning "check for null ptr" buffer->buffers = buffers; buffer->dest = dest; buffer->buffer_size = buffer_size; buffer->byte_index = 0; 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 = config; } 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 if (buffer->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; buffer->byte_index = 0; } //write the byte uint8_t** buffers = (uint8_t**)buffer->buffers; buffers[buffer->buffer_index][buffer->byte_index] = byte; ++buffer->byte_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 && buffer->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], buffer->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->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); }