diff --git a/src/modm/platform/dma/stm32/dma.cpp.in b/src/modm/platform/dma/stm32/dma.cpp.in new file mode 100644 index 0000000000..66a53b2b19 --- /dev/null +++ b/src/modm/platform/dma/stm32/dma.cpp.in @@ -0,0 +1,27 @@ +/* + * 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/. + */ +// ---------------------------------------------------------------------------- + +#include "dma.hpp" + +%% for channels in dma["channels"] +/* + * IRQ handler for DMA{{ channels.instance }} + */ +%% for channel in channels.channel + +MODM_ISR(DMA{{ channels.instance }}_Channel{{ channel.position }}) +{ + using namespace modm::platform; + Dma{{ channels.instance }}::Channel::interruptHandler(); +} + +%% endfor +%% endfor diff --git a/src/modm/platform/dma/stm32/dma.hpp.in b/src/modm/platform/dma/stm32/dma.hpp.in index 39068a1b9b..a953718ecd 100644 --- a/src/modm/platform/dma/stm32/dma.hpp.in +++ b/src/modm/platform/dma/stm32/dma.hpp.in @@ -1,6 +1,7 @@ /* * 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. * @@ -10,22 +11,12 @@ */ // ---------------------------------------------------------------------------- -#ifndef MODM_STM32_DMA{{ id }}_HPP -#define MODM_STM32_DMA{{ id }}_HPP +#ifndef MODM_STM32_DMA_HPP +#define MODM_STM32_DMA_HPP #include #include "../device.hpp" -#include "dma_base.hpp" - -%% if target["family"] == "f4" - %% set streams = range(0,8) -%% elif target["family"] == "f3" - %% if id == 1 - %% set streams = range(1,7) - %% elif id == 2 - %% set streams = range(1,5) - %% endif -%% endif +#include "dma_hal.hpp" namespace modm { @@ -34,107 +25,358 @@ namespace platform { /** - * DMA + * DMA controller * * Does not support - among other things - double buffering or FIFO usage * - * @author Kevin Laeufer - * @ingroup modm_platform_dma modm_platform_dma_{{id}} + * @author Mike Wolfram + * @ingroup modm_platform_dma */ -class Dma{{ id }} +template +class DmaController : public DmaBase { + static_assert(ID >= 1 and ID <= {{ dma.instance | length }}, "invalid DMA controller ID"); + public: - static inline void - enable(); + /** + * Enable the DMA controller in the RCC + */ + static void + enable() + { + if constexpr (ID == 1) + Rcc::enable(); + else + Rcc::enable(); + } + /** + * Disable the DMA controller in the RCC + */ + static void + disable() + { + if constexpr (ID == 1) + Rcc::disable(); + else + Rcc::disable(); + } - static inline void - disable(); + /** + * Class representing a DMA channel/stream + */ + template + class Channel + { + static_assert( +%% for controller in dmaController +%% if controller["instance"] > 1 + or +%% endif + (ID == {{ controller["instance"] }} and + ChannelID >= DmaBase::Channel::Channel1 and + ChannelID <= DmaBase::Channel::Channel{{ controller["channels"] }}) +%% endfor + , "invalid Channel ID for that DMA controller" + ); + using ControlHal = DmaHal; + + static constexpr uint32_t CHANNEL_BASE { ControlHal::CHANNEL_BASE + + uint32_t(ChannelID) * ControlHal::CHANNEL_2_CHANNEL }; + + using ChannelHal = DmaChannelHal; -public: -%% for stream in streams - class Stream{{ stream }} : public DmaBase - { public: - %% set pointer_types = [8, 16, 32] - %% for type in pointer_types - /// will disable the stream - static inline void - setMemorySource(uint{{type}}_t* address, - MemoryIncrementMode inc = MemoryIncrementMode::Increment - %% if target["family"] == "f4" - , MemoryBurstTransfer transfer = MemoryBurstTransfer::Single); - %% elif target["family"] == "f3" - ); - %% endif - - /// will disable the stream - static inline void - setPeripheralSource(uint{{type}}_t* address, - PeripheralIncrementMode inc = PeripheralIncrementMode::Fixed - %% if target["family"] == "f4" - , PeripheralBurstTransfer transfer = PeripheralBurstTransfer::Single); - %% elif target["family"] == "f3" - ); - %% endif - %% endfor + /** + * Configure the DMA channel + * + * Stops the DMA channel and writes the new values to its control register. + * + * @param[in] direction Direction of the DMA channel + * @param[in] memoryDataSize Size of data in memory (byte, halfword, word) + * @param[in] peripheralDataSize Size of data in peripheral (byte, halfword, word) + * @param[in] memoryIncrement Defines whether the memory address is incremented + * after a transfer completed + * @param[in] peripheralIncrement Defines whether the peripheral address is + * incremented after a transfer completed + * @param[in] priority Priority of the DMA channel + * @param[in] circularMode Transfer data in circular mode? + */ + static void + configure(DataTransferDirection direction, MemoryDataSize memoryDataSize, + PeripheralDataSize peripheralDataSize, + MemoryIncrementMode memoryIncrement, + PeripheralIncrementMode peripheralIncrement, + Priority priority = Priority::Medium, + CircularMode circularMode = CircularMode::Disabled) + { + ChannelHal::configure(direction, memoryDataSize, peripheralDataSize, + memoryIncrement, peripheralIncrement, priority, circularMode); + } - %% for type in pointer_types - /// will disable the stream - static inline void - setMemoryDestination(uint{{type}}_t* address, - MemoryIncrementMode inc = MemoryIncrementMode::Increment - %% if target["family"] == "f4" - , MemoryBurstTransfer transfer = MemoryBurstTransfer::Single); - %% elif target["family"] == "f3" - ); - %% endif - - /// will disable the stream - static inline void - setPeripheralDestination(uint{{type}}_t* address, - PeripheralIncrementMode inc = PeripheralIncrementMode::Fixed - %% if target["family"] == "f4" - , PeripheralBurstTransfer transfer = PeripheralBurstTransfer::Single); - %% elif target["family"] == "f3" - ); - %% endif - %% endfor + /** + * Start the transfer of the DMA channel + */ + static void + start() + { + ControlHal::clearInterruptFlags(uint32_t(Interrupt::Global) << (uint32_t(ChannelID) * 4)); + ChannelHal::start(); + } + /** + * Stop a DMA channel transfer + */ + static void + stop() + { + ChannelHal::stop(); + } + + /** + * Get the direction of the data transfer + */ + static DataTransferDirection + getDataTransferDirection() + { + return ChannelHal::getDataTransferDirection(); + } + + /** + * 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 + setMemoryAddress(uintptr_t address) + { + ChannelHal::setMemoryAddress(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 + setPeripheralAddress(uintptr_t address) + { + ChannelHal::setPeripheralAddress(address); + } + + /** + * Enable/disable memory increment + * + * When enabled, the memory address is incremented by the size of the data + * (e.g. 1 for byte transfers, 4 for word transfers) after the transfer + * completed. + * + * @param[in] increment Enable/disable + */ + static void + setMemoryIncrementMode(bool increment) + { + ChannelHal::setMemoryIncrementMode(increment); + } + /** + * Enable/disable peripheral increment + * + * When enabled, the peripheral address is incremented by the size of the data + * (e.g. 1 for byte transfers, 4 for word transfers) after the transfer + * completed. + * + * @param[in] increment Enable/disable + */ + static void + setPeripheralIncrementMode(bool increment) + { + ChannelHal::setPeripheralIncrementMode(increment); + } + + /** + * Set the length of data to be transfered + */ + static void + setDataLength(std::size_t length) + { + ChannelHal::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; + } + /** + * Set the peripheral that operates the channel + */ + template + static void + setPeripheralRequest() + { + DMA_Request_TypeDef *DMA_REQ = reinterpret_cast(ControlHal::DMA_CSEL); + DMA_REQ->CSELR &= ~(0x0f << (uint32_t(ChannelID) * 4)); + DMA_REQ->CSELR |= uint32_t(dmaRequest) << (uint32_t(ChannelID) * 4); + } + + /** + * 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() + { + static const uint32_t TC_Flag { + uint32_t(Interrupt::TransferComplete) << (uint32_t(ChannelID) * 4) + }; + static const uint32_t TE_Flag { + uint32_t(Interrupt::Error) << (uint32_t(ChannelID) * 4) + }; + + auto isr { ControlHal::getInterruptFlags() }; + if (isr & TE_Flag) { + disable(); + if (transferError) + transferError(); + } + if ((isr & TC_Flag) and transferComplete) + transferComplete(); + + ControlHal::clearInterruptFlags(uint32_t(Interrupt::Global) << (uint32_t(ChannelID) * 4)); + } + + /** + * 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(ChannelID)], priority); + NVIC_EnableIRQ(DmaBase::Nvic::DmaIrqs[uint32_t(ChannelID)]); + } + /** + * Disable the IRQ vector of the channel + */ + static void + disableInterruptVector() + { + NVIC_DisableIRQ(DmaBase::Nvic::DmaIrqs[uint32_t(ChannelID)]); + } - static inline void - stop(); - - /// will disable the stream - static inline void - configure( - %% if target["family"] == "f4" - Channel channel, - %% endif - uint16_t sample_length, - Priority priority = Priority::Medium, - CircularMode circular = CircularMode::Disabled - %% if target["family"] == "f4" - , FlowControl flow = FlowControl::Dma); - %% elif target["family"] == "f3" - ); - %% endif - - static inline void - start(); - - static inline bool - isFinished(); - - static inline DataTransferDirection - getDataTransferDirection(); + /** + * Enable the specified interrupt of the channel + */ + static void + enableInterrupt(Interrupt_t irq) + { + ChannelHal::enableInterrupt(irq); + } + /** + * Disable the specified interrupt of the channel + */ + static void + disableInterrupt(Interrupt_t irq) + { + ChannelHal::disableInterrupt(irq); + } + + /** + * Helper to verify that the selected channel supports the selected + * hardware and provides the Request to be set in setPeripheralRequest(). + */ + template + struct RequestMapping { + }; + + private: + static inline DmaBase::IrqHandler transferError { nullptr }; + static inline DmaBase::IrqHandler transferComplete { nullptr }; }; +}; + +/* + * Derive DMA controller classes for convenience. Every derived class defines + * the channels available on that controller. + */ +%% for channels in dma["channels"] +class Dma{{ channels.instance }}: public DmaController<{{ channels.instance }}> +{ +public: + %% for channel in channels.channel + using Channel{{ channel.position }} = DmaController<{{ channels.instance }}>::Channel; + %% endfor +}; %% endfor + + +/* + * Specialization of the RequestMapping. For all hardware supported by DMA the + * RequestMapping structure defines the channel and the Request. It can be used + * by hardware classes to verify that the provided channel is valid and to + * get the value to set in setPeripheralRequest(). + * + * Example: + * template + * class SpiMaster1_Dma : public SpiMaster1 + * { + * using RxChannel = typename DmaRx::template RequestMapping::Channel; + * using TxChannel = typename DmaTx::template RequestMapping::Channel; + * static constexpr DmaBase::Request RxRequest = DmaRx::template RequestMapping::Request; + * static constexpr DmaBase::Request TxRequest = DmaTx::template RequestMapping::Request; + * + * ... + * }; + */ +%% for channels in dma["channels"] + %% for channel in channels.channel + %% for request in channel.request + %% for signal in request.signal + %% set peripheral = signal.driver.capitalize() + %% if signal.instance is defined + %% set peripheral = peripheral ~ signal.instance + %% else + %% if peripheral not in ["Quadspi", "Aes", "Dcmi"] + %% set peripheral = peripheral ~ 1 + %% endif + %% endif +template <> +template <> +template <> +struct DmaController<{{ channels.instance }}>::Channel::RequestMapping +{ + using Channel = DmaController<{{ channels.instance }}>::Channel; + static constexpr DmaBase::Request Request = DmaBase::Request::Request{{ request.position }}; }; + %% endfor + %% endfor + %% endfor +%% endfor + } // namespace platform } // namespace modm -#include "dma_{{ id }}_impl.hpp" - -#endif // MODM_STM32_DMA{{ id }}_HPP +#endif // MODM_STM32_DMA_HPP diff --git a/src/modm/platform/dma/stm32/dma_base.hpp.in b/src/modm/platform/dma/stm32/dma_base.hpp.in index 0a89008f73..913ddc70ba 100644 --- a/src/modm/platform/dma/stm32/dma_base.hpp.in +++ b/src/modm/platform/dma/stm32/dma_base.hpp.in @@ -1,6 +1,7 @@ /* * 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. * @@ -10,15 +11,19 @@ */ // ---------------------------------------------------------------------------- -#ifndef MODM_STM32F3_DMA_BASE_HPP -#define MODM_STM32F3_DMA_BASE_HPP +#ifndef MODM_STM32_DMA_BASE_HPP +#define MODM_STM32_DMA_BASE_HPP #include #include "../device.hpp" +#include +#include +#include + %% if target["family"] == "f4" %% set reg_prefix = "DMA_SxCR" -%% elif target["family"] == "f3" +%% elif target["family"] in ["f3", "l4"] %% set reg_prefix = "DMA_CCR" %% endif @@ -27,11 +32,11 @@ namespace modm namespace platform { - /** * DMA * * @author Kevin Laeufer + * @author Mike Wolfram * @ingroup modm_platform_dma */ class DmaBase @@ -76,6 +81,40 @@ public: 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 + enum class + Channel + { + %% for channel in range(1, channel_count.max_channels + 1) + Channel{{ channel }}{% if channel == 1 %} = 0{% 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) + Request{{ request }}{% if request == 0 %} = 0{% endif %}, + %% endfor + }; + %% endif %% endif enum class @@ -142,7 +181,7 @@ public: MemoryToPeripheral = DMA_SxCR_DIR_0, /// Source: DMA_SxPAR; Sink: DMA_SxM0AR MemoryToMemory = DMA_SxCR_DIR_1, -%% elif target["family"] == "f3" +%% elif target["family"] in ["f3", "l4"] /// Source: DMA_CPARx; Sink: DMA_CMARx PeripheralToMemory = 0, /// Source: DMA_CMARx; Sink: DMA_CPARx @@ -152,6 +191,17 @@ public: %% endif }; + /** + * Peripheral signals that can be used in DMA channels + */ + enum class + Signal : uint8_t { + NoSignal, +%% for signal in dmaSignals + {{ signal }}, +%% endfor + }; + protected: %% if target["family"] == "f4" static constexpr uint32_t memoryMask = @@ -169,7 +219,7 @@ protected: DMA_SxCR_PL_1 | DMA_SxCR_PL_0 | // Priority DMA_SxCR_CIRC | // CircularMode DMA_SxCR_PFCTRL; // FlowControl -%% elif target["family"] == "f3" +%% elif target["family"] in ["f3", "l4"] static constexpr uint32_t memoryMask = DMA_CCR_MSIZE_0 | DMA_CCR_MSIZE_1 | // MemoryDataSize DMA_CCR_MINC | // MemoryIncrementMode @@ -182,10 +232,36 @@ protected: DMA_CCR_CIRC | // CircularMode DMA_CCR_PL_1 | DMA_CCR_PL_0; // Priority %% endif + + enum class Interrupt { + Global = 0x01, + TransferComplete = 0x02, + HalfTransferComplete = 0x04, + Error = 0x08, + All = 0x0f, + }; + MODM_FLAGS32(Interrupt); + + using IrqHandler = void (*)(void); + + template + struct Nvic; +}; + +%% for channels in dma["channels"] +template <> +struct DmaBase::Nvic<{{ channels.instance }}> +{ + static constexpr IRQn_Type DmaIrqs[] { + %% for channel in channels.channel + DMA{{ channels.instance }}_Channel{{ channel.position }}_IRQn, + %% endfor + }; }; +%% endfor } // namespace platform } // namespace modm -#endif // MODM_STM32F3_DMA_BASE_HPP +#endif // MODM_STM32_DMA_BASE_HPP diff --git a/src/modm/platform/dma/stm32/dma_hal.hpp.in b/src/modm/platform/dma/stm32/dma_hal.hpp.in new file mode 100644 index 0000000000..f4aa5dd63f --- /dev/null +++ b/src/modm/platform/dma/stm32/dma_hal.hpp.in @@ -0,0 +1,263 @@ +/* + * 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_HAL_HPP +#define MODM_STM32_DMA_HAL_HPP + +#include + +#include "dma_base.hpp" + +namespace modm +{ + +namespace platform +{ + +/** + * Hardware abstraction of DMA controller + * + * @tparam ID The number of the DMA controller + * + * @author Mike Wolfram + * @ingroup modm_platform_dma + */ +template +class DmaHal : public DmaBase +{ + static_assert(ID >= 1 and ID <= {{ dma.instance | length }}, + "invalid DMA controller ID"); + + /** + * Get the base address of the DMA controller registers + * + * @tparam id The number of the DMA controller + */ + template + static constexpr uint32_t getBaseAddress() { + if (id == 1) + return DMA1_BASE; + else + return DMA2_BASE; + } + /** + * Get the base address of the DMA channel reigsters + * + * @tparam id The number of the DMA channel + */ + template + static constexpr uint32_t getChannelBaseAddress() { + if (id == 1) + return DMA1_Channel1_BASE; + else + return DMA2_Channel1_BASE; + } + /** + * Get the address of the channel selection register + * + * @tparam id The number of the DMA controller + */ + template + static constexpr uint32_t getCselAddress() { + if (id == 1) + return DMA1_CSELR_BASE; + else + return DMA2_CSELR_BASE; + } + +public: + /// DMA base register address + static constexpr uint32_t DMA_BASE { getBaseAddress() }; + /// DMA channel selection register address + static constexpr uint32_t DMA_CSEL { getCselAddress() }; + /// DMA channel base register address + static constexpr uint32_t CHANNEL_BASE { getChannelBaseAddress() }; + /// Register offset from channel to channel + static constexpr uint32_t CHANNEL_2_CHANNEL { 0x14 }; + + static void + clearInterruptFlags(uint32_t flags) + { + DMA_TypeDef *DMA = reinterpret_cast(DMA_BASE); + DMA->IFCR |= flags; + } + + static uint32_t + getInterruptFlags() + { + DMA_TypeDef *DMA = reinterpret_cast(DMA_BASE); + return DMA->ISR; + } +}; + +/** + * Hardware abstraction layer of a DMA channel + * + * @tparam ChannelID the ID of the channel + * @tparam CHANNEL_BASE base address of the channel registers + * + * @author Mike Wolfram + * @ingroup modm_platform_dma + */ +template +class DmaChannelHal : public DmaBase +{ +public: + /** + * Configure the DMA channel (HAL) + * + * Stops the DMA channel and writes the new values to its control register. + * + * @param[in] direction Direction of the DMA channel + * @param[in] memoryDataSize Size of data in memory (byte, halfword, word) + * @param[in] peripheralDataSize Size of data in peripheral (byte, halfword, word) + * @param[in] memoryIncrement Defines whether the memory address is incremented + * after a transfer completed + * @param[in] peripheralIncrement Defines whether the peripheral address is + * incremented after a transfer completed + * @param[in] priority Priority of the DMA channel + * @param[in] circularMode Transfer data in circular mode? + */ + static void + configure(DataTransferDirection direction, MemoryDataSize memoryDataSize, + PeripheralDataSize peripheralDataSize, + MemoryIncrementMode memoryIncrement, + PeripheralIncrementMode peripheralIncrement, + Priority priority = Priority::Medium, + CircularMode circularMode = CircularMode::Disabled) + { + stop(); + + DMA_Channel_TypeDef *Base = (DMA_Channel_TypeDef *) CHANNEL_BASE; + Base->CCR = uint32_t(direction) | uint32_t(memoryDataSize) | + uint32_t(peripheralDataSize) | uint32_t(memoryIncrement) | + uint32_t(peripheralIncrement) | uint32_t(priority) | + uint32_t(circularMode); + } + + /** + * Start the transfer of the DMA channel + */ + static void + start(); + /** + * Stop a DMA channel transfer + */ + static void + stop(); + + /** + * Get the direction of the data transfer + */ + static DataTransferDirection + getDataTransferDirection(); + + /** + * 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 + setMemoryAddress(uintptr_t address) + { + DMA_Channel_TypeDef *Base = (DMA_Channel_TypeDef *) CHANNEL_BASE; + Base->CMAR = 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 + setPeripheralAddress(uintptr_t address) + { + DMA_Channel_TypeDef *Base = (DMA_Channel_TypeDef *) CHANNEL_BASE; + Base->CPAR = address; + } + + /** + * Enable/disable memory increment + * + * When enabled, the memory address is incremented by the size of the data + * (e.g. 1 for byte transfers, 4 for word transfers) after the transfer + * completed. + * + * @param[in] increment Enable/disable + */ + static void + setMemoryIncrementMode(bool increment) + { + DMA_Channel_TypeDef *Base = (DMA_Channel_TypeDef *) CHANNEL_BASE; + if (increment) + Base->CCR |= uint32_t(MemoryIncrementMode::Increment); + else + Base->CCR &= ~uint32_t(MemoryIncrementMode::Increment); + } + + /** + * Enable/disable peripheral increment + * + * When enabled, the peripheral address is incremented by the size of the data + * (e.g. 1 for byte transfers, 4 for word transfers) after the transfer + * completed. + * + * @param[in] increment Enable/disable + */ + static void + setPeripheralIncrementMode(bool increment) + { + DMA_Channel_TypeDef *Base = (DMA_Channel_TypeDef *) CHANNEL_BASE; + if (increment) + Base->CCR |= uint32_t(PeripheralIncrementMode::Increment); + else + Base->CCR &= ~uint32_t(PeripheralIncrementMode::Increment); + } + + /** + * Set length of data to transfer + */ + static void + setDataLength(std::size_t length) + { + DMA_Channel_TypeDef *Base = (DMA_Channel_TypeDef *) CHANNEL_BASE; + Base->CNDTR = length; + } + + /** + * Enable IRQ of this DMA channel (e.g. transfer complete or error) + */ + static void + enableInterrupt(Interrupt_t irq) + { + DMA_Channel_TypeDef *Base = (DMA_Channel_TypeDef *) CHANNEL_BASE; + Base->CCR |= irq.value; + } + /** + * Disable IRQ of this DMA channel (e.g. transfer complete or error) + */ + static void + disableInterrupt(Interrupt_t irq) + { + DMA_Channel_TypeDef *Base = (DMA_Channel_TypeDef *) CHANNEL_BASE; + Base->CCR &= ~(irq.value); + } +}; + +} // namespace platform +} // namespace modm + +#include "dma_hal_impl.hpp" + +#endif // MODM_STM32_DMA_HAL_HPP diff --git a/src/modm/platform/dma/stm32/dma_hal_impl.hpp.in b/src/modm/platform/dma/stm32/dma_hal_impl.hpp.in new file mode 100644 index 0000000000..74368fd20a --- /dev/null +++ b/src/modm/platform/dma/stm32/dma_hal_impl.hpp.in @@ -0,0 +1,58 @@ +/* + * 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_HAL_HPP +# error "Don't include this file directly, use 'dma.hpp' instead!" +#endif +#include + +template +void +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 +void +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 +modm::platform::DmaBase::DataTransferDirection +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/dma_impl.hpp.in b/src/modm/platform/dma/stm32/dma_impl.hpp.in deleted file mode 100644 index 322e0f5ebf..0000000000 --- a/src/modm/platform/dma/stm32/dma_impl.hpp.in +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright (c) 2014, Kevin Läufer - * Copyright (c) 2014, 2016-2017, Niklas Hauser - * - * 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{{ id }}_HPP -# error "Don't include this file directly, use 'dma_{{ id }}.hpp' instead!" -#endif -#include - -%% if target["family"] == "f4" - %% set streams = range(0,8) -%% elif target["family"] == "f3" - %% if id == 1 - %% set streams = range(1,7) - %% elif id == 2 - %% set streams = range(1,5) - %% endif -%% endif - -void -modm::platform::Dma{{ id }}::enable() -{ - Rcc::enable(); -} - -void -modm::platform::Dma{{ id }}::disable() -{ - Rcc::disable(); -} - - -%% for stream_id in streams - -%% if target["family"] == "f4" - %% set stream = "DMA" ~ id ~ "_Stream" ~ stream_id - %% set reg = stream ~ "->CR" - %% set mem = stream ~ "->M0AR" - %% set per = stream ~ "->PAR" - %% set length = stream ~ "->NDTR" - %% set prefix = "DMA_SxCR" -%% elif target["family"] == "f3" - %% set channel = "DMA" ~ id ~ "_Channel" ~ stream_id - %% set reg = channel ~ "->CCR" - %% set mem = channel ~ "->CMAR" - %% set per = channel ~ "->CPAR" - %% set length = channel ~ "->CNDTR" - %% set prefix = "DMA_CCR" -%% endif - -%% set pointer_types = [8, 16, 32] -%% for type in pointer_types -void -modm::platform::Dma{{ id }}::Stream{{ stream_id }}::setMemorySource(uint{{type}}_t* address, - MemoryIncrementMode inc -%% if target["family"] == "f4" -, MemoryBurstTransfer transfer) -%% else -) -%% endif -{ - //stop(); - static constexpr MemoryDataSize size = MemoryDataSize::Bit{{type}}; - // a memory source could mean that either a memory-to-peripheral or a - // memory-to-memory transfer is intended - // we'll use the current transfer mode to determine what to do - DataTransferDirection dir; - if(getDataTransferDirection() == DataTransferDirection::MemoryToPeripheral) { - dir = DataTransferDirection::MemoryToPeripheral; - {{ mem }} = reinterpret_cast(address); - - } else { - dir = DataTransferDirection::MemoryToMemory; - {{ per }} = reinterpret_cast(address); - } - {{ reg }} = ({{ reg }} & ~memoryMask) | static_cast(size) - | static_cast(inc) | static_cast(dir) -%% if target["family"] == "f4" - | static_cast(transfer); -%% else - ; -%% endif -} - -void -modm::platform::Dma{{ id }}::Stream{{ stream_id }}::setPeripheralSource(uint{{type}}_t* address, - PeripheralIncrementMode inc -%% if target["family"] == "f4" -, PeripheralBurstTransfer transfer) -%% else -) -%% endif -{ - //stop(); - static constexpr PeripheralDataSize size = PeripheralDataSize::Bit{{type}}; - static constexpr DataTransferDirection dir = DataTransferDirection::PeripheralToMemory; - {{ per }} = reinterpret_cast(address); - {{ reg }} = ({{ reg }} & ~peripheralMask) | static_cast(size) - | static_cast(inc) | static_cast(dir) -%% if target["family"] == "f4" - | static_cast(transfer); -%% else - ; -%% endif -} -%% endfor - -%% for type in pointer_types - -void -modm::platform::Dma{{ id }}::Stream{{ stream_id }}::setMemoryDestination(uint{{type}}_t* address, - MemoryIncrementMode inc -%% if target["family"] == "f4" -, MemoryBurstTransfer transfer) -%% else -) -%% endif -{ - //stop(); - static constexpr MemoryDataSize size = MemoryDataSize::Bit{{type}}; - // a memory source could mean that either a memory-to-peripheral or a - // memory-to-memory transfer is intended - // we'll use the current transfer mode to determine what to do - DataTransferDirection dir; - if(getDataTransferDirection() == DataTransferDirection::PeripheralToMemory) { - dir = DataTransferDirection::PeripheralToMemory; - {{ mem }} = reinterpret_cast(address); - } else { - dir = DataTransferDirection::MemoryToMemory; - {{ mem }} = reinterpret_cast(address); - } - {{ reg }} = ({{ reg }} & ~memoryMask) | static_cast(size) - | static_cast(inc) | static_cast(dir) -%% if target["family"] == "f4" - | static_cast(transfer); -%% else - ; -%% endif -} - - -void -modm::platform::Dma{{ id }}::Stream{{ stream_id }}::setPeripheralDestination(uint{{type}}_t* address, - PeripheralIncrementMode inc -%% if target["family"] == "f4" -, PeripheralBurstTransfer transfer) -%% else -) -%% endif -{ - //stop(); - static constexpr PeripheralDataSize size = PeripheralDataSize::Bit{{type}}; - // if memory-to-memory mode was selected, the memory source - // will have been saved in the Peripheral Register - // in memory-to-peripheral mode, the memory source will - // need to be saved in the Memory Register - if(getDataTransferDirection() == DataTransferDirection::MemoryToMemory) { - {{ mem }} = {{ per }}; - } - static constexpr DataTransferDirection dir = DataTransferDirection::MemoryToPeripheral; - {{ per }} = reinterpret_cast(address); - {{ reg }} = ({{ reg }} & ~peripheralMask) | static_cast(size) - | static_cast(inc) | static_cast(dir) -%% if target["family"] == "f4" - | static_cast(transfer); -%% else - ; -%% endif -} -%% endfor - -void -modm::platform::Dma{{ id }}::Stream{{ stream_id }}::stop() -{ -%% if target["family"] == "f4" - {{ reg }} &= ~DMA_SxCR_EN; - while({{ reg }} & DMA_SxCR_EN); // wait for stream to be stopped -%% else - {{ reg }} &= ~DMA_CCR_EN; - while({{ reg }} & DMA_CCR_EN); // wait for stream to be stopped -%% endif -} - -void -modm::platform::Dma{{ id }}::Stream{{ stream_id }}::configure( -%% if target["family"] == "f4" - Channel channel, -%% endif - uint16_t sample_length, Priority priority, CircularMode circular -%% if target["family"] == "f4" - , FlowControl flow) -%% elif target["family"] == "f3" - ) -%% endif -{ - //stop(); - {{ reg }} = ({{ reg }} & ~configmask) | static_cast(priority) - | static_cast(circular) -%% if target["family"] == "f4" - | static_cast(channel) | static_cast(flow); -%% else - ; -%% endif - {{ length }} = sample_length; -} - -void -modm::platform::Dma{{ id }}::Stream{{ stream_id }}::start() -{ -%% if target["family"] == "f4" - {{ reg }} |= DMA_SxCR_EN; -%% else - // clear interrupt flags - DMA{{ id }}->ISR |= DMA_ISR_TEIF{{ stream_id }} | - DMA_ISR_TCIF{{ stream_id }} | - DMA_ISR_HTIF{{ stream_id }} | - DMA_ISR_GIF{{ stream_id }}; - {{ reg }} |= DMA_CCR_EN; -%% endif -} - -bool -modm::platform::Dma{{ id }}::Stream{{ stream_id }}::isFinished() -{ -%% if target["family"] == "f4" - return !({{ reg }} & DMA_SxCR_EN); -%% else - return (DMA{{ id }}->ISR & DMA_ISR_TCIF{{ stream_id }}); -%% endif -} - -modm::platform::Dma{{ id }}::Stream{{ stream_id }}::DataTransferDirection -modm::platform::Dma{{ id }}::Stream{{ stream_id }}::getDataTransferDirection() -{ - return static_cast( -%% if target["family"] == "f4" - {{ reg }} & (DMA_SxCR_DIR_0 | DMA_SxCR_DIR_1)); -%% else - {{ reg }} & (DMA_CCR_MEM2MEM | DMA_CCR_DIR)); -%% endif -} - -%% endfor diff --git a/src/modm/platform/dma/stm32/module.lb b/src/modm/platform/dma/stm32/module.lb index 8be23f694d..20d50a2d39 100644 --- a/src/modm/platform/dma/stm32/module.lb +++ b/src/modm/platform/dma/stm32/module.lb @@ -3,6 +3,7 @@ # # Copyright (c) 2016-2018, Niklas Hauser # Copyright (c) 2017, Fabian Greif +# Copyright (c) 2020, Mike Wolfram # # This file is part of the modm project. # @@ -11,31 +12,6 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. # ----------------------------------------------------------------------------- -class Instance(Module): - def __init__(self, instance): - self.instance = instance - - def init(self, module): - module.name = str(self.instance) - module.description = "Instance {}".format(self.instance) - - def prepare(self, module, options): - module.depends(":platform:dma") - return True - - def build(self, env): - device = env[":target"] - - properties = device.properties - properties["target"] = device.identifier - properties["id"] = self.instance - - env.substitutions = properties - env.outbasepath = "modm/src/modm/platform/dma" - - env.template("dma.hpp.in", "dma_{}.hpp".format(self.instance)) - env.template("dma_impl.hpp.in", "dma_{}_impl.hpp".format(self.instance)) - def init(module): module.name = ":platform:dma" module.description = "Direct Memory Access (DMA)" @@ -45,15 +21,14 @@ def prepare(module, options): if not device.has_driver("dma:stm32*"): return False - # FIXME the driver is referenced in STM32F0 device files, but appears only to support STM32F3/4 - if device.identifier["family"] not in ["f3", "f4"]: + # FIXME the driver is for L4 only + if device.identifier["family"] not in ["l4"]: + return False + if device.identifier["name"] not in ["12", "22", "31", "32", "33", "42", "43", "51", "52", "62", "75", "76", "86", "96", "A6"]: return False module.depends(":cmsis:device", ":platform:rcc") - for instance in device.get_driver("dma")["instance"]: - module.add_submodule(Instance(int(instance))) - return True def build(env): @@ -61,8 +36,34 @@ def build(env): 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 = {} + controller = [] + for channels in dma["channels"]: + max_channels = 0 + for channel in channels["channel"]: + max_channels = channel["position"] + for request in channel["request"]: + for signal in request["signal"]: + if "name" in signal: + signal_name = signal["name"].capitalize() + signal_names[signal_name] = 1 + controller.append({"instance": int(channels["instance"]), "channels": int(max_channels)}) + + signal_names = sorted(list(set(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_hal_impl.hpp.in") + env.template("dma.hpp.in") + env.template("dma.cpp.in") +