Audio: STM32 DAC: extended driver to STM32H7 family
This commit is contained in:
parent
404e840370
commit
3d04759e8d
|
|
@ -19,14 +19,36 @@
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
|
|
||||||
#include <kernel/scheduler/scheduler.h>
|
#include <kernel/scheduler/scheduler.h>
|
||||||
|
#include <core/cache_cortexMx.h>
|
||||||
#include <peripherals/gpio.h>
|
#include <peripherals/gpio.h>
|
||||||
#include <data_conversion.h>
|
#include <data_conversion.h>
|
||||||
#include <DmaStream.hpp>
|
#include <DmaStream.hpp>
|
||||||
#include <Timer.hpp>
|
|
||||||
#include <miosix.h>
|
#include <miosix.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <hwconfig.h>
|
||||||
#include "stm32_dac.h"
|
#include "stm32_dac.h"
|
||||||
|
|
||||||
|
#if defined(STM32H743xx)
|
||||||
|
#include <Lptim.hpp>
|
||||||
|
|
||||||
|
#define DAC DAC1
|
||||||
|
#define DAC_TRIG_CH1 (11 << 2) // lptim1_out
|
||||||
|
#define DAC_TRIG_CH2 (12 << 18) // lptim2_out
|
||||||
|
|
||||||
|
typedef Lptim timebase_type;
|
||||||
|
#define TimebaseCh1 Lptim(LPTIM1_BASE, 168000000)
|
||||||
|
#define TimebaseCh2 Lptim(LPTIM2_BASE, 168000000)
|
||||||
|
#else
|
||||||
|
#include <Timer.hpp>
|
||||||
|
|
||||||
|
#define DAC_TRIG_CH1 0x00
|
||||||
|
#define DAC_TRIG_CH2 DAC_CR_TSEL2_1
|
||||||
|
|
||||||
|
typedef Timer timebase_type;
|
||||||
|
#define TimebaseCh1 Timer(TIM6_BASE)
|
||||||
|
#define TimebaseCh2 Timer(TIM7_BASE)
|
||||||
|
#endif
|
||||||
|
|
||||||
struct ChannelState
|
struct ChannelState
|
||||||
{
|
{
|
||||||
struct streamCtx *ctx; // Current stream context
|
struct streamCtx *ctx; // Current stream context
|
||||||
|
|
@ -37,7 +59,7 @@ struct ChannelState
|
||||||
struct DacChannel
|
struct DacChannel
|
||||||
{
|
{
|
||||||
volatile uint32_t *dacReg; // DAC data register
|
volatile uint32_t *dacReg; // DAC data register
|
||||||
Timer tim; // TIM peripheral for DAC trigger
|
timebase_type tim; // TIM peripheral for DAC trigger
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -46,8 +68,8 @@ using Dma1_Stream6 = DmaStream< DMA1_BASE, 6, 7, 3 >; // DMA 1, Stream 6, channe
|
||||||
|
|
||||||
static constexpr DacChannel channels[] =
|
static constexpr DacChannel channels[] =
|
||||||
{
|
{
|
||||||
{&(DAC->DHR12R1), Timer(TIM6_BASE)},
|
{&(DAC->DHR12R1), TimebaseCh1},
|
||||||
{&(DAC->DHR12R2), Timer(TIM7_BASE)},
|
{&(DAC->DHR12R2), TimebaseCh2},
|
||||||
};
|
};
|
||||||
|
|
||||||
#pragma GCC diagnostic ignored "-Wpedantic"
|
#pragma GCC diagnostic ignored "-Wpedantic"
|
||||||
|
|
@ -117,30 +139,46 @@ void __attribute__((used)) DMA1_Stream6_IRQHandler()
|
||||||
void stm32dac_init(const uint8_t instance, const uint16_t idleLevel)
|
void stm32dac_init(const uint8_t instance, const uint16_t idleLevel)
|
||||||
{
|
{
|
||||||
// Enable peripherals
|
// Enable peripherals
|
||||||
|
#if defined(STM32H743xx)
|
||||||
|
RCC->APB1LENR |= RCC_APB1LENR_DAC12EN;
|
||||||
|
#else
|
||||||
RCC->APB1ENR |= RCC_APB1ENR_DACEN;
|
RCC->APB1ENR |= RCC_APB1ENR_DACEN;
|
||||||
|
#endif
|
||||||
|
|
||||||
switch(instance)
|
switch(instance)
|
||||||
{
|
{
|
||||||
case STM32_DAC_CH1:
|
case STM32_DAC_CH1:
|
||||||
{
|
{
|
||||||
|
#if defined(STM32H743xx)
|
||||||
|
RCC->APB1LENR |= RCC_APB1LENR_LPTIM1EN;
|
||||||
|
RCC->D2CCIP2R |= RCC_D2CCIP2R_LPTIM1SEL_0;
|
||||||
|
DMAMUX1_Channel5->CCR = 67;
|
||||||
|
#else
|
||||||
RCC->APB1ENR |= RCC_APB1ENR_TIM6EN;
|
RCC->APB1ENR |= RCC_APB1ENR_TIM6EN;
|
||||||
|
#endif
|
||||||
__DSB();
|
__DSB();
|
||||||
|
|
||||||
DAC->DHR12R1 = idleLevel;
|
DAC->DHR12R1 = idleLevel;
|
||||||
DAC->CR |= DAC_CR_DMAEN1 // Enable DMA
|
DAC->CR |= DAC_CR_DMAEN1 // Enable DMA
|
||||||
| 0x00 // TIM6 as trigger source for CH1
|
| DAC_TRIG_CH1 // Set trigger source for CH1
|
||||||
| DAC_CR_EN1; // Enable CH1
|
| DAC_CR_EN1; // Enable CH1
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case STM32_DAC_CH2:
|
case STM32_DAC_CH2:
|
||||||
{
|
{
|
||||||
|
#if defined(STM32H743xx)
|
||||||
|
RCC->APB4ENR |= RCC_APB4ENR_LPTIM2EN;
|
||||||
|
RCC->D3CCIPR |= RCC_D3CCIPR_LPTIM2SEL_0;
|
||||||
|
DMAMUX1_Channel6->CCR = 68;
|
||||||
|
#else
|
||||||
RCC->APB1ENR |= RCC_APB1ENR_TIM7EN;
|
RCC->APB1ENR |= RCC_APB1ENR_TIM7EN;
|
||||||
|
#endif
|
||||||
__DSB();
|
__DSB();
|
||||||
|
|
||||||
DAC->DHR12R2 = idleLevel;
|
DAC->DHR12R2 = idleLevel;
|
||||||
DAC->CR |= DAC_CR_DMAEN2 // Enable DMA
|
DAC->CR |= DAC_CR_DMAEN2 // Enable DMA
|
||||||
| DAC_CR_TSEL2_1 // TIM7 as trigger source for CH2
|
| DAC_TRIG_CH2 // Set trigger source for CH2
|
||||||
| DAC_CR_EN2; // Enable CH2
|
| DAC_CR_EN2; // Enable CH2
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -165,9 +203,12 @@ void stm32dac_terminate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RCC->APB1ENR &= ~(RCC_APB1ENR_DACEN |
|
#if defined(STM32H743xx)
|
||||||
RCC_APB1ENR_TIM6EN |
|
RCC->APB1LENR &= ~(RCC_APB1LENR_DAC12EN | RCC_APB1LENR_LPTIM1EN);
|
||||||
RCC_APB1ENR_TIM7EN);
|
RCC->APB4ENR &= ~RCC_APB4ENR_LPTIM2EN;
|
||||||
|
#else
|
||||||
|
RCC->APB1ENR &= ~(RCC_APB1ENR_DACEN | RCC_APB1ENR_TIM6EN | RCC_APB1ENR_TIM7EN);
|
||||||
|
#endif
|
||||||
__DSB();
|
__DSB();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -196,6 +237,7 @@ static int stm32dac_start(const uint8_t instance, const void *config,
|
||||||
* has been called.
|
* has been called.
|
||||||
*/
|
*/
|
||||||
S16toU12(ctx->buffer, ctx->bufSize);
|
S16toU12(ctx->buffer, ctx->bufSize);
|
||||||
|
miosix::markBufferBeforeDmaWrite(ctx->buffer, ctx->bufSize*sizeof(int16_t));
|
||||||
|
|
||||||
bool circ = false;
|
bool circ = false;
|
||||||
if(ctx->bufMode == BUF_CIRC_DOUBLE)
|
if(ctx->bufMode == BUF_CIRC_DOUBLE)
|
||||||
|
|
@ -227,7 +269,9 @@ static int stm32dac_sync(struct streamCtx *ctx, uint8_t dirty)
|
||||||
if((ctx->bufMode == BUF_CIRC_DOUBLE) && (dirty != 0))
|
if((ctx->bufMode == BUF_CIRC_DOUBLE) && (dirty != 0))
|
||||||
{
|
{
|
||||||
void *ptr = state->stream.idleBuf();
|
void *ptr = state->stream.idleBuf();
|
||||||
S16toU12(reinterpret_cast< int16_t *>(ptr), ctx->bufSize/2);
|
size_t writeSize = ctx->bufSize/2;
|
||||||
|
S16toU12(reinterpret_cast< int16_t *>(ptr), writeSize);
|
||||||
|
miosix::markBufferBeforeDmaWrite(ctx->buffer, writeSize*sizeof(int16_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ok = state->stream.sync();
|
bool ok = state->stream.sync();
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stm32f4xx.h>
|
|
||||||
#include <interfaces/audio.h>
|
#include <interfaces/audio.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,399 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2024 by Federico Amedeo Izzo IU2NUO, *
|
||||||
|
* Niccolò Izzo IU2KIN *
|
||||||
|
* Frederik Saraci IU2NRO *
|
||||||
|
* Silvano Seva IU2KWO *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef DMA_STREAM_H
|
||||||
|
#define DMA_STREAM_H
|
||||||
|
|
||||||
|
#include <kernel/scheduler/scheduler.h>
|
||||||
|
#include <stm32h7xx.h>
|
||||||
|
#include <miosix.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enumerating type describing the memory and peripheral data sizes allowed
|
||||||
|
* for a DMA transfer.
|
||||||
|
*/
|
||||||
|
enum class DataSize
|
||||||
|
{
|
||||||
|
_8BIT = 0, ///< 8-bit data size
|
||||||
|
_16BIT = 1, ///< 16-bit data size
|
||||||
|
_32BIT = 2, ///< 32-bit data size
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stream handler class for STM32F4 DMA Stream peripheral.
|
||||||
|
*/
|
||||||
|
class StreamHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param s: pointer to the DMA stream peripheral.
|
||||||
|
* @param IRQn: DMA stream IRQ number.
|
||||||
|
*/
|
||||||
|
StreamHandler(DMA_Stream_TypeDef *s, IRQn_Type IRQn) : IRQn(IRQn), stream(s)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor.
|
||||||
|
*/
|
||||||
|
~StreamHandler()
|
||||||
|
{
|
||||||
|
stream->CR = 0;
|
||||||
|
NVIC_DisableIRQ(IRQn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start a DMA stream.
|
||||||
|
*
|
||||||
|
* @param periph: pointer to source/target peripheral.
|
||||||
|
* @param memory: pointer to source/target memory.
|
||||||
|
* @param size: transfer size, in elements.
|
||||||
|
* @param circ: if true the stream runs in circular double buffered mode.
|
||||||
|
*/
|
||||||
|
void start(volatile void *periph, void *memory, const size_t size,
|
||||||
|
const bool circ = false)
|
||||||
|
{
|
||||||
|
uint32_t circFlags = DMA_SxCR_CIRC // Circular buffer mode
|
||||||
|
| DMA_SxCR_HTIE; // Half transfer interrupt
|
||||||
|
|
||||||
|
if(circ)
|
||||||
|
stream->CR |= circFlags;
|
||||||
|
else
|
||||||
|
stream->CR &= ~circFlags;
|
||||||
|
|
||||||
|
transferSize = size;
|
||||||
|
stream->NDTR = size;
|
||||||
|
stream->PAR = reinterpret_cast< uint32_t >(periph);
|
||||||
|
stream->M0AR = reinterpret_cast< uint32_t >(memory);
|
||||||
|
stream->CR |= DMA_SxCR_EN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a pointer to the currently "idle" section of a DMA stream that is,
|
||||||
|
* the section not being read by the DMA.
|
||||||
|
* A call to this function is meaningful only if the stream is running in
|
||||||
|
* circular double buffered mode.
|
||||||
|
*
|
||||||
|
* @return address of the idle section or NULL if the stream is not in
|
||||||
|
* circular mode.
|
||||||
|
*/
|
||||||
|
void *idleBuf()
|
||||||
|
{
|
||||||
|
if((stream->CR & DMA_SxCR_CIRC) == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
miosix::FastInterruptDisableLock dLock;
|
||||||
|
uint32_t curPos = stream->NDTR;
|
||||||
|
uint32_t idle = stream->M0AR;
|
||||||
|
uint32_t size = (stream->CR >> DMA_SxCR_MSIZE_Pos) & 0x03;
|
||||||
|
|
||||||
|
if(curPos > (transferSize / 2))
|
||||||
|
idle += (transferSize / 2) * size * 2;
|
||||||
|
|
||||||
|
return reinterpret_cast< void * >(idle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Syncronize the execution flow with the stream transfer, blocking function.
|
||||||
|
* For cirular buffer mode the calling thread is put to sleep until the DMA
|
||||||
|
* reaches either the half or the end of the transfer, whichever comes first.
|
||||||
|
* For linear buffer mode the calling thread is put to sleep until the DMA
|
||||||
|
* reaches the end of the transfer.
|
||||||
|
*
|
||||||
|
* @return true if thread was effectively put to sleep, false otherwise.
|
||||||
|
*/
|
||||||
|
bool sync()
|
||||||
|
{
|
||||||
|
using namespace miosix;
|
||||||
|
|
||||||
|
// Enter in critical section until the end of the function
|
||||||
|
FastInterruptDisableLock dLock;
|
||||||
|
|
||||||
|
Thread *curThread = Thread::IRQgetCurrentThread();
|
||||||
|
if((waiting != 0) && (waiting != curThread))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
waiting = curThread;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
Thread::IRQwait();
|
||||||
|
{
|
||||||
|
// Re-enable interrupts while waiting for IRQ
|
||||||
|
FastInterruptEnableLock eLock(dLock);
|
||||||
|
Thread::yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while(waiting != 0);
|
||||||
|
|
||||||
|
waiting = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop an ongoing DMA stream.
|
||||||
|
* The stream does not stop immediately but only when it reaches the half
|
||||||
|
* or the end of the transfer.
|
||||||
|
*/
|
||||||
|
inline void stop()
|
||||||
|
{
|
||||||
|
stopTransfer = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forcefully stop an ongoing DMA stream.
|
||||||
|
* Calling this function causes the immediate stop of an ongoing stream.
|
||||||
|
*/
|
||||||
|
inline void halt()
|
||||||
|
{
|
||||||
|
stopTransfer = true;
|
||||||
|
NVIC_SetPendingIRQ(IRQn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query che the current status of the stream.
|
||||||
|
*
|
||||||
|
* @return true if the stream is active, false otherwise.
|
||||||
|
*/
|
||||||
|
inline bool running()
|
||||||
|
{
|
||||||
|
return (stream->CR & DMA_SxCR_EN) ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a function to be called on stream end.
|
||||||
|
*
|
||||||
|
* @param callback: std::function for the stream end callback.
|
||||||
|
*/
|
||||||
|
inline void setEndTransferCallback(std::function<void()>&& callback)
|
||||||
|
{
|
||||||
|
streamEndCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stream interrupt handler function.
|
||||||
|
*
|
||||||
|
* @param irqFlags: IRQ status flags.
|
||||||
|
*/
|
||||||
|
void IRQhandler(const uint32_t irqFlags)
|
||||||
|
{
|
||||||
|
(void) irqFlags;
|
||||||
|
|
||||||
|
using namespace miosix;
|
||||||
|
|
||||||
|
if(((stream->CR & DMA_SxCR_CIRC) == 0) || (stopTransfer == true))
|
||||||
|
{
|
||||||
|
stream->CR &= ~DMA_SxCR_EN;
|
||||||
|
stopTransfer = false;
|
||||||
|
|
||||||
|
if(streamEndCallback)
|
||||||
|
streamEndCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wake up eventual pending threads
|
||||||
|
if(waiting == 0) return;
|
||||||
|
waiting->IRQwakeup();
|
||||||
|
if(waiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
|
||||||
|
Scheduler::IRQfindNextThread();
|
||||||
|
waiting = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
bool stopTransfer;
|
||||||
|
size_t transferSize;
|
||||||
|
const IRQn_Type IRQn;
|
||||||
|
std::function<void()> streamEndCallback;
|
||||||
|
DMA_Stream_TypeDef *stream;
|
||||||
|
miosix::Thread *waiting;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stream class for STM32F4 DMA Stream peripheral.
|
||||||
|
*
|
||||||
|
* @tparam DMA: base address of the DMA peripheral.
|
||||||
|
* @tparam STN: DMA stream number.
|
||||||
|
* @tparam CH: data channel of the DMA stream.
|
||||||
|
* @tparam PL: priority level of the DMA stream.
|
||||||
|
*/
|
||||||
|
template < uint32_t DMA, uint8_t STN, uint8_t CH, uint8_t PL >
|
||||||
|
class DmaStream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize a DMA stream without starting it.
|
||||||
|
*
|
||||||
|
* @param irqPrio: priority of the DMA stream interrupts.
|
||||||
|
* @param ds: data size for both source and destination locations.
|
||||||
|
* @param memToPer: if set to true, transfer goes from memory to peripheral.
|
||||||
|
* @return a StreamHandler object to manage the DMA stream.
|
||||||
|
*/
|
||||||
|
static StreamHandler init(const uint8_t irqPrio, const DataSize ds,
|
||||||
|
const bool memToPer = false)
|
||||||
|
{
|
||||||
|
enableClock();
|
||||||
|
|
||||||
|
uint32_t dir = memToPer ? DMA_SxCR_DIR_0 : 0;
|
||||||
|
uint32_t TS = static_cast < uint8_t >(ds);
|
||||||
|
auto stream = getStream();
|
||||||
|
stream->CR = (PL << 16) // Priority
|
||||||
|
| (TS << 13) // Source transfer size
|
||||||
|
| (TS << 11) // Destination transfer size
|
||||||
|
| DMA_SxCR_MINC // Increment source pointer
|
||||||
|
| dir // Direction
|
||||||
|
| DMA_SxCR_TCIE // Transfer complete interrupt
|
||||||
|
| DMA_SxCR_TEIE; // Transfer error interrupt
|
||||||
|
|
||||||
|
// Enable DMA interrupts
|
||||||
|
IRQn_Type irq = IRQn();
|
||||||
|
NVIC_ClearPendingIRQ(irq);
|
||||||
|
NVIC_SetPriority(irq, irqPrio);
|
||||||
|
NVIC_EnableIRQ(irq);
|
||||||
|
|
||||||
|
return StreamHandler(stream, irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Low-level shutdown of a DMA stream.
|
||||||
|
*/
|
||||||
|
static inline void terminate()
|
||||||
|
{
|
||||||
|
NVIC_DisableIRQ(IRQn());
|
||||||
|
getStream()->CR = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stream IRQ handler, forwards the call to the handler inside a StreamHandler
|
||||||
|
* class.
|
||||||
|
*
|
||||||
|
* @param hdl: pointer to the StreamHandler class managing the stream.
|
||||||
|
*/
|
||||||
|
static inline void IRQhandleInterrupt(StreamHandler *hdl)
|
||||||
|
{
|
||||||
|
uint32_t flags = readIrqFlags();
|
||||||
|
hdl->IRQhandler(flags);
|
||||||
|
clearIrqFlags(flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable DMA AHB clock.
|
||||||
|
*/
|
||||||
|
static inline constexpr void enableClock()
|
||||||
|
{
|
||||||
|
if(reinterpret_cast< DMA_TypeDef *>(DMA) == DMA1)
|
||||||
|
RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;
|
||||||
|
else
|
||||||
|
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
|
||||||
|
|
||||||
|
RCC_SYNC();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain pointer to the DMA stream peripheral given the DMA and the stream
|
||||||
|
* number.
|
||||||
|
*/
|
||||||
|
static inline constexpr DMA_Stream_TypeDef *getStream()
|
||||||
|
{
|
||||||
|
DMA_Stream_TypeDef *base;
|
||||||
|
if(reinterpret_cast< DMA_TypeDef *>(DMA) == DMA1)
|
||||||
|
base = DMA1_Stream0;
|
||||||
|
else
|
||||||
|
base = DMA2_Stream0;
|
||||||
|
|
||||||
|
return base + STN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the IRQ flags of a DMA stream according to a given mask.
|
||||||
|
*/
|
||||||
|
static inline constexpr void clearIrqFlags(const uint32_t mask)
|
||||||
|
{
|
||||||
|
uint32_t shift = (STN % 4) * 8;
|
||||||
|
|
||||||
|
if(STN < 4)
|
||||||
|
reinterpret_cast< DMA_TypeDef *>(DMA)->LIFCR = (mask << shift);
|
||||||
|
else
|
||||||
|
reinterpret_cast< DMA_TypeDef *>(DMA)->HIFCR = (mask << shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current IRQ flags of a DMA stream.
|
||||||
|
*/
|
||||||
|
static inline constexpr uint32_t readIrqFlags()
|
||||||
|
{
|
||||||
|
uint32_t shift = (STN % 4) * 8;
|
||||||
|
uint32_t flags = 0;
|
||||||
|
|
||||||
|
if(STN < 4)
|
||||||
|
flags = reinterpret_cast< DMA_TypeDef *>(DMA)->LISR;
|
||||||
|
else
|
||||||
|
flags = reinterpret_cast< DMA_TypeDef *>(DMA)->HISR;
|
||||||
|
|
||||||
|
return (flags >> shift) & 0x7D;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the IRQ number of a DMA stream.
|
||||||
|
*/
|
||||||
|
static inline constexpr IRQn_Type IRQn()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// NOTE: there is no better implementation than the one below because
|
||||||
|
// DMA stream IRQ numbers are not contiguous...
|
||||||
|
//
|
||||||
|
if(reinterpret_cast< DMA_TypeDef *>(DMA) == DMA1)
|
||||||
|
{
|
||||||
|
switch(STN)
|
||||||
|
{
|
||||||
|
case 0: return DMA1_Stream0_IRQn; break;
|
||||||
|
case 1: return DMA1_Stream1_IRQn; break;
|
||||||
|
case 2: return DMA1_Stream2_IRQn; break;
|
||||||
|
case 3: return DMA1_Stream3_IRQn; break;
|
||||||
|
case 4: return DMA1_Stream4_IRQn; break;
|
||||||
|
case 5: return DMA1_Stream5_IRQn; break;
|
||||||
|
case 6: return DMA1_Stream6_IRQn; break;
|
||||||
|
case 7: return DMA1_Stream7_IRQn; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch(STN)
|
||||||
|
{
|
||||||
|
case 0: return DMA2_Stream0_IRQn; break;
|
||||||
|
case 1: return DMA2_Stream1_IRQn; break;
|
||||||
|
case 2: return DMA2_Stream2_IRQn; break;
|
||||||
|
case 3: return DMA2_Stream3_IRQn; break;
|
||||||
|
case 4: return DMA2_Stream4_IRQn; break;
|
||||||
|
case 5: return DMA2_Stream5_IRQn; break;
|
||||||
|
case 6: return DMA2_Stream6_IRQn; break;
|
||||||
|
case 7: return DMA2_Stream7_IRQn; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return DMA1_Stream0_IRQn;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* DMA_STREAM_H */
|
||||||
Loading…
Reference in New Issue