117 lines
2.9 KiB
C
117 lines
2.9 KiB
C
/** @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;
|
|
}
|
|
|