From fc85e139c0586a45f202b1b54c1b2c1efd4123a8 Mon Sep 17 00:00:00 2001 From: Christopher Durand Date: Tue, 11 Jul 2023 17:20:05 +0200 Subject: [PATCH 1/4] [stm32] Add support for H7 16-bit and 12-bit ADCs in stm32-f3 driver --- README.md | 2 +- src/modm/platform/adc/stm32f3/adc.hpp.in | 94 ++++++++++++++----- src/modm/platform/adc/stm32f3/adc_impl.hpp.in | 57 +++++++++-- src/modm/platform/adc/stm32f3/module.lb | 61 +++++++++++- 4 files changed, 177 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 1373f7e4fb..ec66e03826 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✅ ✅ ✅ -○ +✅ ○ ✅ ✅ diff --git a/src/modm/platform/adc/stm32f3/adc.hpp.in b/src/modm/platform/adc/stm32f3/adc.hpp.in index 9d80c8be5e..610238c983 100644 --- a/src/modm/platform/adc/stm32f3/adc.hpp.in +++ b/src/modm/platform/adc/stm32f3/adc.hpp.in @@ -2,7 +2,7 @@ * Copyright (c) 2013-2014, Kevin Läufer * Copyright (c) 2014, 2016-2018, Niklas Hauser * Copyright (c) 2017, Sascha Schade - * Copyright (c) 2022, Christopher Durand + * Copyright (c) 2022-2023, Christopher Durand * * This file is part of the modm project. * @@ -83,6 +83,21 @@ public: %% elif target["family"] in ["g4"] Opamp2 = 16, Opamp3 = 18, +%% elif target["family"] in ["h7"] +%% if target["name"] in ["a3", "b0", "b3"] + BatDiv4 = 14, + Dac2Out1 = 15, + VSense = 18, + InternalReference = 19, +%% endif + Dac1Out1 = 16, + Dac1Out2 = 17, +%% if target["name"][0] in ["2", "3"] + // BatDiv4 can be connected to channel 16 by setting ADC2_ROUT0 in SYSCFG_ADC2ALT + BatDiv4 = 16, + // The internal reference can be connected to channel 17 by setting ADC2_ROUT1 in SYSCFG_ADC2ALT + InternalReference = 17, +%% endif %% endif %% elif id == 3 %% if target["family"] in ["f3"] @@ -103,6 +118,16 @@ public: BatDiv3 = 17, %% endif InternalReference = 18, +%% elif target["family"] in ["h7"] +%% if resolution == 16 + BatDiv4 = 17, + VSense = 18, + InternalReference = 19, +%% else + BatDiv4 = 16, + VSense = 17, + InternalReference = 18, +%% endif %% endif %% elif id == 4 %% if target["family"] in ["f3"] @@ -175,7 +200,7 @@ public: Div128 = RCC_CFGR2_{{ adc_pre }}_DIV128, Div256 = RCC_CFGR2_{{ adc_pre }}_DIV256, Div256AllBits = RCC_CFGR2_{{ adc_pre }}, // for bit clear -%% elif target["family"] in ["l4", "l5", "g4"] +%% elif target["family"] in ["l4", "l5", "g4", "h7"] Div1 = 0, Div2 = ADC_CCR_PRESC_0, Div4 = ADC_CCR_PRESC_1, @@ -192,29 +217,41 @@ public: %% endif }; -%% if target["family"] in ["l4", "l5", "g4"] +%% if resolution == 16 + enum class SampleTime : uint8_t // TODO: What is the best type? + { + Cycles2 = 0b000, //< 1.5 ADC clock cycles + Cycles3 = 0b001, //< 2.5 ADC clock cycles + Cycles9 = 0b010, //< 8.5 ADC clock cycles + Cycles17 = 0b011, //< 16.5 ADC clock cycles + Cycles33 = 0b100, //< 32.5 ADC clock cycles + Cycles65 = 0b101, //< 64.5 ADC clock cycles + Cycles388 = 0b110, //< 387.5 ADC clock cycles + Cycles811 = 0b111, //< 810.5 ADC clock cycles + }; +%% elif target["family"] in ["l4", "l5", "g4", "h7"] enum class SampleTime : uint8_t // TODO: What is the best type? { - Cycles2 = 0b000, //! 1.5 ADC clock cycles - Cycles7 = 0b001, //! 6.5 ADC clock cycles - Cycles13 = 0b010, //! 12.5 ADC clock cycles - Cycles25 = 0b011, //! 24.5 ADC clock cycles - Cycles48 = 0b100, //! 47.5 ADC clock cycles - Cycles93 = 0b101, //! 92.5 ADC clock cycles - Cycles248 = 0b110, //! 247.5 ADC clock cycles - Cycles641 = 0b111, //! 640.5 ADC clock cycles + Cycles2 = 0b000, //< 1.5 ADC clock cycles + Cycles7 = 0b001, //< 6.5 ADC clock cycles + Cycles13 = 0b010, //< 12.5 ADC clock cycles + Cycles25 = 0b011, //< 24.5 ADC clock cycles + Cycles48 = 0b100, //< 47.5 ADC clock cycles + Cycles93 = 0b101, //< 92.5 ADC clock cycles + Cycles248 = 0b110, //< 247.5 ADC clock cycles + Cycles641 = 0b111, //< 640.5 ADC clock cycles }; %% else enum class SampleTime : uint8_t // TODO: What is the best type? { - Cycles2 = 0b000, //! 1.5 ADC clock cycles - Cycles3 = 0b001, //! 2.5 ADC clock cycles - Cycles5 = 0b010, //! 4.5 ADC clock cycles - Cycles8 = 0b011, //! 7.5 ADC clock cycles - Cycles20 = 0b100, //! 19.5 ADC clock cycles - Cycles62 = 0b101, //! 61.5 ADC clock cycles - Cycles182 = 0b110, //! 181.5 ADC clock cycles - Cycles602 = 0b111, //! 601.5 ADC clock cycles + Cycles2 = 0b000, //< 1.5 ADC clock cycles + Cycles3 = 0b001, //< 2.5 ADC clock cycles + Cycles5 = 0b010, //< 4.5 ADC clock cycles + Cycles8 = 0b011, //< 7.5 ADC clock cycles + Cycles20 = 0b100, //< 19.5 ADC clock cycles + Cycles62 = 0b101, //< 61.5 ADC clock cycles + Cycles182 = 0b110, //< 181.5 ADC clock cycles + Cycles602 = 0b111, //< 601.5 ADC clock cycles }; %% endif enum class CalibrationMode : uint32_t @@ -226,7 +263,7 @@ public: enum class VoltageRegulatorState : uint32_t { -%% if target["family"] in ["l4", "l5", "g4"] +%% if target["family"] in ["l4", "l5", "g4", "h7"] Enabled = ADC_CR_ADVREGEN, %% elif target["family"] in ["f3"] // Intermediate state is needed to move from enabled to disabled @@ -331,6 +368,7 @@ public: static inline void calibrate(const CalibrationMode mode, const bool blocking = true); +%% if resolution < 16 /** * Change the presentation of the ADC conversion result. * @@ -343,6 +381,7 @@ public: */ static inline void setLeftAdjustResult(const bool enable); +%% endif /** * Analog channel selection. @@ -373,11 +412,20 @@ public: return setChannel(getPinChannel(), sampleTime); } /// Get the channel for a Pin +%% if target["family"] in ["h7"] + /// \warning Always returns the positive single-ended channel. + /// Differential mode is not supported yet. +%% endif template< class Gpio > static inline constexpr Channel getPinChannel() { +%% if target["family"] in ["h7"] + using modm::platform::detail::AdcPolarity; + constexpr int8_t channel{detail::AdcChannel}; +%% else constexpr int8_t channel{detail::AdcChannel}; +%% endif static_assert(channel >= 0, "Adc{{id}} does not have a channel for this pin!"); return Channel(channel); } @@ -404,14 +452,14 @@ public: * TODO: is there any limitation to when is can be called?? */ static inline void - startConversion(void); + startConversion(); /** * @return If the conversion is finished. * @pre A conversion should have been stared with startConversion() */ static inline bool - isConversionFinished(void); + isConversionFinished(); /** * @return The most recent 16bit result of the ADC conversion. @@ -426,7 +474,7 @@ public: @endcode */ static inline uint16_t - getValue(void) + getValue() { return ADC{{ id }}->DR; } diff --git a/src/modm/platform/adc/stm32f3/adc_impl.hpp.in b/src/modm/platform/adc/stm32f3/adc_impl.hpp.in index b731b3131b..e854b823db 100644 --- a/src/modm/platform/adc/stm32f3/adc_impl.hpp.in +++ b/src/modm/platform/adc/stm32f3/adc_impl.hpp.in @@ -2,6 +2,7 @@ * Copyright (c) 2013-2014, Kevin Läufer * Copyright (c) 2014, Sascha Schade * Copyright (c) 2014, 2016-2017, Niklas Hauser + * Copyright (c) 2023, Christopher Durand * * This file is part of the modm project. * @@ -29,7 +30,7 @@ modm::platform::Adc{{ id }}::initialize(const ClockMode clk, uint32_t tmp = 0; // enable clock -%% if target["family"] in ["f3", "g4", "l5"] +%% if target["family"] in ["f3", "g4", "l5", "h7"] RCC->{{ ahb }}ENR |= RCC_{{ ahb }}ENR_ADC{{ id_common }}EN; %% elif target["family"] in ["l4"] Rcc::enable(); @@ -40,7 +41,7 @@ modm::platform::Adc{{ id }}::initialize(const ClockMode clk, RCC->{{ ccipr }} |= static_cast(clk_src); %% endif -%% if target["family"] in ["l4", "l5", "g4"] +%% if target["family"] in ["l4", "l5", "g4", "h7"] // Disable deep power down ADC{{ id }}->CR &= ~ADC_CR_DEEPPWD; %% endif @@ -60,11 +61,22 @@ modm::platform::Adc{{ id }}::initialize(const ClockMode clk, ADC{{ id_common_u }}->CCR = tmp; } +%% if resolution == 16 + // Always set boost mode for maximum clock for now. + // This will increase the ADC power consumption on lower ADC clocks. + // TODO: compute ADC input clock and choose correct setting + ADC{{id}}->CR |= ADC_CR_BOOST_Msk; +%% endif + // enable regulator ADC{{ id }}->CR &= ~ADC_CR_ADVREGEN; ADC{{ id }}->CR |= static_cast(VoltageRegulatorState::Enabled); modm::delay_us(10); // FIXME: this is ugly -> find better solution +%% if resolution == 16 + while(!(ADC{{ id }}->ISR & ADC_ISR_LDORDY)); +%% endif + acknowledgeInterruptFlags(InterruptFlag::Ready); calibrate(cal, true); // blocking calibration @@ -90,7 +102,7 @@ modm::platform::Adc{{ id }}::disable(const bool blocking) while(ADC{{ id }}->CR & ADC_CR_ADDIS); } // disable clock -%% if target["family"] in ["f3", "g4", "l5"] +%% if target["family"] in ["f3", "g4", "l5", "h7"] RCC->{{ ahb }}ENR &= ~RCC_{{ ahb }}ENR_ADC{{ id_common }}EN; %% elif target["family"] in ["l4"] Rcc::disable(); @@ -106,7 +118,7 @@ modm::platform::Adc{{ id }}::setPrescaler(const Prescaler pre) tmp &= ~static_cast(Prescaler::Div256AllBits); tmp |= static_cast(pre); RCC->CFGR2 = tmp; -%% elif target["family"] in ["l4", "l5", "g4"] +%% elif target["family"] in ["l4", "l5", "g4", "h7"] tmp = ADC{{ id_common_u }}->CCR; tmp &= ~static_cast(Prescaler::Div256AllBits); tmp |= static_cast(pre); @@ -125,7 +137,11 @@ modm::platform::Adc{{ id }}::calibrate(const CalibrationMode mode, const bool blocking) { if (mode != CalibrationMode::DoNotCalibrate) { +%% if resolution == 16 + ADC{{ id }}->CR |= ADC_CR_ADCAL | ADC_CR_ADCALLIN | +%% else ADC{{ id }}->CR |= ADC_CR_ADCAL | +%% endif static_cast(mode); if(blocking) { // wait for ADC_CR_ADCAL to be cleared by hw @@ -134,24 +150,47 @@ modm::platform::Adc{{ id }}::calibrate(const CalibrationMode mode, } } +%% if resolution < 16 void modm::platform::Adc{{ id }}::setLeftAdjustResult(const bool enable) { if (enable) { +%% if target["family"] in ["h7"] + ADC{{ id }}->CFGR |= ADC3_CFGR_ALIGN; +%% else ADC{{ id }}->CFGR |= ADC_CFGR_ALIGN; - } - else { +%% endif + } else { +%% if target["family"] in ["h7"] + ADC{{ id }}->CFGR &= ~ADC3_CFGR_ALIGN; +%% else ADC{{ id }}->CFGR &= ~ADC_CFGR_ALIGN; +%% endif } } +%% endif bool modm::platform::Adc{{ id }}::setChannel( const Channel channel, const SampleTime sampleTime) { - if(static_cast(channel) > 18) { +%% if target["family"] in ["h7"] and resolution == 16: + if (static_cast(channel) > 19) { + return false; + } +%% else + if (static_cast(channel) > 18) { return false; } +%% endif + +%% if resolution == 16 +%% if target["family"] in ["h7"] and target["name"][0] in ["2", "3"] + ADC{{ id }}->PCSEL_RES0 = (1u << static_cast(channel)); +%% else + ADC{{ id }}->PCSEL = (1u << static_cast(channel)); +%% endif +%% endif uint32_t tmpreg; // SQR1[10:6] contain SQ1[4:0] @@ -206,7 +245,9 @@ void modm::platform::Adc{{ id }}::enableInterruptVector(const uint32_t priority, const bool enable) { -%% if id <= 2 +%% if id <= 2 and target["family"] in ["h7"] + const IRQn_Type INTERRUPT_VECTOR = ADC_IRQn; +%% elif id <= 2 and target["family"] not in ["h7"] const IRQn_Type INTERRUPT_VECTOR = ADC1_2_IRQn; %% elif id <= 5 const IRQn_Type INTERRUPT_VECTOR = ADC{{ id }}_IRQn; diff --git a/src/modm/platform/adc/stm32f3/module.lb b/src/modm/platform/adc/stm32f3/module.lb index cda75902de..ca8fc074bc 100644 --- a/src/modm/platform/adc/stm32f3/module.lb +++ b/src/modm/platform/adc/stm32f3/module.lb @@ -3,7 +3,7 @@ # # Copyright (c) 2016-2018, Niklas Hauser # Copyright (c) 2017, Fabian Greif -# Copyright (c) 2022, Christopher Durand +# Copyright (c) 2022-2023, Christopher Durand # # This file is part of the modm project. # @@ -33,6 +33,14 @@ class Instance(Module): instance_id = int(self.instance) properties["id"] = instance_id + if target["family"] == "h7": + if (instance_id != 3) or (target["name"][0] in ["4", "5"]): + properties["resolution"] = 16 + else: + properties["resolution"] = 12 + else: + properties["resolution"] = 12 + if instance_id == 1: if target["family"] == "f3": # 13-14 reserved @@ -48,6 +56,8 @@ class Instance(Module): # Channel 18: VRefint channels = [1,2,3,4,5,6,7,8,9,10,11,12,14,15] assert(len(channels) == 14) + elif target["family"] == "h7": + channels = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19] else: # 11-14 reserved channels = [1,2,3,4,5,6,7,8,9,10,15,16,17,18] @@ -64,6 +74,19 @@ class Instance(Module): elif target["family"] == "l5": # ADC1 is connected to 16 external channels + 2 internal channels channels = range(1,17) + elif target["family"] == "h7": + if target["name"] in ["a3", "b0", "b3"]: + # Channel 14: VBAT/4 + # Channel 15: DAC2 OUT1 + # Channel 16: DAC1 OUT1 + # Channel 17: DAC1 OUT2 + # Channel 18: VSENSE + # Channel 19: VREFINT + channels = [0,1,2,3,4,5,6,7,8,9,10,11,12,13] + else: + # Channel 16: DAC1 OUT1 + # Channel 17: DAC1 OUT2 + channels = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,18,19] else: # ADC2 is connected to 16 external channels + 2 internal channels channels = range(1,17) @@ -77,6 +100,17 @@ class Instance(Module): # Channel 18: VRefint channels = [1,2,3,4,5,6,7,8,9,10,11,12,14,15,16] assert(len(channels) == 15) + elif target["family"] == "h7": + if properties["resolution"] == 16: + # Channel 17: VBAT/4 + # Channel 18: VSENSE + # Channel 19: VREFINT + channels = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16] + else: + # Channel 16: VBAT/4 + # Channel 17: VSENSE + # Channel 18: VREFINT + channels = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] else: # ADC3 is connected to 12 external channels + 4 internal channels channels = [1,2,3,4,6,7,8,9,10,11,12,13] @@ -153,6 +187,21 @@ class Instance(Module): properties["id_common"] = "345" properties["id_common_u"] = "345_COMMON" properties["clock_mux"] = True + elif target["family"] == "h7": + properties["adc_ccr"] = "ADC_CCR" + if target["name"] in ["a3", "b0", "b3"]: + properties["ccipr"] = "SRDCCIPR" + else: + properties["ccipr"] = "D3CCIPR" + if instance_id in [1, 2]: + properties["ahb"] = "AHB1" + properties["id_common"] = "12" + properties["id_common_u"] = "12_COMMON" + else: + properties["ahb"] = "AHB4" + properties["id_common"] = "3" + properties["id_common_u"] = "3_COMMON" + properties["clock_mux"] = True else: raise NotImplementedError @@ -163,8 +212,6 @@ class Instance(Module): properties["shared_irq_ids"] = props["shared_irq_ids"] props["instances"].append(self.instance) - properties["resolution"] = 12 - env.template("adc.hpp.in", "adc_{}.hpp".format(self.instance)) env.template("adc_impl.hpp.in", "adc_{}_impl.hpp".format(self.instance)) env.template("adc_interrupt.hpp.in", "adc_interrupt_{}.hpp".format(self.instance)) @@ -177,7 +224,7 @@ def init(module): def prepare(module, options): device = options[":target"] - if not device.has_driver("adc:stm32-f3"): + if not device.has_driver("adc:stm32-f3") and not device.has_driver("adc:stm32-h7"): return False module.depends( @@ -199,10 +246,14 @@ def prepare(module, options): props["shared_irq_ids"] = [] for irq in shared_irqs: parts = irq[3:].split("_") - shared_irqs_ids = (int(parts[0]), int(parts[1]) ) + shared_irqs_ids = (int(parts[0]), int(parts[1])) props["shared_irqs"][irq] = shared_irqs_ids props["shared_irq_ids"].extend(shared_irqs_ids) + if device.identifier.family == "h7": + props["shared_irqs"]["ADC"] = (1, 2) + props["shared_irq_ids"].extend((1, 2)) + for instance in listify(device.get_driver("adc")["instance"]): module.add_submodule(Instance(int(instance))) From 478461ccc49306d58e4f8c7464a4e0e7e13b5654 Mon Sep 17 00:00:00 2001 From: Christopher Durand Date: Tue, 11 Jul 2023 18:27:03 +0200 Subject: [PATCH 2/4] [example] Add simple ADC example for Nucleo H723ZG --- examples/nucleo_h723zg/adc_simple/main.cpp | 54 +++++++++++++++++++ examples/nucleo_h723zg/adc_simple/project.xml | 10 ++++ 2 files changed, 64 insertions(+) create mode 100644 examples/nucleo_h723zg/adc_simple/main.cpp create mode 100644 examples/nucleo_h723zg/adc_simple/project.xml diff --git a/examples/nucleo_h723zg/adc_simple/main.cpp b/examples/nucleo_h723zg/adc_simple/main.cpp new file mode 100644 index 0000000000..b23dfa2016 --- /dev/null +++ b/examples/nucleo_h723zg/adc_simple/main.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2023, 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 +#include + +using namespace Board; + +// ADC1 (16 bit) channel 15 +using A0 = GpioA3; +// ADC3 (12 bit) channel 10 +using A1 = GpioC0; + +int main() +{ + Board::initialize(); + + Adc1::connect(); + Adc1::initialize(Adc1::ClockMode::SynchronousPrescaler4, + Adc1::ClockSource::NoClock, + Adc1::Prescaler::Disabled, + Adc1::CalibrationMode::SingleEndedInputsMode); + + Adc3::connect(); + Adc3::initialize(Adc3::ClockMode::SynchronousPrescaler4, + Adc3::ClockSource::NoClock, + Adc3::Prescaler::Disabled, + Adc3::CalibrationMode::SingleEndedInputsMode); + + MODM_LOG_INFO << "ADC Test\n"; + + while (true) { + Adc1::setPinChannel(Adc1::SampleTime::Cycles17); + Adc1::startConversion(); + while (!Adc1::isConversionFinished()); + MODM_LOG_INFO << "ADC1 CH15: " << Adc1::getValue() << '\n'; + + Adc3::setPinChannel(Adc3::SampleTime::Cycles13); + Adc3::startConversion(); + while (!Adc3::isConversionFinished()); + MODM_LOG_INFO << "ADC3 CH10: " << Adc3::getValue() << '\n'; + + modm::delay_ms(500); + } + + return 0; +} diff --git a/examples/nucleo_h723zg/adc_simple/project.xml b/examples/nucleo_h723zg/adc_simple/project.xml new file mode 100644 index 0000000000..f74ce06da3 --- /dev/null +++ b/examples/nucleo_h723zg/adc_simple/project.xml @@ -0,0 +1,10 @@ + + modm:nucleo-h723zg + + + + + modm:build:scons + modm:platform:adc:* + + From 384dd9c518aca6c01a7fbff55fd24bb140467aa4 Mon Sep 17 00:00:00 2001 From: Christopher Durand Date: Wed, 12 Jul 2023 12:33:32 +0200 Subject: [PATCH 3/4] [stm32] Add support for injected conversions in stm32-f3 ADC driver --- src/modm/platform/adc/stm32f3/adc.hpp.in | 70 ++++++++++++++- src/modm/platform/adc/stm32f3/adc_impl.hpp.in | 89 ++++++++++++++++--- 2 files changed, 143 insertions(+), 16 deletions(-) diff --git a/src/modm/platform/adc/stm32f3/adc.hpp.in b/src/modm/platform/adc/stm32f3/adc.hpp.in index 610238c983..947d7f5238 100644 --- a/src/modm/platform/adc/stm32f3/adc.hpp.in +++ b/src/modm/platform/adc/stm32f3/adc.hpp.in @@ -456,14 +456,51 @@ public: /** * @return If the conversion is finished. - * @pre A conversion should have been stared with startConversion() + * @pre A conversion should have been started with startConversion() */ static inline bool isConversionFinished(); /** - * @return The most recent 16bit result of the ADC conversion. - * @pre A conversion should have been stared with startConversion() + * Start a new injected conversion sequence. + * + * @pre Channels must be selected with setInjectedConversionChannel(). + */ + static inline void + startInjectedConversionSequence(); + + /** + * @arg index Index of injected conversion in sequence (0..3) + * @return true if configuration is successful, false if arguments are invalid + */ + static inline bool + setInjectedConversionChannel(uint8_t index, Channel channel, SampleTime sampleTime); + + /** + * @arg index Index of injected conversion in sequence (0..3) + * @return true if configuration is successful, false if arguments are invalid + */ + template + static inline bool + setInjectedConversionChannel(uint8_t index, SampleTime sampleTime); + + /** + * @arg length Length of injected conversion sequence (1..4) + * @return true if configuration is successful, false if arguments are invalid + */ + static inline bool + setInjectedConversionSequenceLength(uint8_t length); + + /** + * @return If the injected conversion sequence is finished. + * @pre An injected conversion should have been started with startInjectedConversionSequence() + */ + static inline bool + isInjectedConversionFinished(); + + /** + * @return The most recent result of the ADC conversion. + * @pre A conversion should have been started with startConversion() * * To have a blocking GET you might do it this way: * @code @@ -473,12 +510,33 @@ public: } @endcode */ - static inline uint16_t + static inline auto getValue() { return ADC{{ id }}->DR; } + /** + * Get result of injected conversion. + * @pre The injected conversion sequence is completed. + */ + static inline auto + getInjectedConversionValue(uint8_t index) + { + switch (index) { + case 0: + return ADC{{ id }}->JDR1; + case 1: + return ADC{{ id }}->JDR2; + case 2: + return ADC{{ id }}->JDR3; + case 3: + [[fallthrough]]; + default: + return ADC{{ id }}->JDR4; + } + } + static inline void enableInterruptVector(const uint32_t priority, const bool enable = true); @@ -493,6 +551,10 @@ public: static inline void acknowledgeInterruptFlags(const InterruptFlag_t flags); + +private: + static inline bool + configureChannel(Channel channel, SampleTime sampleTime); }; } diff --git a/src/modm/platform/adc/stm32f3/adc_impl.hpp.in b/src/modm/platform/adc/stm32f3/adc_impl.hpp.in index e854b823db..45a3720009 100644 --- a/src/modm/platform/adc/stm32f3/adc_impl.hpp.in +++ b/src/modm/platform/adc/stm32f3/adc_impl.hpp.in @@ -62,7 +62,7 @@ modm::platform::Adc{{ id }}::initialize(const ClockMode clk, } %% if resolution == 16 - // Always set boost mode for maximum clock for now. + // Always set highest boost mode for maximum clock for now. // This will increase the ADC power consumption on lower ADC clocks. // TODO: compute ADC input clock and choose correct setting ADC{{id}}->CR |= ADC_CR_BOOST_Msk; @@ -171,8 +171,8 @@ modm::platform::Adc{{ id }}::setLeftAdjustResult(const bool enable) %% endif bool -modm::platform::Adc{{ id }}::setChannel( const Channel channel, - const SampleTime sampleTime) +modm::platform::Adc{{ id }}::configureChannel(Channel channel, + SampleTime sampleTime) { %% if target["family"] in ["h7"] and resolution == 16: if (static_cast(channel) > 19) { @@ -186,16 +186,13 @@ modm::platform::Adc{{ id }}::setChannel( const Channel channel, %% if resolution == 16 %% if target["family"] in ["h7"] and target["name"][0] in ["2", "3"] - ADC{{ id }}->PCSEL_RES0 = (1u << static_cast(channel)); + ADC{{ id }}->PCSEL_RES0 |= (1u << static_cast(channel)); %% else - ADC{{ id }}->PCSEL = (1u << static_cast(channel)); + ADC{{ id }}->PCSEL |= (1u << static_cast(channel)); %% endif %% endif - uint32_t tmpreg; - // SQR1[10:6] contain SQ1[4:0] - ADC{{ id }}->SQR1 = (static_cast(channel) & 0b11111) << 6; - + uint32_t tmpreg = 0; if (static_cast(channel) < 10) { tmpreg = ADC{{ id }}->SMPR1 & ((~ADC_SMPR1_SMP0) << (static_cast(channel) * 3)); @@ -211,6 +208,20 @@ modm::platform::Adc{{ id }}::setChannel( const Channel channel, ADC{{ id }}->SMPR2 = tmpreg; } return true; + +} + + +bool +modm::platform::Adc{{ id }}::setChannel(Channel channel, + SampleTime sampleTime) +{ + if (!configureChannel(channel, sampleTime)) { + return false; + } + + ADC{{ id }}->SQR1 = (static_cast(channel) << ADC_SQR1_SQ1_Pos) & ADC_SQR1_SQ1_Msk; + return true; } void @@ -223,9 +234,8 @@ modm::platform::Adc{{ id }}::setFreeRunningMode(const bool enable) } } - void -modm::platform::Adc{{ id }}::startConversion(void) +modm::platform::Adc{{ id }}::startConversion() { // TODO: maybe add more interrupt flags acknowledgeInterruptFlags(InterruptFlag::EndOfRegularConversion | @@ -235,11 +245,66 @@ modm::platform::Adc{{ id }}::startConversion(void) } bool -modm::platform::Adc{{ id }}::isConversionFinished(void) +modm::platform::Adc{{ id }}::isConversionFinished() { return static_cast(getInterruptFlags() & InterruptFlag::EndOfRegularConversion); } +void +modm::platform::Adc{{ id }}::startInjectedConversionSequence() +{ + acknowledgeInterruptFlags(InterruptFlag::EndOfInjectedConversion | + InterruptFlag::EndOfInjectedSequenceOfConversions); + + ADC{{ id }}->CR |= ADC_CR_JADSTART; +} + +bool +modm::platform::Adc{{ id }}::setInjectedConversionChannel(uint8_t index, Channel channel, + SampleTime sampleTime) +{ + if (index >= 4) { + return false; + } + if (!configureChannel(channel, sampleTime)) { + return false; + } + + static_assert(ADC_JSQR_JSQ2_Pos == ADC_JSQR_JSQ1_Pos + 6); + static_assert(ADC_JSQR_JSQ3_Pos == ADC_JSQR_JSQ2_Pos + 6); + static_assert(ADC_JSQR_JSQ4_Pos == ADC_JSQR_JSQ3_Pos + 6); + + const uint32_t pos = ADC_JSQR_JSQ1_Pos + 6 * index; + const uint32_t mask = ADC_JSQR_JSQ1_Msk << (6 * index); + ADC{{ id }}->JSQR = (ADC{{ id }}->JSQR & ~mask) | (static_cast(channel) << pos); + return true; +} + +template +bool +modm::platform::Adc{{ id }}::setInjectedConversionChannel(uint8_t index, + SampleTime sampleTime) +{ + return setInjectedConversionChannel(index, getPinChannel(), sampleTime); +} + +bool +modm::platform::Adc{{ id }}::setInjectedConversionSequenceLength(uint8_t length) +{ + if (length < 1 or length > 4) { + return false; + } + ADC{{ id }}->JSQR = (ADC{{ id }}->JSQR & ~ADC_JSQR_JL) + | ((length - 1) << ADC_JSQR_JL_Pos); + return true; +} + +bool +modm::platform::Adc{{ id }}::isInjectedConversionFinished() +{ + return static_cast(getInterruptFlags() & InterruptFlag::EndOfInjectedSequenceOfConversions); +} + // ---------------------------------------------------------------------------- void modm::platform::Adc{{ id }}::enableInterruptVector(const uint32_t priority, From 4d692276573720d59642f262d8a8b0019b4d5829 Mon Sep 17 00:00:00 2001 From: Christopher Durand Date: Wed, 12 Jul 2023 12:35:24 +0200 Subject: [PATCH 4/4] [example] Add injected conversion ADC example for Nucleo H723ZG --- .../adc_injected_conversion/main.cpp | 56 +++++++++++++++++++ .../adc_injected_conversion/project.xml | 10 ++++ 2 files changed, 66 insertions(+) create mode 100644 examples/nucleo_h723zg/adc_injected_conversion/main.cpp create mode 100644 examples/nucleo_h723zg/adc_injected_conversion/project.xml diff --git a/examples/nucleo_h723zg/adc_injected_conversion/main.cpp b/examples/nucleo_h723zg/adc_injected_conversion/main.cpp new file mode 100644 index 0000000000..56ac8b1974 --- /dev/null +++ b/examples/nucleo_h723zg/adc_injected_conversion/main.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2023, 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 +#include + +using namespace Board; + +int main() +{ + Board::initialize(); + + Adc1::connect(); + Adc1::initialize(Adc1::ClockMode::SynchronousPrescaler4, + Adc1::ClockSource::NoClock, + Adc1::Prescaler::Disabled, + Adc1::CalibrationMode::SingleEndedInputsMode); + + MODM_LOG_INFO << "ADC Injected Conversion Test\n"; + + Adc1::setInjectedConversionSequenceLength(4); + Adc1::setInjectedConversionChannel(0, Adc1::SampleTime::Cycles17); + Adc1::setInjectedConversionChannel(1, Adc1::SampleTime::Cycles17); + Adc1::setInjectedConversionChannel(2, Adc1::SampleTime::Cycles17); + Adc1::setInjectedConversionChannel(3, Adc1::SampleTime::Cycles17); + + while (true) { + // start regular conversion + Adc1::setPinChannel(Adc1::SampleTime::Cycles17); + Adc1::startConversion(); + + Adc1::startInjectedConversionSequence(); + while (!Adc1::isInjectedConversionFinished()); + + MODM_LOG_INFO << "ADC1 CH15 (injected): " << Adc1::getInjectedConversionValue(0) << '\n'; + MODM_LOG_INFO << "ADC1 CH15 (injected): " << Adc1::getInjectedConversionValue(2) << '\n'; + MODM_LOG_INFO << "ADC1 CH10 (injected): " << Adc1::getInjectedConversionValue(1) << '\n'; + MODM_LOG_INFO << "ADC1 CH10 (injected): " << Adc1::getInjectedConversionValue(3) << '\n'; + + // wait for regular conversion to finish + while (!Adc1::isConversionFinished()); + MODM_LOG_INFO << "ADC1 CH10 (regular): " << Adc1::getValue() << "\n\n"; + + Leds::toggle(); + modm::delay_ms(500); + } + + return 0; +} diff --git a/examples/nucleo_h723zg/adc_injected_conversion/project.xml b/examples/nucleo_h723zg/adc_injected_conversion/project.xml new file mode 100644 index 0000000000..c79cb1f183 --- /dev/null +++ b/examples/nucleo_h723zg/adc_injected_conversion/project.xml @@ -0,0 +1,10 @@ + + modm:nucleo-h723zg + + + + + modm:build:scons + modm:platform:adc:1 + +