From 1ccdb2ec20237d5ca79ed86ea4b1f9f0c52a9558 Mon Sep 17 00:00:00 2001 From: matthew Date: Fri, 13 Nov 2020 19:43:32 +0000 Subject: [PATCH 1/6] Initial DMA port, need to make extensible --- src/modm/platform/dma/stm32/f4/dma.cpp.in | 109 ++++++ src/modm/platform/dma/stm32/f4/dma.hpp.in | 315 ++++++++++++++++++ .../platform/dma/stm32/f4/dma_base.hpp.in | 299 +++++++++++++++++ src/modm/platform/dma/stm32/f4/dma_hal.hpp.in | 311 +++++++++++++++++ src/modm/platform/dma/stm32/f4/module.lb | 96 ++++++ .../platform/dma/stm32/{ => l4}/dma.cpp.in | 0 .../platform/dma/stm32/{ => l4}/dma.hpp.in | 0 .../dma/stm32/{ => l4}/dma_base.hpp.in | 111 ++---- .../dma/stm32/{ => l4}/dma_hal.hpp.in | 0 .../dma/stm32/{ => l4}/dma_hal_impl.hpp.in | 13 - .../platform/dma/stm32/{ => l4}/module.lb | 0 src/modm/platform/spi/stm32/module.lb | 3 +- src/modm/platform/uart/stm32/module.lb | 13 +- src/modm/platform/uart/stm32/uart_base.hpp.in | 2 + src/modm/platform/uart/stm32/uart_dma.cpp.in | 14 + src/modm/platform/uart/stm32/uart_dma.hpp.in | 258 ++++++++++++++ 16 files changed, 1437 insertions(+), 107 deletions(-) create mode 100644 src/modm/platform/dma/stm32/f4/dma.cpp.in create mode 100644 src/modm/platform/dma/stm32/f4/dma.hpp.in create mode 100644 src/modm/platform/dma/stm32/f4/dma_base.hpp.in create mode 100644 src/modm/platform/dma/stm32/f4/dma_hal.hpp.in create mode 100644 src/modm/platform/dma/stm32/f4/module.lb rename src/modm/platform/dma/stm32/{ => l4}/dma.cpp.in (100%) rename src/modm/platform/dma/stm32/{ => l4}/dma.hpp.in (100%) rename src/modm/platform/dma/stm32/{ => l4}/dma_base.hpp.in (55%) rename src/modm/platform/dma/stm32/{ => l4}/dma_hal.hpp.in (100%) rename src/modm/platform/dma/stm32/{ => l4}/dma_hal_impl.hpp.in (82%) rename src/modm/platform/dma/stm32/{ => l4}/module.lb (100%) create mode 100644 src/modm/platform/uart/stm32/uart_dma.cpp.in create mode 100644 src/modm/platform/uart/stm32/uart_dma.hpp.in diff --git a/src/modm/platform/dma/stm32/f4/dma.cpp.in b/src/modm/platform/dma/stm32/f4/dma.cpp.in new file mode 100644 index 0000000000..b7c030fd11 --- /dev/null +++ b/src/modm/platform/dma/stm32/f4/dma.cpp.in @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2020, Mike Wolfram + * Copyright (c) 2020, Matthew Arnold + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#include "dma.hpp" + +MODM_ISR(DMA1_Stream0) +{ + using namespace modm::platform; + Dma1::Stream::interruptHandler(); +} + +MODM_ISR(DMA1_Stream1) +{ + using namespace modm::platform; + Dma1::Stream::interruptHandler(); +} + +MODM_ISR(DMA1_Stream2) +{ + using namespace modm::platform; + Dma1::Stream::interruptHandler(); +} + +MODM_ISR(DMA1_Stream3) +{ + using namespace modm::platform; + Dma1::Stream::interruptHandler(); +} + +MODM_ISR(DMA1_Stream4) +{ + using namespace modm::platform; + Dma1::Stream::interruptHandler(); +} + +MODM_ISR(DMA1_Stream5) +{ + using namespace modm::platform; + Dma1::Stream::interruptHandler(); +} + +MODM_ISR(DMA1_Stream6) +{ + using namespace modm::platform; + Dma1::Stream::interruptHandler(); +} + +MODM_ISR(DMA1_Stream7) +{ + using namespace modm::platform; + Dma1::Stream::interruptHandler(); +} + +MODM_ISR(DMA2_Stream0) +{ + using namespace modm::platform; + Dma2::Stream::interruptHandler(); +} + +MODM_ISR(DMA2_Stream1) +{ + using namespace modm::platform; + Dma2::Stream::interruptHandler(); +} + +MODM_ISR(DMA2_Stream2) +{ + using namespace modm::platform; + Dma2::Stream::interruptHandler(); +} + +MODM_ISR(DMA2_Stream3) +{ + using namespace modm::platform; + Dma2::Stream::interruptHandler(); +} + +MODM_ISR(DMA2_Stream4) +{ + using namespace modm::platform; + Dma2::Stream::interruptHandler(); +} + +MODM_ISR(DMA2_Stream5) +{ + using namespace modm::platform; + Dma2::Stream::interruptHandler(); +} + +MODM_ISR(DMA2_Stream6) +{ + using namespace modm::platform; + Dma2::Stream::interruptHandler(); +} + +MODM_ISR(DMA2_Stream7) +{ + using namespace modm::platform; + Dma2::Stream::interruptHandler(); +} diff --git a/src/modm/platform/dma/stm32/f4/dma.hpp.in b/src/modm/platform/dma/stm32/f4/dma.hpp.in new file mode 100644 index 0000000000..7040d9e729 --- /dev/null +++ b/src/modm/platform/dma/stm32/f4/dma.hpp.in @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2014, Kevin Läufer + * Copyright (c) 2014-2017, Niklas Hauser + * Copyright (c) 2020, Mike Wolfram + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#ifndef MODM_STM32_DMA_HPP +#define MODM_STM32_DMA_HPP + +#include + +#include + +#include "dma_hal.hpp" + +namespace modm +{ +namespace platform +{ +/** + * DMA controller for stm32f4 series microcontrollers + */ +template +class DmaController : public DmaBase +{ + static_assert(ID == 1 || ID == 2, "dma id invalid"); + +public: + /** + * Enable the DMA controller in the RCC. + */ + static void enableRcc() + { + if constexpr (ID == 1) + { + Rcc::enable(); + } + else if constexpr (ID == 2) + { + Rcc::enable(); + } + } + + /** + * Disable the DMA controller in the RCC. + */ + static void disableRcc() + { + if constexpr (ID == 1) + { + Rcc::disable(); + } + else if constexpr (ID == 2) + { + Rcc::disable(); + } + } + + /** + * Class representing a DMA channel/stream. + */ + template + class Stream + { + using ControlHal = DmaHal; + using StreamHal = DmaStreamHal; + + public: + static constexpr uint32_t DMA_ID = ID; + static constexpr DmaBase::StreamID STREAM_ID = SID; + + /** + * Configure the DMA channel + * + * @see DmaStreamHal::configure + */ + static void configure( + ChannelSelection channel, + DataTransferDirection direction, + PeripheralIncrementMode periphInc, + MemoryIncrementMode memInc, + PeripheralDataSize peripDataSize, + MemoryDataSize memDataSize, + ControlMode mode, + PriorityLevel priority, + FifoMode fifoMode, + FifoThreshold fifoThreshold, + MemoryBurstTransfer memBurst, + PeripheralBurstTransfer periphBurst) + { + StreamHal::configure( + channel, + direction, + periphInc, + memInc, + peripDataSize, + memDataSize, + mode, + priority, + fifoMode, + fifoThreshold, + memBurst, + periphBurst); + } + + static void selectChannel(DmaBase::ChannelSelection channel) + { + StreamHal::selectChannel(channel); + } + + /** + * Start the transfer of the DMA channel + */ + static void enable() + { + ControlHal::clearInterruptFlags(STREAM_ID); + StreamHal::enable(); + } + /** + * Stop a DMA channel transfer + */ + static bool disable() { return StreamHal::disable(); } + + /** + * Get the direction of the data transfer + */ + static DataTransferDirection getDataTransferDirection() + { + return StreamHal::getDataTransferDirection(); + } + + /** + * Configure the DMA channel to write to the requested data buffer length bytes. + * + * @note When in continuous read mode, other read operations are disabled. + */ + static bool configureRead(uint8_t *data, std::size_t length) + { + // Disable to allow modifications of the DMA stream control register. + if (!disable()) + { + return false; + } + // configure total # of bytes + setDataLength(length); + // Write the memory address in the DMA control register to configure it as the + // destination of the transfer. The data will be loaded from USART_DR to this memory + // area after each RXNE event. + setDestinationAddress(reinterpret_cast(data)); + // activate channel in DMA control register + enable(); + return true; + } + + static bool configureWrite(uint8_t *data, std::size_t length) + { + if (disable()) + { + return false; + } + setDataLength(length); + setSourceAddress(reinterpret_cast(data)); + enable(); + return true; + } + + /** + * Set the memory address of the DMA channel + * + * @note In Mem2Mem mode use this method to set the memory source address. + * + * @param[in] address Source address + */ + static void setSourceAddress(uintptr_t address) { StreamHal::setSourceAddress(address); } + + /** + * Set the peripheral address of the DMA channel + * + * @note In Mem2Mem mode use this method to set the memory destination address. + * + * @param[in] address Destination address + */ + static void setDestinationAddress(uintptr_t address) + { + StreamHal::setDestinationAddress(address); + } + + /** + * Set the length of data to be transfered + */ + static void setDataLength(std::size_t length) { StreamHal::setDataLength(length); } + + /** + * Set the IRQ handler for transfer errors. + * + * The handler will be called from the channels IRQ handler function + * when the IRQ status indicates an error occured. + */ + static void setTransferErrorIrqHandler(IrqHandler irqHandler) + { + transferError = irqHandler; + } + + /** + * Set the IRQ handler for transfer complete. + * + * Called by the channels IRQ handler when the transfer is complete. + */ + static void setTransferCompleteIrqHandler(IrqHandler irqHandler) + { + transferComplete = irqHandler; + } + + static uint32_t getInterruptFlags() { return ControlHal::getInterruptFlags(STREAM_ID); } + + /** + * IRQ handler of the DMA channel. + * + * Reads the IRQ status and checks for error or transfer complete. In case + * of error the DMA channel will be disabled. + */ + static void interruptHandler() + { + uint32_t currIsrs = ControlHal::getInterruptFlags(STREAM_ID); + ControlHal::clearInterruptFlags(STREAM_ID); + if (currIsrs & (uint32_t(InterruptStatusMsks::TRANSFER_ERROR) | + uint32_t(InterruptStatusMsks::DIRECT_MODE_ERROR) | + uint32_t(InterruptStatusMsks::FIFO_ERROR))) + { + if (transferError != nullptr) + { + transferError(); + } + } + if ((currIsrs & uint32_t(InterruptStatusMsks::TRANSFER_COMPLETE)) && transferComplete) + { + transferComplete(); + } + } + + /** + * Enable the IRQ vector of the channel. + * + * @param[in] priority Priority of the IRQ + */ + static void enableInterruptVector(uint32_t priority = 1) + { + NVIC_SetPriority(DmaBase::Nvic::DmaIrqs[uint32_t(STREAM_ID)], priority); + NVIC_EnableIRQ(DmaBase::Nvic::DmaIrqs[uint32_t(STREAM_ID)]); + } + + /** + * Disable the IRQ vector of the channel. + */ + static void disableInterruptVector() + { + NVIC_DisableIRQ(DmaBase::Nvic::DmaIrqs[uint32_t(STREAM_ID)]); + } + + /** + * Enable the specified interrupt of the channel. + */ + static void enableInterrupt(Interrupt_t irq) { StreamHal::enableInterrupt(irq); } + + /** + * Disable the specified interrupt of the channel. + */ + static void disableInterrupt(Interrupt_t irq) { StreamHal::disableInterrupt(irq); } + + private: + static inline DmaBase::IrqHandler transferError{nullptr}; + static inline DmaBase::IrqHandler transferComplete{nullptr}; + }; +}; // class DmaController + +/* + * Derive DMA controller classes for convenience. Every derived class defines + * the streams available on that controller. + */ +class Dma1 : public DmaController<1> +{ +public: + using Stream0 = DmaController<1>::Stream; + using Stream1 = DmaController<1>::Stream; + using Stream2 = DmaController<1>::Stream; + using Stream3 = DmaController<1>::Stream; + using Stream4 = DmaController<1>::Stream; + using Stream5 = DmaController<1>::Stream; + using Stream6 = DmaController<1>::Stream; + using Stream7 = DmaController<1>::Stream; +}; // class Dma1 + +class Dma2 : public DmaController<2> +{ +public: + using Stream0 = DmaController<2>::Stream; + using Stream1 = DmaController<2>::Stream; + using Stream2 = DmaController<2>::Stream; + using Stream3 = DmaController<2>::Stream; + using Stream4 = DmaController<2>::Stream; + using Stream5 = DmaController<2>::Stream; + using Stream6 = DmaController<2>::Stream; + using Stream7 = DmaController<2>::Stream; +}; // class Dma2 +} // namespace platform +} // namespace modm + +#endif // MODM_STM32_DMA_HPP diff --git a/src/modm/platform/dma/stm32/f4/dma_base.hpp.in b/src/modm/platform/dma/stm32/f4/dma_base.hpp.in new file mode 100644 index 0000000000..f23b1b243d --- /dev/null +++ b/src/modm/platform/dma/stm32/f4/dma_base.hpp.in @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2020, Matthew Arnold + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#ifndef MODM_STM32_DMA_BASE_HPP +#define MODM_STM32_DMA_BASE_HPP + +#include +#include "../device.hpp" + +#include +#include +#include + + +namespace modm +{ +namespace platform +{ +/** + * Class that defines register masks and interrupt handlers for DMA. + * + * @author Matthew Arnold + */ +class DmaBase +{ +public: + // Enums, for register bit masking + + // Use the following bit masks on a DMA stream x configuration register + // DMA_xCR, where x = [0,7] + + // Bits 27:25 + enum class ChannelSelection : uint32_t + { + CHANNEL_0 = 0, + CHANNEL_1 = DMA_SxCR_CHSEL_0, + CHANNEL_2 = DMA_SxCR_CHSEL_1, + CHANNEL_3 = DMA_SxCR_CHSEL_1 | DMA_SxCR_CHSEL_0, + CHANNEL_4 = DMA_SxCR_CHSEL_2, + CHANNEL_5 = DMA_SxCR_CHSEL_2 | DMA_SxCR_CHSEL_0, + CHANNEL_6 = DMA_SxCR_CHSEL_2 | DMA_SxCR_CHSEL_1, + CHANNEL_7 = DMA_SxCR_CHSEL_2 | DMA_SxCR_CHSEL_1 | DMA_SxCR_CHSEL_0, + }; + + // Bits 24:23 + enum class MemoryBurstTransfer : uint32_t + { + SINGLE = 0, + INCREMENT_4 = DMA_SxCR_MBURST_0, + INCREMENT_8 = DMA_SxCR_MBURST_1, + INCREMENT_16 = DMA_SxCR_MBURST, + }; + + // Bits 22:21 + enum class PeripheralBurstTransfer : uint32_t + { + SINGLE = 0, + INCREMENT_4 = DMA_SxCR_PBURST_0, + INCREMENT_8 = DMA_SxCR_PBURST_1, + INCREMENT_16 = DMA_SxCR_PBURST, + }; + + // Bit 19 + // Only used in double buffer mode + enum class CurrentTarget : uint32_t + { + MEMORY_0 = 0, + MEMORY_1 = DMA_SxCR_CT, + }; + + // Bit 18 + enum class DoubleBufferMode : uint32_t + { + NO_BUFFER_SWITCHING = 0, + MEMORY_TARGET_SWITCHED = DMA_SxCR_DBM, + }; + + // Bits 17:16 + enum class PriorityLevel : uint32_t + { + LOW = 0, + MEDIUM = DMA_SxCR_PL_0, + HIGH = DMA_SxCR_PL_1, + VERY_HIGH = DMA_SxCR_PL_0 | DMA_SxCR_PL_1, + }; + + // Bit 15 + enum class PeripheralIncrementOffsetSize : uint32_t + { + LINKED_TO_PSIZE = 0, + FIXED_TO_4 = DMA_SxCR_PINC, + }; + + // Bits 14:13 + /// In direct mode (if the FIFO is not used) + /// MSIZE is forced by hardware to the same value as PSIZE + enum class MemoryDataSize : uint32_t + { + BYTE = 0, + HALF_WORD = DMA_SxCR_MSIZE_0, + WORD = DMA_SxCR_MSIZE_1, + }; + + // Bits 12:11 + enum class PeripheralDataSize : uint32_t + { + BYTE = 0, + HALF_WORD = DMA_SxCR_PSIZE_0, + WORD = DMA_SxCR_PSIZE_1, + }; + + // Bit 10 + enum class MemoryIncrementMode : uint32_t + { + FIXED = 0, + INCREMENT = DMA_SxCR_MINC, ///< incremented according to MemoryDataSize + }; + + // Bit 9 + enum class PeripheralIncrementMode : uint32_t + { + FIXED = 0, + INCREMENT = DMA_SxCR_PINC, ///< incremented according to PeripheralDataSize + }; + + // Bits 8 and 5 + enum class ControlMode : uint32_t + { + NORMAL = 0, + CIRCULAR = DMA_SxCR_CIRC, + PERIPHERAL_FLOW = DMA_SxCR_PFCTRL, + }; + + // Bits 7:6 + enum class DataTransferDirection : uint32_t + { + ///< Source: DMA_SxPAR; Sink: DMA_SxM0AR + PERIPH_TO_MEM = 0, + ///< Source: DMA_SxM0AR; Sink: DMA_SxPAR + MEM_TO_PERIPH = DMA_SxCR_DIR_0, + ///< Source: DMA_SxPAR; Sink: DMA_SxM0AR + MEM_TO_MEM = DMA_SxCR_DIR_1, + }; + + // Bit 1-4 are interrupt enable settings. + enum class Interrupt + { + TRANSFER_COMPLETE = DMA_SxCR_TCIE, + HALF_TRANSFER_COMPLETE = DMA_SxCR_HTIE, + ERROR = DMA_SxCR_TEIE, + ALL = DMA_SxCR_TCIE | DMA_SxCR_HTIE | DMA_SxCR_DMEIE, + }; + MODM_FLAGS32(Interrupt); + + /** + * Bit 0 + * + * When disabled, software allowed to program the Configuration and FIFO + * bit registers. It is forbidden to write these registers when EN bit is + * read as 1. + * @note Before setting EN bit to '1' to start a new transfer, the event + * flags corresponding to the stream in DMA_LISR or DMA_HISR register + * must be cleared. + */ + enum class StreamEnableFlag : uint32_t + { + STREAM_DISABLED = 0, + STREAM_ENABLED = DMA_SxCR_EN, + }; + + // End DMA_xCR masks + + // masks for the DMA_SxFCR register (fifo control) + + enum class FifoMode : uint32_t + { + DISABLED = 0, + ENABLED = DMA_SxFCR_DMDIS, + }; + + enum class FifoThreshold : uint32_t + { + QUARTER_FULL = 0, + HALFFULL = DMA_SxFCR_FTH_0, + THREE_QUARTER_FULL = DMA_SxFCR_FTH_1, + FULL = DMA_SxFCR_FTH, + }; + + // End DMA_SxFCR masks + + enum class StreamID : uint32_t + { + STREAM_0 = 0, + STREAM_1 = 1, + STREAM_2 = 2, + STREAM_3 = 3, + STREAM_4 = 4, + STREAM_5 = 5, + STREAM_6 = 6, + STREAM_7 = 7, + }; + + enum class Signal : uint8_t { + NoSignal, +%% for signal in dmaSignals + {{ signal }}, +%% endfor + }; + +protected: + /** + * Use to clear interrupts on a particular stream. Masks all interrupts for the + * specified stream. + */ + static constexpr uint32_t dmaStreamIFCRMasks[] { + DMA_LIFCR_CFEIF0 | DMA_LIFCR_CDMEIF0 | DMA_LIFCR_CTEIF0 | DMA_LIFCR_CHTIF0 | DMA_LIFCR_CTCIF0, + DMA_LIFCR_CFEIF1 | DMA_LIFCR_CDMEIF1 | DMA_LIFCR_CTEIF1 | DMA_LIFCR_CHTIF1 | DMA_LIFCR_CTCIF1, + DMA_LIFCR_CFEIF2 | DMA_LIFCR_CDMEIF2 | DMA_LIFCR_CTEIF2 | DMA_LIFCR_CHTIF2 | DMA_LIFCR_CTCIF2, + DMA_LIFCR_CFEIF3 | DMA_LIFCR_CDMEIF3 | DMA_LIFCR_CTEIF3 | DMA_LIFCR_CHTIF3 | DMA_LIFCR_CTCIF3, + DMA_HIFCR_CFEIF4 | DMA_HIFCR_CDMEIF4 | DMA_HIFCR_CTEIF4 | DMA_HIFCR_CHTIF4 | DMA_HIFCR_CTCIF4, + DMA_HIFCR_CFEIF5 | DMA_HIFCR_CDMEIF5 | DMA_HIFCR_CTEIF5 | DMA_HIFCR_CHTIF5 | DMA_HIFCR_CTCIF5, + DMA_HIFCR_CFEIF6 | DMA_HIFCR_CDMEIF6 | DMA_HIFCR_CTEIF6 | DMA_HIFCR_CHTIF6 | DMA_HIFCR_CTCIF6, + DMA_HIFCR_CFEIF7 | DMA_HIFCR_CDMEIF7 | DMA_HIFCR_CTEIF7 | DMA_HIFCR_CHTIF7 | DMA_HIFCR_CTCIF7, + }; + + /** + * Masks the 5 interrupt bits for stream 0. This may be used to mask any stream's + * interrupt status by shifting over the stream's interrupt bits over by some + * amount. + */ + static constexpr uint32_t INTERRUPT_STATUS_MSK = + DMA_LISR_TCIF0 | DMA_LISR_HTIF0 | DMA_LISR_TEIF0 | DMA_LISR_DMEIF0 | DMA_LISR_FEIF0; + + /** + * When shifted over and masked using `INTERRUPT_STATUS_MSK`, defines the interrupt + * status flags for a particular stream. + */ + enum class InterruptStatusMsks : uint32_t + { + TRANSFER_COMPLETE = DMA_LISR_TCIF0, + HALF_TRANSFER_COMPLETE = DMA_LISR_HTIF0, + TRANSFER_ERROR = DMA_LISR_TEIF0, + DIRECT_MODE_ERROR = DMA_LISR_DMEIF0, + FIFO_ERROR = DMA_LISR_FEIF0, + }; + + using IrqHandler = void (*)(void); + + /** + * Defines a list of interrupt vectors to be enabled for each DMA stream. + * + * @tparam The DMA identifier. + */ + template + struct Nvic; +}; // class DmaBase + +template <> +struct DmaBase::Nvic<1> +{ + static constexpr IRQn_Type DmaIrqs[]{ + DMA1_Stream0_IRQn, + DMA1_Stream1_IRQn, + DMA1_Stream2_IRQn, + DMA1_Stream3_IRQn, + DMA1_Stream4_IRQn, + DMA1_Stream5_IRQn, + DMA1_Stream6_IRQn, + DMA1_Stream7_IRQn, + }; +}; + +template <> +struct DmaBase::Nvic<2> +{ + static constexpr IRQn_Type DmaIrqs[]{ + DMA2_Stream0_IRQn, + DMA2_Stream1_IRQn, + DMA2_Stream2_IRQn, + DMA2_Stream3_IRQn, + DMA2_Stream4_IRQn, + DMA2_Stream5_IRQn, + DMA2_Stream6_IRQn, + DMA2_Stream7_IRQn, + }; +}; + +} // namespace platform +} // namespace modm + +#endif // MODM_STM32_DMA_BASE_HPP diff --git a/src/modm/platform/dma/stm32/f4/dma_hal.hpp.in b/src/modm/platform/dma/stm32/f4/dma_hal.hpp.in new file mode 100644 index 0000000000..921b790a4a --- /dev/null +++ b/src/modm/platform/dma/stm32/f4/dma_hal.hpp.in @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2020, Matthew Arnold + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#ifndef PLATFORM_HOSTED + +#ifndef DMA_HAL_HPP_ +#define DMA_HAL_HPP_ + +#include +#include + +#include "dma_base.hpp" + +namespace modm +{ +namespace platform +{ +template +class DmaHal : public DmaBase +{ +public: + static_assert(ID == 1 || ID == 2, "invalid dma id"); + + static constexpr uint32_t streamBase() + { + if constexpr (ID == 1) + { + return DMA1_Stream0_BASE; + } + else + { + return DMA2_Stream0_BASE; + } + } + + static constexpr DMA_TypeDef *dmaDef() + { + if constexpr (ID == 1) + { + return DMA1; + } + else + { + return DMA2; + } + } + + /** + * Sets the bits associated with the stream's interrupt flag clear register to high + * (either LIFCR or HIFCR). + */ + static void clearInterruptFlags(StreamID s) + { + if (uint32_t(s) > uint32_t(StreamID::STREAM_3)) + { + dmaDef()->HIFCR |= dmaStreamIFCRMasks[uint32_t(s)]; + } + else + { + dmaDef()->LIFCR |= dmaStreamIFCRMasks[uint32_t(s)]; + } + } + + /** + * Interrupt flag, use InterruptStatusMsks enum masks to interpret the interrupt flags + */ + static uint32_t getInterruptFlags(StreamID s) + { + static constexpr uint32_t INTERRUPT_STREAM_BLOCK_SIZE = 6; + int shiftOffset = 0; + if (uint32_t(s) >= uint32_t(StreamID::STREAM_4)) + { + shiftOffset = + INTERRUPT_STREAM_BLOCK_SIZE * (uint32_t(s) - uint32_t(StreamID::STREAM_4)); + if (uint32_t(s) >= uint32_t(StreamID::STREAM_6)) + { + shiftOffset += 4; // 4 bytes of padding in the middle of the register + } + + return (dmaDef()->HISR >> shiftOffset) & INTERRUPT_STATUS_MSK; + } + else + { + shiftOffset = INTERRUPT_STREAM_BLOCK_SIZE * uint32_t(s); + if (uint32_t(s) >= uint32_t(StreamID::STREAM_2)) + { + shiftOffset += 4; + } + return (dmaDef()->LISR >> shiftOffset) & INTERRUPT_STATUS_MSK; + } + } +}; // class DmaHal + +/** + * Hardware abstraction of a DMA stream. + * + * TODO + * - Double buffering mode + * + * @note ID must match the DMA number for the stream + */ +template +class DmaStreamHal : public DmaBase +{ + static inline DMA_Stream_TypeDef *streamPtr() + { + return reinterpret_cast(StreamBase) + uint32_t(S); + } + + static constexpr int32_t DMA_DISABLE_TIMEOUT = 2; + +public: + static_assert( + StreamBase == DMA1_Stream0_BASE || StreamBase == DMA2_Stream0_BASE, + "StreamBase invalid"); + + /** + * Configure the DMA stream. + * + * Stops the DMA stream and writes the new values to its control register. + * + * @param[in] channel Specifies the channel used for the specified stream. + * @param[in] direction Specifies if the data will be transferred from + * memory to peripheral, from memory to memory or from peripheral to + * memory. + * @param[in] periphInc Specifies whether the peripheral address register + * should be incremented or not. + * @param[in] memInc Specifies whether the memory address register should + * be incremented or not. + * @param[in] periphDataAlignment Specifies the Peripheral data width. + * @param[in] memDataAlignment Specifies the Memory data width. + * @param[in] mode Specifies the operation mode of the DMAy Streamx. + * @param[in] priority Specifies the software priority for the DMA ID y + * Stream x. + * @param[in] fifoMode Specifies if the FIFO mode or Direct mode will be + * used for the specified stream. + * @param[in] fifoThreshold Specifies the FIFO threshold level. + * @param[in] memBurst Specifies the Burst transfer configuration for the + * memory transfers. It specifies the amount of data to be transferred + * in a single non interruptible transaction. + * @param[in] periphBurst Specifies the burst transfer configuration for the + * peripheral transfers. It specifies the amount of data to be + * transferred in a single non interruptible transaction. + * + * @note The burst mode is possible only if the address increment mode is enabled. + * @note The circular buffer mode cannot be used if the memory-to-memory + * data transfer is configured on the selected stream. + * @note The Direct mode (FIFO mode disabled) cannot be used if the + * memory-to-memory data transfer is configured on the selected stream + * @note The burst mode is possible only if the address increment mode is enabled. + */ + static void configure( + ChannelSelection channel, + DataTransferDirection direction, + PeripheralIncrementMode periphInc, + MemoryIncrementMode memInc, + PeripheralDataSize peripDataSize, + MemoryDataSize memDataSize, + ControlMode mode, + PriorityLevel priority, + FifoMode fifoMode, + FifoThreshold fifoThreshold, + MemoryBurstTransfer memBurst, + PeripheralBurstTransfer periphBurst) + { + disable(); + + uint32_t tmp = streamPtr()->CR; + + tmp &= ((uint32_t) ~( + DMA_SxCR_CHSEL | DMA_SxCR_MBURST | DMA_SxCR_PBURST | DMA_SxCR_PL | DMA_SxCR_MSIZE | + DMA_SxCR_PSIZE | DMA_SxCR_MINC | DMA_SxCR_PINC | DMA_SxCR_CIRC | DMA_SxCR_DIR | + DMA_SxCR_CT | DMA_SxCR_DBM)); + + tmp |= uint32_t(channel) | uint32_t(direction) | uint32_t(periphInc) | uint32_t(memInc) | + uint32_t(peripDataSize) | uint32_t(memDataSize) | uint32_t(priority) | + uint32_t(mode) | uint32_t(priority); + + if (fifoMode == FifoMode::ENABLED) + { + tmp |= uint32_t(memBurst) | uint32_t(periphBurst); + } + + streamPtr()->CR = tmp; + + tmp = streamPtr()->FCR; + + // Clear direct mode and FIFO threshold bits + tmp &= ~(DMA_SxFCR_DMDIS | DMA_SxFCR_FTH); + + tmp |= uint32_t(fifoMode); + + if (fifoMode == FifoMode::ENABLED) + { + tmp |= uint32_t(fifoThreshold); + + if (memBurst != MemoryBurstTransfer::SINGLE) + { + // Check fifo parameters for compatibility between threshold level and size of + // memory burst + // TODO + } + } + + streamPtr()->FCR = tmp; + } + + static void configureDoubleBufferMode(DoubleBufferMode) + { + // TODO + } + + static void enableInterrupt(Interrupt_t irq) { streamPtr()->CR |= irq.value; } + + static void disableInterrupt(Interrupt_t irq) { streamPtr()->CR &= ~irq.value; } + + /** + * Enable the DMA stream to send/receive. + */ + static void enable() { streamPtr()->CR |= uint32_t(StreamEnableFlag::STREAM_ENABLED); } + + /** + * Disable send/receive on the DMA stream + */ + static bool disable() + { + streamPtr()->CR &= ~uint32_t(StreamEnableFlag::STREAM_ENABLED); + // wait for stream to be stopped + uint32_t start = modm::Clock::now().time_since_epoch().count(); + while ((streamPtr()->CR & uint32_t(StreamEnableFlag::STREAM_ENABLED)) && + modm::Clock::now().time_since_epoch().count() - start > DMA_DISABLE_TIMEOUT) + ; + if (streamPtr()->CR & uint32_t(StreamEnableFlag::STREAM_ENABLED)) + { + return false; + } + return true; + } + + /** + * Get the direction of the data transfer + */ + static DataTransferDirection getDataTransferDirection() + { + return static_cast( + streamPtr()->CR & (DMA_SxCR_DIR_0 | DMA_SxCR_DIR_1)); + } + + /** + * Sets the sources address register based on if the transfer mode is memory to peripheral, + * peripheral to memory, or memory to memory. + */ + static void setSourceAddress(uintptr_t src) + { + if ((streamPtr()->CR & uint32_t(DataTransferDirection::MEM_TO_MEM)) == + uint32_t(DataTransferDirection::MEM_TO_MEM)) + { + streamPtr()->PAR = src; + } + else if ( + (streamPtr()->CR & uint32_t(DataTransferDirection::MEM_TO_PERIPH)) == + uint32_t(DataTransferDirection::MEM_TO_PERIPH)) + { + streamPtr()->M0AR = src; + } + else // peripheral to memory + { + streamPtr()->PAR = src; + } + } + + /** + * Sets the destination address register based on if the transfer mode is memory to peripheral, + * peripheral to memory, or memory to memory. + */ + static void setDestinationAddress(uintptr_t dst) + { + if ((streamPtr()->CR & uint32_t(DataTransferDirection::MEM_TO_MEM)) == + uint32_t(DataTransferDirection::MEM_TO_MEM)) + { + streamPtr()->M0AR = dst; + } + else if ( + (streamPtr()->CR & uint32_t(DataTransferDirection::MEM_TO_PERIPH)) == + uint32_t(DataTransferDirection::MEM_TO_PERIPH)) + { + streamPtr()->PAR = dst; + } + else + { + streamPtr()->M0AR = dst; + } + } + + static void setDataLength(std::size_t length) { streamPtr()->NDTR = length; } +}; // class DmaStreamHal + +} // namespace platform +} // namespace modm + +#endif // MODM_STM32_DMA_HAL_HPP + +#endif diff --git a/src/modm/platform/dma/stm32/f4/module.lb b/src/modm/platform/dma/stm32/f4/module.lb new file mode 100644 index 0000000000..d25929af3d --- /dev/null +++ b/src/modm/platform/dma/stm32/f4/module.lb @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2016-2018, Niklas Hauser +# Copyright (c) 2017, Fabian Greif +# Copyright (c) 2020, Mike Wolfram +# Copyright (c) 2020, Matthew Arnold +# +# This file is part of the modm project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# ----------------------------------------------------------------------------- + +def init(module): + module.name = ":platform:dma" + module.description = "Direct Memory Access (DMA)" + +def prepare(module, options): + device = options[":target"] + if not device.has_driver("dma:stm32*"): + return False + + # FIXME the driver is for L4 only + if device.identifier["family"] not in ["f4"]: + return False + if device.identifier["name"] not in ["05", "15", "07", "17", "27", "37", "29", "39"]: + return False + + module.depends(":cmsis:device", ":platform:rcc") + + return True + +def build(env): + device = env[":target"] + + properties = device.properties + properties["target"] = device.identifier + dma = device.get_driver("dma") + properties["dma"] = dma + + # Get the peripheral supported by DMA from device info and create a list of signals + # (also determines the maximum number of channels per controller) + + signal_names = [] + streams = {} + controller = [] + + # Each DMA instance has a number of streams, and each stream has a number of channels + for streams in dma["streams"]: + streamController = [] + instance = streams["instance"] + for stream in streams["stream"]: + for channel in stream["channel"]: + channelNum = channel["position"] + streamNum = stream["position"] + drivers = [] + for signal in channel["signal"]: + if "instance" in signal: + drivers.append(signal["driver"].capitalize() + signal["instance"]) + else: + drivers.append(signal["driver"].capitalize()) + if "name" in signal: + signal_name = signal["name"].capitalize() + if signal_name not in signal_names: + signal_names.append(signal_name) + print(signal_name) + # signal_names[signal_name] = 1 + # print(signal) + # print(signal_names) + + streamController.append({ + "stream": int(stream["position"]), + "channel": int(channel["position"]), + "drivers": drivers + }) + + controller.append({ + "dma controller": int(streams["instance"]), + "stream ch mapping": streamController + }) + + signal_names = sorted(list(set(signal_names))) + print(signal_names) + properties["dmaSignals"] = signal_names + properties["dmaController"] = controller + + env.substitutions = properties + env.outbasepath = "modm/src/modm/platform/dma" + + env.template("dma_base.hpp.in") + env.template("dma_hal.hpp.in") + env.template("dma.hpp.in") + env.template("dma.cpp.in") + diff --git a/src/modm/platform/dma/stm32/dma.cpp.in b/src/modm/platform/dma/stm32/l4/dma.cpp.in similarity index 100% rename from src/modm/platform/dma/stm32/dma.cpp.in rename to src/modm/platform/dma/stm32/l4/dma.cpp.in diff --git a/src/modm/platform/dma/stm32/dma.hpp.in b/src/modm/platform/dma/stm32/l4/dma.hpp.in similarity index 100% rename from src/modm/platform/dma/stm32/dma.hpp.in rename to src/modm/platform/dma/stm32/l4/dma.hpp.in diff --git a/src/modm/platform/dma/stm32/dma_base.hpp.in b/src/modm/platform/dma/stm32/l4/dma_base.hpp.in similarity index 55% rename from src/modm/platform/dma/stm32/dma_base.hpp.in rename to src/modm/platform/dma/stm32/l4/dma_base.hpp.in index 913ddc70ba..7580709594 100644 --- a/src/modm/platform/dma/stm32/dma_base.hpp.in +++ b/src/modm/platform/dma/stm32/l4/dma_base.hpp.in @@ -21,11 +21,7 @@ #include #include -%% if target["family"] == "f4" - %% set reg_prefix = "DMA_SxCR" -%% elif target["family"] in ["f3", "l4"] - %% set reg_prefix = "DMA_CCR" -%% endif +%% set reg_prefix = "DMA_CCR" namespace modm { @@ -43,78 +39,38 @@ class DmaBase { public: // Enums -%% if target["family"] == "f4" - enum class - Channel : uint32_t - { - Channel0 = 0, - Channel1 = DMA_SxCR_CHSEL_0, - Channel2 = DMA_SxCR_CHSEL_1, - Channel3 = DMA_SxCR_CHSEL_1 | DMA_SxCR_CHSEL_0, - Channel4 = DMA_SxCR_CHSEL_2, - Channel5 = DMA_SxCR_CHSEL_2 | DMA_SxCR_CHSEL_0, - Channel6 = DMA_SxCR_CHSEL_2 | DMA_SxCR_CHSEL_1, - Channel7 = DMA_SxCR_CHSEL_2 | DMA_SxCR_CHSEL_1 | DMA_SxCR_CHSEL_0, - }; - - enum class - MemoryBurstTransfer : uint32_t - { - Single = 0, - Increment4 = DMA_SxCR_MBURST_0, - Increment8 = DMA_SxCR_MBURST_1, - Increment16 = DMA_SxCR_MBURST_1 | DMA_SxCR_MBURST_0, - }; - - enum class - PeripheralBurstTransfer : uint32_t - { - Single = 0, - Increment4 = DMA_SxCR_PBURST_0, - Increment8 = DMA_SxCR_PBURST_1, - Increment16 = DMA_SxCR_PBURST_1 | DMA_SxCR_PBURST_0, - }; - - enum class - FlowControl : uint32_t - { - Dma = 0, - Peripheral = DMA_SxCR_PFCTRL, ///< the peripheral is the flow controller - }; -%% elif target["family"] in ["f3", "l4"] - %% set channel_count = namespace(max_channels = 0) - %% for controller in dmaController - %% if channel_count.max_channels < controller.channels - %% set channel_count.max_channels = controller.channels - %% endif - %% endfor +%% set channel_count = namespace(max_channels = 0) +%% for controller in dmaController + %% if channel_count.max_channels < controller.channels + %% set channel_count.max_channels = controller.channels + %% endif +%% endfor enum class Channel { - %% for channel in range(1, channel_count.max_channels + 1) +%% for channel in range(1, channel_count.max_channels + 1) Channel{{ channel }}{% if channel == 1 %} = 0{% endif %}, - %% endfor +%% endfor }; - %% if target["family"] == "l4" - %% set request_count = namespace(max_requests = 0) - %% for channels in dma["channels"] - %% for channel in channels.channel - %% for request in channel.request - %% if request_count.max_requests < request.position | int - %% set request_count.max_requests = request.position | int - %% endif - %% endfor +%% if target["family"] == "l4" +%% set request_count = namespace(max_requests = 0) +%% for channels in dma["channels"] + %% for channel in channels.channel + %% for request in channel.request + %% if request_count.max_requests < request.position | int + %% set request_count.max_requests = request.position | int + %% endif %% endfor %% endfor +%% endfor enum class Request { - %% for request in range(0, request_count.max_requests + 1) +%% for request in range(0, request_count.max_requests + 1) Request{{ request }}{% if request == 0 %} = 0{% endif %}, - %% endfor +%% endfor }; - %% endif %% endif enum class @@ -174,21 +130,12 @@ public: enum class DataTransferDirection : uint32_t { -%% if target["family"] == "f4" - /// Source: DMA_SxPAR; Sink: DMA_SxM0AR - PeripheralToMemory = 0, - /// Source: DMA_SxM0AR; Sink: DMA_SxPAR - MemoryToPeripheral = DMA_SxCR_DIR_0, - /// Source: DMA_SxPAR; Sink: DMA_SxM0AR - MemoryToMemory = DMA_SxCR_DIR_1, -%% elif target["family"] in ["f3", "l4"] /// Source: DMA_CPARx; Sink: DMA_CMARx PeripheralToMemory = 0, /// Source: DMA_CMARx; Sink: DMA_CPARx MemoryToPeripheral = DMA_CCR_DIR, /// Source: DMA_CPARx; Sink: DMA_CMARx MemoryToMemory = DMA_CCR_MEM2MEM, -%% endif }; /** @@ -203,23 +150,6 @@ public: }; protected: -%% if target["family"] == "f4" - static constexpr uint32_t memoryMask = - DMA_SxCR_MBURST_1 | DMA_SxCR_MBURST_0 | // MemoryBurstTransfer - DMA_SxCR_MSIZE_0 | DMA_SxCR_MSIZE_1 | // MemoryDataSize - DMA_SxCR_MINC | // MemoryIncrementMode - DMA_SxCR_DIR_0 | DMA_SxCR_DIR_1; // DataTransferDirection - static constexpr uint32_t peripheralMask = - DMA_SxCR_PBURST_1 | DMA_SxCR_PBURST_0 | // PeripheralBurstTransfer - DMA_SxCR_PSIZE_0 | DMA_SxCR_PSIZE_1 | // PeripheralDataSize - DMA_SxCR_PINC | // PeripheralIncrementMode - DMA_SxCR_DIR_0 | DMA_SxCR_DIR_1; // DataTransferDirection - static constexpr uint32_t configmask = - DMA_SxCR_CHSEL_2 | DMA_SxCR_CHSEL_1 | DMA_SxCR_CHSEL_0 | // Channel - DMA_SxCR_PL_1 | DMA_SxCR_PL_0 | // Priority - DMA_SxCR_CIRC | // CircularMode - DMA_SxCR_PFCTRL; // FlowControl -%% elif target["family"] in ["f3", "l4"] static constexpr uint32_t memoryMask = DMA_CCR_MSIZE_0 | DMA_CCR_MSIZE_1 | // MemoryDataSize DMA_CCR_MINC | // MemoryIncrementMode @@ -231,7 +161,6 @@ protected: static constexpr uint32_t configmask = DMA_CCR_CIRC | // CircularMode DMA_CCR_PL_1 | DMA_CCR_PL_0; // Priority -%% endif enum class Interrupt { Global = 0x01, diff --git a/src/modm/platform/dma/stm32/dma_hal.hpp.in b/src/modm/platform/dma/stm32/l4/dma_hal.hpp.in similarity index 100% rename from src/modm/platform/dma/stm32/dma_hal.hpp.in rename to src/modm/platform/dma/stm32/l4/dma_hal.hpp.in diff --git a/src/modm/platform/dma/stm32/dma_hal_impl.hpp.in b/src/modm/platform/dma/stm32/l4/dma_hal_impl.hpp.in similarity index 82% rename from src/modm/platform/dma/stm32/dma_hal_impl.hpp.in rename to src/modm/platform/dma/stm32/l4/dma_hal_impl.hpp.in index 74368fd20a..6c41dbfb60 100644 --- a/src/modm/platform/dma/stm32/dma_hal_impl.hpp.in +++ b/src/modm/platform/dma/stm32/l4/dma_hal_impl.hpp.in @@ -21,11 +21,7 @@ modm::platform::DmaChannelHal::start() { DMA_Channel_TypeDef *Base = (DMA_Channel_TypeDef *) CHANNEL_BASE; -%% if target["family"] == "f4" - Base->CR |= DMA_SxCR_EN; -%% else Base->CCR |= DMA_CCR_EN; -%% endif } template @@ -34,13 +30,8 @@ modm::platform::DmaChannelHal::stop() { DMA_Channel_TypeDef *Base = (DMA_Channel_TypeDef *) CHANNEL_BASE; -%% if target["family"] == "f4" - Base->CR &= ~DMA_SxCR_EN; - while (Base->SCR & DMA_SxCR_EN); // wait for stream to be stopped -%% else Base->CCR &= ~DMA_CCR_EN; while (Base->CCR & DMA_CCR_EN); // wait for stream to be stopped -%% endif } template @@ -50,9 +41,5 @@ modm::platform::DmaChannelHal::getDataTransferDirection DMA_Channel_TypeDef *Base = (DMA_Channel_TypeDef *) CHANNEL_BASE; return static_cast( -%% if target["family"] == "f4" - Base->CR & (DMA_SxCR_DIR_0 | DMA_SxCR_DIR_1)); -%% else Base->CCR & (DMA_CCR_MEM2MEM | DMA_CCR_DIR)); -%% endif } diff --git a/src/modm/platform/dma/stm32/module.lb b/src/modm/platform/dma/stm32/l4/module.lb similarity index 100% rename from src/modm/platform/dma/stm32/module.lb rename to src/modm/platform/dma/stm32/l4/module.lb diff --git a/src/modm/platform/spi/stm32/module.lb b/src/modm/platform/spi/stm32/module.lb index 783b3eb8a3..c8805af74b 100644 --- a/src/modm/platform/spi/stm32/module.lb +++ b/src/modm/platform/spi/stm32/module.lb @@ -30,6 +30,7 @@ class Instance(Module): def prepare(self, module, options): module.depends(":platform:spi") + self.device = options[":target"] return True def build(self, env): @@ -43,7 +44,7 @@ class Instance(Module): env.template("spi_hal_impl.hpp.in", "spi_hal_{}_impl.hpp".format(self.instance)) env.template("spi_master.hpp.in", "spi_master_{}.hpp".format(self.instance)) env.template("spi_master.cpp.in", "spi_master_{}.cpp".format(self.instance)) - if env.has_module(":platform:dma"): + if env.has_module(":platform:dma") and self.device.identifier["family"] in ["l4"]: env.template("spi_master_dma.hpp.in", "spi_master_{}_dma.hpp".format(self.instance)) env.template("spi_master_dma_impl.hpp.in", "spi_master_{}_dma_impl.hpp".format(self.instance)) diff --git a/src/modm/platform/uart/stm32/module.lb b/src/modm/platform/uart/stm32/module.lb index fe2ee2aeec..3b777884bc 100644 --- a/src/modm/platform/uart/stm32/module.lb +++ b/src/modm/platform/uart/stm32/module.lb @@ -42,6 +42,11 @@ class Instance(Module): description="", minimum=1, maximum=2 ** 16 - 2, default=16)) + module.add_option( + BooleanOption( + name="usedma", + description="", + default=False)) return True @@ -58,8 +63,12 @@ class Instance(Module): env.template("uart_hal.hpp.in", "uart_hal_{}.hpp".format(self.instance)) env.template("uart_hal_impl.hpp.in", "uart_hal_{}_impl.hpp".format(self.instance)) - env.template("uart.hpp.in", "uart_{}.hpp".format(self.instance)) - env.template("uart.cpp.in", "uart_{}.cpp".format(self.instance)) + if env["usedma"]: + env.template("uart_dma.hpp.in", "uart_{}.hpp".format(self.instance)) + env.template("uart_dma.cpp.in", "uart_{}.cpp".format(self.instance)) + else: + env.template("uart.hpp.in", "uart_{}.hpp".format(self.instance)) + env.template("uart.cpp.in", "uart_{}.cpp".format(self.instance)) props["instances"].append(self.instance) diff --git a/src/modm/platform/uart/stm32/uart_base.hpp.in b/src/modm/platform/uart/stm32/uart_base.hpp.in index 917d6309d1..81c343aa84 100644 --- a/src/modm/platform/uart/stm32/uart_base.hpp.in +++ b/src/modm/platform/uart/stm32/uart_base.hpp.in @@ -68,6 +68,8 @@ public: TxComplete = USART_CR1_TCIE, /// Call interrupt when char received (RXNE) or overrun occurred (ORE) RxNotEmpty = USART_CR1_RXNEIE, + /// Set if idle flag set. + RxIdle = USART_CR1_IDLEIE, }; MODM_FLAGS32(Interrupt); diff --git a/src/modm/platform/uart/stm32/uart_dma.cpp.in b/src/modm/platform/uart/stm32/uart_dma.cpp.in new file mode 100644 index 0000000000..0d178f7214 --- /dev/null +++ b/src/modm/platform/uart/stm32/uart_dma.cpp.in @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2020 Matthew Arnold + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#include "uart_1.hpp" + +MODM_ISR(USART1) { modm::platform::uart1IrqHandler(); } diff --git a/src/modm/platform/uart/stm32/uart_dma.hpp.in b/src/modm/platform/uart/stm32/uart_dma.hpp.in new file mode 100644 index 0000000000..6d4846cd18 --- /dev/null +++ b/src/modm/platform/uart/stm32/uart_dma.hpp.in @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2020 Matthew Arnold + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#ifndef MODM_STM32_UART_1_DMA_HPP +#define MODM_STM32_UART_1_DMA_HPP + +#include +#include +#include "uart_hal_1.hpp" + +#include + +namespace modm +{ +namespace platform +{ +/** + * Interrupt handler callback for USART1_isr. + */ +__attribute__((weak)) void uart1IrqHandler(void); +/** + * Interrupt handlers for USART1 DMA TX and RX streams. + */ +__attribute__((weak)) void uart1DmaTxComplete(void); +__attribute__((weak)) void uart1DmaTxError(void); +__attribute__((weak)) void uart1DmaRxComplete(void); +__attribute__((weak)) void uart1DmaRxError(void); + +template < + typename DmaStreamTx, + DmaBase::ChannelSelection ChannelIdRx, + typename DmaStreamRx, + DmaBase::ChannelSelection ChannelIdTx> +class Usart1Dma : public modm::platform::UartBase, public modm::Uart +{ +private: + static_assert( + DmaRequestMapping::validateRequestMapping< + DmaStreamRx::DMA_ID, + ChannelIdRx, + DmaStreamRx::STREAM_ID, + DmaRequestMapping::Peripheral::USART1_RX>(), + "dma usart1 tx channel or stream selection invalid"); + + static_assert( + DmaRequestMapping::validateRequestMapping< + DmaStreamTx::DMA_ID, + ChannelIdTx, + DmaStreamTx::STREAM_ID, + DmaRequestMapping::Peripheral::USART1_TX>(), + "dma usart1 tx channel or stream selection invalid"); + +public: + template