Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[stm32] Add F3 ADC regular conversion sequence support #1126

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions examples/nucleo_g474re/adc_sequence_dma/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright (c) 2021, Raphael Lehmann
* Copyright (c) 2024, Christopher Durand
*
* 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 <modm/board.hpp>
#include <array>

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

MODM_LOG_INFO << "STM32G4 ADC regular conversion sequence example" << modm::endl;

// max. ADC clock for STM32G474: 60 MHz
// 170 MHz AHB clock / 4 = 42.5 MHz
Adc1::initialize(
Adc1::ClockMode::SynchronousPrescaler4,
Adc1::ClockSource::SystemClock,
Adc1::Prescaler::Disabled,
Adc1::CalibrationMode::SingleEndedInputsMode,
true);

Adc1::connect<Board::A0::In1, Board::A1::In2, Board::A3::In15, Board::A4::In7>();

constexpr auto sampleTime = Adc1::SampleTime::Cycles641;
constexpr std::array<Adc1::SequenceChannel, 5> sequence{{
// Perform dummy conversion (ADC errata 2.7.7 and 2.7.8)
{Adc1::getPinChannel<Board::A0::In1>(), sampleTime},
// Read A0, A1, A3, A4
{Adc1::getPinChannel<Board::A0::In1>(), sampleTime},
{Adc1::getPinChannel<Board::A1::In2>(), sampleTime},
{Adc1::getPinChannel<Board::A3::In15>(), sampleTime},
{Adc1::getPinChannel<Board::A4::In7>(), sampleTime}
}};
Adc1::setChannelSequence(sequence);
Adc1::setDmaMode(Adc1::DmaMode::OneShot);

Dma1::enable();
using DmaChannel = Dma1::Channel1;
DmaChannel::configure(Dma1::DataTransferDirection::PeripheralToMemory, Dma1::MemoryDataSize::Bit16,
Dma1::PeripheralDataSize::Bit16, Dma1::MemoryIncrementMode::Increment,
Dma1::PeripheralIncrementMode::Fixed);
DmaChannel::setPeripheralAddress(reinterpret_cast<uintptr_t>(Adc1::dataRegister()));
constexpr auto request = DmaChannel::RequestMapping<Peripheral::Adc1>::Request;
DmaChannel::setPeripheralRequest<request>();

std::array<uint16_t, 5> samples{};

while (true)
{
DmaChannel::setMemoryAddress(reinterpret_cast<uintptr_t>(samples.data()));
DmaChannel::setDataLength(samples.size());
DmaChannel::start();
Adc1::startConversion();

while (!(DmaChannel::getInterruptFlags() & Dma1::InterruptFlags::TransferComplete));
DmaChannel::stop();

const double factor = 3.3 / 4095;
MODM_LOG_INFO.printf("A0: %1.3f V\n", samples[1] * factor);
MODM_LOG_INFO.printf("A1: %1.3f V\n", samples[2] * factor);
MODM_LOG_INFO.printf("A3: %1.3f V\n", samples[3] * factor);
MODM_LOG_INFO.printf("A4: %1.3f V\n", samples[4] * factor);
MODM_LOG_INFO << '\n';
modm::delay(500ms);
}

return 0;
}
12 changes: 12 additions & 0 deletions examples/nucleo_g474re/adc_sequence_dma/project.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<library>
<extends>modm:nucleo-g474re</extends>
<options>
<option name="modm:build:build.path">../../../build/nucleo_g474re/adc_sequence_dma</option>
</options>
<modules>
<module>modm:debug</module>
<module>modm:platform:adc:1</module>
<module>modm:platform:dma</module>
<module>modm:build:scons</module>
</modules>
</library>
50 changes: 49 additions & 1 deletion src/modm/platform/adc/stm32f3/adc.hpp.in
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@
#ifndef MODM_STM32F3_ADC{{ id }}_HPP
#define MODM_STM32F3_ADC{{ id }}_HPP

#include <stdint.h>
#include <cstdint>
#include <span>
#include <utility>
#include "../device.hpp"
#include <modm/architecture/interface/register.hpp>
#include <modm/platform/gpio/connector.hpp>
#include <modm/math/algorithm/enumerate.hpp>

namespace modm
{
Expand Down Expand Up @@ -260,6 +263,13 @@ public:
Cycles602 = 0b111, //< 601.5 ADC clock cycles
};
%% endif

struct SequenceChannel
{
Channel channel{};
SampleTime sampleTime{};
};

enum class CalibrationMode : uint32_t
{
SingleEndedInputsMode = 0,
Expand All @@ -280,6 +290,25 @@ public:
%% endif
};

enum class DmaMode : uint32_t
{
Disabled = 0,
%% if target["family"] in ["h7"] and resolution == 12:
OneShot = ADC3_CFGR_DMAEN,
Circular = ADC3_CFGR_DMACFG | ADC3_CFGR_DMAEN,
Mask = Circular
%% elif target["family"] in ["h7"] and resolution == 16:
OneShot = ADC_CFGR_DMNGT_0,
Dfsdm = ADC_CFGR_DMNGT_1,
Circular = ADC_CFGR_DMNGT_1 | ADC_CFGR_DMNGT_0,
Mask = ADC_CFGR_DMNGT_Msk
%% else
OneShot = ADC_CFGR_DMAEN,
Circular = ADC_CFGR_DMACFG | ADC_CFGR_DMAEN,
Mask = Circular
%% endif
};

enum class Interrupt : uint32_t
{
Ready = ADC_IER_ADRDYIE,
Expand Down Expand Up @@ -414,6 +443,9 @@ public:
setChannel(const Channel channel,
const SampleTime sampleTime=static_cast<SampleTime>(0b000));

static inline bool
setChannelSequence(std::span<const SequenceChannel> sequence);

/// Setting the channel for a Pin
template< class Gpio >
static inline bool
Expand Down Expand Up @@ -464,13 +496,19 @@ public:
static inline void
startConversion();

static inline void
stopConversion();

/**
* @return If the conversion is finished.
* @pre A conversion should have been started with startConversion()
*/
static inline bool
isConversionFinished();

static inline bool
isConversionSequenceFinished();

/**
* Start a new injected conversion sequence.
*
Expand Down Expand Up @@ -562,6 +600,16 @@ public:
static inline void
acknowledgeInterruptFlags(const InterruptFlag_t flags);

/// @return ADC data register pointer, for DMA use only.
static inline volatile uint32_t*
dataRegister()
{
return &(ADC{{ id }}->DR);
}

static inline void
setDmaMode(DmaMode mode);

private:
static inline bool
configureChannel(Channel channel, SampleTime sampleTime);
Expand Down
63 changes: 61 additions & 2 deletions src/modm/platform/adc/stm32f3/adc_impl.hpp.in
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,6 @@ modm::platform::Adc{{ id }}::configureChannel(Channel channel,

}


bool
modm::platform::Adc{{ id }}::setChannel(Channel channel,
SampleTime sampleTime)
Expand All @@ -224,6 +223,42 @@ modm::platform::Adc{{ id }}::setChannel(Channel channel,
return true;
}

bool
modm::platform::Adc{{ id }}::setChannelSequence(std::span<const SequenceChannel> sequence)
{
if (sequence.size() > 16 || sequence.size() == 0) {
return false;
}

ADC{{ id }}->SQR1 = sequence.size() - 1;

for (const auto [i, config] : modm::enumerate(sequence)) {
const auto [channel, sampleTime] = config;
if (!configureChannel(channel, sampleTime)) {
return false;
}

if (i < 4) {
const auto shift = (i + 1) * 6;
const auto mask = 0b111111u << shift;
ADC{{ id }}->SQR1 = (ADC{{ id }}->SQR1 & ~mask) | (std::to_underlying(channel) << shift);
} else if (i < 9) {
const auto shift = (i - 4) * 6;
const auto mask = 0b111111u << shift;
ADC{{ id }}->SQR2 = (ADC{{ id }}->SQR2 & ~mask) | (std::to_underlying(channel) << shift);
} else if (i < 14) {
const auto shift = (i - 9) * 6;
const auto mask = 0b111111u << shift;
ADC{{ id }}->SQR3 = (ADC{{ id }}->SQR3 & ~mask) | (std::to_underlying(channel) << shift);
} else {
const auto shift = (i - 14) * 6;
const auto mask = 0b111111u << shift;
ADC{{ id }}->SQR4 = (ADC{{ id }}->SQR4 & ~mask) | (std::to_underlying(channel) << shift);
}
}
return true;
}

void
modm::platform::Adc{{ id }}::setFreeRunningMode(const bool enable)
{
Expand All @@ -239,17 +274,34 @@ modm::platform::Adc{{ id }}::startConversion()
{
// TODO: maybe add more interrupt flags
acknowledgeInterruptFlags(InterruptFlag::EndOfRegularConversion |
InterruptFlag::EndOfSampling | InterruptFlag::Overrun);
InterruptFlag::EndOfSampling | InterruptFlag::Overrun |
InterruptFlag::EndOfRegularSequenceOfConversions);
// starts single conversion for the regular group
ADC{{ id }}->CR |= ADC_CR_ADSTART;
}

void
modm::platform::Adc{{ id }}::stopConversion()
{
ADC{{ id }}->CR |= ADC_CR_ADSTP | ADC_CR_JADSTP;
while ((ADC{{ id }}->CR & (ADC_CR_ADSTP | ADC_CR_JADSTP)) != 0);

acknowledgeInterruptFlags(InterruptFlag::EndOfRegularConversion |
InterruptFlag::EndOfSampling | InterruptFlag::Overrun);
}

bool
modm::platform::Adc{{ id }}::isConversionFinished()
{
return static_cast<bool>(getInterruptFlags() & InterruptFlag::EndOfRegularConversion);
}

bool
modm::platform::Adc{{ id }}::isConversionSequenceFinished()
{
return static_cast<bool>(getInterruptFlags() & InterruptFlag::EndOfRegularSequenceOfConversions);
}

void
modm::platform::Adc{{ id }}::startInjectedConversionSequence()
{
Expand Down Expand Up @@ -305,6 +357,13 @@ modm::platform::Adc{{ id }}::isInjectedConversionFinished()
return static_cast<bool>(getInterruptFlags() & InterruptFlag::EndOfInjectedSequenceOfConversions);
}

void
modm::platform::Adc{{ id }}::setDmaMode(DmaMode mode)
{
constexpr uint32_t mask = std::to_underlying(DmaMode::Mask);
ADC{{ id }}->CFGR = (ADC{{ id }}->CFGR & ~mask) | std::to_underlying(mode);
}

// ----------------------------------------------------------------------------
void
modm::platform::Adc{{ id }}::enableInterruptVector(const uint32_t priority,
Expand Down
2 changes: 1 addition & 1 deletion src/modm/platform/adc/stm32f3/module.lb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class Instance(Module):
module.description = "Instance {}".format(self.instance)

def prepare(self, module, options):
module.depends(":platform:adc")
module.depends(":platform:adc", ":math:algorithm")
return True

def build(self, env):
Expand Down
Loading