126 lines
3.4 KiB
C
126 lines
3.4 KiB
C
/** @file dma_mbuf.c
|
|
* Module handling Direct Memory Access controller's TX functions
|
|
*
|
|
* The module provides convenient tools to send data to a peripheral or memory
|
|
* area in a buffered, 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, simply wait for the DMA to empty one
|
|
dma_exit_critical(buffer->dma, buffer->channel);
|
|
while (buffer->free_buffer_nb == 0) {}
|
|
dma_enter_critical(buffer->dma, buffer->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(buffer->dma, buffer->channel);
|
|
}
|
|
|
|
//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);
|
|
}
|
|
|