Compare commits

..

10 Commits

Author SHA1 Message Date
Steins7
c26b1cd703 Merge pull request 'usart' (#1) from usart into rework
Reviewed-on: https://git.steins7.ovh/Steins7/stm32f1xx_HBL/pulls/1
2024-04-06 21:05:00 +00:00
cf077d2e51 Fix unwanted changes 2024-04-06 23:02:08 +02:00
4eec301d17 Optimize USART driver's code size
Using tables reduces code size while also improving lisibility. That is a
win-win
2024-04-06 22:56:28 +02:00
c6da4e11d8 Document USART driver 2024-04-06 22:19:19 +02:00
85e7ad5ef1 Document DMA driver 2024-04-06 17:26:56 +02:00
a1ae904239 Document dma_cbuf service 2024-04-06 16:59:57 +02:00
5e961b15fc Document dma_mbuf service 2024-04-06 16:57:36 +02:00
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
11 changed files with 486 additions and 459 deletions

View File

@ -19,9 +19,9 @@
//--local definitions----------------------------------------------------------- //--local definitions-----------------------------------------------------------
static void configure_dma(volatile struct DMA* dma, enum DmaChannel channel, static void configure_dma(volatile struct DMA* dma, enum DmaChannel channel,
enum DmaConfig config_mask, volatile void* periph, volatile void* mem, enum DmaConfig config_mask, volatile void* periph);
uint16_t size); static void start_dma(volatile struct DMA* dma, enum DmaChannel channel,
volatile void* mem, uint16_t size);
//--local variables------------------------------------------------------------- //--local variables-------------------------------------------------------------
@ -36,8 +36,8 @@ static volatile void* dma2_cb_params[5];
//--public functions------------------------------------------------------------ //--public functions------------------------------------------------------------
void dma_configure(enum DmaPeriph dma, enum DmaChannel channel, void dma_configure(enum DmaPeriph dma, enum DmaChannel channel,
enum DmaConfig config_mask, volatile void* periph, volatile void* mem, enum DmaConfig config_mask, volatile void* periph,
uint16_t size, DmaCallback callback, volatile void* cb_param) DmaCallback callback, volatile void* cb_param)
{ {
//reset peripheral first, to ensure proper configuration //reset peripheral first, to ensure proper configuration
dma_reset(dma, channel); dma_reset(dma, channel);
@ -45,7 +45,7 @@ void dma_configure(enum DmaPeriph dma, enum DmaChannel channel,
switch (dma) { switch (dma) {
case DMA_PERIPH_1: case DMA_PERIPH_1:
rcc_enable(RCC_AHB_DMA1, RCC_APB1_NONE, RCC_APB2_NONE); 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) { if (callback) {
dma1_callbacks[channel] = callback; dma1_callbacks[channel] = callback;
dma1_cb_params[channel] = cb_param; dma1_cb_params[channel] = cb_param;
@ -54,7 +54,7 @@ void dma_configure(enum DmaPeriph dma, enum DmaChannel channel,
break; break;
case DMA_PERIPH_2: case DMA_PERIPH_2:
rcc_enable(RCC_AHB_DMA2, RCC_APB1_NONE, RCC_APB2_NONE); 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) { if (callback) {
dma2_callbacks[channel] = callback; dma2_callbacks[channel] = callback;
dma2_cb_params[channel] = cb_param; dma2_cb_params[channel] = cb_param;
@ -95,14 +95,21 @@ void dma_reset(enum DmaPeriph dma, enum DmaChannel channel)
regs->CPAR = 0; regs->CPAR = 0;
} }
void dma_exit_critical(enum DmaPeriph dma, enum DmaChannel channel) void dma_start(enum DmaPeriph dma, enum DmaChannel channel,
volatile void* mem, uint16_t size)
{ {
switch (dma) { switch (dma) {
case DMA_PERIPH_1: case DMA_PERIPH_1:
if (dma1_callbacks[channel]) {
nvic_enable(NVIC_IRQ_DMA1_CHANNEL1 + channel); nvic_enable(NVIC_IRQ_DMA1_CHANNEL1 + channel);
}
start_dma(dma1, channel, mem, size);
break; break;
case DMA_PERIPH_2: case DMA_PERIPH_2:
if (dma2_callbacks[channel]) {
nvic_enable(NVIC_IRQ_DMA2_CHANNEL1 + channel); nvic_enable(NVIC_IRQ_DMA2_CHANNEL1 + channel);
}
start_dma(dma2, channel, mem, size);
break; break;
default: default:
return; return;
@ -110,19 +117,19 @@ void dma_exit_critical(enum DmaPeriph dma, enum DmaChannel channel)
} }
} }
void dma_enable(enum DmaPeriph dma, enum DmaChannel channel) void dma_stop(enum DmaPeriph dma, enum DmaChannel channel)
{ {
switch (dma) { switch (dma) {
case DMA_PERIPH_1: case DMA_PERIPH_1:
reg_set(dma1->CHANNELS[channel].CCR, DMA_CCR_EN); reg_reset(dma1->CHANNELS[channel].CCR, DMA_CCR_EN);
if (dma1_callbacks[channel]) { if (dma1_callbacks[channel]) {
nvic_enable(NVIC_IRQ_DMA1_CHANNEL1 + channel); nvic_disable(NVIC_IRQ_DMA1_CHANNEL1 + channel);
} }
break; break;
case DMA_PERIPH_2: case DMA_PERIPH_2:
reg_set(dma2->CHANNELS[channel].CCR, DMA_CCR_EN); reg_reset(dma2->CHANNELS[channel].CCR, DMA_CCR_EN);
if (dma2_callbacks[channel]) { if (dma2_callbacks[channel]) {
nvic_enable(NVIC_IRQ_DMA2_CHANNEL1 + channel); nvic_disable(NVIC_IRQ_DMA2_CHANNEL1 + channel);
} }
break; break;
default: default:
@ -146,20 +153,14 @@ void dma_enter_critical(enum DmaPeriph dma, enum DmaChannel channel)
} }
} }
void dma_disable(enum DmaPeriph dma, enum DmaChannel channel) void dma_exit_critical(enum DmaPeriph dma, enum DmaChannel channel)
{ {
switch (dma) { switch (dma) {
case DMA_PERIPH_1: case DMA_PERIPH_1:
reg_reset(dma1->CHANNELS[channel].CCR, DMA_CCR_EN); nvic_enable(NVIC_IRQ_DMA1_CHANNEL1 + channel);
if (dma1_callbacks[channel]) {
nvic_disable(NVIC_IRQ_DMA1_CHANNEL1 + channel);
}
break; break;
case DMA_PERIPH_2: case DMA_PERIPH_2:
reg_reset(dma2->CHANNELS[channel].CCR, DMA_CCR_EN); nvic_enable(NVIC_IRQ_DMA2_CHANNEL1 + channel);
if (dma2_callbacks[channel]) {
nvic_disable(NVIC_IRQ_DMA2_CHANNEL1 + channel);
}
break; break;
default: default:
return; return;
@ -184,22 +185,34 @@ uint16_t dma_get_remaining(enum DmaPeriph dma, enum DmaChannel channel)
//--local functions------------------------------------------------------------- //--local functions-------------------------------------------------------------
/**
* Applies the given configuration mask to the given DMA channel
*/
static void configure_dma(volatile struct DMA* dma, enum DmaChannel channel, static void configure_dma(volatile struct DMA* dma, enum DmaChannel channel,
enum DmaConfig config_mask, volatile void* periph, volatile void* mem, enum DmaConfig config_mask, volatile void* periph)
uint16_t size)
{ {
volatile struct DMA_CHANNEL* regs = &dma->CHANNELS[channel]; volatile struct DMA_CHANNEL* regs = &dma->CHANNELS[channel];
//registers should already be at reset value, apply new config //registers should already be at reset value, apply new config
regs->CCR.word = config_mask; regs->CCR.word = config_mask;
reg_write(regs->CNDTR, DMA_CNDTR_NDT, size);
regs->CPAR = (uint32_t)periph; regs->CPAR = (uint32_t)periph;
regs->CMAR = (uint32_t)mem;
//only enable channel when everything is configured
reg_set(regs->CCR, DMA_CCR_EN);
} }
/**
* Starts the given DMA channel using the given parameters
*/
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------------------------------------------------------------------------ //--ISRs------------------------------------------------------------------------

View File

@ -15,11 +15,19 @@
//--type definitions------------------------------------------------------------ //--type definitions------------------------------------------------------------
/**
* Available DMA peripherals. Note that some of these peripherals may not be
* available on all chips
*/
enum DmaPeriph { enum DmaPeriph {
DMA_PERIPH_1, DMA_PERIPH_1,
DMA_PERIPH_2, DMA_PERIPH_2,
}; };
/**
* Available DMA channels. Note that some of these channels may not be
* available on all chips and all DMA peripheral
*/
enum DmaChannel { enum DmaChannel {
DMA_CHANNEL_1 = 0, DMA_CHANNEL_1 = 0,
DMA_CHANNEL_2, DMA_CHANNEL_2,
@ -30,6 +38,9 @@ enum DmaChannel {
DMA_CHANNEL_7, //not available for DMA 2 DMA_CHANNEL_7, //not available for DMA 2
}; };
/**
* Configuration options for memory-to-peripheral transfers
*/
enum DmaConfig { enum DmaConfig {
DMA_CONFIG_IRQ_COMPLETE = (0x1 << 1), DMA_CONFIG_IRQ_COMPLETE = (0x1 << 1),
DMA_CONFIG_IRQ_HALF = (0x1 << 2), DMA_CONFIG_IRQ_HALF = (0x1 << 2),
@ -51,6 +62,9 @@ enum DmaConfig {
DMA_CONFIG_PRIO_VHIGH = (0x3 << 12), DMA_CONFIG_PRIO_VHIGH = (0x3 << 12),
}; };
/**
* Configuration options for memory-to-memory transfers
*/
enum DmaConfigM2M { enum DmaConfigM2M {
DMA_CONFIG_M2M_IRQ_COMPLETE = (0x1 << 1), DMA_CONFIG_M2M_IRQ_COMPLETE = (0x1 << 1),
DMA_CONFIG_M2M_IRQ_HALF = (0x1 << 2), DMA_CONFIG_M2M_IRQ_HALF = (0x1 << 2),
@ -69,35 +83,96 @@ enum DmaConfigM2M {
DMA_CONFIG_M2M_PRIO_VHIGH = (0x3 << 12), DMA_CONFIG_M2M_PRIO_VHIGH = (0x3 << 12),
}; };
/**
* Available sources for a DMA IRQ. These sources can be enabled independently
* in the DMA configuration.
*/
enum DmaIRQSource { enum DmaIRQSource {
DMA_IRQ_SOURCE_COMPLETE = (0x1 << 1), DMA_IRQ_SOURCE_COMPLETE = (0x1 << 1),
DMA_IRQ_SOURCE_HALF = (0x1 << 2), DMA_IRQ_SOURCE_HALF = (0x1 << 2),
DMA_IQR_SOURCE_ERROR = (0x2 << 3), DMA_IQR_SOURCE_ERROR = (0x2 << 3),
}; };
/**
* Prototype of the IRQ callbacks that the applicative code can provide
*/
typedef void (*DmaCallback)(enum DmaIRQSource, volatile void* param); typedef void (*DmaCallback)(enum DmaIRQSource, volatile void* param);
/**
* Generic struct used to share DAM configs between peripheral drivers and
* services providing DMA interfaces
*/
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------------------------------------------------------------------- //--functions-------------------------------------------------------------------
/**
* Configures the given DMA channel with the provided configuration, to perform
* a transfer to or from the given peripheral register. The specified callback,
* if any, will be called on the enabled IRQ sources, with the specified
* parameter, if any.
*
* This function doesn't initiate transfers, use dma_start() for that
*/
void dma_configure(enum DmaPeriph dma, enum DmaChannel channel, void dma_configure(enum DmaPeriph dma, enum DmaChannel channel,
enum DmaConfig config_mask, volatile void* periph, volatile void* mem, enum DmaConfig config_mask, volatile void* periph,
uint16_t size, DmaCallback callback, volatile void* cb_param); DmaCallback callback, volatile void* cb_param);
/**
* Unimplemented
*/
void dma_configure_mem2mem(enum DmaPeriph dma, enum DmaChannel channel, void dma_configure_mem2mem(enum DmaPeriph dma, enum DmaChannel channel,
enum DmaConfigM2M config_mask, const void* src, void* dest, enum DmaConfigM2M config_mask, const void* src, void* dest,
uint16_t size, DmaCallback callback, void* cb_param); uint16_t size, DmaCallback callback, void* cb_param);
/**
* Resets the given DMA channel, restoring the default configuration and
* disabling it
*/
void dma_reset(enum DmaPeriph dma, enum DmaChannel channel); void dma_reset(enum DmaPeriph dma, enum DmaChannel channel);
void dma_exit_critical(enum DmaPeriph dma, enum DmaChannel channel); /**
* Initiate a transfer on the given DMA channel, to or from the given memory
* address, and of the specified size.
*
* Should only be used after the channel has been configured through
* dma_configure()
* All transfers started must be eventually stopped, or the channel reset, for
* proper IRQ behavior.
*/
void dma_start(enum DmaPeriph dma, enum DmaChannel channel,
volatile void* mem, uint16_t size);
void dma_enable(enum DmaPeriph dma, enum DmaChannel channel); /**
* Stops a transfer on the given DMA channel. If the transfer has already
* ended, properly shutdown the channel. Configuration is not lost after calling
* this function and dma_start() may be called again
*/
void dma_stop(enum DmaPeriph dma, enum DmaChannel channel);
/**
* Enters a DMA critical section, disabling the given channel's IRQs until
* dma_exit_critical() is called
*/
void dma_enter_critical(enum DmaPeriph dma, enum DmaChannel channel); void dma_enter_critical(enum DmaPeriph dma, enum DmaChannel channel);
void dma_disable(enum DmaPeriph dma, enum DmaChannel channel); /**
* Exists a DMA critical section previously entered through
* dma_enter_critical(). Reenables the given channel's IRQs
*/
void dma_exit_critical(enum DmaPeriph dma, enum DmaChannel channel);
/**
* Returns the remaining number of bytes to be transmitted while a transfer is
* running. When no transfer is running, returns the number of bytes to transfer
* next
*/
uint16_t dma_get_remaining(enum DmaPeriph dma, enum DmaChannel channe); 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

@ -1,78 +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
*/
#ifndef _DMA_MBUF_H_
#define _DMA_MBUF_H_
//--includes--------------------------------------------------------------------
#include "dma.h"
//--type definitions------------------------------------------------------------
/**
* Struct used by the multibuffer system. This system allows bufferized writes
* to a peripheral with no latency, minimal cpu usage and customisable buffer
* sizes
*/
struct DmaMultiBuffer {
void** buffers; //list of buffers to write to
volatile void* dest; //destination peripheral register
uint16_t buffer_size; //size of a single buffer
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
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
};
//--functions-------------------------------------------------------------------
/**
* Configure a DMA multibuffer for a single DMA channel. A list of buffers is
* used to allow concurent write and DMA tranfers to the specified destination
* wich must be a peripheral. The DMA's priority is also given as parameters.
* The peripheral's specific configuration must be handled separately. 2 bytes
* are reserved in each buffer for index storage.
*
* This system needs to be started manually: dma_mbuf_refresh() should be called
* whenever a DMA transfer can be started. This can be done manually after
* 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);
/**
* Write the given byte to the given buffer. Returns 0 if the write operation
* was successfull, 1 otherwise. The function is non-blocking.
*
* Note: calling this function will not trigger a DMA transfer, see
* dma_mbuf_refresh() to do that
*/
uint32_t dma_mbuf_write_byte(volatile struct DmaMultiBuffer* buffer,
uint8_t byte);
/**
* Refresh the buffer state, checking for bytes to send and triggering a DMA
* transfer if necessary. Should be called for the first transfer, any remaining
* transfer will be handled automatically until there are no bytes left in the
* buffer
*/
void dma_mbuf_refresh(volatile struct DmaMultiBuffer* buffer);
#endif //_DMA_MBUF_H_

View File

@ -8,14 +8,11 @@
//--includes-------------------------------------------------------------------- //--includes--------------------------------------------------------------------
#include "usart.h" #include "usart.h"
#include "nvic.h"
#include "usart_regs.h" #include "usart_regs.h"
#include "reg.h" #include "reg.h"
#include "rcc.h" #include "rcc.h"
#include "dma.h" #include "dma.h"
#include "dma_mbuf.h"
#include "dma_cbuf.h"
#include "stddef.h" #include "stddef.h"
@ -27,19 +24,58 @@ static void configure_usart(volatile struct USART* regs,
static void configure_baudrate(volatile struct USART* regs, uint32_t clock, static void configure_baudrate(volatile struct USART* regs, uint32_t clock,
uint32_t baudrate); uint32_t baudrate);
#define DMA_CONFIG (DMA_CONFIG_PSIZE_8BITS)
//--local variables------------------------------------------------------------- //--local variables-------------------------------------------------------------
static volatile struct USART* const usart1 = (struct USART*)USART1_BASE_ADDRESS; static volatile struct USART* const usarts[] = {
static volatile struct USART* const usart2 = (struct USART*)USART2_BASE_ADDRESS; (struct USART*)USART1_BASE_ADDRESS,
static volatile struct USART* const usart3 = (struct USART*)USART3_BASE_ADDRESS; (struct USART*)USART2_BASE_ADDRESS,
(struct USART*)USART3_BASE_ADDRESS,
};
static volatile struct DmaCircBuffer usart1_rx_buffer; static const struct DmaParam usarts_rx_param[] = {
static volatile struct DmaMultiBuffer usart1_tx_buffer; {
static volatile struct DmaCircBuffer usart2_rx_buffer; (void*)&usarts[USART_PERIPH_1]->DR,
static volatile struct DmaMultiBuffer usart2_tx_buffer; DMA_CONFIG,
static volatile struct DmaCircBuffer usart3_rx_buffer; DMA_PERIPH_1,
static volatile struct DmaMultiBuffer usart3_tx_buffer; DMA_CHANNEL_5,
},
{
(void*)&usarts[USART_PERIPH_2]->DR,
DMA_CONFIG,
DMA_PERIPH_1,
DMA_CHANNEL_6,
},
{
(void*)&usarts[USART_PERIPH_3]->DR,
DMA_CONFIG,
DMA_PERIPH_1,
DMA_CHANNEL_3,
},
};
static const struct DmaParam usarts_tx_param[] = {
{
(void*)&usarts[USART_PERIPH_1]->DR,
DMA_CONFIG,
DMA_PERIPH_1,
DMA_CHANNEL_4,
},
{
(void*)&usarts[USART_PERIPH_2]->DR,
DMA_CONFIG,
DMA_PERIPH_1,
DMA_CHANNEL_7,
},
{
(void*)&usarts[USART_PERIPH_3]->DR,
DMA_CONFIG,
DMA_PERIPH_1,
DMA_CHANNEL_2,
},
};
//--public functions------------------------------------------------------------ //--public functions------------------------------------------------------------
@ -53,161 +89,77 @@ void usart_configure(enum UsartPeriph periph, enum UsartConfig config,
switch (periph) { switch (periph) {
case USART_PERIPH_1: case USART_PERIPH_1:
rcc_enable(RCC_AHB_NONE, RCC_APB1_NONE, RCC_APB2_USART); rcc_enable(RCC_AHB_NONE, RCC_APB1_NONE, RCC_APB2_USART);
configure_baudrate(usart1, clocks.apb2_freq, baudrate); configure_baudrate(usarts[USART_PERIPH_1], clocks.apb2_freq,
configure_usart(usart1, config); baudrate);
usart1_tx_buffer.buffers = NULL; configure_usart(usarts[USART_PERIPH_1], config);
usart1_rx_buffer.buffer = NULL;
break; break;
case USART_PERIPH_2: case USART_PERIPH_2:
rcc_enable(RCC_AHB_NONE, RCC_APB1_USART2, RCC_APB2_NONE); rcc_enable(RCC_AHB_NONE, RCC_APB1_USART2, RCC_APB2_NONE);
configure_baudrate(usart2, clocks.apb1_freq, baudrate); configure_baudrate(usarts[USART_PERIPH_2], clocks.apb1_freq,
configure_usart(usart2, config); baudrate);
usart2_tx_buffer.buffers = NULL; configure_usart(usarts[USART_PERIPH_2], config);
usart2_rx_buffer.buffer = NULL;
break; break;
case USART_PERIPH_3: case USART_PERIPH_3:
rcc_enable(RCC_AHB_NONE, RCC_APB1_USART3, RCC_APB2_NONE); rcc_enable(RCC_AHB_NONE, RCC_APB1_USART3, RCC_APB2_NONE);
configure_baudrate(usart3, clocks.apb1_freq, baudrate); configure_baudrate(usarts[USART_PERIPH_3], clocks.apb1_freq,
configure_usart(usart3, config); baudrate);
usart3_tx_buffer.buffers = NULL; configure_usart(usarts[USART_PERIPH_3], config);
usart3_rx_buffer.buffer = NULL;
break; break;
default: default:
break; break;
} }
} }
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);
return 0;
} else {
return 1;
}
}
}
uint32_t usart_read_byte(enum UsartPeriph periph, uint8_t* byte) uint32_t usart_read_byte(enum UsartPeriph periph, uint8_t* byte)
{ {
volatile struct USART* regs; if (periph > USART_PERIPH_3) {
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; return 1;
break;
} }
if (buffer->buffer) { if (usarts[periph]->SR.RXNE) {
return dma_cbuf_read_byte(buffer, byte); *byte = usarts[periph]->DR.DR;
} else {
if (regs->SR.RXNE) {
*byte = regs->DR.DR;
return 0; return 0;
} else { } else {
return 1; return 1;
} }
}
} }
void usart_set_tx_buffer(enum UsartPeriph periph, uint8_t** buffers, uint32_t usart_write_byte(enum UsartPeriph periph, uint8_t byte)
uint16_t buffer_size, uint8_t buffer_nb, enum DmaConfig priority)
{ {
switch (periph) { if (periph > USART_PERIPH_3) {
case USART_PERIPH_1: return 1;
dma_mbuf_configure(&usart1_tx_buffer, (void**)buffers, &usart1->DR, }
buffer_size, buffer_nb, DMA_PERIPH_1, DMA_CHANNEL_4,
priority); //only write data if the tx register it empty, give up otherwise
break; if (usarts[periph]->SR.TXE) {
case USART_PERIPH_2: reg_write(usarts[periph]->DR, USART_DR_DR, byte);
dma_mbuf_configure(&usart2_tx_buffer, (void**)buffers, &usart2->DR, return 0;
buffer_size, buffer_nb, DMA_PERIPH_1, DMA_CHANNEL_7, } else {
priority); return 1;
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;
} }
} }
void usart_set_rx_buffer(enum UsartPeriph periph, uint8_t* buffer, const struct DmaParam* usart_configure_rx_dma(enum UsartPeriph periph)
uint16_t size, enum DmaConfig priority)
{ {
switch (periph) { if (periph > USART_PERIPH_3) {
case USART_PERIPH_1: return nullptr;
dma_cbuf_configure(&usart1_rx_buffer, buffer, (void*)&usart1->DR,
size, DMA_PERIPH_1, DMA_CHANNEL_5, priority);
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);
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);
reg_set(usart3->CR3, USART_CR3_DMAR);
break;
} }
reg_set(usarts[periph]->CR3, USART_CR3_DMAR);
return &usarts_rx_param[periph];
} }
const struct DmaParam* usart_configure_tx_dma(enum UsartPeriph periph)
{
if (periph > USART_PERIPH_3) {
return nullptr;
}
reg_set(usarts[periph]->CR3, USART_CR3_DMAT);
return &usarts_tx_param[periph];
}
//--local functions------------------------------------------------------------- //--local functions-------------------------------------------------------------
/** /**
@ -293,7 +245,7 @@ static void configure_usart(volatile struct USART* regs,
/** /**
* Configure the given registers with the given baudrate. Baudrate is dependant * Configure the given registers with the given baudrate. Baudrate is dependant
* on the peripheric's clock and may not be exact due to precision errors (see * on the peripheral's clock and may not be exact due to precision errors (see
* table 192 in documentation) * table 192 in documentation)
*/ */
static void configure_baudrate(volatile struct USART* regs, uint32_t clock, static void configure_baudrate(volatile struct USART* regs, uint32_t clock,
@ -309,39 +261,3 @@ static void configure_baudrate(volatile struct USART* regs, uint32_t clock,
reg_write(regs->BRR, USART_BRR_DIV_Fraction, divider & 0xF); 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

@ -17,12 +17,19 @@
//--type definitions------------------------------------------------------------ //--type definitions------------------------------------------------------------
/**
* Available USART peripherals. Note that some of these peripherals may not be
* available on all chips
*/
enum UsartPeriph { enum UsartPeriph {
USART_PERIPH_1, USART_PERIPH_1,
USART_PERIPH_2, USART_PERIPH_2,
USART_PERIPH_3, USART_PERIPH_3,
}; };
/**
* Available configuration options
*/
enum UsartConfig { enum UsartConfig {
USART_CONFIG_7E1, USART_CONFIG_7E1,
USART_CONFIG_7E2, USART_CONFIG_7E2,
@ -39,20 +46,65 @@ enum UsartConfig {
//--functions------------------------------------------------------------------- //--functions-------------------------------------------------------------------
/**
* Configures the given USART peripheral using the provided condiguration
* options and baudrate. The baudrate may be any value supported by the
* peripheral, though some may not be exact due to precision errors (see
* table 192 in documentation). The baudrate is dependant on the peripheral's
* clock and changes to the later after this function has been called will cause
* the effective baudrate to change
*
* This function doesn't configure the required ports. This should be done using
* the gpio driver:
* - Tx port should be using GPIO_CONFIG_OUT_ALT_PUSH_PULL with
* the appropriate output speed based on the baurate (see GpioMode)
* - Rx port should be using GPIO_CONFIG_IN_FLOATING in input mode
* Both ports do not need to be configured if not used (e.g. if only using Tx,
* the Rx port can be left unconfigured)
*/
void usart_configure(enum UsartPeriph periph, enum UsartConfig config, void usart_configure(enum UsartPeriph periph, enum UsartConfig config,
uint32_t baudrate); uint32_t baudrate);
/**
* Resets the given USART peripheral, applying the default configuration and
* disabling it
*/
void usart_reset(enum UsartPeriph periph); void usart_reset(enum UsartPeriph periph);
uint32_t usart_write_byte(enum UsartPeriph periph, uint8_t byte); /**
* Reads a single byte to the given USART peripheral, returning 0 if
* successfull, 1 otherwise.
*
* The Rx port must be configured for this function to ever return successfully
*/
uint32_t usart_read_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); * Writes a single byte to the given USART peripheral, returning 0 if
* successfull, 1 otherwise.
*
* The Tx port must be configured for this function to do anything, though the
* function would still return 0
*/
uint32_t usart_write_byte(enum UsartPeriph periph, uint8_t byte);
void usart_set_rx_buffer(enum UsartPeriph periph, uint8_t* buffer, /**
uint16_t size, enum DmaConfig priority); * Configures the given USART peripheral for DMA Rx operations, returning the
* corresponding DMA parameters to be used.
*
* The DMA must be configured separately using the DMA driver or an existing
* service
*/
const struct DmaParam* usart_configure_rx_dma(enum UsartPeriph periph);
/**
* Configures the given USART peripheral for DMA Rx operations, returning the
* corresponding DMA parameters to be used.
*
* The DMA must be configured separately using the DMA driver or an existing
* service
*/
const struct DmaParam* usart_configure_tx_dma(enum UsartPeriph periph);
#endif //_USART_H_ #endif //_USART_H_

View File

@ -12,9 +12,13 @@
//--local definitions----------------------------------------------------------- //--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_PERIPH \ #define DMA_CONFIG (DMA_CONFIG_IRQ_COMPLETE | DMA_CONFIG_FROM_PERIPH \
| DMA_CONFIG_CIRCULAR | DMA_CONFIG_INC_MEM \ | DMA_CONFIG_CIRCULAR | DMA_CONFIG_INC_MEM \
| DMA_CONFIG_PSIZE_8BITS | DMA_CONFIG_MSIZE_8BITS) | DMA_CONFIG_MSIZE_8BITS)
static void cbuf_callback(enum DmaIRQSource src, volatile void* param); static void cbuf_callback(enum DmaIRQSource src, volatile void* param);
@ -24,25 +28,25 @@ static void cbuf_callback(enum DmaIRQSource src, volatile void* param);
//--public functions------------------------------------------------------------ //--public functions------------------------------------------------------------
void dma_cbuf_configure(volatile struct DmaCircBuffer* buffer, void dma_cbuf_configure(volatile struct DmaCircBuffer* buffer,
volatile void* raw_buffer, volatile void* src, uint16_t buffer_size, const struct DmaParam* param, enum DmaConfig priority,
enum DmaPeriph dma, enum DmaChannel channel, enum DmaConfig priority) volatile void* raw_buffer, uint16_t buffer_size)
{ {
#warning "check for null ptr" #warning "check for null ptr"
buffer->buffer = raw_buffer; buffer->buffer = raw_buffer;
buffer->src = src;
buffer->size = buffer_size; buffer->size = buffer_size;
buffer->begin = 0; buffer->begin = 0;
buffer->dma = dma; buffer->dma = param->dma;
buffer->channel = channel; buffer->channel = param->channel;
buffer->config = DMA_CONFIG | priority;
buffer->dma_looped = false; buffer->dma_looped = false;
dma_configure(buffer->dma, buffer->channel, buffer->config, buffer->src, dma_configure(buffer->dma, buffer->channel,
buffer->buffer, buffer->size, cbuf_callback, buffer); 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, uint32_t dma_cbuf_read_byte(volatile struct DmaCircBuffer* buffer,
@ -86,4 +90,3 @@ static void cbuf_callback(enum DmaIRQSource src, volatile void* param)
buffer->dma_looped = true; buffer->dma_looped = true;
} }

View File

@ -10,7 +10,7 @@
//--includes-------------------------------------------------------------------- //--includes--------------------------------------------------------------------
#include "dma.h" #include "../drv/dma.h"
//--type definitions------------------------------------------------------------ //--type definitions------------------------------------------------------------
@ -22,14 +22,12 @@
*/ */
struct DmaCircBuffer { struct DmaCircBuffer {
volatile void* buffer; //the buffer to use as a circular buffer 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 size; //the size of the buffer
uint16_t begin; //pointer to the current begin of the buffer uint16_t begin; //pointer to the current begin of the buffer
enum DmaPeriph dma; //DMA peripheral, must correspond to peripheral enum DmaPeriph dma; //DMA peripheral used for transfers
enum DmaChannel channel; //DMA channel, must correspond to peripheral enum DmaChannel channel; //DMA channel used for transfers
enum DmaConfig config; //DMA config, must correspond to peripheral
bool dma_looped; //whether the DMA looped or not (buffer overflow) bool dma_looped; //whether the DMA looped or not (buffer overflow)
}; };
@ -40,16 +38,24 @@ struct DmaCircBuffer {
/** /**
* Configure a DMA circular buffer for a single DMA channel. A standard buffer * Configure a DMA circular buffer for a single DMA channel. A standard buffer
* is used a base to construct a circular buffer, supplied in data by DMA * is used a base to construct a circular buffer, supplied in data by DMA
* tranfers from a peripheral. The DMA's priority is given as parameters. * tranfers from a peripheral.
* The peripheral's specific configuration must be handled separately. * The peripheral's specific configuration must be handled separately.
* Each peripheral's driver is responsible for providing the DMA configuration
* for said peripheral, though it can be manually given too. Please note that
* multiple peripherals may share the same DMA channel, which will cause issue
* when used with this buffer.
* *
* Once this function is called, the system will start running right away and * Once this function is called, the system will start running right away and
* will stay running until manually stopped * will stay running until manually stopped
*/ */
void dma_cbuf_configure(volatile struct DmaCircBuffer* buffer, void dma_cbuf_configure(volatile struct DmaCircBuffer* buffer,
volatile void* raw_buffer, volatile void* src, uint16_t buffer_size, const struct DmaParam* param, enum DmaConfig priority,
enum DmaPeriph dma, enum DmaChannel channel, enum DmaConfig priority); volatile void* raw_buffer, uint16_t buffer_size);
/**
* Reads a bytes from the buffer, returning if the read operation was
* successfull, 1 otherwise. This function is non-blocking.
*/
uint32_t dma_cbuf_read_byte(volatile struct DmaCircBuffer* buffer, uint32_t dma_cbuf_read_byte(volatile struct DmaCircBuffer* buffer,
uint8_t* byte); uint8_t* byte);

107
srv/dma_mbuf.c Normal file
View File

@ -0,0 +1,107 @@
/** @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-----------------------------------------------------------
/**
* 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)
{
#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;
}
uint32_t dma_mbuf_switch(volatile struct DmaMultiBuffer* buffer)
{
//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
volatile struct DmaMultiBuffer* buffer = param;
dma_stop(buffer->dma, buffer->channel);
buffer->dma_running = false;
}

76
srv/dma_mbuf.h Normal file
View File

@ -0,0 +1,76 @@
/** @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-latency, low-cpu usage, non-blocking way
*/
#ifndef _DMA_MBUF_H_
#define _DMA_MBUF_H_
//--includes--------------------------------------------------------------------
#include "../drv/dma.h"
//--type definitions------------------------------------------------------------
/**
* Struct used by the multibuffer system. This system allows bufferized writes
* to a peripheral with controlled latency, minimal cpu usage and customisable
* buffer size
*/
struct DmaMultiBuffer {
uint8_t* raw_buffer; //the buffer to use as a multi-buffer
uint16_t buffer_size; //the size of the buffer
uint8_t byte_index; //index of the next byte to be written
uint8_t buffer_index; //index of the buffer being written to
enum DmaPeriph dma; //DMA peripheral used for transfers
enum DmaChannel channel; //DMA channel used for transfers
bool dma_running; //whether the dam is currently running or not
};
//--functions-------------------------------------------------------------------
/**
* Configure a DMA multibuffer for a single DMA channel. The given buffer is
* divided into 2 to allow one part to be written to while the other is being
* transfered by the DMA to a peripheral.
* The peripheral's specific configuration must be handled separately.
* Each peripheral's driver is responsible for providing the DMA configuration
* for said peripheral, though it can be manually given too. Please note that
* multiple peripherals may share the same DMA channel, which will cause issue
* when used with this buffer.
*
* This buffer can be written to at any given time using dma_mbuf_write_byte(),
* but its content are only written to the corresponding peripheral when
* dma_mbuf_switch() is called.
*/
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
* was successfull, 1 otherwise. The function is non-blocking.
*
* Note: calling this function will not trigger a DMA transfer, see
* dma_mbuf_switch() to do that
*/
uint32_t dma_mbuf_write_byte(volatile struct DmaMultiBuffer* buffer,
uint8_t byte);
/**
* Switch the buffers, triggering a DMA transfer while allowing writes to
* continue. This function may fail if a DMA transfer is already running,
* returning 1
*/
uint32_t 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 * 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 * (stack pointer, static data, ...) as well as the vector table and the
* differents interrupt handlers that go with it. By default, all handlers use * 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 .syntax unified