Skip to content

Commit

Permalink
[stm32] add dma capabilities to G0 adc
Browse files Browse the repository at this point in the history
  • Loading branch information
wald-zat authored and victorandrehc committed Feb 26, 2024
1 parent c47e7b7 commit a217cc4
Show file tree
Hide file tree
Showing 9 changed files with 566 additions and 0 deletions.
97 changes: 97 additions & 0 deletions examples/nucleo_g070rb/adc_dma/adc_dma.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Copyright (c) 2024, 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();
}

static void
startDma()
{
Adc::enableDmaRequests();
Adc::enableDmaMode();
DmaChannel::start();
}

static void
setTransferErrorCallback(modm::platform::DmaBase::IrqHandler transferErrorCallback)
{
if (transferErrorCallback == nullptr) { return; }
Dma::AdcChannel::enableInterruptVector();
Dma::AdcChannel::enableInterrupt(modm::platform::DmaBase::InterruptEnable::TransferError);
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
92 changes: 92 additions & 0 deletions examples/nucleo_g070rb/adc_dma/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Copyright (c) 2024, 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 s channels 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, Dma1::Channel1>;

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

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

int
main()
{
Board::initialize();

// Use the logging streams to print some messages.
// Change MODM_LOG_LEVEL above to enable or disable these messages
MODM_LOG_INFO << "Start Setup" << modm::endl;

LedD13::setOutput();
LedD13::reset();

adc_results.fill(0);

Adc1::connect<GpioInputA0::In0>();
Adc1::connect<GpioInputA1::In1>();

const auto a0_channel = Adc1::getPinChannel<GpioInputA0>();
const auto a1_channel = Adc1::getPinChannel<GpioInputA1>();
Adc1::setSampleTime(Adc1::SampleTime::Cycles3_5);
Adc1::initialize<SystemClock, Adc1::ClockMode::Asynchronous>();
Adc1::enableScanMode();
modm::delay(500ms);
// On STM32G0 Event1 means TIM1's channel 4 capture and compare event.
// Each controller has a different trigger mapping, check the reference
// manual for more information on the trigger mapping of your controller.
Adc1::enableRegularConversionExternalTrigger(Adc1::ExternalTriggerPolarity::RisingEdge,
Adc1::RegularConversionExternalTrigger::Event1);
Adc1::addChannel(a0_channel);
Adc1::addChannel(a1_channel);
Dma1::enable();
Adc1Dma::initialize<SystemClock>((uintptr_t)(&adc_results[0]), adc_results.size());
Adc1Dma::setCompletedConversionCallback(completedCallback);
Adc1Dma::startDma();
Adc1::startConversion();

advancedTimerConfig<Timer1>(adc_results.size() / 2 - 1);
timerStart<Timer1>();

while (true)
{
modm::delay(0.5s);
if (!dma_completed) { continue; }
dma_completed = false;
MODM_LOG_INFO << "Measurements"
<< "\r" << modm::endl;
for (const uint16_t& sample : adc_results) { MODM_LOG_INFO << sample << ", "; }
MODM_LOG_INFO << "\r" << modm::endl;
adc_results.fill(0);
timerStart<Timer1>();
}
return 0;
}
12 changes: 12 additions & 0 deletions examples/nucleo_g070rb/adc_dma/project.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<library>
<extends>modm:nucleo-g070rb</extends>
<options>
<option name="modm:build:build.path">../../../build/nucleo_g070rb/adc_dma</option>
</options>
<modules>
<module>modm:build:scons</module>
<module>modm:platform:timer:1</module>
<module>modm:platform:adc</module>
<module>modm:platform:dma</module>
</modules>
</library>
47 changes: 47 additions & 0 deletions examples/nucleo_g070rb/adc_dma/timer_handler.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2024, 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(4, Timer::OutputCompareMode::Pwm, 999, Timer::PinState::Enable);
}

template<class Timer>
static void
timerStart()
{
Timer::applyAndReset();
Timer::start();
}

#endif // EXAMPLE_TimerHANDLER_HPP
102 changes: 102 additions & 0 deletions src/modm/platform/adc/stm32f0/adc.hpp.in
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* Copyright (c) 2018, Álan Crístoffer
* Copyright (c) 2018, Carl Treudler
* Copyright (c) 2018-2019, Niklas Hauser
* Copyright (c) 2023, Daniel Waldhaeusl (Zuehlke Engineering)
*
* This file is part of the modm project.
*
Expand Down Expand Up @@ -140,6 +141,32 @@ public:
};
MODM_FLAGS32(InterruptFlag);

enum class ExternalTriggerPolarity
{
NoTriggerDetection = 0x0u,
RisingEdge = 0x1u,
FallingEdge = 0x2u,
RisingAndFallingEdge = 0x3u,
};

/**
* Enum mapping all events on a external trigger converter.
* The source mapped to each event varies on controller family,
* refer to the ADC external trigger section on reference manual
* of your controller for more information
*/
enum class RegularConversionExternalTrigger
{
Event0 = 0x0u,
Event1 = 0x1u,
Event2 = 0x2u,
Event3 = 0x3u,
Event4 = 0x4u,
Event5 = 0x5u,
Event6 = 0x6u,
Event7 = 0x7u,
};

public:
// start inherited documentation
template< class... Signals >
Expand Down Expand Up @@ -169,6 +196,9 @@ public:
static void
initialize();

static inline void
enable();

static inline void
disable();

Expand Down Expand Up @@ -314,6 +344,78 @@ public:
static inline void
acknowledgeInterruptFlags(InterruptFlag_t flags);

%% if target.family in ["g0"]
static inline uintptr_t
getDataRegisterAddress();

static inline void
enableRegularConversionExternalTrigger(
ExternalTriggerPolarity externalTriggerPolarity,
RegularConversionExternalTrigger regularConversionExternalTrigger);

/// Add a channel to conversion group.
static inline bool
addChannel(const Channel channel);

/**
* Enable Dma mode for the ADC
*/
static inline void
enableDmaMode();

/**
* Disable Dma mode for the ADC
*/
static inline void
disableDmaMode();

/**
* get if adc is enabled
* @return true if ADC_CR2_ADON bit is set, false otherwise
*/
static inline bool
getAdcEnabled();

/**
* enable DMA selection for the ADC. If this is enabled DMA
* requests are issued as long as data are converted and
* the adc has dma enabled
*/
static inline void
enableDmaRequests();

/**
* disable dma selection for the ADC. If this is disabled
* no new DMA requests are issued after last transfer
*/
static inline void
disableDmaRequests();

/**
* Waits until the CCR bit is set.
*/
static inline void
waitChannelConfigReady();

/**
* Resets the CCR bit.
*/
static inline void
resetChannelConfigReady();

/**
* Enables scan mode
*/
static inline void
enableScanMode();

/**
* Disables scan mode
*/
static inline void
disableScanMode();
%% endif

public:
static constexpr uint8_t TS_CAL1_TEMP{30};
%% if target.family in ["f0"]
Expand Down
Loading

0 comments on commit a217cc4

Please sign in to comment.