diff --git a/cpu/stm32_common/cpu_init.c b/cpu/stm32_common/cpu_init.c index d1a0bfc76a..c11866217a 100644 --- a/cpu/stm32_common/cpu_init.c +++ b/cpu/stm32_common/cpu_init.c @@ -3,6 +3,7 @@ * 2014 Freie Universität Berlin * 2016 TriaGnoSys GmbH * 2018 Kaspar Schleiser + * 2018 OTA keys S.A. * * * This file is subject to the terms and conditions of the GNU Lesser General @@ -24,6 +25,7 @@ * @author Nick van IJzendoorn * @author Víctor Ariño * @author Kaspar Schleiser + * @author Vincent Dupont * * @} */ @@ -47,6 +49,10 @@ void cpu_init(void) periph_clk_en(APB1, BIT_APB_PWREN); /* initialize the system clock as configured in the periph_conf.h */ stmclk_init_sysclk(); +#ifdef MODULE_PERIPH_DMA + /* initialize DMA streams */ + dma_init(); +#endif /* trigger static peripheral initialization */ periph_init(); } diff --git a/cpu/stm32_common/include/periph_cpu_common.h b/cpu/stm32_common/include/periph_cpu_common.h index 9eee755469..56eac8b6d1 100644 --- a/cpu/stm32_common/include/periph_cpu_common.h +++ b/cpu/stm32_common/include/periph_cpu_common.h @@ -218,6 +218,49 @@ typedef enum { #endif /* ndef DOXYGEN */ #endif /* ndef CPU_FAM_STM32F1 */ +#ifdef MODULE_PERIPH_DMA +/** + * @brief DMA configuration + */ +typedef struct { + int stream; /**< DMA stream */ +} dma_conf_t; + +/** + * @brief DMA type + */ +typedef unsigned dma_t; + +/** + * @brief DMA modes + */ +typedef enum { + DMA_PERIPH_TO_MEM, /**< Peripheral to memory */ + DMA_MEM_TO_PERIPH, /**< Memory to peripheral */ + DMA_MEM_TO_MEM, /**< Memory to memory */ +} dma_mode_t; + +/** + * @name DMA Increment modes + * @{ + */ +#define DMA_INC_SRC_ADDR (0x01) +#define DMA_INC_DST_ADDR (0x02) +#define DMA_INC_BOTH_ADDR (DMA_INC_SRC_ADDR | DMA_INC_DST_ADDR) +/** @} */ + +/** + * @name DMA data width + * @{ + */ +#define DMA_DATA_WIDTH_BYTE (0x00) +#define DMA_DATA_WIDTH_HALF_WORD (0x04) +#define DMA_DATA_WIDTH_WORD (0x08) +#define DMA_DATA_WIDTH_MASK (0x0C) +#define DMA_DATA_WIDTH_SHIFT (2) +/** @} */ +#endif /* MODULE_PERIPH_DMA */ + /** * @brief DAC line configuration data */ @@ -293,8 +336,8 @@ typedef struct { #endif uint8_t bus; /**< APB bus */ uint8_t irqn; /**< IRQ channel */ -#if 0 /* TODO */ - uint8_t dma_stream; /**< DMA stream used for TX */ +#ifdef MODULE_PERIPH_DMA + dma_t dma; /**< Logical DMA stream used for TX */ uint8_t dma_chan; /**< DMA channel used for TX */ #endif #ifdef MODULE_STM32_PERIPH_UART_HW_FC @@ -321,8 +364,15 @@ typedef struct { #endif uint32_t rccmask; /**< bit in the RCC peripheral enable register */ uint8_t apbbus; /**< APBx bus the device is connected to */ +#ifdef MODULE_PERIPH_DMA + dma_t tx_dma; /**< Logical DMA stream used for TX */ + uint8_t tx_dma_chan; /**< DMA channel used for TX */ + dma_t rx_dma; /**< Logical DMA stream used for RX */ + uint8_t rx_dma_chan; /**< DMA channel used for RX */ +#endif } spi_conf_t; + /** * @brief Get the actual bus clock frequency for the APB buses * @@ -372,6 +422,247 @@ void gpio_init_af(gpio_t pin, gpio_af_t af); */ void gpio_init_analog(gpio_t pin); +#ifdef MODULE_PERIPH_DMA +/** + * @brief DMA stream not defined + */ +#define DMA_STREAM_UNDEF (UINT_MAX) + +/** + * @brief Initialize DMA + */ +void dma_init(void); + +/** + * @brief Execute a DMA transfer + * + * This function blocks until the transfer is completed. This is a convenience + * function which configure, start, wait and stop a DMA transfer. + * + * @param[in] dma logical DMA stream + * @param[in] chan DMA channel + * @param[in] src source buffer + * @param[out] dst destination buffer + * @param[in] len length to transfer + * @param[in] mode DMA mode + * @param[in] flags DMA configuration + * + * @return < 0 on error, the number of transfered bytes otherwise + */ +int dma_transfer(dma_t dma, int chan, const void *src, void *dst, size_t len, + dma_mode_t mode, uint8_t flags); + +/** + * @brief Acquire a DMA stream + * + * @param[in] dma logical DMA stream + */ +void dma_acquire(dma_t dma); + +/** + * @brief Release a DMA stream + * + * @param[in] dma logical DMA stream + */ +void dma_release(dma_t dma); + +/** + * @brief Start a DMA transfer on a stream + * + * Start a DMA transfer on a given stream. The stream must be configured first + * by a @p dma_configure call. + * + * @param[in] dma logical DMA stream + */ +void dma_start(dma_t dma); + +/** + * @brief Suspend a DMA transfer on a stream + * + * @param[in] dma logical DMA stream + * + * @return the remaining number of bytes to transfer + */ +uint16_t dma_suspend(dma_t dma); + +/** + * @brief Resume a suspended DMA transfer on a stream + * + * @param[in] dma logical DMA stream + * @param[in] reamaining the remaining number of bytes to transfer + */ +void dma_resume(dma_t dma, uint16_t remaining); + +/** + * @brief Stop a DMA transfer on a stream + * + * @param[in] dma logical DMA stream + */ +void dma_stop(dma_t dma); + +/** + * @brief Wait for the end of a transfer + * + * @param[in] dma logical DMA stream + */ +void dma_wait(dma_t dma); + +/** + * @brief Configure a DMA stream for a new transfer + * + * @param[in] dma logical DMA stream + * @param[in] chan DMA channel + * @param[in] src source buffer + * @param[out] dst destination buffer + * @param[in] len length to transfer + * @param[in] mode DMA mode + * @param[in] flags DMA configuration + * + * @return < 0 on error, 0 on success + */ +int dma_configure(dma_t dma, int chan, const void *src, void *dst, size_t len, + dma_mode_t mode, uint8_t flags); + +/** + * @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) +{ + return (stream < 8) ? DMA1 : DMA2; +} + +/** + * @brief Power on the DMA device the given stream belongs to + * + * @param[in] stream physical DMA stream + */ +static inline void dma_poweron(int stream) +{ + if (stream < 8) { + periph_clk_en(AHB1, RCC_AHB1ENR_DMA1EN); + } + else { + periph_clk_en(AHB1, RCC_AHB1ENR_DMA2EN); + } +} + +/** + * @brief Get the DMA stream base address + * + * @param[in] stream physical DMA stream + * + * @return base address for the selected DMA stream + */ +static inline DMA_Stream_TypeDef *dma_stream(int stream) +{ + uint32_t base = (uint32_t)dma_base(stream); + + return (DMA_Stream_TypeDef *)(base + (0x10 + (0x18 * (stream & 0x7)))); +} + +/** + * @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); +} + +/** + * @brief Get the interrupt flag clear bit position in the DMA LIFCR register + * + * @param[in] stream physical DMA stream + */ +static inline uint32_t dma_ifc(int stream) +{ + switch (stream & 0x3) { + case 0: + return (1 << 5); + case 1: + return (1 << 11); + case 2: + return (1 << 21); + case 3: + return (1 << 27); + default: + return 0; + } +} + +/** + * @brief Enable the interrupt of a given stream + * + * @param[in] stream physical DMA stream + */ +static inline void dma_isr_enable(int stream) +{ + if (stream < 7) { + NVIC_EnableIRQ((IRQn_Type)((int)DMA1_Stream0_IRQn + stream)); + } + else if (stream == 7) { + NVIC_EnableIRQ(DMA1_Stream7_IRQn); + } + else if (stream < 13) { + NVIC_EnableIRQ((IRQn_Type)((int)DMA2_Stream0_IRQn + (stream - 8))); + } + else if (stream < 16) { + NVIC_EnableIRQ((IRQn_Type)((int)DMA2_Stream5_IRQn + (stream - 13))); + } +} + +/** + * @brief Disable the interrupt of a given stream + * + * @param[in] stream physical DMA stream + */ +static inline void dma_isr_disable(int stream) +{ + if (stream < 7) { + NVIC_DisableIRQ((IRQn_Type)((int)DMA1_Stream0_IRQn + stream)); + } + else if (stream == 7) { + NVIC_DisableIRQ(DMA1_Stream7_IRQn); + } + else if (stream < 13) { + NVIC_DisableIRQ((IRQn_Type)((int)DMA2_Stream0_IRQn + (stream - 8))); + } + else if (stream < 16) { + NVIC_DisableIRQ((IRQn_Type)((int)DMA2_Stream5_IRQn + (stream - 13))); + } +} + +/** + * @brief Clear the interrupt of a given stream + * + * @param[in] stream physical DMA stream + */ +static inline void dma_isr_clear(int stream) +{ + if (stream < 7) { + NVIC_ClearPendingIRQ((IRQn_Type)((int)DMA1_Stream0_IRQn + stream)); + } + else if (stream == 7) { + NVIC_ClearPendingIRQ((IRQn_Type)DMA1_Stream7_IRQn); + } + else if (stream < 13) { + NVIC_ClearPendingIRQ((IRQn_Type)((int)DMA2_Stream0_IRQn + (stream - 8))); + } + else if (stream < 16) { + NVIC_ClearPendingIRQ((IRQn_Type)((int)DMA2_Stream5_IRQn + (stream - 13))); + } +} +#endif /* MODULE_PERIPH_DMA */ + #ifdef __cplusplus } #endif diff --git a/cpu/stm32_common/periph/dma.c b/cpu/stm32_common/periph/dma.c new file mode 100644 index 0000000000..dc971b09b6 --- /dev/null +++ b/cpu/stm32_common/periph/dma.c @@ -0,0 +1,306 @@ +/* + * 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_common + * @{ + * + * @file + * @brief Low-level DMA driver implementation + * + * @author Vincent Dupont + * + * @} + */ + +#include + +#include "periph_cpu.h" +#include "periph_conf.h" +#include "mutex.h" +#include "assert.h" + +#if !(defined(CPU_FAM_STM32F2) || defined(CPU_FAM_STM32F4) || defined(CPU_FAM_STM32F7)) +#error "DMA is not supported for target CPU" +#endif + +#define DMA_STREAM_IT_MASK (DMA_LISR_FEIF0 | DMA_LISR_DMEIF0 | \ + DMA_LISR_TEIF0 | DMA_LISR_HTIF0 | \ + DMA_LISR_TCIF0) + +struct dma_ctx { + mutex_t conf_lock; + mutex_t sync_lock; + uint16_t len; +}; + +static struct dma_ctx dma_ctx[DMA_NUMOF]; + +static inline uint32_t dma_all_flags(dma_t dma) +{ + assert(dma < DMA_NUMOF); + + 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; + } +} + +static void dma_clear_all_flags(dma_t dma) +{ + assert(dma < DMA_NUMOF); + + DMA_TypeDef *stream = dma_base(dma_config[dma].stream); + + /* Clear all flags */ + if (dma_hl(dma_config[dma].stream) == 0) { + stream->LIFCR = dma_all_flags(dma); + } + else { + stream->HIFCR = dma_all_flags(dma); + } +} + + +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 dma_transfer(dma_t dma, int chan, const void *src, 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); +} + +void dma_release(dma_t dma) +{ + assert(dma < DMA_NUMOF); + + mutex_unlock(&dma_ctx[dma].conf_lock); +} + +int dma_configure(dma_t dma, int chan, const void *src, void *dst, size_t len, + dma_mode_t mode, uint8_t flags) +{ + assert(src != NULL); + assert(dst != NULL); + assert(dma < DMA_NUMOF); + + int stream_n = dma_config[dma].stream; + uint32_t inc_periph; + uint32_t inc_mem; + + DMA_Stream_TypeDef *stream = dma_stream(stream_n); + dma_poweron(stream_n); + dma_clear_all_flags(dma); + + switch (mode) { + case DMA_MEM_TO_MEM: + case DMA_PERIPH_TO_MEM: + stream->PAR = (uint32_t)src; + stream->M0AR = (uint32_t)dst; + inc_periph = (flags & DMA_INC_SRC_ADDR); + inc_mem = (flags & DMA_INC_DST_ADDR) >> 1; + break; + case DMA_MEM_TO_PERIPH: + stream->PAR = (uint32_t)dst; + stream->M0AR = (uint32_t)src; + inc_periph = (flags & DMA_INC_DST_ADDR) >> 1; + inc_mem = (flags & DMA_INC_SRC_ADDR); + break; + default: + return -1; + } + + uint32_t width = (flags & DMA_DATA_WIDTH_MASK) >> DMA_DATA_WIDTH_SHIFT; + /* Set channel, data width, inc and mode */ + stream->CR = (chan & 0xF) << DMA_SxCR_CHSEL_Pos | + width << DMA_SxCR_MSIZE_Pos | width << DMA_SxCR_PSIZE_Pos | + inc_periph << DMA_SxCR_PINC_Pos | inc_mem << DMA_SxCR_MINC_Pos | + (mode & 3) << DMA_SxCR_DIR_Pos; + /* Enable interrupts */ + stream->CR |= DMA_SxCR_TCIE | DMA_SxCR_TEIE; + /* Configure FIFO */ + stream->FCR = 0; + + /* Set length */ + stream->NDTR = len; + dma_ctx[dma].len = len; + + dma_isr_enable(stream_n); + + return 0; +} + +void dma_start(dma_t dma) +{ + assert(dma < DMA_NUMOF); + + DMA_Stream_TypeDef *stream = dma_stream(dma_config[dma].stream); + + stream->CR |= DMA_SxCR_EN; +} + +uint16_t dma_suspend(dma_t dma) +{ + assert(dma < DMA_NUMOF); + + int stream_n = dma_config[dma].stream; + DMA_Stream_TypeDef *stream = dma_stream(stream_n); + uint16_t left = 0; + + if ((stream->CR & DMA_SxCR_EN) == DMA_SxCR_EN) { + dma_isr_disable(stream_n); + stream->CR &= ~(uint32_t)DMA_SxCR_EN; + while ((stream->CR & DMA_SxCR_EN) == DMA_SxCR_EN) {} + dma_clear_all_flags(dma); + left = stream->NDTR; + 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; + DMA_Stream_TypeDef *stream = dma_stream(stream_n); + + if (remaining > 0) { + dma_isr_enable(stream_n); + stream->NDTR = remaining; + stream->M0AR += dma_ctx[dma].len - remaining; + dma_ctx[dma].len = remaining; + stream->CR |= (uint32_t)DMA_SxCR_EN; + } +} + +void dma_stop(dma_t dma) +{ + assert(dma < DMA_NUMOF); + + DMA_Stream_TypeDef *stream = dma_stream(dma_config[dma].stream); + + stream->CR &= ~(uint32_t)DMA_SxCR_EN; +} + +void dma_wait(dma_t dma) +{ + assert(dma < DMA_NUMOF); + + 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 diff --git a/cpu/stm32f2/include/periph_cpu.h b/cpu/stm32f2/include/periph_cpu.h index 56d50efd57..a45254b11e 100644 --- a/cpu/stm32f2/include/periph_cpu.h +++ b/cpu/stm32f2/include/periph_cpu.h @@ -76,101 +76,6 @@ typedef enum { } adc_res_t; /** @} */ -/** - * @brief Power on the DMA device the given stream belongs to - * - * @param[in] stream logical DMA stream - */ -static inline void dma_poweron(int stream) -{ - if (stream < 8) { - periph_clk_en(AHB1, RCC_AHB1ENR_DMA1EN); - } else { - periph_clk_en(AHB1, RCC_AHB1ENR_DMA2EN); - } -} - -/** - * @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 logical DMA stream - */ -static inline DMA_TypeDef *dma_base(int stream) -{ - return (stream < 8) ? DMA1 : DMA2; -} - -/** - * @brief Get the DMA stream base address - * - * @param[in] stream logical DMA stream - * - * @return base address for the selected DMA stream - */ -static inline DMA_Stream_TypeDef *dma_stream(int stream) -{ - uint32_t base = (uint32_t)dma_base(stream); - return (DMA_Stream_TypeDef *)(base + (0x10 + (0x18 * (stream & 0x7)))); -} - -/** - * @brief Select high or low DMA interrupt register based on stream number - * - * @param[in] stream logical DMA stream - * - * @return 0 for streams 0-3, 1 for streams 3-7 - */ -static inline int dma_hl(int stream) -{ - return ((stream & 0x4) >> 2); -} - -/** - * @brief Get the interrupt flag clear bit position in the DMA LIFCR register - * - * @param[in] stream logical DMA stream - */ -static inline uint32_t dma_ifc(int stream) -{ - switch (stream & 0x3) { - case 0: /* 0 and 4 */ - return (1 << 5); - case 1: /* 1 and 5 */ - return (1 << 11); - case 2: /* 2 and 6 */ - return (1 << 21); - case 3: /* 3 and 7 */ - return (1 << 27); - default: - return 0; - } -} - -/** - * @brief Enable DMA interrupts - * - * @param[in] stream logical DMA stream - */ -static inline void dma_isr_enable(int stream) -{ - if (stream < 7) { - NVIC_EnableIRQ((IRQn_Type)((int)DMA1_Stream0_IRQn + stream)); - } - else if (stream == 7) { - NVIC_EnableIRQ(DMA1_Stream7_IRQn); - } - else if (stream < 13) { - NVIC_EnableIRQ((IRQn_Type)((int)DMA2_Stream0_IRQn + (stream - 8))); - } - else if (stream < 16) { - NVIC_EnableIRQ((IRQn_Type)((int)DMA2_Stream5_IRQn + (stream - 13))); - } -} - #ifdef __cplusplus } #endif diff --git a/cpu/stm32f4/include/periph_cpu.h b/cpu/stm32f4/include/periph_cpu.h index 736356e1e6..7f86e972d1 100644 --- a/cpu/stm32f4/include/periph_cpu.h +++ b/cpu/stm32f4/include/periph_cpu.h @@ -89,98 +89,6 @@ typedef struct { uint8_t chan; /**< CPU ADC channel connected to the pin */ } adc_conf_t; -/** - * @brief Power on the DMA device the given stream belongs to - * - * @param[in] stream logical DMA stream - */ -static inline void dma_poweron(int stream) -{ - if (stream < 8) { - periph_clk_en(AHB1, RCC_AHB1ENR_DMA1EN); - } - else { - periph_clk_en(AHB1, RCC_AHB1ENR_DMA2EN); - } -} - -/** - * @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 logical DMA stream - */ -static inline DMA_TypeDef *dma_base(int stream) -{ - return (stream < 8) ? DMA1 : DMA2; -} - -/** - * @brief Get the DMA stream base address - * - * @param[in] stream logical DMA stream - * - * @return base address for the selected DMA stream - */ -static inline DMA_Stream_TypeDef *dma_stream(int stream) -{ - uint32_t base = (uint32_t)dma_base(stream); - - return (DMA_Stream_TypeDef *)(base + (0x10 + (0x18 * (stream & 0x7)))); -} - -/** - * @brief Select high or low DMA interrupt register based on stream number - * - * @param[in] stream logical DMA stream - * - * @return 0 for streams 0-3, 1 for streams 3-7 - */ -static inline int dma_hl(int stream) -{ - return ((stream & 0x4) >> 2); -} - -/** - * @brief Get the interrupt flag clear bit position in the DMA LIFCR register - * - * @param[in] stream logical DMA stream - */ -static inline uint32_t dma_ifc(int stream) -{ - switch (stream & 0x3) { - case 0: - return (1 << 5); - case 1: - return (1 << 11); - case 2: - return (1 << 21); - case 3: - return (1 << 27); - default: - return 0; - } -} - -static inline void dma_isr_enable(int stream) -{ - if (stream < 7) { - NVIC_EnableIRQ((IRQn_Type)((int)DMA1_Stream0_IRQn + stream)); - } - else if (stream == 7) { - NVIC_EnableIRQ(DMA1_Stream7_IRQn); - } - else if (stream < 13) { - NVIC_EnableIRQ((IRQn_Type)((int)DMA2_Stream0_IRQn + (stream - 8))); - } - else if (stream < 16) { - NVIC_EnableIRQ((IRQn_Type)((int)DMA2_Stream5_IRQn + (stream - 13))); - } -} - #ifdef __cplusplus } #endif