mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
731 lines
19 KiB
C
731 lines
19 KiB
C
/*
|
|
* Copyright (C) 2017 OTA keys S.A.
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU Lesser
|
|
* General Public License v2.1. See the file LICENSE in the top level
|
|
* directory for more details.
|
|
*/
|
|
|
|
/**
|
|
* @ingroup cpu_stm32
|
|
* @{
|
|
*
|
|
* @file
|
|
* @brief Low-level DMA driver implementation
|
|
*
|
|
* @author Vincent Dupont <vincent@otakeys.com>
|
|
* @author Joshua DeWeese <jdeweese@primecontrols.com>
|
|
*
|
|
* @}
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
|
|
#include "periph_cpu.h"
|
|
#include "periph_conf.h"
|
|
#include "mutex.h"
|
|
#include "assert.h"
|
|
#include "pm_layered.h"
|
|
|
|
#if CPU_FAM_STM32F2 || CPU_FAM_STM32F4 || CPU_FAM_STM32F7
|
|
#define STM32_DMA_Stream_Type DMA_Stream_TypeDef
|
|
#define CLOCK AHB1
|
|
#define PERIPH_ADDR PAR
|
|
#define MEM_ADDR M0AR
|
|
#define NDTR_REG NDTR
|
|
#define CONTROL_REG CR
|
|
#define RCC_MASK_DMA1 RCC_AHB1ENR_DMA1EN
|
|
#define RCC_MASK_DMA2 RCC_AHB1ENR_DMA2EN
|
|
#define DMA_STREAM_IT_MASK (DMA_LISR_FEIF0 | DMA_LISR_DMEIF0 | \
|
|
DMA_LISR_TEIF0 | DMA_LISR_HTIF0 | \
|
|
DMA_LISR_TCIF0)
|
|
#define DMA_EN DMA_SxCR_EN
|
|
#else /* CPU_FAM_STM32F2 || CPU_FAM_STM32F4 || CPU_FAM_STM32F7 */
|
|
#define STM32_DMA_Stream_Type DMA_Channel_TypeDef
|
|
#if CPU_FAM_STM32L4
|
|
#define CLOCK AHB1
|
|
#define RCC_MASK_DMA1 RCC_AHB1ENR_DMA1EN
|
|
#define RCC_MASK_DMA2 RCC_AHB1ENR_DMA2EN
|
|
#else /* CPU_FAM_STM32L4 */
|
|
#define CLOCK AHB
|
|
#if CPU_FAM_STM32F1 || CPU_FAM_STM32F3 || CPU_FAM_STM32L1
|
|
#define RCC_MASK_DMA1 RCC_AHBENR_DMA1EN
|
|
#else /* CPU_FAM_STM32F1 || CPU_FAM_STM32F3 || CPU_FAM_STM32L1 */
|
|
#define RCC_MASK_DMA1 RCC_AHBENR_DMAEN
|
|
#endif /* CPU_FAM_STM32F1 || CPU_FAM_STM32F3 || CPU_FAM_STM32L1 */
|
|
#define RCC_MASK_DMA2 RCC_AHBENR_DMA2EN
|
|
#endif /* CPU_FAM_STM32L4 */
|
|
#define PERIPH_ADDR CPAR
|
|
#define MEM_ADDR CMAR
|
|
#define NDTR_REG CNDTR
|
|
#define CONTROL_REG CCR
|
|
#define DMA_EN DMA_CCR_EN
|
|
#define DMA_STREAM_IT_MASK (DMA_IFCR_CGIF1 | DMA_IFCR_CTCIF1 | \
|
|
DMA_IFCR_CHTIF1 | DMA_IFCR_CTEIF1)
|
|
#ifndef DMA_CCR_MSIZE_Pos
|
|
#define DMA_CCR_MSIZE_Pos (10)
|
|
#endif
|
|
#ifndef DMA_CCR_PSIZE_Pos
|
|
#define DMA_CCR_PSIZE_Pos (8)
|
|
#endif
|
|
#ifndef DMA_CCR_MINC_Pos
|
|
#define DMA_CCR_MINC_Pos (7)
|
|
#endif
|
|
#ifndef DMA_CCR_PINC_Pos
|
|
#define DMA_CCR_PINC_Pos (6)
|
|
#endif
|
|
#ifndef DMA_CCR_DIR_Pos
|
|
#define DMA_CCR_DIR_Pos (4)
|
|
#endif
|
|
#ifndef DMA_CCR_MEM2MEM_Pos
|
|
#define DMA_CCR_MEM2MEM_Pos (14)
|
|
#endif
|
|
#if defined(CPU_FAM_STM32F0) && !defined(DMA1_Channel4_5_6_7_IRQn)
|
|
#define DMA1_Channel4_5_6_7_IRQn DMA1_Channel4_5_IRQn
|
|
#endif
|
|
#endif /* CPU_FAM_STM32F2 || CPU_FAM_STM32F4 || CPU_FAM_STM32F7 */
|
|
|
|
#define DMA_DATA_WIDTH_MASK (0x03)
|
|
#define DMA_DATA_WIDTH_SHIFT (0)
|
|
|
|
struct dma_ctx {
|
|
STM32_DMA_Stream_Type *stream;
|
|
mutex_t conf_lock;
|
|
mutex_t sync_lock;
|
|
uint16_t len;
|
|
};
|
|
|
|
static struct dma_ctx dma_ctx[DMA_NUMOF];
|
|
|
|
/**
|
|
* @brief Get DMA base register
|
|
*
|
|
* For simplifying DMA stream handling, we map the DMA channels transparently to
|
|
* one integer number, such that DMA1 stream0 equals 0, DMA2 stream0 equals 8,
|
|
* DMA2 stream 7 equals 15 and so on.
|
|
*
|
|
* @param[in] stream physical DMA stream
|
|
*/
|
|
static inline DMA_TypeDef *dma_base(int stream)
|
|
{
|
|
#if CPU_FAM_STM32F2 || CPU_FAM_STM32F4 || CPU_FAM_STM32F7
|
|
return (stream < 8) ? DMA1 : DMA2;
|
|
#elif defined(DMA2)
|
|
return (stream < 7) ? DMA1 : DMA2;
|
|
#else
|
|
(void)stream;
|
|
return DMA1;
|
|
#endif
|
|
}
|
|
|
|
#if CPU_FAM_STM32F0 || CPU_FAM_STM32F3
|
|
static inline DMA_TypeDef *dma_req(int stream_n)
|
|
{
|
|
return dma_base(stream_n);
|
|
}
|
|
#elif CPU_FAM_STM32L0 || CPU_FAM_STM32L4 || CPU_FAM_STM32G0 || CPU_FAM_STM32C0
|
|
static inline DMA_Request_TypeDef *dma_req(int stream_n)
|
|
{
|
|
#ifdef DMA2
|
|
return (stream_n < 7) ? DMA1_CSELR : DMA2_CSELR;
|
|
#else
|
|
(void)stream_n;
|
|
return DMA1_CSELR;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @brief Get the DMA stream base address
|
|
*
|
|
* @param[in] stream physical DMA stream
|
|
*
|
|
* @return base address for the selected DMA stream
|
|
*/
|
|
static inline STM32_DMA_Stream_Type *dma_stream(int stream)
|
|
{
|
|
uint32_t base = (uint32_t)dma_base(stream);
|
|
|
|
#if CPU_FAM_STM32F2 || CPU_FAM_STM32F4 || CPU_FAM_STM32F7
|
|
return (DMA_Stream_TypeDef *)(base + (0x10 + (0x18 * (stream & 0x7))));
|
|
#else
|
|
return (DMA_Channel_TypeDef *)(base + (0x08 + (0x14 * (stream & 0x7))));
|
|
#endif
|
|
}
|
|
|
|
#if CPU_FAM_STM32F2 || CPU_FAM_STM32F4 || CPU_FAM_STM32F7
|
|
/**
|
|
* @brief Select high or low DMA interrupt register based on stream number
|
|
*
|
|
* @param[in] stream physical DMA stream
|
|
*
|
|
* @return 0 for streams 0-3, 1 for streams 3-7
|
|
*/
|
|
static inline int dma_hl(int stream)
|
|
{
|
|
return ((stream & 0x4) >> 2);
|
|
}
|
|
#endif
|
|
|
|
static IRQn_Type dma_get_irqn(int stream)
|
|
{
|
|
#if CPU_FAM_STM32F2 || CPU_FAM_STM32F4 || CPU_FAM_STM32F7
|
|
if (stream < 7) {
|
|
return ((IRQn_Type)((int)DMA1_Stream0_IRQn + stream));
|
|
}
|
|
else if (stream == 7) {
|
|
return DMA1_Stream7_IRQn;
|
|
}
|
|
else if (stream < 13) {
|
|
return ((IRQn_Type)((int)DMA2_Stream0_IRQn + (stream - 8)));
|
|
}
|
|
else if (stream < 16) {
|
|
return ((IRQn_Type)((int)DMA2_Stream5_IRQn + (stream - 13)));
|
|
}
|
|
#elif CPU_FAM_STM32F0 || CPU_FAM_STM32L0 || CPU_FAM_STM32G0 || CPU_FAM_STM32C0
|
|
if (stream == 0) {
|
|
return (DMA1_Channel1_IRQn);
|
|
}
|
|
else if (stream < 3 || (stream >= 8 && stream < 11)) {
|
|
return (DMA1_Channel2_3_IRQn);
|
|
}
|
|
else if (stream < 7 || stream >= 11) {
|
|
return (DMA1_Channel4_5_6_7_IRQn);
|
|
}
|
|
#else
|
|
if (stream < 7) {
|
|
return ((IRQn_Type)((int)DMA1_Channel1_IRQn + stream));
|
|
}
|
|
#if defined(DMA2_BASE)
|
|
/* stream 7 is invalid for these CPU families */
|
|
else if (stream == 7) {
|
|
return -1;
|
|
}
|
|
#if defined(CPU_FAM_STM32F1)
|
|
else if (stream < 11) {
|
|
#else
|
|
else if (stream < 13 ) {
|
|
#endif
|
|
/* magic number 8 is first DMA2 stream */
|
|
return ((IRQn_Type)((int)DMA2_Channel1_IRQn + stream - 8));
|
|
}
|
|
#if !defined(CPU_FAM_STM32L1) && !defined(CPU_FAM_STM32F3)
|
|
else {
|
|
#if defined(CPU_FAM_STM32F1)
|
|
return (DMA2_Channel4_5_IRQn);
|
|
#else
|
|
/* magic number 13 is 8 (first DMA2 stream) + 5 (Channel6) */
|
|
return ((IRQn_Type)((int)DMA2_Channel6_IRQn + stream - 13));
|
|
#endif
|
|
}
|
|
#endif /* !defined(CPU_FAM_STM32L1) && !defined(CPU_FAM_STM32F3) */
|
|
#endif /* defined(DMA2_BASE) */
|
|
#endif
|
|
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* @brief Disable the interrupt of a given stream
|
|
*
|
|
* @param[in] stream physical DMA stream
|
|
*/
|
|
static inline void dma_isr_disable(int stream)
|
|
{
|
|
NVIC_DisableIRQ(dma_get_irqn(stream));
|
|
}
|
|
|
|
/**
|
|
* @brief Clear the interrupt of a given stream
|
|
*
|
|
* @param[in] stream physical DMA stream
|
|
*/
|
|
static inline void dma_isr_clear(int stream)
|
|
{
|
|
NVIC_ClearPendingIRQ(dma_get_irqn(stream));
|
|
}
|
|
|
|
/**
|
|
* @brief Enable the interrupt of a given stream
|
|
*
|
|
* @param[in] stream physical DMA stream
|
|
*/
|
|
static inline void dma_isr_enable(int stream)
|
|
{
|
|
NVIC_EnableIRQ(dma_get_irqn(stream));
|
|
}
|
|
|
|
static inline uint32_t dma_all_flags(dma_t dma)
|
|
{
|
|
#if CPU_FAM_STM32F2 || CPU_FAM_STM32F4 || CPU_FAM_STM32F7
|
|
switch (dma_config[dma].stream & 0x3) {
|
|
case 0: /* 0 and 4 */
|
|
return (DMA_STREAM_IT_MASK);
|
|
case 1: /* 1 and 5 */
|
|
return (DMA_STREAM_IT_MASK << 6);
|
|
case 2: /* 2 and 6 */
|
|
return (DMA_STREAM_IT_MASK << 16);
|
|
case 3: /* 3 and 7 */
|
|
return (DMA_STREAM_IT_MASK << 22);
|
|
default:
|
|
return 0;
|
|
}
|
|
#else
|
|
return DMA_STREAM_IT_MASK << ((dma_config[dma].stream & 0x7) * 4);
|
|
#endif
|
|
}
|
|
|
|
static void dma_clear_all_flags(dma_t dma)
|
|
{
|
|
DMA_TypeDef *dma_dev = dma_base(dma_config[dma].stream);
|
|
|
|
#if CPU_FAM_STM32F2 || CPU_FAM_STM32F4 || CPU_FAM_STM32F7
|
|
/* Clear all flags */
|
|
if (dma_hl(dma_config[dma].stream) == 0) {
|
|
dma_dev->LIFCR = dma_all_flags(dma);
|
|
}
|
|
else {
|
|
dma_dev->HIFCR = dma_all_flags(dma);
|
|
}
|
|
#else
|
|
dma_dev->IFCR = dma_all_flags(dma);
|
|
#endif
|
|
}
|
|
|
|
static void dma_poweron(int stream)
|
|
{
|
|
if (stream < 8) {
|
|
periph_clk_en(CLOCK, RCC_MASK_DMA1);
|
|
}
|
|
#if defined(DMA2)
|
|
else {
|
|
periph_clk_en(CLOCK, RCC_MASK_DMA2);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void dma_init(void)
|
|
{
|
|
for (unsigned i = 0; i < DMA_NUMOF; i++) {
|
|
mutex_init(&dma_ctx[i].conf_lock);
|
|
mutex_init(&dma_ctx[i].sync_lock);
|
|
mutex_lock(&dma_ctx[i].sync_lock);
|
|
int stream_n = dma_config[i].stream;
|
|
dma_poweron(stream_n);
|
|
dma_isr_enable(stream_n);
|
|
dma_ctx[i].stream = dma_stream(stream_n);
|
|
}
|
|
}
|
|
|
|
int dma_transfer(dma_t dma, int chan, const volatile void *src, volatile void *dst, size_t len,
|
|
dma_mode_t mode, uint8_t flags)
|
|
{
|
|
int ret = dma_configure(dma, chan, src, dst, len, mode, flags);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
dma_start(dma);
|
|
dma_wait(dma);
|
|
dma_stop(dma);
|
|
|
|
return len;
|
|
}
|
|
|
|
void dma_acquire(dma_t dma)
|
|
{
|
|
assert(dma < DMA_NUMOF);
|
|
|
|
mutex_lock(&dma_ctx[dma].conf_lock);
|
|
|
|
dma_clear_all_flags(dma);
|
|
|
|
#if CPU_FAM_STM32F2 || CPU_FAM_STM32F4 || CPU_FAM_STM32F7
|
|
STM32_DMA_Stream_Type *stream = dma_ctx[dma].stream;
|
|
stream->FCR = 0;
|
|
#endif
|
|
|
|
#ifdef STM32_PM_STOP
|
|
/* block STOP mode */
|
|
pm_block(STM32_PM_STOP);
|
|
#endif
|
|
}
|
|
|
|
void dma_release(dma_t dma)
|
|
{
|
|
assert(dma < DMA_NUMOF);
|
|
|
|
#ifdef STM32_PM_STOP
|
|
/* unblock STOP mode */
|
|
pm_unblock(STM32_PM_STOP);
|
|
#endif
|
|
mutex_unlock(&dma_ctx[dma].conf_lock);
|
|
}
|
|
|
|
void dma_setup(dma_t dma, int chan, void *periph_addr, dma_mode_t mode,
|
|
uint8_t width, bool inc_periph)
|
|
{
|
|
STM32_DMA_Stream_Type *stream = dma_ctx[dma].stream;
|
|
|
|
#if CPU_FAM_STM32F2 || CPU_FAM_STM32F4 || CPU_FAM_STM32F7
|
|
/* Set channel, data width, inc and mode */
|
|
uint32_t cr_settings = (chan & 0xF) << DMA_SxCR_CHSEL_Pos |
|
|
(width << DMA_SxCR_MSIZE_Pos) |
|
|
(width << DMA_SxCR_PSIZE_Pos) |
|
|
(inc_periph << DMA_SxCR_PINC_Pos) |
|
|
(mode & 3) << DMA_SxCR_DIR_Pos |
|
|
DMA_SxCR_TCIE |
|
|
DMA_SxCR_TEIE;
|
|
/* Configure FIFO */
|
|
stream->CONTROL_REG = cr_settings;
|
|
#else
|
|
#if defined(DMA_CSELR_C1S) || defined(DMA1_CSELR_DEFAULT)
|
|
int stream_n = dma_config[dma].stream;
|
|
dma_req(stream_n)->CSELR &= ~((0xF) << ((stream_n & 0x7) << 2));
|
|
dma_req(stream_n)->CSELR |= (chan & 0xF) << ((stream_n & 0x7) << 2);
|
|
#else
|
|
(void)chan;
|
|
#endif
|
|
uint32_t ctr_reg = (width << DMA_CCR_MSIZE_Pos) |
|
|
(width << DMA_CCR_PSIZE_Pos) |
|
|
(inc_periph << DMA_CCR_PINC_Pos) |
|
|
(mode & 1) << DMA_CCR_DIR_Pos |
|
|
((mode & 2) >> 1) << DMA_CCR_MEM2MEM_Pos |
|
|
DMA_CCR_TCIE |
|
|
DMA_CCR_TEIE;
|
|
stream->CONTROL_REG = ctr_reg;
|
|
#endif
|
|
stream->PERIPH_ADDR = (uint32_t)periph_addr;
|
|
}
|
|
|
|
void dma_prepare(dma_t dma, void *mem, size_t len, bool incr_mem)
|
|
{
|
|
STM32_DMA_Stream_Type *stream = dma_ctx[dma].stream;
|
|
uint32_t ctr_reg = stream->CONTROL_REG;
|
|
|
|
#ifdef DMA_SxCR_MINC
|
|
stream->CONTROL_REG = (ctr_reg & ~(DMA_SxCR_MINC)) |
|
|
(incr_mem << DMA_SxCR_MINC_Pos);
|
|
#else
|
|
stream->CONTROL_REG = (ctr_reg & ~(DMA_CCR_MINC)) |
|
|
(incr_mem << DMA_CCR_MINC_Pos);
|
|
#endif
|
|
stream->MEM_ADDR = (uint32_t)mem;
|
|
|
|
/* Set length */
|
|
stream->NDTR_REG = len;
|
|
dma_ctx[dma].len = len;
|
|
}
|
|
|
|
void dma_setup_ext(dma_t dma, dma_burst_t pburst, dma_burst_t mburst,
|
|
bool fifo, dma_fifo_thresh_t thresh, bool pfctrl)
|
|
{
|
|
#if CPU_FAM_STM32F2 || CPU_FAM_STM32F4 || CPU_FAM_STM32F7
|
|
STM32_DMA_Stream_Type *stream = dma_ctx[dma].stream;
|
|
|
|
/* configuraition can be done only if DMA stream is disabled */
|
|
assert((stream->CR & DMA_EN) == 0);
|
|
|
|
/* FIFO configuration if enabled */
|
|
if (fifo) {
|
|
uint8_t width = (stream->CR & DMA_SxCR_MSIZE_Msk) >> DMA_SxCR_MSIZE_Pos;
|
|
|
|
/* check valid combinations of MSIZE, MBURST and FIFO threshold level */
|
|
switch (width) {
|
|
case DMA_DATA_WIDTH_BYTE:
|
|
switch (thresh) {
|
|
case DMA_FIFO_FULL_1_4:
|
|
/* fall through */
|
|
case DMA_FIFO_FULL_3_4:
|
|
assert(mburst == DMA_BURST_INCR4);
|
|
break;
|
|
case DMA_FIFO_FULL_1_2:
|
|
assert((mburst == DMA_BURST_INCR4) || (mburst == DMA_BURST_INCR8));
|
|
break;
|
|
case DMA_FIFO_FULL: /* all mburst values are valid */
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case DMA_DATA_WIDTH_HALF_WORD:
|
|
switch (thresh) {
|
|
case DMA_FIFO_FULL_1_2:
|
|
assert(mburst == DMA_BURST_INCR4);
|
|
break;
|
|
case DMA_FIFO_FULL:
|
|
assert((mburst == DMA_BURST_INCR4) || (mburst == DMA_BURST_INCR8));
|
|
break;
|
|
default:
|
|
assert(false); /* all other combinations are invalid) */
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case DMA_DATA_WIDTH_WORD:
|
|
assert((thresh == DMA_FIFO_FULL) && (mburst == DMA_BURST_INCR4));
|
|
break;
|
|
}
|
|
|
|
stream->FCR = (fifo << DMA_SxFCR_DMDIS_Pos) |
|
|
(thresh << DMA_SxFCR_FTH_Pos);
|
|
}
|
|
else {
|
|
stream->FCR = 0;
|
|
}
|
|
|
|
stream->CR &= ~(DMA_SxCR_PFCTRL | DMA_SxCR_MBURST | DMA_SxCR_PBURST);
|
|
stream->CR |= pfctrl ? DMA_SxCR_PFCTRL : 0;
|
|
stream->CR |= (mburst << DMA_SxCR_MBURST_Pos);
|
|
stream->CR |= (pburst << DMA_SxCR_PBURST_Pos);
|
|
|
|
#else
|
|
(void)dma;
|
|
(void)pburst;
|
|
(void)pburst;
|
|
(void)mburst;
|
|
(void)fifo;
|
|
(void)thresh;
|
|
(void)pfctrl;
|
|
#endif
|
|
}
|
|
|
|
int dma_configure(dma_t dma, int chan, const volatile void *src, volatile void *dst, size_t len,
|
|
dma_mode_t mode, uint8_t flags)
|
|
{
|
|
assert(src != NULL);
|
|
assert(dst != NULL);
|
|
|
|
bool inc_periph;
|
|
bool inc_mem;
|
|
void *periph_addr;
|
|
void *mem_addr;
|
|
|
|
switch (mode) {
|
|
case DMA_MEM_TO_MEM:
|
|
case DMA_PERIPH_TO_MEM:
|
|
periph_addr = (void*)src;
|
|
mem_addr = (void*)dst;
|
|
inc_periph = (flags & DMA_INC_SRC_ADDR);
|
|
inc_mem = (flags & DMA_INC_DST_ADDR);
|
|
break;
|
|
case DMA_MEM_TO_PERIPH:
|
|
periph_addr = (void*)dst;
|
|
/* This discards the const specifier which should be fine as the DMA
|
|
* stream promises not to write to this location */
|
|
mem_addr = (void*)src;
|
|
inc_periph = (flags & DMA_INC_DST_ADDR);
|
|
inc_mem = (flags & DMA_INC_SRC_ADDR);
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
uint32_t width = (flags & DMA_DATA_WIDTH_MASK) >> DMA_DATA_WIDTH_SHIFT;
|
|
|
|
dma_setup(dma, chan, periph_addr, mode, width, inc_periph);
|
|
dma_prepare(dma, mem_addr, len, inc_mem);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void dma_start(dma_t dma)
|
|
{
|
|
STM32_DMA_Stream_Type *stream = dma_ctx[dma].stream;
|
|
|
|
stream->CONTROL_REG |= DMA_EN;
|
|
}
|
|
|
|
uint16_t dma_suspend(dma_t dma)
|
|
{
|
|
assert(dma < DMA_NUMOF);
|
|
|
|
int stream_n = dma_config[dma].stream;
|
|
STM32_DMA_Stream_Type *stream = dma_ctx[dma].stream;
|
|
uint16_t left = 0;
|
|
|
|
if ((stream->CONTROL_REG & DMA_EN) == DMA_EN) {
|
|
dma_isr_disable(stream_n);
|
|
stream->CONTROL_REG &= ~(uint32_t)DMA_EN;
|
|
while ((stream->CONTROL_REG & DMA_EN) == DMA_EN) {}
|
|
dma_clear_all_flags(dma);
|
|
left = stream->NDTR_REG;
|
|
dma_isr_clear(stream_n);
|
|
}
|
|
return left;
|
|
|
|
}
|
|
|
|
void dma_resume(dma_t dma, uint16_t remaining)
|
|
{
|
|
assert(dma < DMA_NUMOF);
|
|
|
|
int stream_n = dma_config[dma].stream;
|
|
STM32_DMA_Stream_Type *stream = dma_ctx[dma].stream;
|
|
|
|
#ifdef DMA_SxCR_MINC
|
|
const bool mem_inc = stream->CONTROL_REG & DMA_SxCR_MINC;
|
|
const bool periph_inc = stream->CONTROL_REG & DMA_SxCR_PINC;
|
|
const int msize_reg =
|
|
(stream->CONTROL_REG & DMA_SxCR_MSIZE) >> DMA_SxCR_MSIZE_Pos;
|
|
const int psize_reg =
|
|
(stream->CONTROL_REG & DMA_SxCR_MSIZE) >> DMA_SxCR_MSIZE_Pos;
|
|
#else
|
|
const bool mem_inc = stream->CONTROL_REG & DMA_CCR_MINC;
|
|
const bool periph_inc = stream->CONTROL_REG & DMA_CCR_PINC;
|
|
const int msize_reg =
|
|
(stream->CONTROL_REG & DMA_CCR_MSIZE) >> DMA_CCR_MSIZE_Pos;
|
|
const int psize_reg =
|
|
(stream->CONTROL_REG & DMA_CCR_PSIZE) >> DMA_CCR_PSIZE_Pos;
|
|
#endif
|
|
|
|
const int mpitch = (mem_inc) ? msize_reg + 1 : 0;
|
|
const int ppitch = (periph_inc) ? psize_reg + 1 : 0;
|
|
|
|
if (remaining > 0) {
|
|
dma_isr_enable(stream_n);
|
|
stream->NDTR_REG = remaining;
|
|
stream->MEM_ADDR += mpitch * (dma_ctx[dma].len - remaining);
|
|
stream->PERIPH_ADDR += ppitch * (dma_ctx[dma].len - remaining);
|
|
dma_ctx[dma].len = remaining;
|
|
stream->CONTROL_REG |= DMA_EN;
|
|
}
|
|
}
|
|
|
|
void dma_stop(dma_t dma)
|
|
{
|
|
STM32_DMA_Stream_Type *stream = dma_stream(dma_config[dma].stream);
|
|
|
|
stream->CONTROL_REG &= ~(uint32_t)DMA_EN;
|
|
}
|
|
|
|
void dma_wait(dma_t dma)
|
|
{
|
|
mutex_lock(&dma_ctx[dma].sync_lock);
|
|
}
|
|
|
|
void dma_isr_handler(dma_t dma)
|
|
{
|
|
dma_clear_all_flags(dma);
|
|
|
|
mutex_unlock(&dma_ctx[dma].sync_lock);
|
|
|
|
cortexm_isr_end();
|
|
}
|
|
|
|
#ifdef DMA_0_ISR
|
|
void DMA_0_ISR(void)
|
|
{
|
|
dma_isr_handler(0);
|
|
}
|
|
#endif
|
|
|
|
#ifdef DMA_1_ISR
|
|
void DMA_1_ISR(void)
|
|
{
|
|
dma_isr_handler(1);
|
|
}
|
|
#endif
|
|
|
|
#ifdef DMA_2_ISR
|
|
void DMA_2_ISR(void)
|
|
{
|
|
dma_isr_handler(2);
|
|
}
|
|
#endif
|
|
|
|
#ifdef DMA_3_ISR
|
|
void DMA_3_ISR(void)
|
|
{
|
|
dma_isr_handler(3);
|
|
}
|
|
#endif
|
|
|
|
#ifdef DMA_4_ISR
|
|
void DMA_4_ISR(void)
|
|
{
|
|
dma_isr_handler(4);
|
|
}
|
|
#endif
|
|
|
|
#ifdef DMA_5_ISR
|
|
void DMA_5_ISR(void)
|
|
{
|
|
dma_isr_handler(5);
|
|
}
|
|
#endif
|
|
|
|
#ifdef DMA_6_ISR
|
|
void DMA_6_ISR(void)
|
|
{
|
|
dma_isr_handler(6);
|
|
}
|
|
#endif
|
|
|
|
#ifdef DMA_7_ISR
|
|
void DMA_7_ISR(void)
|
|
{
|
|
dma_isr_handler(7);
|
|
}
|
|
#endif
|
|
|
|
#ifdef DMA_8_ISR
|
|
void DMA_8_ISR(void)
|
|
{
|
|
dma_isr_handler(8);
|
|
}
|
|
#endif
|
|
|
|
#ifdef DMA_9_ISR
|
|
void DMA_9_ISR(void)
|
|
{
|
|
dma_isr_handler(9);
|
|
}
|
|
#endif
|
|
|
|
#if defined(DMA_SHARED_ISR_0) || defined(DMA_SHARED_ISR_1)
|
|
static int dma_is_isr(dma_t dma)
|
|
{
|
|
DMA_TypeDef *dma_dev = dma_base(dma_config[dma].stream);
|
|
|
|
#if CPU_FAM_STM32F2 || CPU_FAM_STM32F4 || CPU_FAM_STM32F7
|
|
/* Clear all flags */
|
|
if (dma_hl(dma_config[dma].stream) == 0) {
|
|
return dma_dev->LISR & dma_all_flags(dma);
|
|
}
|
|
else {
|
|
return dma_dev->HISR & dma_all_flags(dma);
|
|
}
|
|
#else
|
|
return dma_dev->ISR & dma_all_flags(dma);
|
|
#endif
|
|
}
|
|
|
|
static void shared_isr(uint8_t *streams, size_t nb)
|
|
{
|
|
for (size_t i = 0; i < nb; i++) {
|
|
dma_t dma = streams[i];
|
|
if (dma_is_isr(dma)) {
|
|
dma_clear_all_flags(dma);
|
|
mutex_unlock(&dma_ctx[dma].sync_lock);
|
|
}
|
|
}
|
|
|
|
cortexm_isr_end();
|
|
}
|
|
#endif
|
|
|
|
#ifdef DMA_SHARED_ISR_0
|
|
void DMA_SHARED_ISR_0(void)
|
|
{
|
|
uint8_t streams[] = DMA_SHARED_ISR_0_STREAMS;
|
|
shared_isr(streams, ARRAY_SIZE(streams));
|
|
}
|
|
#endif
|
|
|
|
#ifdef DMA_SHARED_ISR_1
|
|
void DMA_SHARED_ISR_1(void)
|
|
{
|
|
uint8_t streams[] = DMA_SHARED_ISR_1_STREAMS;
|
|
shared_isr(streams, ARRAY_SIZE(streams));
|
|
}
|
|
#endif
|