diff --git a/examples/stm32f3_discovery/blink/main.cpp b/examples/stm32f3_discovery/blink/main.cpp index 12ac9b1080..149831823f 100644 --- a/examples/stm32f3_discovery/blink/main.cpp +++ b/examples/stm32f3_discovery/blink/main.cpp @@ -15,30 +15,45 @@ using namespace Board; +RtGpio north = Board::LedNorth(); +RtGpio northEast = Board::LedNorthEast(); +RtGpio east = Board::LedEast(); +RtGpio southEast = Board::LedSouthEast(); +RtGpio south = Board::LedSouth(); +RtGpio southWest = Board::LedSouthWest(); +RtGpio west = Board::LedWest(); +RtGpio northWest = Board::LedNorthWest(); + +modm::Gpio &gnorth = north; + int main() { Board::initialize(); - Board::LedNorth::set(); + using Config = modm::Gpio::Config; + gnorth.configure(Config::Output | Config::OpenDrain | + Config::PullUp | Config::SpeedLow | + Config::LogicInverted); + gnorth.set(true); while (true) { - Board::LedNorth::toggle(); + north.toggle(); modm::delay(100ms); - Board::LedNorthEast::toggle(); + northEast.toggle(); modm::delay(100ms); - Board::LedEast::toggle(); + east.toggle(); modm::delay(100ms); - Board::LedSouthEast::toggle(); + southEast.toggle(); modm::delay(100ms); - Board::LedSouth::toggle(); + south.toggle(); modm::delay(100ms); - Board::LedSouthWest::toggle(); + southWest.toggle(); modm::delay(100ms); - Board::LedWest::toggle(); + west.toggle(); modm::delay(100ms); - Board::LedNorthWest::toggle(); + northWest.toggle(); modm::delay(100ms); } diff --git a/src/modm/architecture/interface/gpio.hpp b/src/modm/architecture/interface/gpio.hpp index cf015b6d9c..8eab236d3a 100644 --- a/src/modm/architecture/interface/gpio.hpp +++ b/src/modm/architecture/interface/gpio.hpp @@ -10,18 +10,19 @@ */ // ---------------------------------------------------------------------------- -#ifndef MODM_GPIO_HPP -#define MODM_GPIO_HPP +#pragma once #include #include +#include namespace modm { /// @ingroup modm_architecture_gpio -struct Gpio +class Gpio { +public: /** * These constants refer to the *logical* state of the GPIO. * The physical state is determined by the configuration and external connection. @@ -40,6 +41,39 @@ struct Gpio InOut = 2, //< GPIO is both Input and Output Special = 3, //< GPIO is configured with a special purpose }; + + // Delegate to the platform for the Config_t implementation + #include + +public: + virtual ~Gpio() {}; + + // Config methods + virtual void + configure(Config_t config) = 0; + + // Input methods + virtual bool + read() const = 0; + + // Output methods + virtual void + set(bool value) = 0; + + virtual bool + isSet() const = 0; + + virtual void + toggle() + { set(not isSet()); } + + inline void + set() + { set(true); } + + inline void + reset() + { set(false); } }; /** @@ -187,4 +221,3 @@ class GpioPort } // namespace modm -#endif // MODM_GPIO_HPP diff --git a/src/modm/platform/gpio/stm32/config.hpp.in b/src/modm/platform/gpio/stm32/config.hpp.in new file mode 100644 index 0000000000..4d7fc1eb0b --- /dev/null +++ b/src/modm/platform/gpio/stm32/config.hpp.in @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2016-2018, Niklas Hauser + * + * 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/. + */ +// ---------------------------------------------------------------------------- + +#pragma once + +enum class +Config : uint32_t +{ + /// @cond + _Mode = 0x8000'0000, + _ModeMaskAf = 0x0000'000f, + _ModeShiftAf = 0, + _ModeMask = 0x0000'0030, + _ModeShift = 4, + _Alternate = 0x20 | _Mode, + + _Input = 0x4000'0000, + _InputMask = 0x0000'0f00, + _InputShift = 8, + + _Output = 0x2000'0000, + _OutputMask = 0x0000'f000, + _OutputShift = 12, + + _Speed = 0x1000'0000, + _SpeedMask = 0x0003'0000, + _SpeedShift = 16, + + _Logic = 0x0800'0000, + _LogicMask = 0x0010'0000, + _LogicShift = 20, + /// @endcond + + Input = 0x00 | _Mode, + Output = 0x10 | _Mode, +%% if target["family"] in ["f1"] + Alternate = 0x20 | _Mode, +%% endif + Analog = 0x30 | _Mode, + +%% if target["family"] in ["f1"] + Floating = 0x400 | _Input, + PullUp = 0x900 | _Input, + PullDown = 0x800 | _Input, +%% else + Floating = 0x000 | _Input, + PullUp = 0x100 | _Input, + PullDown = 0x200 | _Input, +%% endif + + PushPull = 0x0000 | _Output, +%% if target["family"] in ["f1"] + OpenDrain = 0x4000 | _Output, +%% else + OpenDrain = 0x1000 | _Output, +%% endif + +%% if target["family"] in ["f1"] + SpeedLow = 0x2'0000 | _Speed, + SpeedMedium = 0x1'0000 | _Speed, + SpeedHigh = 0x3'0000 | _Speed, + SpeedVeryHigh = 0x3'0000 | _Speed, +%% elif target["family"] in ["f0", "f3"] + SpeedLow = 0x0'0000 | _Speed, + SpeedMedium = 0x1'0000 | _Speed, + SpeedHigh = 0x3'0000 | _Speed, + SpeedVeryHigh = 0x3'0000 | _Speed, +%% else + SpeedLow = 0x0'0000 | _Speed, + SpeedMedium = 0x1'0000 | _Speed, + SpeedHigh = 0x2'0000 | _Speed, + SpeedVeryHigh = 0x3'0000 | _Speed, +%% endif + + LogicNormal = 0x00'0000 | _Logic, + LogicInverted = 0x10'0000 | _Logic, +}; +MODM_FLAGS32(Config); + +%% if target["family"] not in ["f1"] +static constexpr Config_t +ConfigAlternate(uint8_t af) +{ return Config::_Mode | Config(0x20 | (af & 0xf)); } +%% endif + diff --git a/src/modm/platform/gpio/stm32/module.lb b/src/modm/platform/gpio/stm32/module.lb index 2e4814dcbe..acb943b79a 100644 --- a/src/modm/platform/gpio/stm32/module.lb +++ b/src/modm/platform/gpio/stm32/module.lb @@ -300,6 +300,7 @@ def build(env): if "f1" in driver["type"]: env.template("connector_specialized.hpp.in", filters={"formatPeripheral": get_driver, "printSignalMap": print_remap_group_table}) env.template("base.hpp.in") + env.template("config.hpp.in") env.template("unused.hpp.in") if len(env["enable_ports"]): env.template("enable.cpp.in") diff --git a/src/modm/platform/gpio/stm32/pin.hpp.in b/src/modm/platform/gpio/stm32/pin.hpp.in index 68335efc6d..04688a0745 100644 --- a/src/modm/platform/gpio/stm32/pin.hpp.in +++ b/src/modm/platform/gpio/stm32/pin.hpp.in @@ -31,7 +31,7 @@ using GpioInput{{ port ~ pin }} = Gpio{{ port ~ pin }}; /// IO class for Pin {{port ~ pin}} /// @ingroup modm_platform_gpio -class Gpio{{ port ~ pin }} : public Gpio, public ::modm::GpioIO +class Gpio{{ port ~ pin }} : public Gpio { template friend class GpioSet; @@ -40,10 +40,14 @@ class Gpio{{ port ~ pin }} : public Gpio, public ::modm::GpioIO friend class Adc1; friend class Adc2; friend class Adc3; friend class Adc4; public: + using Direction = ::modm::Gpio::Direction; + using Config = ::modm::Gpio::Config; + using Config_t = ::modm::Gpio::Config_t; using Output = Gpio{{ port ~ pin }}; using Input = Gpio{{ port ~ pin }}; using IO = Gpio{{ port ~ pin }}; using Type = Gpio{{ port ~ pin }}; + static constexpr Direction direction = Direction::InOut; static constexpr bool isInverted = false; static constexpr Port port = Port::{{port}}; ///< Port name static constexpr uint8_t pin = {{pin}}; ///< Pin number @@ -85,6 +89,24 @@ public: }; %% endif + inline static void configure(Config_t config) + { + if (config & Config::_Input) + configure(InputType( (config & Config::_InputMask).value >> uint8_t(Config::_InputShift) )); + if (config & Config::_Output) + configure(OutputType( (config & Config::_OutputMask).value >> uint8_t(Config::_OutputShift) ), + (config & Config::_Speed) ? OutputSpeed( (config & Config::_SpeedMask).value >> + uint8_t(Config::_SpeedShift) ) : + OutputSpeed::MHz50); else + if (config & Config::_Speed) + configure(OutputSpeed( (config & Config::_SpeedMask).value >> uint8_t(Config::_SpeedShift) )); + + if (config & Config::Input) setInput(); else + if (config & Config::Output) setOutput(); else + if (config & Config::Analog) setAnalogInput(); else + if (config & Config::_Alternate) setAlternateFunction(config.value); + } + public: // GpioOutput // start documentation inherited @@ -99,6 +121,7 @@ public: } inline static bool isSet() { return ({{reg}}->ODR & mask); } // stop documentation inherited + inline static void configure(OutputSpeed speed) { PinSet::configure(speed); } inline static void configure(OutputType type, OutputSpeed speed = OutputSpeed::MHz50) { PinSet::configure(type, speed); } inline static void setOutput(OutputType type, OutputSpeed speed = OutputSpeed::MHz50) { PinSet::setOutput(type, speed); } // GpioInput diff --git a/src/modm/platform/gpio/stm32/pin_f1.hpp.in b/src/modm/platform/gpio/stm32/pin_f1.hpp.in index 469e5d7661..d1e5a6d12e 100644 --- a/src/modm/platform/gpio/stm32/pin_f1.hpp.in +++ b/src/modm/platform/gpio/stm32/pin_f1.hpp.in @@ -43,10 +43,14 @@ struct Gpio{{ port ~ pin }} : public Gpio, ::modm::GpioIO using PinSet = GpioSet; friend class Adc1; friend class Adc2; friend class Adc3; public: + using Direction = ::modm::Gpio::Direction; + using Config = ::modm::Gpio::Config; + using Config_t = ::modm::Gpio::Config_t; using Output = Gpio{{ port ~ pin }}; using Input = Gpio{{ port ~ pin }}; using IO = Gpio{{ port ~ pin }}; using Type = Gpio{{ port ~ pin }}; + static constexpr Direction direction = Direction::InOut; static constexpr bool isInverted = false; static constexpr Port port = Port::{{port}}; ///< Port name static constexpr uint8_t pin = {{pin}}; ///< Pin Number @@ -67,6 +71,23 @@ public: inline static void setAnalogInput() { PinSet::setAnalogInput(); } /// @endcond + inline static void configure(Config_t config) + { + if (config & Config::_Input) + configure(InputType( (config & Config::_InputMask).value >> uint8_t(Config::_InputShift) )); + if (config & Config::_Output) + configure(OutputType( (config & Config::_OutputMask).value >> uint8_t(Config::_OutputShift) ), + (config & Config::_Speed) ? OutputSpeed( (config & Config::_SpeedMask).value >> + uint8_t(Config::_SpeedShift) ) : + OutputSpeed::MHz50); else + if (config & Config::_Speed) + configure(OutputSpeed( (config & Config::_SpeedMask).value >> uint8_t(Config::_SpeedShift) )); + + if (config & Config::Input) setInput(); else + if (config & Config::Output) setOutput(); else + if (config & Config::Analog) setAnalogInput(); + } + public: // GpioOutput // start documentation inherited @@ -81,6 +102,7 @@ public: } inline static bool isSet() { return ({{reg}}->ODR & mask); } // stop documentation inherited + inline static void configure(OutputSpeed speed) { PinSet::configure(speed); } inline static void configure(OutputType type, OutputSpeed speed = OutputSpeed::MHz50) { PinSet::configure(type, speed); } inline static void setOutput(OutputType type, OutputSpeed speed = OutputSpeed::MHz50) { PinSet::setOutput(type, speed); } // GpioInput diff --git a/src/modm/platform/gpio/stm32/runtime_gpio.hpp.in b/src/modm/platform/gpio/stm32/runtime_gpio.hpp.in new file mode 100644 index 0000000000..fa19cd7760 --- /dev/null +++ b/src/modm/platform/gpio/stm32/runtime_gpio.hpp.in @@ -0,0 +1,228 @@ +/* +* Copyright (c) 2021, 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/. +*/ +// ---------------------------------------------------------------------------- + +#pragma once + +#include + +#include "../device.hpp" +#include "base.hpp" + +namespace modm::platform +{ + +/** +* Runtime Gpio wrapper +* +* Example: +* @code +* RtGpio gpio = GpioA1(); +* gpio.toggle(); +* @endcode +* +* @author Christopher Durand, Niklas Hauser +* @ingroup modm_platform_gpio +*/ +class RtGpio : public Gpio, public ::modm::Gpio +{ + const uintptr_t portAddress; + const uint16_t pinMask; + const uint8_t position2; + bool isInverted{false}; + + inline GPIO_TypeDef* p() const { return reinterpret_cast(portAddress); } + inline uint32_t mask() const { return pinMask; } +%% if target["family"] in ["f1"] + inline uint32_t crl(uint8_t value=0xf) const { + return uint32_t(value) << ((position2 & 0xf) << 1); + } + inline uint32_t crh(uint8_t value=0xf) const { + return uint32_t(value) << (((position2 & 0xf) << 1) - 32); + } +%% else + inline uint32_t mask2(uint32_t value=3ul) const { return value << position2; } +%% endif + +public: + constexpr RtGpio(Gpio::Port port, uint8_t pin, bool inverted=false): + portAddress(GPIOA_BASE + (uint32_t(port) << 10)), + pinMask(1u << (pin & 0xf)), position2((pin & 0xf) << 1), isInverted(inverted) + {} + + // Construct from a specific Gpio type + template requires requires { Gpio::port; Gpio::pin; Gpio::isInverted; } + constexpr RtGpio(Gpio) : RtGpio(Gpio::port, Gpio::pin, Gpio::isInverted) + {} + +public: + constexpr uint8_t + port() const + { + return uint8_t(portAddress >> 10) & 0xf; + } + + constexpr uint8_t + pin() const + { + return position2 >> 1; + } + + inline void + disconnect() + { + configure(Config::Input); + +%% if target["family"] not in ["f1"] + const uint8_t af_id = position2 >> 4; + const uint8_t af_offset = (position2 * 2) % 32; + p()->AFR[af_id] &= ~(0xf << af_offset); + %% if target["family"] in ["l4"] and target["name"] in ["71", "75", "76", "85", "86"] + p()->ASCR &= ~mask(); + %% endif +%% endif + } + + inline void + lock() + { + p()->LCKR = 0x10000 | mask(); + p()->LCKR = 0x00000 | mask(); + p()->LCKR = 0x10000 | mask(); + (void) p()->LCKR; + } + +public: + inline void + configure(Config_t config) override + { +%% if target["family"] in ["f1"] + const uint8_t cr_offset = (position2 & 0xf) << 1; + const auto cr_ptr = (position2 & 0x10) ? p().CRH : p().CRL; + const uint32_t cr = *cr_ptr; + const uint32_t mode = (cr >> cr_offset) | 0xf; + + if (config & Config::_Mode) + { + switch((config & Config::_ModeMask).value >> uint8_t(Config::_ModeShift)) + { + case i(Mode::Input): default: + if (not (config & Config::_Input)) config |= Config::Floating; + break; + case i(Mode::Output): + if (not (config & Config::_Output)) config |= Config::PushPull; + break; + case i(Mode::Analog): + mode &= ~0xf; + config = Config(0); + break; + case i(Mode::AlternateFunction): + if (config & Config::OpenDrain) mode |= 0b1100; + else mode |= 0b1000; + if (not (mode & 0b0011)) mode |= i(OutputSpeed::Medium); + break; + } + } + + if (config & Config::_Input) + { + const uint32_t type{ (config & Config::_InputMask).value >> uint8_t(Config::_InputShift) }; + mode = (mode & ~0xf) | type; + p()->BSRR = mask() << ((InputType(type) == InputType::PullUp) ? 0 : 16); + } + if (config & Config::_Output) + { + const uint32_t type{ (config & Config::_OutputMask).value >> uint8_t(Config::_OutputShift) }; + mode = (mode & ~0xc) | type; + if (not (config & Config::_Speed)) config |= Config::SpeedMedium; + } + if (config & Config::_Speed) + { + const uint32_t speed{ (config & Config::_SpeedMask).value >> uint8_t(Config::_SpeedShift) }; + mode = (mode & ~0x3) | speed; + } + *cr_ptr = (cr & ~(0xf << cr_offset)) | (mode << cr_offset); +%% else + if (config & Config::_Input) + { + const uint32_t type{ (config & Config::_InputMask).value >> uint8_t(Config::_InputShift) }; + p()->PUPDR = (p()->PUPDR & ~mask2()) | mask2(type); + } + if (config & Config::_Output) + { + if (config & Config::_OutputMask) p()->OTYPER |= mask(); + else p()->OTYPER &= ~mask(); + // if (not (config & Config::_Speed)) config |= Config::SpeedMedium; + } + if (config & Config::_Speed) + { + const uint32_t speed{ (config & Config::_SpeedMask).value >> uint8_t(Config::_SpeedShift) }; + p()->OSPEEDR = (p()->OSPEEDR & ~mask2()) | mask2(speed); + } + + if (config & Config::_Mode) + { + const uint8_t mode = (config & Config::_ModeMask).value >> uint8_t(Config::_ModeShift); + if (mode == i(Mode::Analog)) + { + p()->PUPDR &= ~mask2(); + %% if target["family"] in ["l4"] and target["name"] in ["71", "75", "76", "85", "86"] + p()->ASCR |= mask(); + %% endif + } + else if (mode == i(Mode::AlternateFunction)) + { + const uint8_t reg = position2 >> 4; + const uint8_t offset = (position2 & 0xf) << 1; + p()->AFR[reg] = (p()->AFR[reg] & ~(0xf << offset)) | ((config.value & 0xf) << offset); + } + p()->MODER = (p()->MODER & ~mask2()) | mask2(mode); + } +%% endif + + if (config & Config::_Logic) + { + const bool newInverted = bool(config & Config::_LogicMask); + if (isInverted ^ newInverted) + { + isInverted = newInverted; + toggle(); + } + } + } + + inline bool + read() const override + { + const bool value = p()->IDR & mask(); + return isInverted xor value; + } + + inline void + set(bool value) override + { + p()->BSRR = mask() << ((isInverted xor value) ? 0 : 16); + } + + inline bool + isSet() const override + { + const bool value = p()->ODR & mask(); + return isInverted xor value; + } +}; + +template +constexpr auto makeGpioArray() +{ + return std::array{ Gpios()... }; +} + +} // namespace modm::platform diff --git a/src/modm/platform/gpio/stm32/set.hpp.in b/src/modm/platform/gpio/stm32/set.hpp.in index 9b643af7ee..32c66b4510 100644 --- a/src/modm/platform/gpio/stm32/set.hpp.in +++ b/src/modm/platform/gpio/stm32/set.hpp.in @@ -110,6 +110,22 @@ public: %% endif } + static void configure(OutputSpeed speed) + { +%% if target["family"] in ["f1"] + %% for port, id in ports.items() + if constexpr (crl({{id}})) GPIO{{port}}->CRL = (GPIO{{port}}->CRL & ~crl({{id}}, 0b0011)) | (i(speed) * crl({{id}}, 0b0001)); + if constexpr (crh({{id}})) GPIO{{port}}->CRH = (GPIO{{port}}->CRH & ~crh({{id}}, 0b0011)) | (i(speed) * crh({{id}}, 0b0001)); + %% endfor +%% else + %% for port, id in ports.items() + if constexpr (mask({{id}})) { + GPIO{{port}}->OSPEEDR = (GPIO{{port}}->OSPEEDR & ~mask2({{id}})) | (i(speed) * mask2({{id}}, 0b01)); + } + %% endfor +%% endif + } + static void setInput() { %% if target["family"] in ["f1"] diff --git a/src/modm/platform/gpio/stm32/unused.hpp.in b/src/modm/platform/gpio/stm32/unused.hpp.in index c37b665c16..0270da83ed 100644 --- a/src/modm/platform/gpio/stm32/unused.hpp.in +++ b/src/modm/platform/gpio/stm32/unused.hpp.in @@ -53,61 +53,69 @@ namespace platform * @author Niklas Hauser * @ingroup modm_platform_gpio */ -class GpioUnused : public Gpio, public ::modm::GpioIO +class GpioUnused : public Gpio { public: + using Direction = ::modm::Gpio::Direction; + using Config = ::modm::Gpio::Config; + using Config_t = ::modm::Gpio::Config_t; + using Output = GpioUnused; using Input = GpioUnused; using IO = GpioUnused; using Type = GpioUnused; + + static constexpr Direction direction = Direction::InOut; static constexpr bool isInverted = false; static constexpr Port port = Port(-1); static constexpr uint8_t pin = uint8_t(-1); static constexpr uint16_t mask = 0; -protected: +public: /// @cond - static void setAlternateFunction(uint8_t) {} - static void setAnalogInput() {} + static inline void setAlternateFunction(uint8_t) {} + static inline void setAnalogInput() {} + static inline void configure(Config_t) {} /// @endcond public: // GpioOutput // start documentation inherited - static void setOutput() {} - static void setOutput(bool) {} - static void set() {} - static void set(bool) {} - static void reset() {} - static void toggle() {} - static bool isSet() { return false; } + static inline void setOutput() {} + static inline void setOutput(bool) {} + static inline void set() {} + static inline void set(bool) {} + static inline void reset() {} + static inline void toggle() {} + static inline bool isSet() { return false; } // stop documentation inherited - static void configure(OutputType, OutputSpeed = OutputSpeed::MHz50) {} - static void setOutput(OutputType, OutputSpeed = OutputSpeed::MHz50) {} + static inline void configure(OutputSpeed) {} + static inline void configure(OutputType, OutputSpeed = OutputSpeed::MHz50) {} + static inline void setOutput(OutputType, OutputSpeed = OutputSpeed::MHz50) {} // GpioInput // start documentation inherited - static void setInput() {} - static bool read() { return false; } + static inline void setInput() {} + static inline bool read() { return false; } // end documentation inherited - static void configure(InputType) {} - static void setInput(InputType) {} + static inline void configure(InputType) {} + static inline void setInput(InputType) {} // External Interrupts - static void enableExternalInterrupt() {} - static void disableExternalInterrupt() {} - static void enableExternalInterruptVector(const uint32_t) {} - static void disableExternalInterruptVector() {} - static void setInputTrigger(const InputTrigger) {} - static bool getExternalInterruptFlag() { return false; } + static inline void enableExternalInterrupt() {} + static inline void disableExternalInterrupt() {} + static inline void enableExternalInterruptVector(const uint32_t) {} + static inline void disableExternalInterruptVector() {} + static inline void setInputTrigger(const InputTrigger) {} + static inline bool getExternalInterruptFlag() { return false; } /// Reset the interrupt flag in the interrupt routine. - static void acknowledgeExternalInterruptFlag() {} + static inline void acknowledgeExternalInterruptFlag() {} // GpioIO // start documentation inherited - static Direction getDirection() { return Direction::Special; } + static inline Direction getDirection() { return Direction::Special; } // end documentation inherited - static void lock() {} - static void disconnect() {} + static inline void lock() {} + static inline void disconnect() {} public: /// @cond @@ -117,7 +125,7 @@ public: { using Gpio = GpioUnused; static constexpr Gpio::Signal Signal = Gpio::Signal::{{ name }}; - static void connect() {} + static inline void connect() {} }; %% endfor /// @endcond