Compare commits

...

3 Commits

Author SHA1 Message Date
fd33003e26 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
2024-04-06 16:29:45 +02:00
7a19ae9223 Clean minor issues in startup.s 2024-04-06 16:28:08 +02:00
ccf36ac400 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
2024-04-03 22:03:15 +02:00
10 changed files with 256 additions and 329 deletions

View File

@ -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------------------------------------------------------------------------

View File

@ -77,12 +77,19 @@ 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-------------------------------------------------------------------
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,
@ -92,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);

View File

@ -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; i<buffer->buffer_nb; ++i) {
for (uint16_t j=0; j<buffer->buffer_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);
}

View File

@ -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,43 +109,22 @@ 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);
}
} 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);
@ -124,90 +132,84 @@ uint32_t usart_write_byte(enum UsartPeriph periph, uint8_t byte)
} else {
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);
} else {
if (regs->SR.RXNE) {
*byte = regs->DR.DR;
return 0;
} else {
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;
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;
}
return param;
}
//--local functions-------------------------------------------------------------
/**
@ -309,39 +311,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);
}

View File

@ -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_

View File

@ -24,25 +24,25 @@ 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->size = buffer_size;
buffer->begin = 0;
buffer->dma = dma;
buffer->channel = channel;
buffer->config = DMA_CONFIG | priority;
buffer->dma = param->dma;
buffer->channel = param->channel;
buffer->dma_looped = false;
dma_configure(buffer->dma, buffer->channel, buffer->config, buffer->src,
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,
@ -86,4 +86,3 @@ static void cbuf_callback(enum DmaIRQSource src, volatile void* param)
buffer->dma_looped = true;
}

View File

@ -10,7 +10,7 @@
//--includes--------------------------------------------------------------------
#include "dma.h"
#include "../drv/dma.h"
//--type definitions------------------------------------------------------------
@ -22,14 +22,12 @@
*/
struct DmaCircBuffer {
volatile void* buffer; //the buffer to use as a circular buffer
volatile void* src; //source peripheral register
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
enum DmaPeriph dma;
enum DmaChannel channel;
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);

100
srv/dma_mbuf.c Normal file
View File

@ -0,0 +1,100 @@
/** @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->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)
{
//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
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;
}
//--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;
dma_stop(buffer->dma, buffer->channel);
buffer->dma_running = false;
}

View File

@ -10,7 +10,7 @@
//--includes--------------------------------------------------------------------
#include "dma.h"
#include "../drv/dma.h"
//--type definitions------------------------------------------------------------
@ -21,19 +21,16 @@
* 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
uint16_t buffer_size;
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
uint8_t byte_index;
uint8_t buffer_index;
enum DmaPeriph dma;
enum DmaChannel channel;
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_running;
};
@ -51,9 +48,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 +68,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_

View File

@ -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
@ -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