From 04ed0a52e03964811eb9764306bb1b61de5b5f60 Mon Sep 17 00:00:00 2001 From: Victor Costa Date: Wed, 22 Mar 2023 12:23:42 +0100 Subject: [PATCH] [example] Add ADC DMA example for Nucleo F401RE --- examples/nucleo_f401re/adc_dma/adc_dma.hpp | 97 +++++++++++++++++++ examples/nucleo_f401re/adc_dma/main.cpp | 88 +++++++++++++++++ examples/nucleo_f401re/adc_dma/project.xml | 12 +++ .../nucleo_f401re/adc_dma/timer_handler.hpp | 48 +++++++++ src/modm/platform/adc/stm32/adc.hpp.in | 2 +- 5 files changed, 246 insertions(+), 1 deletion(-) create mode 100644 examples/nucleo_f401re/adc_dma/adc_dma.hpp create mode 100644 examples/nucleo_f401re/adc_dma/main.cpp create mode 100644 examples/nucleo_f401re/adc_dma/project.xml create mode 100644 examples/nucleo_f401re/adc_dma/timer_handler.hpp diff --git a/examples/nucleo_f401re/adc_dma/adc_dma.hpp b/examples/nucleo_f401re/adc_dma/adc_dma.hpp new file mode 100644 index 0000000000..308faa3c44 --- /dev/null +++ b/examples/nucleo_f401re/adc_dma/adc_dma.hpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2023, Zühlke Engineering (Austria) GmbH + * + * 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 EXAMPLE_ADCDMA_HPP +#define EXAMPLE_ADCDMA_HPP + +#include + +template +class AdcDma +{ + struct Dma + { + using AdcChannel = + typename DmaChannel::template RequestMapping::Channel; + static constexpr modm::platform::DmaBase::Request AdcRequest = + DmaChannel::template RequestMapping::Request; + }; + +public: + /** + * \brief initialize both adc and dma + * @tparam SystemClock + */ + template + static void + initialize(uintptr_t destination_ptr, size_t length, + modm::platform::DmaBase::Priority priority = modm::platform::DmaBase::Priority::Low, + modm::platform::DmaBase::CircularMode circularMode = + modm::platform::DmaBase::CircularMode::Enabled, + modm::platform::DmaBase::IrqHandler transferErrorCallback = nullptr, + modm::platform::DmaBase::IrqHandler halfCompletedCallback = nullptr, + modm::platform::DmaBase::IrqHandler completedCallback = nullptr) + { + Dma::AdcChannel::configure( + modm::platform::DmaBase::DataTransferDirection::PeripheralToMemory, + modm::platform::DmaBase::MemoryDataSize::HalfWord, + modm::platform::DmaBase::PeripheralDataSize::HalfWord, + modm::platform::DmaBase::MemoryIncrementMode::Increment, + modm::platform::DmaBase::PeripheralIncrementMode::Fixed, priority, circularMode); + Dma::AdcChannel::setPeripheralAddress(Adc::getDataRegisterAddress()); + Dma::AdcChannel::setDataLength(length); + Dma::AdcChannel::setMemoryAddress(destination_ptr); + + setTransferErrorCallback(transferErrorCallback); + setHalfCompletedConversionCallback(halfCompletedCallback); + setCompletedConversionCallback(completedCallback); + + Dma::AdcChannel::template setPeripheralRequest(); + Adc::disableDmaMode(); + // Start Conversion if adc is enabled + if (Adc::getAdcEnabled()) + { + Adc::enableDmaMode(); + Adc::enableDmaRequests(); + Dma::AdcChannel::start(); + } + } + + static void + setTransferErrorCallback(modm::platform::DmaBase::IrqHandler transferErrorCallback) + { + if (transferErrorCallback == nullptr) { return; } + Dma::AdcChannel::enableInterruptVector(); + Dma::AdcChannel::enableInterrupt(modm::platform::DmaBase::InterruptEnable::TransferError | + modm::platform::DmaBase::InterruptEnable::DirectModeError); + Dma::AdcChannel::setTransferErrorIrqHandler(transferErrorCallback); + } + + static void + setHalfCompletedConversionCallback(modm::platform::DmaBase::IrqHandler halfCompletedCallback) + { + if (halfCompletedCallback == nullptr) { return; } + Dma::AdcChannel::enableInterruptVector(); + Dma::AdcChannel::enableInterrupt(modm::platform::DmaBase::InterruptEnable::HalfTransfer); + Dma::AdcChannel::setHalfTransferCompleteIrqHandler(halfCompletedCallback); + } + + static void + setCompletedConversionCallback(modm::platform::DmaBase::IrqHandler completedCallback) + { + if (completedCallback == nullptr) { return; } + Dma::AdcChannel::enableInterruptVector(); + Dma::AdcChannel::enableInterrupt( + modm::platform::DmaBase::InterruptEnable::TransferComplete); + Dma::AdcChannel::setTransferCompleteIrqHandler(completedCallback); + } +}; + +#endif // EXAMPLE_ADCDMA_HPP diff --git a/examples/nucleo_f401re/adc_dma/main.cpp b/examples/nucleo_f401re/adc_dma/main.cpp new file mode 100644 index 0000000000..b0b9779989 --- /dev/null +++ b/examples/nucleo_f401re/adc_dma/main.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2023, Zühlke Engineering (Austria) GmbH + * + * 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/. + */ + +// This Example uses adc to sample 2 channels in scan mode 50 times every 0.5s and store the value +// in a buffer via dma. The adc is set to be triggered by the Timer1 compare event and then to +// sample both channels using scan mode. The compare event is triggered for a defined number of +// times by configuring the Timer1 to use one pulse mode with an repetition count. For more +// information on the timer please refer to the timer register count example. In this configuration, +// the adc sampling process is started by the cpu but handled completely by peripherals clearing CPU +// time for other tasks. + +#include +#include +#include + +#include "adc_dma.hpp" +#include "timer_handler.hpp" + +using namespace Board; +using namespace modm::platform; +using Adc1Dma = AdcDma; + +std::array adc_results; +volatile bool dma_completed = false; + +void +completedCallback() +{ + LedD13::toggle(); + dma_completed = true; +} + +int +main() +{ + Board::initialize(); + Adc1::initialize(); + Dma2::enable(); + + // Use the logging streams to print some messages. + // Change MODM_LOG_LEVEL above to enable or disable these messages + MODM_LOG_DEBUG << "debug" << modm::endl; + MODM_LOG_INFO << "info" << modm::endl; + MODM_LOG_WARNING << "warning" << modm::endl; + MODM_LOG_ERROR << "error" << modm::endl; + + LedD13::setOutput(); + LedD13::reset(); + advancedTimerConfig(adc_results.size() / 2 - 1); + Adc1Dma::initialize((uintptr_t)(&adc_results[0]), adc_results.size()); + const auto c0_channel = Adc1::getPinChannel(); + const auto c1_channel = Adc1::getPinChannel(); + Adc1::connect(); + Adc1::connect(); + Adc1::setChannel(c0_channel, Adc1::SampleTime::Cycles3); + Adc1::addChannel(c1_channel, Adc1::SampleTime::Cycles3); + Adc1::enableScanMode(); + // On STM32F4 Event0 means TIM1's channel 1 capture and compare event. + // Each controller has a different trigger mapping, check the reference + // manual to more information on the trigger mapping of yours controller. + Adc1::enableRegularConversionExternalTrigger(Adc1::ExternalTriggerPolarity::FallingEdge, + Adc1::RegularConversionExternalTrigger::Event0); + Adc1Dma::setCompletedConversionCallback(completedCallback); + + adc_results.fill(0); + timerStart(); + while (true) + { + modm::delay(0.5s); + if (!dma_completed) { continue; } + dma_completed = false; + MODM_LOG_DEBUG << "Measurements" + << "\r" << modm::endl; + for (const uint16_t& sample : adc_results) { MODM_LOG_DEBUG << sample << ", "; } + MODM_LOG_DEBUG << "\r" << modm::endl; + adc_results.fill(0); + timerStart(); + } + + return 0; +} diff --git a/examples/nucleo_f401re/adc_dma/project.xml b/examples/nucleo_f401re/adc_dma/project.xml new file mode 100644 index 0000000000..4b9b76268b --- /dev/null +++ b/examples/nucleo_f401re/adc_dma/project.xml @@ -0,0 +1,12 @@ + + modm:nucleo-f401re + + + + + modm:build:scons + modm:platform:timer:1 + modm:platform:adc:1 + modm:platform:dma + + diff --git a/examples/nucleo_f401re/adc_dma/timer_handler.hpp b/examples/nucleo_f401re/adc_dma/timer_handler.hpp new file mode 100644 index 0000000000..03ff06ec59 --- /dev/null +++ b/examples/nucleo_f401re/adc_dma/timer_handler.hpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2023, Zühlke Engineering (Austria) GmbH + * + * 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 EXAMPLE_TimerHANDLER_HPP +#define EXAMPLE_TimerHANDLER_HPP + +#include +#include +#include + +#include "adc_dma.hpp" + +using namespace Board; +using namespace modm::platform; +using namespace std::chrono_literals; + +template +void +advancedTimerConfig(uint8_t repetitionCount) +{ + Timer::enable(); + Timer::setMode(Timer::Mode::UpCounter, Timer::SlaveMode::Disabled, + Timer::SlaveModeTrigger::Internal0, Timer::MasterMode::Update, true); + Timer::setPrescaler(84); + Timer::setOverflow(9999); + Timer::setRepetitionCount(repetitionCount); + + Timer::enableOutput(); + Timer::configureOutputChannel(1, Timer::OutputCompareMode::Pwm, 999, Timer::PinState::Enable); +} + +template +static void +timerStart() +{ + Timer::applyAndReset(); + Timer::start(); +} + + +#endif // EXAMPLE_TimerHANDLER_HPP diff --git a/src/modm/platform/adc/stm32/adc.hpp.in b/src/modm/platform/adc/stm32/adc.hpp.in index aaae771b1b..e7257c36b3 100644 --- a/src/modm/platform/adc/stm32/adc.hpp.in +++ b/src/modm/platform/adc/stm32/adc.hpp.in @@ -241,7 +241,7 @@ public: static inline void enable(); - + static inline void disable();