Skip to content

Commit

Permalink
[example] Add ADC DMA example for Nucleo F401RE
Browse files Browse the repository at this point in the history
  • Loading branch information
victorandrehc authored and chris-durand committed Apr 1, 2023
1 parent fe722c7 commit 04ed0a5
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 1 deletion.
97 changes: 97 additions & 0 deletions examples/nucleo_f401re/adc_dma/adc_dma.hpp
Original file line number Diff line number Diff line change
@@ -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 <modm/platform.hpp>

template<class Adc, class DmaChannel>
class AdcDma
{
struct Dma
{
using AdcChannel =
typename DmaChannel::template RequestMapping<modm::platform::Peripheral::Adc1>::Channel;
static constexpr modm::platform::DmaBase::Request AdcRequest =
DmaChannel::template RequestMapping<modm::platform::Peripheral::Adc1>::Request;
};

public:
/**
* \brief initialize both adc and dma
* @tparam SystemClock
*/
template<class SystemClock>
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<Dma::AdcRequest>();
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
88 changes: 88 additions & 0 deletions examples/nucleo_f401re/adc_dma/main.cpp
Original file line number Diff line number Diff line change
@@ -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 <modm/architecture/interface/interrupt.hpp>
#include <modm/board.hpp>
#include <modm/platform.hpp>

#include "adc_dma.hpp"
#include "timer_handler.hpp"

using namespace Board;
using namespace modm::platform;
using Adc1Dma = AdcDma<Adc1, Dma2::Channel0>;

std::array<uint16_t, 100> adc_results;
volatile bool dma_completed = false;

void
completedCallback()
{
LedD13::toggle();
dma_completed = true;
}

int
main()
{
Board::initialize();
Adc1::initialize<SystemClock>();
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<Timer1>(adc_results.size() / 2 - 1);
Adc1Dma::initialize<SystemClock>((uintptr_t)(&adc_results[0]), adc_results.size());
const auto c0_channel = Adc1::getPinChannel<GpioInputC0>();
const auto c1_channel = Adc1::getPinChannel<GpioInputC1>();
Adc1::connect<GpioInputC0::In10>();
Adc1::connect<GpioInputC1::In11>();
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<Timer1>();
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<Timer1>();
}

return 0;
}
12 changes: 12 additions & 0 deletions examples/nucleo_f401re/adc_dma/project.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<library>
<extends>modm:nucleo-f401re</extends>
<options>
<option name="modm:build:build.path">../../../build/nucleo_f401re/adc_dma</option>
</options>
<modules>
<module>modm:build:scons</module>
<module>modm:platform:timer:1</module>
<module>modm:platform:adc:1</module>
<module>modm:platform:dma</module>
</modules>
</library>
48 changes: 48 additions & 0 deletions examples/nucleo_f401re/adc_dma/timer_handler.hpp
Original file line number Diff line number Diff line change
@@ -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 <modm/architecture/interface/interrupt.hpp>
#include <modm/board.hpp>
#include <modm/platform.hpp>

#include "adc_dma.hpp"

using namespace Board;
using namespace modm::platform;
using namespace std::chrono_literals;

template<class Timer>
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<class Timer>
static void
timerStart()
{
Timer::applyAndReset();
Timer::start();
}


#endif // EXAMPLE_TimerHANDLER_HPP
2 changes: 1 addition & 1 deletion src/modm/platform/adc/stm32/adc.hpp.in
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ public:

static inline void
enable();

static inline void
disable();

Expand Down

0 comments on commit 04ed0a5

Please sign in to comment.