diff --git a/src/modm/platform/adc/stm32f3/adc.hpp.in b/src/modm/platform/adc/stm32f3/adc.hpp.in index 893a0f64a2..7072d5869b 100644 --- a/src/modm/platform/adc/stm32f3/adc.hpp.in +++ b/src/modm/platform/adc/stm32f3/adc.hpp.in @@ -309,6 +309,14 @@ public: %% endif }; + enum class ExternalTriggerPolarity + { + NoTriggerDetection = 0x0u, + RisingEdge = 0x1u, + FallingEdge = 0x2u, + RisingAndFallingEdge = 0x3u, + }; + enum class Interrupt : uint32_t { Ready = ADC_IER_ADRDYIE, @@ -342,6 +350,57 @@ public: }; MODM_FLAGS32(InterruptFlag); + /** + * 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 = 0x00u, + Event1 = 0x01u, + Event2 = 0x02u, + Event3 = 0x03u, + Event4 = 0x04u, + Event5 = 0x05u, + Event6 = 0x06u, + Event7 = 0x07u, + Event9 = 0x09u, + Event10 = 0x0Au, + Event11 = 0x0Bu, + Event12 = 0x0Cu, + Event13 = 0x0Du, + Event14 = 0x0Eu, + Event15 = 0x0Fu, +%% if target["family"] in ["g4"] + Event16 = 0x10u, + Event17 = 0x11u, + Event18 = 0x12u, + Event19 = 0x13u, + Event20 = 0x14u, + Event21 = 0x15u, + Event22 = 0x16u, + Event23 = 0x17u, + Event24 = 0x18u, + Event25 = 0x19u, + Event26 = 0x1Au, + Event27 = 0x1Bu, + Event28 = 0x1Cu, + Event29 = 0x1Du, + Event30 = 0x1Eu, + Event31 = 0x1Fu, +%% endif + }; + + enum class OffsetSlot : uint8_t + { + Slot0 = 0, + Slot1 = 1, + Slot2 = 2, + Slot3 = 3, + }; + public: template< class... Signals > static void @@ -499,6 +558,23 @@ public: static inline void stopConversion(); + /** + * enable regular conversions on external trigger. + * + * @param externalTriggerPolarity + * Polarity of the external trigger signal. + * @param regularConversionExternalTrigger + * Regular conversion external trigger source. + */ + static inline void enableRegularConversionExternalTrigger( + ExternalTriggerPolarity externalTriggerPolarity, + RegularConversionExternalTrigger regularConversionExternalTrigger); + + /** + * Disable regular conversions external trigger. + */ + static inline void disableRegularConversionExternalTrigger( void ); + /** * @return If the conversion is finished. * @pre A conversion should have been started with startConversion() @@ -539,6 +615,23 @@ public: static inline bool setInjectedConversionSequenceLength(uint8_t length); + /** + * enable injected conversions on external trigger. + * + * @param externalTriggerPolarity + * Polarity of the external trigger signal. + * @param regularConversionExternalTrigger + * Regular conversion external trigger source. + */ + static inline void enableInjectedConversionExternalTrigger( + ExternalTriggerPolarity externalTriggerPolarity, + RegularConversionExternalTrigger regularConversionExternalTrigger); + + /** + * Disable injected conversions external trigger. + */ + static inline void disableInjectedConversionExternalTrigger(); + /** * @return If the injected conversion sequence is finished. * @pre An injected conversion should have been started with startInjectedConversionSequence() @@ -585,6 +678,76 @@ public: } } +%% if target["family"] in ["f3", "l4", "l5"] or (target["family"] in ["h7"] and target["name"][0] in ["4", "5", "a", "b"]): + /** + * @arg slot for the offset register (0..3) + * @arg channel channel to which the offset is applied + * @arg offset offset value to be applied to the channel + * @return true if the offset was successfully enabled, false if the slot is invalid + * @note The offset can only be applied when the adc is stopped! + */ + static inline bool + enableChannelOffset(const OffsetSlot slot, const Channel channel, const uint16_t offset); + + /** + * @arg slot for the offset register (0..3) + * @arg offset value applied to the channel + * @return true if the offset was successfully enabled, false if the slot is invalid + * @note The channel is determined by the GPIO pin type. + * @note The offset can only be applied when the adc is stopped! + */ + template + static inline bool enableChannelOffset(const OffsetSlot slot, const uint16_t offset) + { + return enableChannelOffset(slot, getPinChannel(), offset); + } + +%% else + /** + * @arg slot for the offset register (0..3) + * @arg channel channel to which the offset is applied + * @arg offset offset value to be applied to the channel + * @arg saturate if true, the adc result value is saturated to the range of the ADC + * @return true if the offset was successfully enabled, false if the slot is invalid + * @note The offset can only be applied when the adc is stopped! + */ + static inline bool + enableChannelOffset(const OffsetSlot slot, const Channel channel, const int16_t offset, const bool saturate = false); + + /** + * @arg slot for the offset register (0..3) + * @arg offset value applied to the channel + * @arg saturate if true, the adc result value is saturated to the range of the ADC + * @return true if the offset was successfully enabled, false if the slot is invalid + * @note The channel is determined by the GPIO pin type. + * @note The offset can only be applied when the adc is stopped! + */ + template + static inline bool enableChannelOffset(const OffsetSlot slot, const int16_t offset, const bool saturate = false) + { + return enableChannelOffset(slot, getPinChannel(), offset, saturate); + } +%% endif + + /** + * @arg slot for the offset register (0..3) + * @return true if the offset was successfully disabled, false if the slot is invalid + * @note The offset can only be disabled when the adc is stopped! + */ + static inline bool disableChannelOffset(OffsetSlot slot); + + /** + * @arg slot for the offset register (0..3) + * @return true if the offset was successfully disabled, false if the slot is invalid + * @note The channel is determined by the GPIO pin type. + * @note The offset can only be disabled when the adc is stopped! + */ + template + static inline bool disableChannelOffset(OffsetSlot slot) + { + return disableChannelOffset(slot); + } + static inline void enableInterruptVector(const uint32_t priority, const bool enable = true); diff --git a/src/modm/platform/adc/stm32f3/adc_impl.hpp.in b/src/modm/platform/adc/stm32f3/adc_impl.hpp.in index a8b59115bf..66724179c7 100644 --- a/src/modm/platform/adc/stm32f3/adc_impl.hpp.in +++ b/src/modm/platform/adc/stm32f3/adc_impl.hpp.in @@ -302,6 +302,25 @@ modm::platform::Adc{{ id }}::isConversionSequenceFinished() return static_cast(getInterruptFlags() & InterruptFlag::EndOfRegularSequenceOfConversions); } +void +modm::platform::Adc{{ id }}::enableRegularConversionExternalTrigger( + ExternalTriggerPolarity externalTriggerPolarity, + RegularConversionExternalTrigger regularConversionExternalTrigger) +{ + const auto polarity = (static_cast(externalTriggerPolarity) << ADC_CFGR_EXTEN_Pos); + const auto externalTrigger = (static_cast(regularConversionExternalTrigger) << ADC_CFGR_EXTSEL_Pos); + const auto mask = ADC_CFGR_EXTEN_Msk | ADC_CFGR_EXTSEL_Msk; + ADC{{ id }}->CFGR = (ADC{{ id }}->CFGR & ~mask) | polarity | externalTrigger; +} + +void +modm::platform::Adc{{ id }}::disableRegularConversionExternalTrigger() +{ + // Disable regular conversions external trigger by clearing the bits + // for polarity and external trigger source. + ADC{{ id }}->CFGR &= ~(ADC_CFGR_EXTEN_Msk | ADC_CFGR_EXTSEL_Msk); +} + void modm::platform::Adc{{ id }}::startInjectedConversionSequence() { @@ -351,6 +370,26 @@ modm::platform::Adc{{ id }}::setInjectedConversionSequenceLength(uint8_t length) return true; } +void +modm::platform::Adc{{ id }}::enableInjectedConversionExternalTrigger( + ExternalTriggerPolarity externalTriggerPolarity, + RegularConversionExternalTrigger regularConversionExternalTrigger) +{ + const auto polarity = (static_cast(externalTriggerPolarity) << ADC_JSQR_JEXTEN_Pos); + const auto externalTrigger = + (static_cast(regularConversionExternalTrigger) << ADC_JSQR_JEXTSEL_Pos); + const auto mask = ADC_JSQR_JEXTEN_Msk | ADC_JSQR_JEXTSEL_Msk; + ADC{{ id }}->JSQR = (ADC{{ id }}->JSQR & ~mask) | polarity | externalTrigger; +} + +void +modm::platform::Adc{{ id }}::disableInjectedConversionExternalTrigger() +{ + // Disable injected conversions external trigger by clearing the bits + // for polarity and external trigger source. + ADC{{id}}->JSQR &= ~(ADC_JSQR_JEXTEN_Msk | ADC_JSQR_JEXTSEL_Msk); +} + bool modm::platform::Adc{{ id }}::isInjectedConversionFinished() { @@ -410,3 +449,89 @@ modm::platform::Adc{{ id }}::acknowledgeInterruptFlags(const InterruptFlag_t fla // Writing a zero is ignored. ADC{{ id }}->ISR = flags.value; } + +%% if target["family"] in ["f3", "l4", "l5"] or (target["family"] in ["h7"] and target["name"][0] in ["4", "5", "a", "b"]): +bool +modm::platform::Adc{{ id }}::enableChannelOffset( const OffsetSlot slot, const Channel channel, const uint16_t offset) +{ + const uint32_t offsetMask = (offset << ADC_OFR1_OFFSET1_Pos) & ADC_OFR1_OFFSET1_Msk; + const uint32_t signMask = 0u; + const uint32_t saturateMask = 0u; +%% else +bool +modm::platform::Adc{{ id }}::enableChannelOffset( const OffsetSlot slot, const Channel channel, const int16_t offset, const bool saturate) +{ + const uint32_t offsetMask = (std::abs(offset) << ADC_OFR1_OFFSET1_Pos) & ADC_OFR1_OFFSET1_Msk; +%% if target["family"] in["h7"] +%# H7 names the bits differently, but the logic is the same + const uint32_t signMask = (offset > 0) ? ADC3_OFR1_OFFSETPOS : 0u; + const uint32_t saturateMask = saturate ? ADC3_OFR1_SATEN : 0u; +%% else + const uint32_t signMask = (offset > 0) ? ADC_OFR1_OFFSETPOS : 0u; + const uint32_t saturateMask = saturate ? ADC_OFR1_SATEN : 0u; +%% endif +%% endif +%% if target["family"] in ["h7"] +%% if target["name"][0] in ["4" , "5", "a", "b"] +%# no specific enable bit for H74x/H75x, so always 0 + const uint32_t enableMask = 0u; +%% else + %# H7 uses a different register bit names + const uint32_t enableMask = ADC3_OFR1_OFFSET1_EN; +%% endif +%% else + const uint32_t enableMask = ADC_OFR1_OFFSET1_EN; +%% endif + const uint32_t channelMask = (static_cast(channel) << ADC_OFR1_OFFSET1_CH_Pos) & ADC_OFR1_OFFSET1_CH_Msk; + const uint32_t offsetValue = channelMask | offsetMask | enableMask | saturateMask | signMask; + + if ((ADC{{id}}->CR & ADC_CR_JADSTART) || (ADC{{id}}->CR & ADC_CR_ADSTART)) + { + // ADC is currently converting, cannot set offset + return false; + } + + switch (slot) + { + case OffsetSlot::Slot0: ADC{{id}}->OFR1 = offsetValue; break; + case OffsetSlot::Slot1: ADC{{id}}->OFR2 = offsetValue; break; + case OffsetSlot::Slot2: ADC{{id}}->OFR3 = offsetValue; break; + case OffsetSlot::Slot3: ADC{{id}}->OFR4 = offsetValue; break; + default: + return false; // invalid slot + } + + return true; +} + +bool +modm::platform::Adc{{ id }}::disableChannelOffset(const OffsetSlot slot) +{ + if ( (ADC{{ id }}->CR & ADC_CR_JADSTART) || (ADC{{ id }}->CR & ADC_CR_ADSTART) ) { + // ADC is currently converting, cannot disable offset + return false; + } + +%% if target["family"] in ["h7"] +%% if target["name"][0] in ["4" , "5", "a", "b"] + %# no specific enable bit for H74x/H75x, so always 0 + const uint32_t enableMask = ADC_OFR1_OFFSET1_Msk; +%% else + %#H7 uses a different register bit names + const uint32_t enableMask = ADC3_OFR1_OFFSET1_EN; +%% endif +%% else + const uint32_t enableMask = ADC_OFR1_OFFSET1_EN; +%%endif + + switch (slot) + { + case OffsetSlot::Slot0: ADC{{id}}->OFR1 &= ~enableMask; break; + case OffsetSlot::Slot1: ADC{{id}}->OFR2 &= ~enableMask; break; + case OffsetSlot::Slot2: ADC{{id}}->OFR3 &= ~enableMask; break; + case OffsetSlot::Slot3: ADC{{id}}->OFR4 &= ~enableMask; break; + default: + return false; // invalid slot + } + return true; +}