From 4bc421eeb3d1eb7b22401d5b5d18a72a8c978714 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Fri, 24 Sep 2021 18:43:26 +0200 Subject: [PATCH 1/4] [gpio] Refactor GPIO and Signal implementation - Fewer files to generate and include - Signals without templates Co-authored-by: Christopher Durand --- docs/src/how-modm-works.md | 3 - .../platform/gpio/at90_tiny_mega/base.hpp.in | 74 ++--- .../platform/gpio/at90_tiny_mega/data.hpp.in | 84 ++++++ .../platform/gpio/at90_tiny_mega/define.h | 5 +- .../platform/gpio/at90_tiny_mega/module.lb | 26 +- .../platform/gpio/at90_tiny_mega/module.md | 157 ++++++++++ .../platform/gpio/at90_tiny_mega/pin.hpp.in | 198 ------------- .../gpio/at90_tiny_mega/static.hpp.in | 106 +++++++ .../gpio/at90_tiny_mega/unused.hpp.in | 112 ------- src/modm/platform/gpio/common/connector.hpp | 52 ---- .../platform/gpio/common/connector.hpp.in | 158 ++++++++++ .../gpio/common/connector_detail.hpp.in | 99 ------- src/modm/platform/gpio/common/inverted.hpp | 1 + src/modm/platform/gpio/common/pins.hpp.in | 34 +++ .../gpio/{stm32 => common}/unused.hpp.in | 69 ++--- src/modm/platform/gpio/hosted/base.hpp.in | 152 ---------- src/modm/platform/gpio/hosted/module.lb | 3 - src/modm/platform/gpio/hosted/unused.hpp.in | 15 +- src/modm/platform/gpio/rpi/base.hpp | 25 +- src/modm/platform/gpio/rpi/module.lb | 2 - src/modm/platform/gpio/rpi/pin.hpp | 69 ++--- src/modm/platform/gpio/sam/connector.hpp | 4 +- src/modm/platform/gpio/sam/enable.cpp.in | 10 +- .../gpio/stm32/connector_specialized.hpp.in | 78 ----- src/modm/platform/gpio/stm32/data.hpp.in | 77 +++++ src/modm/platform/gpio/stm32/enable.cpp.in | 40 +-- src/modm/platform/gpio/stm32/module.lb | 35 +-- src/modm/platform/gpio/stm32/module.md | 129 ++++++++ .../platform/gpio/stm32/peripherals.hpp.in | 5 +- src/modm/platform/gpio/stm32/pin.hpp.in | 277 ------------------ src/modm/platform/gpio/stm32/pin_f1.hpp.in | 235 --------------- src/modm/platform/gpio/stm32/port.hpp.in | 38 +-- src/modm/platform/gpio/stm32/set.hpp.in | 19 +- .../platform/gpio/stm32/software_port.hpp.in | 14 +- src/modm/platform/gpio/stm32/static.hpp.in | 205 +++++++++++++ 35 files changed, 1120 insertions(+), 1490 deletions(-) create mode 100644 src/modm/platform/gpio/at90_tiny_mega/data.hpp.in create mode 100644 src/modm/platform/gpio/at90_tiny_mega/module.md delete mode 100644 src/modm/platform/gpio/at90_tiny_mega/pin.hpp.in create mode 100644 src/modm/platform/gpio/at90_tiny_mega/static.hpp.in delete mode 100644 src/modm/platform/gpio/at90_tiny_mega/unused.hpp.in delete mode 100644 src/modm/platform/gpio/common/connector.hpp create mode 100644 src/modm/platform/gpio/common/connector.hpp.in delete mode 100644 src/modm/platform/gpio/common/connector_detail.hpp.in create mode 100644 src/modm/platform/gpio/common/pins.hpp.in rename src/modm/platform/gpio/{stm32 => common}/unused.hpp.in (75%) delete mode 100644 src/modm/platform/gpio/hosted/base.hpp.in delete mode 100644 src/modm/platform/gpio/stm32/connector_specialized.hpp.in create mode 100644 src/modm/platform/gpio/stm32/data.hpp.in create mode 100644 src/modm/platform/gpio/stm32/module.md delete mode 100644 src/modm/platform/gpio/stm32/pin.hpp.in delete mode 100644 src/modm/platform/gpio/stm32/pin_f1.hpp.in create mode 100644 src/modm/platform/gpio/stm32/static.hpp.in diff --git a/docs/src/how-modm-works.md b/docs/src/how-modm-works.md index 64e7c430ca..7f9e987ecc 100644 --- a/docs/src/how-modm-works.md +++ b/docs/src/how-modm-works.md @@ -284,9 +284,6 @@ bool state = Button::read(); // Depending on your targets, additional functions are available Led::setOutput(Gpio::OutputType::OpenDrain); Button::setInput(Gpio::InputType::PullUp); -Button::setInputTrigger(Gpio::InputTrigger::RisingEdge); -Button::enableExternalInterrupt(); -Button::acknowledgeExternalInterruptFlag(); ``` You can use these GPIOs as building blocks for more complex drivers and diff --git a/src/modm/platform/gpio/at90_tiny_mega/base.hpp.in b/src/modm/platform/gpio/at90_tiny_mega/base.hpp.in index 48541ade10..f0700294b0 100644 --- a/src/modm/platform/gpio/at90_tiny_mega/base.hpp.in +++ b/src/modm/platform/gpio/at90_tiny_mega/base.hpp.in @@ -9,16 +9,12 @@ */ // ---------------------------------------------------------------------------- -#ifndef MODM_AVR_GPIO_BASE_HPP -#define MODM_AVR_GPIO_BASE_HPP +#pragma once #include "define.h" #include -namespace modm -{ - -namespace platform +namespace modm::platform { /// @ingroup modm_platform_gpio @@ -75,14 +71,6 @@ struct Gpio {{ signal }}, %% endfor }; - -protected: - /// @cond - static constexpr uint8_t - i(InputType config) { return uint8_t(config); } - static constexpr uint8_t - i(InputTrigger trigger) { return uint8_t(trigger); } - /// @endcond }; /** @@ -95,12 +83,12 @@ protected: template< class Pin > class GpioOpenDrain : public Pin { - static Gpio::InputType inputType; + static inline Gpio::InputType inputType = Gpio::InputType::Floating; static_assert(Pin::direction == modm::Gpio::Direction::InOut, "Pin must inherit from modm::GpioIO"); public: - using Output = GpioOpenDrain; - using Input = GpioOpenDrain; using IO = GpioOpenDrain; + using Output = IO; + using Input = IO; using Type = typename Pin::Type; static constexpr modm::Gpio::Direction direction = modm::Gpio::Direction::Out; @@ -113,42 +101,24 @@ public: }; public: - inline static void configure(Gpio::InputType type) - { inputType = type; } - modm_always_inline static void setInput() {} - inline static void setInput(Gpio::InputType type) - { inputType = type; } - modm_always_inline static void setOutput() {} - modm_always_inline static void setOutput(OutputType) {} - modm_always_inline static void setOutput(bool status) { - set(status); - } - /// maps to `setInput(InputType::Floating)` or `setInput(InputType::PullUp)` - inline static void set() { - Pin::setInput(inputType); - } - /// maps to `setOutput(::modm::Gpio::Low)` - modm_always_inline static void reset() { - Pin::setOutput(::modm::Gpio::Low); - } - inline static void set(bool status) { - if (status) { set(); } - else { reset(); } - } - inline static bool isSet() { - return (Pin::getDirection() == modm::Gpio::Direction::In); - } - modm_always_inline static modm::Gpio::Direction getDirection() { - return modm::Gpio::Direction::Out; - } -}; + inline static void setInput() {} + inline static void setInput(Gpio::InputType type) { inputType = type; } + inline static void configure(Gpio::InputType type) { inputType = type; } -} // namespace platform + inline static void setOutput() {} + inline static void setOutput(OutputType) {} + inline static void setOutput(bool status) { set(status); } -} // namespace modm + /// maps to `setInput(InputType::Floating)` or `setInput(InputType::PullUp)` + inline static void set() { Pin::setInput(inputType); } + /// maps to `setOutput(::modm::Gpio::Low)` + inline static void reset() { Pin::setOutput(::modm::Gpio::Low); } + inline static void set(bool status) { status ? set() : reset(); } + inline static bool isSet() + { return (Pin::getDirection() == modm::Gpio::Direction::In); } -template< class Pin > -modm::platform::Gpio::InputType -modm::platform::GpioOpenDrain::inputType(modm::platform::Gpio::InputType::Floating); + inline static modm::Gpio::Direction getDirection() + { return modm::Gpio::Direction::Out; } +}; -#endif // MODM_AVR_GPIO_BASE_HPP +} // namespace modm::platform diff --git a/src/modm/platform/gpio/at90_tiny_mega/data.hpp.in b/src/modm/platform/gpio/at90_tiny_mega/data.hpp.in new file mode 100644 index 0000000000..75554fcd53 --- /dev/null +++ b/src/modm/platform/gpio/at90_tiny_mega/data.hpp.in @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2021, 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 + +#include "base.hpp" + +/// @cond +namespace modm::platform::detail +{ + +template struct SignalConnection; + +struct DataUnused {}; +%% for gpio in gpios +%% set port = gpio.gpio.port | upper +%% set pin = gpio.gpio.pin +%# +struct Data{{ port ~ pin }} { + static constexpr Gpio::Port port = Gpio::Port::{{port}}; + static constexpr uint8_t pin = {{pin}}; + + %% if gpio.extint >= 0 + inline static void setInputTrigger(Gpio::InputTrigger trigger) { + %% if gpio.extint < 4 + %% if isc2 is defined and gpio.extint == 2 + {{isc2}} = ({{isc2}} & ~(1 << ISC2)) | ((uint8_t(trigger) & 0b01) << ISC2); + %% else + {{eicra}} = ({{eicra}} & ~(0b11 << 2*{{gpio.extint}})) | (uint8_t(trigger) << 2*{{gpio.extint}}); + %% endif + %% else + EICRB = (EICRB & ~(0b11 << 2*{{gpio.extint - 4}})) | (uint8_t(trigger) << 2*{{gpio.extint - 4}}); + %% endif + } + inline static void enableExternalInterrupt() { EIMSK |= (1 << INT{{gpio.extint}}); } + inline static void disableExternalInterrupt() { EIMSK &= ~(1 << INT{{gpio.extint}}); } + inline static bool getExternalInterruptFlag() { return (EIFR & (1 << INTF{{gpio.extint}})); } + inline static void acknowledgeExternalInterruptFlag() { EIFR |= (1 << INTF{{gpio.extint}}); } + %% endif + %% if gpio.pcint >= 0 + %% set af_id = gpio.pcint + %% set af_reg = (af_id // 8) + %% if target["family"] == "attiny" + %% if (target["name"] in ["2313", "4313"]) and af_id >= 11 and af_id <= 17 + %% set af_reg = 2 + %% elif (target["name"] in ["20", "40", "1634"]) and af_id >= 12 and af_id <= 17 + %% set af_reg = 2 + %% elif (target["name"] in ["13", "25", "45", "85", "2313", "4313"]) + %% set af_reg = "" + %% endif + %% endif + inline static void enablePcInterrupt() { PCMSK{{af_reg}} |= (1 << PCINT{{af_id}}); PCICR |= (1 << PCIE{{af_reg}}); } + inline static void disablePcInterrupt() { + uint8_t pcmsk = PCMSK{{af_reg}} & ~(1 << PCINT{{af_id}}); PCMSK{{af_reg}} = pcmsk; + if (pcmsk == 0) PCICR &= ~(1 << PCIE{{af_reg}}); + } + inline static bool getPcInterruptFlag() { return (PCIFR & (1 << PCIF{{af_reg}})); } + inline static void acknowledgePcInterruptFlag() { PCIFR |= (1 << PCIF{{af_reg}}); } + %% endif + + struct BitBang { using Data = Data{{ port ~ pin }}; static constexpr Gpio::Signal Signal = Gpio::Signal::BitBang; }; +%% for signal_name in gpio.signals + struct {{ signal_name }} { using Data = Data{{ port ~ pin }}; static constexpr Gpio::Signal Signal = Gpio::Signal::{{ signal_name }}; }; +%% endfor +}; +template struct SignalConnection { + static_assert(p == Peripheral::BitBang, "Gpio{{ port ~ pin }}::BitBang only connects to software drivers!"); }; +%% for signal_name, signal_group in gpio.signals.items() +template struct SignalConnection { + static_assert((p == Peripheral::{{ signal_group | map(attribute="driver") | join(") or (p == Peripheral::") }}),{% if signal_group | length > 1 %} + {% endif %}"Gpio{{ port ~ pin }}::{{ signal_name }} only connects to {{ signal_group | map(attribute="driver") | join(" or ") }}!"); }; +%% endfor +%% endfor +%# +} // namespace modm::platform::detail +/// @endcond diff --git a/src/modm/platform/gpio/at90_tiny_mega/define.h b/src/modm/platform/gpio/at90_tiny_mega/define.h index 111d74691d..ab64761689 100644 --- a/src/modm/platform/gpio/at90_tiny_mega/define.h +++ b/src/modm/platform/gpio/at90_tiny_mega/define.h @@ -11,8 +11,7 @@ */ // ---------------------------------------------------------------------------- -#ifndef MODM_AT90_ATTINY_ATMEGA_GPIO_DEFINES_H -#define MODM_AT90_ATTINY_ATMEGA_GPIO_DEFINES_H +#pragma once /// @cond #include @@ -76,4 +75,4 @@ #endif /// @endcond -#endif // MODM_AT90_ATTINY_ATMEGA_GPIO_DEFINES_H + diff --git a/src/modm/platform/gpio/at90_tiny_mega/module.lb b/src/modm/platform/gpio/at90_tiny_mega/module.lb index 3eae2a69e9..a3954040ef 100644 --- a/src/modm/platform/gpio/at90_tiny_mega/module.lb +++ b/src/modm/platform/gpio/at90_tiny_mega/module.lb @@ -47,7 +47,7 @@ def extract_signals(signals): def init(module): module.name = ":platform:gpio" - module.description = "General Purpose I/O (GPIO)" + module.description = FileReader("module.md") def prepare(module, options): if not options[":target"].has_driver("gpio:avr"): @@ -108,6 +108,7 @@ def validate(env): bprops["all_signals"] = sorted(list(set(s["name"] for s in all_signals.values()))) bprops["ranges"] = port_ranges(driver["gpio"]) bprops["ports"] = {k:i for i, k in enumerate([p["name"] for p in bprops["ranges"]])} + bprops["gpios"] = [bprops[g["port"] + g["pin"]] for g in driver["gpio"]] def build(env): device = env[":target"] @@ -128,33 +129,30 @@ def build(env): else: properties["pue"] = False - if ((target["family"] == "mega" and target["name"] in ["8", "16", "32", "162", "8515", "8535"]) or - (target["family"] == "tiny" and target["name"] in ["13", "24", "25", "40", "44", "45", "84", "85", "261", "461", "861", "441", "841"])): + if ((target.family == "mega" and target.name in ["8", "16", "32", "162", "8515", "8535"]) or + (target.family == "tiny" and target.name in ["13", "24", "25", "40", "44", "45", "84", "85", "261", "461", "861", "441", "841"])): properties["eicra"] = "MCUCR" else: properties["eicra"] = "EICRA" - if target["family"] == "mega" and target["name"] in ["8", "16", "32", "8515", "8535"] and target["type"] not in ["u2", "u4", "u4rc"]: + if target.family == "mega" and target.name in ["8", "16", "32", "8515", "8535"] and target["type"] not in ["u2", "u4", "u4rc"]: properties["isc2"] = "MCUCSR" - if target["family"] == "mega" and target["name"] in ["162"]: + if target.family == "mega" and target.name in ["162"]: properties["isc2"] = "EMCUCR" env.substitutions = properties env.outbasepath = "modm/src/modm/platform/gpio" - for gpio in driver["gpio"]: - po, pi = gpio["port"], gpio["pin"] - properties.update(bprops[po + pi]) - header_name = "gpio_{}{}.hpp".format(po.upper(), pi) - env.template("pin.hpp.in", header_name) - env.copy("define.h") + env.template("data.hpp.in") + env.template("../common/pins.hpp.in", "pins.hpp") + env.template("static.hpp.in") env.template("base.hpp.in") env.template("port.hpp.in") env.template("software_port.hpp.in") env.template("set.hpp.in") - env.template("unused.hpp.in") + env.template("../common/unused.hpp.in", "unused.hpp") env.copy("../common/inverted.hpp", "inverted.hpp") - env.copy("../common/connector.hpp", "connector.hpp") - env.template("../common/connector_detail.hpp.in", "connector_detail.hpp") + env.template("../common/connector.hpp.in", "connector.hpp", + filters={"formatPeripheral": "", "printSignalMap": ""}) diff --git a/src/modm/platform/gpio/at90_tiny_mega/module.md b/src/modm/platform/gpio/at90_tiny_mega/module.md new file mode 100644 index 0000000000..8259a01b8f --- /dev/null +++ b/src/modm/platform/gpio/at90_tiny_mega/module.md @@ -0,0 +1,157 @@ +# General Purpose I/O (GPIO) + +This module provides register access to GPIO and connect their signals to the +respective peripherals in a compile-time verified way. + +Each GPIO is represented as its own class with only static methods, which +implement the `modm::GpioIO` interface and provide additional platform-specific +methods. + +```cpp +using namespace modm::platform; + +using Button = GpioA0; +Button::setInput(Gpio::InputType::PullUp); +bool input = Button::read(); + +using Led = GpioInverted; // inverts the IO logic of the pin + +Led::setOutput(); +Led::set(input); +``` + +You can also use an unordered set of GPIOs, which is useful when configuring a +large number of pins, since the register accesses will be bundled and thus less +code is generated. + +```cpp +using Set = GpioSet; +Set::setInput(); +``` + +To write and read a set of GPIOs, you need to use an ordered implementation, +which defines the pins from MSB to LSB, left-to-right. You can also check the +number of ports in case your use-case requires atomic reads/writes. + +```cpp +using Port = SoftwareGpioPort; +static_assert(Port::number_of_ports == 1, "Read/write needs to be atomic"); +Port::setInput(Gpio::InputType::PullUp); +uint8_t nibble = Port::read(); +Port::setOutput(); +Port::write(nibble); +``` + +For efficient access you can use a strictly-ordered implementation with a start +pin and width. Note that you can reverse the data order with a negative width. + +```cpp +using Port = GpioPort; +Port::setOutput(); +Port::write(data); + +using ReversePort = GpioPort; +ReversePort::setInput(); +uint8_t data = ReversePort::read(); +``` + +Finally, you can use an empty GPIO implementation in cases where the API +requires a GPIO, but you don't need one, for example, a bit-banged SPI without +MISO pin: + +```cpp +// write only SPI +using SPI = modm::platform::BitBangSpiMaster; +``` + + +## Peripheral Signals + +To make it easier to connect pins with peripherals, this module implements a +compile-time map of (pin, signal, peripheral). Note that you must provide both +peripherals and signals to be unambiguous. + +```cpp +GpioConnector::connect(); +``` + +However, it is recommended to wrap this functionality into a separate function +`Driver::connect(config)`, so that additional driver specific pin +configuration can be done: + +```cpp +template< class... Signals > +void Uart0::connect() +{ + Connector = GpioConnector; + Connector::disconnect(); // reset to floating input + + // extract pins from signals + using Rxd = Connector::GetSignal; + using Txd = Connector::GetSignal; + // if not found, returns GpioUnused, you can check for this case + static_assert(not Connector::isValid, + "This UART driver requires the Txd signal"); + + // configure both pins + Rxd::configure(Gpio::InputType::PullUp); + Txd::setOutput(); + + // connect both pins to alternate functions + // This will static assert if signals do not make sense + Connector::connect(); +} +// Connect these pin signals to Uart0 +Uart0::connect(); +``` + +Note that you may pass a *variable* number of signals to this connect function, +leaving out signals you don't need and adding signals that are not required. + +```cpp +// Connect only one signal +Uart1::connect(); +// Connect more signals than required +Uart1::connect(); +``` + + +## External Interrupts + +You can also configure the external interrupts, however, you must +provide the interrupt yourself. + +```cpp +GpioD2::setInputTrigger(Gpio::InputTrigger::RisingEdge); +GpioD2::enableExternalInterrupt(); + +MODM_ISR(INT0) +{ + // your code + GpioD2::acknowledgeInterruptFlag(); +} + +GpioD2::disableExternalInterrupt(); +``` + +When using multiple Pin-Change interrupts you need to first check the +corresponding flag to determine which pin triggered the collective interrupt. + +```cpp +GpioB1::enablePcInterrupt(); +GpioB2::enablePcInterrupt(); + +MODM_ISR(PCINT0) +{ + if (GpioB1::getPcInterruptFlag()) { + bool state = GpioB2::read(); + // your code + GpioB1::acknowledgePcInterruptFlag(); + } + if (GpioB2::getPcInterruptFlag()) { + bool state = GpioB2::read(); + // your code + GpioB2::acknowledgePcInterruptFlag(); + } +} +``` diff --git a/src/modm/platform/gpio/at90_tiny_mega/pin.hpp.in b/src/modm/platform/gpio/at90_tiny_mega/pin.hpp.in deleted file mode 100644 index 41987a2248..0000000000 --- a/src/modm/platform/gpio/at90_tiny_mega/pin.hpp.in +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright (c) 2017-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/. - */ -// ---------------------------------------------------------------------------- - -%% set port = gpio["port"] | upper -%% set pin = gpio["pin"] - -#ifndef MODM_AVR_GPIO_PIN_{{ port ~ pin }}_HPP -#define MODM_AVR_GPIO_PIN_{{ port ~ pin }}_HPP - -#include "base.hpp" -#include "set.hpp" - -namespace modm -{ - -namespace platform -{ - -/// @cond -class Gpio{{ port ~ pin }}; -using GpioOutput{{ port ~ pin }} = Gpio{{ port ~ pin }}; -using GpioInput{{ port ~ pin }} = Gpio{{ port ~ pin }}; -/// @endcond - -/// IO class for Pin {{port ~ pin}} -/// @ingroup modm_platform_gpio -class Gpio{{ port ~ pin }} : public Gpio, public ::modm::GpioIO -{ - template< class... Gpios > - friend class GpioSet; - using PinSet = GpioSet; - -public: - using Output = GpioOutput{{ port ~ pin }}; - using Input = GpioInput{{ port ~ pin }}; - using IO = Gpio{{ port ~ pin }}; - using Type = Gpio{{ port ~ pin }}; - static constexpr bool isInverted = false; - static constexpr Port port = Port::{{port}}; - static constexpr uint8_t pin = {{pin}}; - -protected: - static constexpr uint8_t mask = (1 << pin); - -public: - // GpioOutput - inline static void setOutput(bool status) { PinSet::setOutput(status); } - inline static void setOutput(OutputType type) { PinSet::setOutput(type); } - inline static void setOutput() { PinSet::setOutput(); } - inline static void configure(OutputType type) { PinSet::configure(type); } - inline static void set() { PinSet::set(); } - inline static void set(bool status) { PinSet::set(status); } - inline static void reset() { PinSet::reset(); } - inline static void toggle() { PinSet::toggle(); } - inline static bool isSet() { - return (PORT{{port}} & mask); - } - // GpioInput - inline static void configure(InputType type) { PinSet::configure(type); } - inline static void setInput(InputType type) { PinSet::setInput(type); } - inline static void setInput() { PinSet::setInput(); } - inline static bool read() { - return (PIN{{port}} & mask); - } - %% if extint >= 0 - inline static void setInputTrigger(InputTrigger trigger) { - %% if extint < 4 - %% if isc2 is defined and extint == 2 - {{isc2}} = ({{isc2}} & ~(1 << ISC2)) | ((i(trigger) & 0b01) << ISC2); - %% else - {{eicra}} = ({{eicra}} & ~(0b11 << 2*{{extint}})) | (i(trigger) << 2*{{extint}}); - %% endif - %% else - EICRB = (EICRB & ~(0b11 << 2*{{extint - 4}})) | (i(trigger) << 2*{{extint - 4}}); - %% endif - } - inline static void enableExternalInterrupt() { - EIMSK |= (1 << INT{{extint}}); - } - inline static void disableExternalInterrupt() { - EIMSK &= ~(1 << INT{{extint}}); - } - inline static bool getExternalInterruptFlag() { - return (EIFR & (1 << INTF{{extint}})); - } - inline static void acknowledgeExternalInterruptFlag() { - EIFR |= (1 << INTF{{extint}}); - } - %% endif - %% if pcint >= 0 - %% set af_id = pcint - %% set af_reg = (af_id // 8) - %% if target["family"] == "attiny" - %% if (target["name"] in ["2313", "4313"]) and af_id >= 11 and af_id <= 17 - %% set af_reg = 2 - %% elif (target["name"] in ["20", "40", "1634"]) and af_id >= 12 and af_id <= 17 - %% set af_reg = 2 - %% elif (target["name"] in ["13", "25", "45", "85", "2313", "4313"]) - %% set af_reg = "" - %% endif - %% endif - inline static void enablePcInterrupt() { - PCMSK{{af_reg}} |= (1 << PCINT{{af_id}}); - PCICR |= (1 << PCIE{{af_reg}}); - } - inline static void disablePcInterrupt() { - uint8_t pcmsk = PCMSK{{af_reg}} & ~(1 << PCINT{{af_id}}); - PCMSK{{af_reg}} = pcmsk; - if (!pcmsk) { - PCICR &= ~(1 << PCIE{{af_reg}}); - } - } - inline static bool readPcInterruptFlag() { - return (PCIFR & (1 << PCIF{{af_reg}})); - } - inline static void acknowledgePcInterruptFlag() { - PCIFR |= (1 << PCIF{{af_reg}}); - } - %% endif - // GpioIO - inline static Direction getDirection() { - return (DDR{{port}} & mask) ? Direction::Out : Direction::In; - } - inline static void - disconnect() { - DDR{{port}} &= ~mask; - %% if pue - PUE{{port}} &= ~mask; - %% else - PORT{{port}} &= ~mask; - %% endif - } - -public: -#ifdef __DOXYGEN__ - /// @{ - /// Connect to any software peripheral - struct BitBang; - %% for name, group in signals.items() - /// Connect to {% for sig in group %}{{ sig.driver }}{{ "" if loop.last else " or "}}{% endfor %} - using {{ name }} = GpioSignal; - %% endfor - /// @} -#endif - /// @cond - template< Peripheral peripheral > - struct BitBang { static void connect(); - static_assert( - (peripheral == Peripheral::BitBang), - "Gpio{{ port ~ pin }}::BitBang only connects to bit-bang drivers!"); - }; - %% for signal_name, signal_group in signals.items() - template< Peripheral peripheral > - struct {{ signal_name }} { static void connect(); - static_assert( - %% for signal in signal_group - (peripheral == Peripheral::{{ signal.driver }}){% if loop.last %},{% else %} ||{% endif %} - %% endfor - "Gpio{{ port ~ pin }}::{{ signal_name }} only connects to {% for signal in signal_group %}{{signal.driver}}{% if not loop.last %} or {% endif %}{% endfor %}!"); - }; - %% endfor - /// @endcond -}; - -/// @cond -template<> -struct Gpio{{ port ~ pin }}::BitBang -{ - using Gpio = Gpio{{ port ~ pin }}; - static constexpr Gpio::Signal Signal = Gpio::Signal::BitBang; - inline static void connect() {} -}; -%% for signal_group in signals.values() - %% for signal in signal_group -template<> -struct Gpio{{ port ~ pin }}::{{ signal.name }} -{ - using Gpio = Gpio{{ port ~ pin }}; - static constexpr Gpio::Signal Signal = Gpio::Signal::{{ signal.name }}; - inline static void connect() { /* tumbleweed */ } -}; - %% endfor -%% endfor -/// @endcond - -} // namespace platform - -} // namespace modm - -#endif // MODM_AVR_GPIO_PIN_{{ port ~ pin }}_HPP diff --git a/src/modm/platform/gpio/at90_tiny_mega/static.hpp.in b/src/modm/platform/gpio/at90_tiny_mega/static.hpp.in new file mode 100644 index 0000000000..a110a49185 --- /dev/null +++ b/src/modm/platform/gpio/at90_tiny_mega/static.hpp.in @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2017-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 + +#include "base.hpp" +#include "set.hpp" + +namespace modm::platform +{ + +/// @ingroup modm_platform_gpio +template< class GpioData > +class GpioStatic : public Gpio, public GpioData +{ + template< class... Gpios > + friend class GpioSet; + using PinSet = GpioSet>; + +public: + using Direction = modm::Gpio::Direction; + using Type = GpioStatic; + using Output = Type; + using Input = Type; + using IO = Type; + using Data = GpioData; + using GpioData::port; + using GpioData::pin; + static constexpr Direction direction = Direction::InOut; + static constexpr bool isInverted = false; + +protected: + static constexpr uint8_t mask = (1 << pin); + + static constexpr volatile uint8_t* gport() { +%% for port in ports + if constexpr (port == Gpio::Port::{{port}}) return &PORT{{port}}; +%% endfor + return nullptr; + } + static constexpr volatile uint8_t* gpin() { +%% for port in ports + if constexpr (port == Gpio::Port::{{port}}) return &PIN{{port}}; +%% endfor + return nullptr; + } + static constexpr volatile uint8_t* gddr() { +%% for port in ports + if constexpr (port == Gpio::Port::{{port}}) return &DDR{{port}}; +%% endfor + return nullptr; + } +%% if pue + static constexpr volatile uint8_t* gpue() { +%% for port in ports + if constexpr (port == Gpio::Port::{{port}}) return &PUE{{port}}; +%% endfor + return nullptr; + } +%% endif + +public: + // GpioOutput + inline static void setOutput() { PinSet::setOutput(); } + inline static void setOutput(bool status) { PinSet::setOutput(status); } + inline static void setOutput(OutputType type) { PinSet::setOutput(type); } + inline static void configure(OutputType type) { PinSet::configure(type); } + + inline static void set() { PinSet::set(); } + inline static void set(bool status) { PinSet::set(status); } + inline static void reset() { PinSet::reset(); } + inline static bool isSet() { return (*gport() & mask); } + inline static void toggle() { PinSet::toggle(); } + + // GpioInput + inline static void setInput() { PinSet::setInput(); } + inline static void setInput(InputType type) { PinSet::setInput(type); } + inline static void configure(InputType type) { PinSet::configure(type); } + + inline static bool read() { return (*gpin() & mask); } + + // GpioIO + inline static Direction getDirection() + { return (*gddr() & mask) ? Direction::Out : Direction::In; } + + inline static void disconnect() + { + *gddr() &= ~mask; + %% if pue + *gpue() &= ~mask; + %% else + *gport() &= ~mask; + %% endif + } +}; + +} // namespace platform::modm + diff --git a/src/modm/platform/gpio/at90_tiny_mega/unused.hpp.in b/src/modm/platform/gpio/at90_tiny_mega/unused.hpp.in deleted file mode 100644 index f708c0587d..0000000000 --- a/src/modm/platform/gpio/at90_tiny_mega/unused.hpp.in +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2017-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/. - */ -// ---------------------------------------------------------------------------- - -#ifndef MODM_AVR_GPIO_PIN_UNUSED_HPP -#define MODM_AVR_GPIO_PIN_UNUSED_HPP - -#include "base.hpp" -#include - -namespace modm -{ - -namespace platform -{ - -/** - * Dummy implementation of an I/O pin. - * - * This class can be used when a pin is not required. All functions - * are dummy functions which do nothing. `read()` will always - * return `false`. - * - * For example when creating a software SPI with the modm::SoftwareSimpleSpi - * class and the return channel (MISO - Master In Slave Out) is not needed, - * a good way is to use this class as a parameter when defining the - * SPI class. - * - * Example: - * @code - * #include - * - * namespace pin - * { - * typedef GpioOutputD7 Clk; - * typedef GpioOutputD5 Mosi; - * } - * - * modm::SoftwareSpiMaster< pin::Clk, pin::Mosi, GpioUnused > Spi; - * - * ... - * Spi::write(0xaa); - * @endcode - * - * @author Fabian Greif - * @author Niklas Hauser - * @ingroup modm_platform_gpio - */ -class GpioUnused : public Gpio, public ::modm::GpioIO -{ -public: - using Output = GpioUnused; - using Input = GpioUnused; - using IO = GpioUnused; - using Type = GpioUnused; - static constexpr bool isInverted = false; - static constexpr Port port = Port(-1); - static constexpr uint8_t pin = uint8_t(-1); - static constexpr uint8_t mask = 0; - -public: - static void setOutput(bool) {} - static void setOutput(OutputType) {} - static void setOutput() {} - static void configure(OutputType) {} - static void set() {} - static void set(bool) {} - static void reset() {} - static void toggle() {} - static bool isSet() { return false; } - static void configure(InputType) {} - static void setInput(InputType) {} - static void setInput() {} - static bool read() { return false; } - static void setInputTrigger(InputTrigger) {} - static void enableExternalInterrupt() {} - static void disableExternalInterrupt() {} - static bool getExternalInterruptFlag() { return false; } - static void acknowledgeExternalInterruptFlag() {} - static void enablePcInterrupt() {} - static void disablePcInterrupt() {} - static bool readPcInterruptFlag() { return false; } - static void acknowledgePcInterruptFlag() {} - static Direction getDirection() { return Direction::Special; } - static void disconnect() {} - -public: - /// @cond -%% for name in all_signals - template< Peripheral _ > - struct {{ name }} - { - using Gpio = GpioUnused; - static constexpr Gpio::Signal Signal = Gpio::Signal::{{ name }}; - static void connect() {} - }; -%% endfor - /// @endcond -}; - -} // namespace platform - -} // namespace modm - -#endif // MODM_AVR_GPIO_PIN_UNUSED_HPP diff --git a/src/modm/platform/gpio/common/connector.hpp b/src/modm/platform/gpio/common/connector.hpp deleted file mode 100644 index bfbf6f773e..0000000000 --- a/src/modm/platform/gpio/common/connector.hpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2017, 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/. - */ -// ---------------------------------------------------------------------------- - -#ifndef MODM_PLATFORM_GPIO_CONNECTOR_HPP -#define MODM_PLATFORM_GPIO_CONNECTOR_HPP - -#include "base.hpp" -#include "connector_detail.hpp" - -namespace modm -{ - -namespace platform -{ - -/// @cond -template< Peripheral peripheral, template class... Signals > -struct GpioConnector -{ - template< class GpioQuery > - static constexpr bool Contains = detail::GpioContains::value; - template< class GpioQuery > - static constexpr bool IsValid = not std::is_same_v; - template< Gpio::Signal signal > - using GetSignal = typename detail::GpioGetSignal::Gpio; - - inline static void - connect() - { - detail::GpioSignalConnect::connect(); - } - inline static void - disconnect() - { - detail::GpioSignalConnect::disconnect(); - } -}; -/// @endcond - -} // namespace platform - -} // namespace modm - -#endif // MODM_PLATFORM_GPIO_CONNECTOR_HPP diff --git a/src/modm/platform/gpio/common/connector.hpp.in b/src/modm/platform/gpio/common/connector.hpp.in new file mode 100644 index 0000000000..1fb4dcbd41 --- /dev/null +++ b/src/modm/platform/gpio/common/connector.hpp.in @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2017, 2021, 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 + +#include "unused.hpp" +#include "static.hpp" +#include + +/// @cond +namespace modm::platform +{ + +namespace detail +{ + +template< Gpio::Signal signal, class... Signals > +struct GpioGetSignal; +template< Gpio::Signal signal, class SignalT, class... Signals > +struct GpioGetSignal +{ + using Gpio = std::conditional_t< + (SignalT::Signal == signal), + typename modm::platform::GpioStatic, + typename GpioGetSignal::Gpio + >; +}; +template< Gpio::Signal signal > +struct GpioGetSignal +{ + using Gpio = GpioUnused; +}; + +} // namespace detail + +template< Peripheral peripheral, class... Signals > +struct GpioConnector +{ + template< class GpioQuery > + static constexpr bool Contains = ( + std::is_same_v or ...); + + template< class GpioQuery > + static constexpr bool IsValid = not std::is_same_v; + + template< Gpio::Signal signal > + using GetSignal = typename detail::GpioGetSignal::Gpio; + + template< class Signal > + static void connectSignal() + { + using Connection = detail::SignalConnection; +%% if target.platform in ["stm32"] + using Pin = GpioStatic; + if constexpr(Connection::af == -2) { +%% if target.family in ["f1"] + Pin::setAnalogInput(); + } + else { + Pin::setAlternateFunction(); + } +%% else + Pin::disconnect(); + Pin::setAnalogInput(); + } + if constexpr (Connection::af >= 0) { + Pin::setAlternateFunction(Connection::af); + } +%% endif +%% elif target.platform in ["avr"] + static Connection check [[maybe_unused]]; +%% endif + } + + static inline void connect() + { + (connectSignal(), ...); + } + + static inline void disconnect() + { + (GpioStatic::disconnect(), ...); + } +}; + +} // namespace modm::platform + +%% if target.family in ["f1"] +#include + +/// @cond +namespace modm::platform +{ +%% for remap in remaps + %% set reg = "MAPR" if (remap.position | int) < 32 else "MAPR2" + %% set per = remap | formatPeripheral +%# +template< class... Signals > +struct GpioConnector +{ + template< class GpioQuery > + static constexpr bool Contains = ( + std::is_same_v or ...); + + template< class GpioQuery > + static constexpr bool IsValid = not std::is_same_v; + + template< Gpio::Signal signal > + using GetSignal = typename detail::GpioGetSignal::Gpio; + + template< class Signal > + static void connectSignal() + { + using Connection = detail::SignalConnection; + using Pin = GpioStatic; + if constexpr(Connection::af == -2) { + Pin::setAnalogInput(); + } + else { + Pin::setAlternateFunction(); + } + } + + static inline void connect() + { + constexpr auto lmb = [](uint32_t id) { return 31 - std::countl_zero(id); }; + static constexpr uint32_t id = (detail::SignalConnection::Groups & ... & uint32_t(-1)); + static_assert((id == uint32_t(-1)) or (lmb(id) == {{ remap.group | map(attribute="id") | join("ul) or (lmb(id) == ") }}ul), + "This pin set contains conflicting remap groups!\nAvailable groups for {{per}} are:\n" + %% for line in group_map | printSignalMap(per) + "{{line}}\n" + %% endfor + ); + if (id != uint32_t(-1)) { + AFIO->{{reg}} = (AFIO->{{reg}} & ~({{ remap.mask }}ul << {{ (remap.position | int) % 32 }})) | (lmb(id) << {{ (remap.position | int) % 32 }}); + } + (connectSignal(), ...); + } + + static inline void disconnect() + { + (GpioStatic::disconnect(), ...); + } +}; +%% endfor + +} // namespace modm::platform +%% endif +/// @endcond + diff --git a/src/modm/platform/gpio/common/connector_detail.hpp.in b/src/modm/platform/gpio/common/connector_detail.hpp.in deleted file mode 100644 index aa6330b702..0000000000 --- a/src/modm/platform/gpio/common/connector_detail.hpp.in +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2017, 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/. - */ -// ---------------------------------------------------------------------------- - -#ifndef MODM_PLATFORM_GPIO_CONNECTOR_DETAIL_HPP -#define MODM_PLATFORM_GPIO_CONNECTOR_DETAIL_HPP - -#include "unused.hpp" -#include - -namespace modm -{ - -namespace platform -{ - -/// @cond -namespace detail -{ - -template< Peripheral peripheral, class GpioQ, template class... Signals > -struct GpioContains; -template< Peripheral peripheral, class GpioQ, template class SignalT, template class... Signals > -struct GpioContains -{ - using SGpio = typename SignalT::Gpio; - static constexpr bool value = ( - std::is_same_v ? - true : - GpioContains::value - ); -}; -template< Peripheral peripheral, class GpioQ > -struct GpioContains -{ - static constexpr bool value = false; -}; - -template< Peripheral peripheral, Gpio::Signal signal, template class... Signals > -struct GpioGetSignal; -template< Peripheral peripheral, Gpio::Signal signal, template class SignalT, template class... Signals > -struct GpioGetSignal -{ - using Gpio = std::conditional_t< - (SignalT::Signal == signal), - typename SignalT::Gpio, - typename GpioGetSignal::Gpio - >; -}; -template< Peripheral peripheral, Gpio::Signal signal > -struct GpioGetSignal -{ - using Gpio = GpioUnused; -}; - -template< Peripheral peripheral, template class... Signals > -struct GpioSignalConnect; -template< Peripheral peripheral, template class SignalT, template class... Signals > -struct GpioSignalConnect -{ -%% if target.family in ["f1", "l1"] - static constexpr uint32_t id = GpioSignalConnect::id & SignalT::Groups; -%% endif - static inline void connect() - { - SignalT::connect(); - GpioSignalConnect::connect(); - } - static inline void disconnect() - { - SignalT::Gpio::disconnect(); - GpioSignalConnect::disconnect(); - } -}; -template< Peripheral peripheral > -struct GpioSignalConnect -{ -%% if target.family in ["f1", "l1"] - static constexpr uint32_t id = uint32_t(-1); -%% endif - static inline void connect() {} - static inline void disconnect() {} -}; - -} // namespace detail -/// @endcond - -} // namespace platform - -} // namespace modm - -#endif // MODM_PLATFORM_GPIO_CONNECTOR_DETAIL_HPP diff --git a/src/modm/platform/gpio/common/inverted.hpp b/src/modm/platform/gpio/common/inverted.hpp index 3bcf0dfbde..877e043dad 100644 --- a/src/modm/platform/gpio/common/inverted.hpp +++ b/src/modm/platform/gpio/common/inverted.hpp @@ -44,6 +44,7 @@ class GpioInverted : public Pin using Input = GpioInverted; using IO = GpioInverted; using Type = typename Pin::Type; + using Data = typename Pin::Data; static constexpr bool isInverted = not Pin::isInverted; public: diff --git a/src/modm/platform/gpio/common/pins.hpp.in b/src/modm/platform/gpio/common/pins.hpp.in new file mode 100644 index 0000000000..8547ba4c25 --- /dev/null +++ b/src/modm/platform/gpio/common/pins.hpp.in @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021, 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 + +#include "data.hpp" +#include "static.hpp" + +namespace modm::platform +{ + +/// @ingroup modm_platform_gpio +/// @{ +%% for gpio in gpios +%% set port = gpio.gpio.port | upper +%% set pin = gpio.gpio.pin +using Gpio{{ port ~ pin }} = GpioStatic; +using GpioOutput{{ port ~ pin }} = Gpio{{ port ~ pin }}; +using GpioInput{{ port ~ pin }} = Gpio{{ port ~ pin }}; +%# +%% endfor +/// @} + +} // namespace modm::platform + + diff --git a/src/modm/platform/gpio/stm32/unused.hpp.in b/src/modm/platform/gpio/common/unused.hpp.in similarity index 75% rename from src/modm/platform/gpio/stm32/unused.hpp.in rename to src/modm/platform/gpio/common/unused.hpp.in index c37b665c16..53ebac3552 100644 --- a/src/modm/platform/gpio/stm32/unused.hpp.in +++ b/src/modm/platform/gpio/common/unused.hpp.in @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018, Niklas Hauser + * Copyright (c) 2017-2018, 2021, Niklas Hauser * * This file is part of the modm project. * @@ -9,18 +9,17 @@ */ // ---------------------------------------------------------------------------- -#ifndef MODM_STM32_GPIO_PIN_UNUSED_HPP -#define MODM_STM32_GPIO_PIN_UNUSED_HPP +#pragma once #include "base.hpp" -#include +#include "data.hpp" +#include "static.hpp" -namespace modm -{ - -namespace platform +namespace modm::platform { +/// @ingroup modm_platform_gpio +using GpioUnused = GpioStatic; /** * Dummy implementation of an I/O pin. * @@ -53,45 +52,49 @@ namespace platform * @author Niklas Hauser * @ingroup modm_platform_gpio */ -class GpioUnused : public Gpio, public ::modm::GpioIO +template<> +class GpioStatic : public Gpio, public ::modm::GpioIO { public: using Output = GpioUnused; using Input = GpioUnused; using IO = GpioUnused; using Type = GpioUnused; + using Data = detail::DataUnused; 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; - + static constexpr uint{{8 if target.platform == "avr" else 16}}_t mask = 0; +%% if target.platform in ["stm32"] protected: - /// @cond static void setAlternateFunction(uint8_t) {} + static void setAlternateFunction() {} static void setAnalogInput() {} - /// @endcond - +%% endif public: // GpioOutput - // start documentation inherited static void setOutput() {} static void setOutput(bool) {} +%% if target.platform in ["stm32"] + static void setOutput(OutputType, OutputSpeed = OutputSpeed::MHz50) {} + static void configure(OutputType, OutputSpeed = OutputSpeed::MHz50) {} +%% else + static void setOutput(OutputType) {} + static void configure(OutputType) {} +%% endif + static void set() {} static void set(bool) {} static void reset() {} - static void toggle() {} static bool isSet() { return false; } - // stop documentation inherited - static void configure(OutputType, OutputSpeed = OutputSpeed::MHz50) {} - static void setOutput(OutputType, OutputSpeed = OutputSpeed::MHz50) {} + static void toggle() {} // GpioInput - // start documentation inherited static void setInput() {} - static bool read() { return false; } - // end documentation inherited - static void configure(InputType) {} static void setInput(InputType) {} + static void configure(InputType) {} + static bool read() { return false; } + // External Interrupts static void enableExternalInterrupt() {} static void disableExternalInterrupt() {} @@ -103,16 +106,19 @@ public: static void acknowledgeExternalInterruptFlag() {} // GpioIO - // start documentation inherited - static Direction getDirection() { return Direction::Special; } - // end documentation inherited + static Direction getDirection() { return Direction::In; } +%% if target.platform in ["stm32"] static void lock() {} +%% endif static void disconnect() {} public: - /// @cond + struct BitBang + { + using Data = detail::DataUnused; + static constexpr Gpio::Signal Signal = Gpio::Signal::BitBang; + }; %% for name in all_signals - template< Peripheral _ > struct {{ name }} { using Gpio = GpioUnused; @@ -120,11 +126,6 @@ public: static void connect() {} }; %% endfor - /// @endcond }; -} // namespace platform - -} // namespace modm - -#endif // MODM_STM32_GPIO_PIN_UNUSED_HPP +} // namespace modm::platform diff --git a/src/modm/platform/gpio/hosted/base.hpp.in b/src/modm/platform/gpio/hosted/base.hpp.in deleted file mode 100644 index a14249ed19..0000000000 --- a/src/modm/platform/gpio/hosted/base.hpp.in +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (c) 2017, Niklas Hauser - * Copyright (c) 2018, Fabian Greif - * - * 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 MODM_HOSTED_GPIO_BASE_HPP -#define MODM_HOSTED_GPIO_BASE_HPP - -#include - - -namespace modm -{ - -namespace platform -{ - -/// @ingroup modm_platform_gpio -enum class -Peripheral -{ - BitBang, - // ... -}; - -/// @ingroup modm_platform_gpio -struct Gpio -{ - /// Each Input Pin can be configured in one of these states. - enum class - InputType : uint8_t - { - Floating, ///< The input pin is left floating - PullUp, ///< The input pin is pulled to Vcc - }; - - enum class - OutputType : uint8_t - { - PushPull ///< push-pull on output - }; - - /// Each External Interrupt can be configured to trigger on these conditions. - enum class - InputTrigger : uint8_t - { - LowLevel = 0b00, ///< triggers **continuously** during low level - BothEdges = 0b01, ///< triggers on both rising and falling edge - FallingEdge = 0b10, ///< triggers on falling edge - RisingEdge = 0b11, ///< triggers on rising edge - }; - - /// Available ports on this device. - enum class - Port - { - // ... - }; - - enum class - Signal - { - BitBang, - // ... - }; - -protected: - /// @cond - static constexpr uint8_t - i(InputType config) { return uint8_t(config); } - static constexpr uint8_t - i(InputTrigger trigger) { return uint8_t(trigger); } - /// @endcond -}; - -/** - * Gpio OpenDrain template, which remaps the behavior of the Gpio pin to - * simulate an open-drain output (with internal pullups if needed). - * - * @see BitBangI2cMaster - * @ingroup modm_platform_gpio - * @{ - */ -template< class Pin > -class GpioOpenDrain : public Pin -{ - static Gpio::InputType inputType; - static_assert(Pin::direction == modm::Gpio::Direction::InOut, "Pin must inherit from modm::GpioIO"); -public: - using Output = GpioOpenDrain; - using Input = GpioOpenDrain; - using IO = GpioOpenDrain; - using Type = typename Pin::Type; - - static constexpr modm::Gpio::Direction direction = modm::Gpio::Direction::Out; - - enum class - OutputType - { - PushPull, - OpenDrain, - }; - -public: - inline static void configure(Gpio::InputType type) - { inputType = type; } - modm_always_inline static void setInput() {} - inline static void setInput(Gpio::InputType type) - { inputType = type; } - modm_always_inline static void setOutput() {} - modm_always_inline static void setOutput(OutputType) {} - modm_always_inline static void setOutput(bool status) { - set(status); - } - /// maps to `setInput(InputType::Floating)` or `setInput(InputType::PullUp)` - inline static void set() { - Pin::setInput(inputType); - } - /// maps to `setOutput(::modm::Gpio::Low)` - modm_always_inline static void reset() { - Pin::setOutput(::modm::Gpio::Low); - } - inline static void set(bool status) { - if (status) { set(); } - else { reset(); } - } - inline static bool isSet() { - return (Pin::getDirection() == modm::Gpio::Direction::In); - } - modm_always_inline static modm::Gpio::Direction getDirection() { - return modm::Gpio::Direction::Out; - } -}; - -/// @} - -} // namespace platform - -} // namespace modm - -template< class Pin > -modm::platform::Gpio::InputType -modm::platform::GpioOpenDrain::inputType(modm::platform::Gpio::InputType::Floating); - -#endif diff --git a/src/modm/platform/gpio/hosted/module.lb b/src/modm/platform/gpio/hosted/module.lb index 99f48eb6ec..84b2e75010 100644 --- a/src/modm/platform/gpio/hosted/module.lb +++ b/src/modm/platform/gpio/hosted/module.lb @@ -29,8 +29,5 @@ def build(env): env.substitutions = {"target": env[":target"].identifier} env.outbasepath = "modm/src/modm/platform/gpio" - env.template("base.hpp.in") env.template("unused.hpp.in") env.copy("../common/inverted.hpp", "inverted.hpp") - env.copy("../common/connector.hpp", "connector.hpp") - env.template("../common/connector_detail.hpp.in", "connector_detail.hpp") diff --git a/src/modm/platform/gpio/hosted/unused.hpp.in b/src/modm/platform/gpio/hosted/unused.hpp.in index 22d177f233..8a6746aea1 100644 --- a/src/modm/platform/gpio/hosted/unused.hpp.in +++ b/src/modm/platform/gpio/hosted/unused.hpp.in @@ -10,16 +10,11 @@ */ // ---------------------------------------------------------------------------- -#ifndef MODM_HOSTED_GPIO_PIN_UNUSED_HPP -#define MODM_HOSTED_GPIO_PIN_UNUSED_HPP +#pragma once -#include "base.hpp" #include -namespace modm -{ - -namespace platform +namespace modm::platform { /** @@ -86,8 +81,4 @@ public: // end documentation inherited }; -} // namespace platform - -} // namespace modm - -#endif +} // namespace modm::platform diff --git a/src/modm/platform/gpio/rpi/base.hpp b/src/modm/platform/gpio/rpi/base.hpp index c100a958ce..dad07bc557 100644 --- a/src/modm/platform/gpio/rpi/base.hpp +++ b/src/modm/platform/gpio/rpi/base.hpp @@ -13,10 +13,7 @@ #include -namespace modm -{ - -namespace platform +namespace modm::platform { /// @ingroup modm_platform_gpio @@ -34,7 +31,9 @@ struct Gpio enum class InputType : uint8_t { - Floating, ///< The input pin is left floating + Floating = PUD_OFF, + PullUp = PUD_UP, + PullDown = PUD_DOWN, }; enum class @@ -43,25 +42,11 @@ struct Gpio PushPull ///< push-pull on output }; - /// Available ports on this device. - enum class - Port - { - // ... - }; - enum class Signal { BitBang, - // ... }; - - /// @endcond }; -/// @} - -} // namespace platform - -} // namespace modm +} // namespace modm::platform diff --git a/src/modm/platform/gpio/rpi/module.lb b/src/modm/platform/gpio/rpi/module.lb index 81169377dd..1fe5cd177f 100644 --- a/src/modm/platform/gpio/rpi/module.lb +++ b/src/modm/platform/gpio/rpi/module.lb @@ -32,5 +32,3 @@ def build(env): env.copy(".") env.copy("../common/inverted.hpp", "inverted.hpp") - env.copy("../common/connector.hpp", "connector.hpp") - env.template("../common/connector_detail.hpp.in", "connector_detail.hpp") diff --git a/src/modm/platform/gpio/rpi/pin.hpp b/src/modm/platform/gpio/rpi/pin.hpp index 14ca2e2780..14511a793a 100644 --- a/src/modm/platform/gpio/rpi/pin.hpp +++ b/src/modm/platform/gpio/rpi/pin.hpp @@ -1,6 +1,5 @@ /* - * Copyright (c) 2017, Niklas Hauser - * Copyright (c) 2018, Fabian Greif + * Copyright (c) 2021, Niklas Hauser * * This file is part of the modm project. * @@ -18,16 +17,13 @@ #include "base.hpp" -namespace modm -{ - -namespace platform +namespace modm::platform { template< int Pin > class GpioPin : public Gpio, public modm::GpioIO { - static Gpio::InputType inputType; + static inline bool output{false}; public: using Output = GpioPin; using Input = GpioPin; @@ -35,47 +31,36 @@ class GpioPin : public Gpio, public modm::GpioIO using Type = GpioPin; public: - inline static void configure(Gpio::InputType type) {} - modm_always_inline static void setInput() { - pinMode(Pin, INPUT); - } - inline static void setInput(Gpio::InputType type) { - setInput(); - } - modm_always_inline static void setOutput() { - pinMode(Pin, OUTPUT); - } - modm_always_inline static void setOutput(OutputType) { - setOutput(); - } - modm_always_inline static void setOutput(bool status) { - setOutput(); + inline static void setOutput() { pinMode(Pin, OUTPUT);} + inline static void setOutput(OutputType) { setOutput(); } + inline static void setOutput(bool status) + { + setOutput(); set(status); } - inline static void set() { - digitalWrite(Pin, HIGH); - } - modm_always_inline static void reset() { - digitalWrite(Pin, LOW); - } - inline static void set(bool status) { - digitalWrite(Pin, status); - } - inline static bool isSet() { - return digitalRead(Pin); - } - inline static void toggle() { + + inline static void set() { set(true); } + inline static void reset() { set(false); } + inline static bool isSet() { return output; } + inline static void set(bool status) { digitalWrite(Pin, status); output = status; } + inline static void toggle() + { if (isSet()) { set(); } else { reset(); } - } - modm_always_inline static modm::Gpio::Direction getDirection() { - return modm::Gpio::Direction::Out; } -}; -/// @} + inline static void setInput() { pinMode(Pin, INPUT); } + inline static void setInput(Gpio::InputType type) { setInput(); configure(type); } + inline static void configure(Gpio::InputType type) { pullUpDnControl(Pin, int(type)); } + + inline static bool read() { return digitalRead(Pin); } -} // namespace platform + inline static modm::Gpio::Direction getDirection() + { return modm::Gpio::Direction::InOut; } + +public: + struct BitBang {} +}; -} // namespace modm +} // namespace modm::platform diff --git a/src/modm/platform/gpio/sam/connector.hpp b/src/modm/platform/gpio/sam/connector.hpp index 89400f7c2e..b83a04a522 100644 --- a/src/modm/platform/gpio/sam/connector.hpp +++ b/src/modm/platform/gpio/sam/connector.hpp @@ -23,7 +23,7 @@ namespace platform using Peripheral = PeripheralPin; // GpioConnector only exists for backwards compability with bitbang API. -template class... Signals> +template struct GpioConnector { template @@ -35,4 +35,4 @@ struct GpioConnector } // namespace platform -} // namespace modm \ No newline at end of file +} // namespace modm diff --git a/src/modm/platform/gpio/sam/enable.cpp.in b/src/modm/platform/gpio/sam/enable.cpp.in index 0b9c7130fd..efa607f865 100644 --- a/src/modm/platform/gpio/sam/enable.cpp.in +++ b/src/modm/platform/gpio/sam/enable.cpp.in @@ -19,16 +19,12 @@ modm_gpio_enable(void) %% if target["family"] in ["g", "v"] PMC->PMC_PCER0 = -%% for port in options["enable_ports"] -%% if not loop.last - (1<PIO_OWER = 0xFFFFFFFF; %% endfor diff --git a/src/modm/platform/gpio/stm32/connector_specialized.hpp.in b/src/modm/platform/gpio/stm32/connector_specialized.hpp.in deleted file mode 100644 index 72f2e9295a..0000000000 --- a/src/modm/platform/gpio/stm32/connector_specialized.hpp.in +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2017, 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/. - */ -// ---------------------------------------------------------------------------- - -#ifndef MODM_STM32_GPIO_CONNECTOR_HPP -#define MODM_STM32_GPIO_CONNECTOR_HPP - -#include "base.hpp" -#include "connector.hpp" -#include - -namespace modm -{ - -namespace platform -{ - -/// @cond -namespace detail_gpio_connector -{ -/* FIXME: literally copied from ! */ -static constexpr uint8_t lmbH(uint32_t value) -{ return (value == 0)? 0 : (1 + lmbH(value >> 1)); } -static constexpr uint8_t lmb(uint32_t value) -{ return lmbH(value) - 1; } - -} // namespace detail - -// specializations -%% for remap in driver.remap - %% set reg = "MAPR" if (remap["position"] | int) < 32 else "MAPR2" - %% set per = remap | formatPeripheral -template< template class... Signals > -struct GpioConnector -{ - template< class GpioQuery > - static constexpr bool Contains = detail::GpioContains::value; - template< class GpioQuery > - static constexpr bool IsValid = not std::is_same_v; - template< Gpio::Signal signal > - using GetSignal = typename detail::GpioGetSignal::Gpio; - - inline static void connect() - { - using namespace detail_gpio_connector; - static constexpr uint32_t id = detail::GpioSignalConnect::id; - static_assert((id == uint32_t(-1)) || {% for group in remap.group %}(lmb(id) == {{group.id}}UL){% if not loop.last %} || {% else %},{% endif %}{% endfor %} - "This pin set contains conflicting remap groups!\nAvailable groups for {{per}} are:\n" - %% for line in group_map | printSignalMap(per) - "{{line}}\n" - %% endfor - ); - if (id != uint32_t(-1)) { - AFIO->{{reg}} = (AFIO->{{reg}} & ~({{ remap["mask"] }}UL << {{ (remap["position"] | int) % 32 }})) | (lmb(id) << {{ (remap["position"] | int) % 32 }}); - } - detail::GpioSignalConnect::connect(); - } - inline static void - disconnect() - { - detail::GpioSignalConnect::disconnect(); - } -}; -%% endfor -/// @endcond - -} // namespace platform - -} // namespace modm - -#endif // MODM_STM32_GPIO_CONNECTOR_HPP diff --git a/src/modm/platform/gpio/stm32/data.hpp.in b/src/modm/platform/gpio/stm32/data.hpp.in new file mode 100644 index 0000000000..5332bf549e --- /dev/null +++ b/src/modm/platform/gpio/stm32/data.hpp.in @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2021, 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 + +#include "base.hpp" + +/// @cond +namespace modm::platform::detail +{ + +%% if target.family in ["h7"] +enum class +AdcPolarity +{ + Positive, + Negative +}; +%% endif +template struct SignalConnection; +template static constexpr int8_t AdcChannel = -1; +template static constexpr int8_t DacChannel = -1; + +struct DataUnused {}; +%% for gpio in gpios +%% set port = gpio.gpio.port | upper +%% set pin = gpio.gpio.pin +%# +struct Data{{ port ~ pin }} { + static constexpr Gpio::Port port = Gpio::Port::{{port}}; + static constexpr uint8_t pin = {{pin}}; + static constexpr IRQn_Type ExternalInterruptIRQ = EXTI{{ gpio.exti_irqn }}_IRQn; +%% if gpio.has_remap + inline static void remap() { {{gpio.remap_reg}} {% if gpio.remap_value %}|= {% else %}&= ~{% endif %}{{gpio.remap_mask}}; } +%% endif + struct BitBang { using Data = Data{{ port ~ pin }}; static constexpr Gpio::Signal Signal = Gpio::Signal::BitBang; }; +%% for signal_name in gpio.signals + struct {{ signal_name }} { using Data = Data{{ port ~ pin }}; static constexpr Gpio::Signal Signal = Gpio::Signal::{{ signal_name }}; }; +%% endfor +}; +template struct SignalConnection { + static_assert(p == Peripheral::BitBang, "Gpio{{ port ~ pin }}::BitBang only connects to software drivers!"); }; +%% for signal_name, signal_group in gpio.signals.items() +template struct SignalConnection { + static_assert((p == Peripheral::{{ signal_group | map(attribute="driver") | join(") or (p == Peripheral::") }}),{% if signal_group | length > 1 %} + {% endif %}"Gpio{{ port ~ pin }}::{{ signal_name }} only connects to {{ signal_group | map(attribute="driver") | join(" or ") }}!"); }; +%% endfor +template<> struct SignalConnection { {% if target.family in ["f1"] %} + static constexpr uint32_t Groups = uint32_t(-1); {% endif %}static constexpr int8_t af = -1; }; +%% for signal_group in gpio.signals.values() + %% for signal in signal_group +template<> struct SignalConnection { {% if target.family in ["f1"] %} + static constexpr uint32_t Groups = {% if signal.af | length %}(1ul << {{signal.af | join (") | (1ul << ")}}){% else %}uint32_t(-1){% endif %}; {% endif %}static constexpr int8_t af = {{ signal.af[0] if signal.af | length else (-2 if (signal.driver.startswith("Adc") or signal.driver.startswith("Dac") or signal.driver.startswith("Comp")) else -1) }}; }; + %% endfor +%% endfor +%% for signal_name, signal_group in gpio.signals.items() + %% for signal in signal_group + %% if signal.driver.startswith("Adc") and signal.name.startswith("In") +template<> constexpr int8_t AdcChannel = {{ signal.name | to_adc_channel }}; + %% endif + %% if signal.driver.startswith("Dac") and signal.name.startswith("Out") +template<> constexpr int8_t DacChannel = {{ signal.name | to_adc_channel }}; + %% endif + %% endfor +%% endfor +%% endfor +%# +} // namespace modm::platform::detail +/// @endcond diff --git a/src/modm/platform/gpio/stm32/enable.cpp.in b/src/modm/platform/gpio/stm32/enable.cpp.in index e3f24852e5..8b3b0d96d5 100644 --- a/src/modm/platform/gpio/stm32/enable.cpp.in +++ b/src/modm/platform/gpio/stm32/enable.cpp.in @@ -17,57 +17,45 @@ void modm_gpio_enable(void) { %% set prefix = "GPIO" -%% if target["family"] in ["h7"] +%% if target.family in ["h7"] %% set clock_tree = "AHB4" -%% elif target["family"] in ["f2", "f4", "f7"] +%% elif target.family in ["f2", "f4", "f7"] %% set clock_tree = "AHB1" -%% elif target["family"] in ["f0", "f3", "l1"] +%% elif target.family in ["f0", "f3", "l1"] %% set clock_tree = "AHB" -%% elif target["family"] in ["f1"] +%% elif target.family in ["f1"] %% set clock_tree = "APB2" %% set prefix = "IOP" -%% elif target["family"] in ["l4", "g4"] +%% elif target.family in ["l4", "g4"] %% set clock_tree = 'AHB2' -%% elif target["family"] in ["g0", "l0"] +%% elif target.family in ["g0", "l0"] %% set clock_tree = 'IOP' %% endif -%% if target["family"] in ["h7"] +%% if target.family in ["h7"] // Enable I/O compensation cell SYSCFG->CCCSR = SYSCFG_CCCSR_EN; %% endif -%% if target["family"] in ["f2", "f4", "f7"] +%% if target.family in ["f2", "f4", "f7"] // Enable I/O compensation cell SYSCFG->CMPCR = SYSCFG_CMPCR_CMP_PD; %% endif // Enable GPIO clock RCC->{{ clock_tree }}ENR |= -%% for port in options["enable_ports"] -%% if not loop.last - RCC_{{ clock_tree }}ENR_{{ prefix }}{{ port | upper }}EN | -%% else - RCC_{{ clock_tree }}ENR_{{ prefix }}{{ port | upper }}EN; -%% endif +%% for port in options.enable_ports + RCC_{{ clock_tree }}ENR_{{ prefix }}{{ port | upper }}EN{% if loop.last %};{% else %} |{% endif %} %% endfor // Reset GPIO peripheral RCC->{{ clock_tree }}RSTR |= -%% for port in options["enable_ports"] -%% if not loop.last - RCC_{{ clock_tree }}RSTR_{{ prefix }}{{ port | upper }}RST | -%% else - RCC_{{ clock_tree }}RSTR_{{ prefix }}{{ port | upper }}RST; -%% endif +%% for port in options.enable_ports + RCC_{{ clock_tree }}RSTR_{{ prefix }}{{ port | upper }}RST{% if loop.last %};{% else %} |{% endif %} %% endfor RCC->{{ clock_tree }}RSTR &= ~( -%% for port in options["enable_ports"] -%% if not loop.last - RCC_{{ clock_tree }}RSTR_{{ prefix }}{{ port | upper }}RST | -%% else - RCC_{{ clock_tree }}RSTR_{{ prefix }}{{ port | upper }}RST); -%% endif +%% for port in options.enable_ports + RCC_{{ clock_tree }}RSTR_{{ prefix }}{{ port | upper }}RST{% if loop.last %});{% else %} |{% endif %} %% endfor } diff --git a/src/modm/platform/gpio/stm32/module.lb b/src/modm/platform/gpio/stm32/module.lb index a9f84dc485..c217404f7f 100644 --- a/src/modm/platform/gpio/stm32/module.lb +++ b/src/modm/platform/gpio/stm32/module.lb @@ -142,7 +142,7 @@ bprops = {} # ----------------------------------------------------------------------------- def init(module): module.name = ":platform:gpio" - module.description = "General Purpose I/O (GPIO)" + module.description = FileReader("module.md") def prepare(module, options): device = options[":target"] @@ -193,6 +193,7 @@ def validate(env): sig["instance"] = remap["instance"] signal_map[key].append(sig) bprops["group_map"] = signal_map + bprops["remaps"] = driver["remap"] # These are all exti possible vectors: 0, 0_1, 1, 15_10, 2, 2_3, 2_TSC, 3, 4, 4_15, 9_5 all_exti = { @@ -272,6 +273,10 @@ def validate(env): bprops["all_peripherals"] = sorted(list(set(all_peripherals))) bprops["all_signals"] = sorted(list(set(s["name"] for s in all_signals.values()))) bprops["pf"] = "1" if device.identifier["family"] in ["l4", "g0", "g4", "h7"] else "" + bprops["exti_reg"] = "SYSCFG" + if device.identifier.family in ["g0"]: bprops["exti_reg"] = "EXTI" + if device.identifier.family in ["f1"]: bprops["exti_reg"] = "AFIO" + bprops["gpios"] = [bprops[g["port"] + g["pin"]] for g in driver["gpio"]] # Check the max number of ADC instances max_adc_instance = max(map(int, device.get_driver("adc").get("instance", [1]))) @@ -279,36 +284,26 @@ def validate(env): raise ValidateException("Too many ADC instances: '{}'".format(max_adc_instance)) def build(env): - device = env[":target"] - driver = device.get_driver("gpio") - properties = device.properties - properties["target"] = device.identifier - properties["driver"] = driver - properties.update(bprops) - - env.substitutions = properties + env.substitutions["target"] = env[":target"].identifier + env.substitutions.update(bprops) env.outbasepath = "modm/src/modm/platform/gpio" - gpio_source = "pin_f1.hpp.in" if "f1" in driver["type"] else "pin.hpp.in" - for gpio in driver["gpio"]: - po, pi = gpio["port"], gpio["pin"] - properties.update(bprops[po + pi]) - header_name = "gpio_{}{}.hpp".format(po.upper(), pi) - env.template(gpio_source, header_name, filters={ "to_adc_channel" : lambda name : "".join(filter(str.isdigit, name)) }) + env.template("data.hpp.in", filters={"to_adc_channel": lambda name: "".join(filter(str.isdigit, name))}) + env.template("static.hpp.in") + env.template("../common/pins.hpp.in", "pins.hpp") env.template("port.hpp.in") env.template("software_port.hpp.in") env.template("set.hpp.in") - 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("unused.hpp.in") + env.template("../common/unused.hpp.in", "unused.hpp") if len(env["enable_ports"]): env.template("enable.cpp.in") env.copy("../common/inverted.hpp", "inverted.hpp") - env.copy("../common/connector.hpp", "connector.hpp") - env.template("../common/connector_detail.hpp.in", "connector_detail.hpp") + env.template("../common/connector.hpp.in", "connector.hpp", + filters={"formatPeripheral": get_driver, + "printSignalMap": print_remap_group_table}) # FIXME: Move to modm:platform:core! env.outbasepath = "modm/src/modm/platform/core" diff --git a/src/modm/platform/gpio/stm32/module.md b/src/modm/platform/gpio/stm32/module.md new file mode 100644 index 0000000000..a23aa171dc --- /dev/null +++ b/src/modm/platform/gpio/stm32/module.md @@ -0,0 +1,129 @@ +# General Purpose I/O (GPIO) + +This module provides register access to GPIO and connect their signals to the +respective peripherals in a compile-time verified way. This module also enables +all GPIO peripheral clocks on startup by default. + +Each GPIO is represented as its own class with only static methods, which +implement the `modm::GpioIO` interface and provide additional platform-specific +methods. + +```cpp +using namespace modm::platform; + +using Button = GpioA0; +Button::setInput(Gpio::InputType::PullUp); +bool input = Button::read(); + +using Led = GpioInverted; // inverts the IO logic of the pin + +Led::setOutput(Gpio::OutputType::OpenDrain, Gpio::OutputSpeed::MHz2); +Led::set(input); + +using Analog = GpioC12; +Analog::setAnalogInput(); // Use pin for ADC/DAC/COMP +Analog::lock(); // this prevents changes until next reboot + +using Signal = GpioD9; +Signal::setAlternateFunction(4); // For AF id see datasheet +Signal::setAlternateFunction(); // STM32F1 has no AF id +Signal::disconnect(); // Switch back to floating input + +// Some STM32s have remappable pinouts +GpioA11::remap(); // STM32G0: Remap A9 -> A11. +``` + +You can also use an unordered set of GPIOs, which is useful when configuring a +large number of pins, since the register accesses will be bundled and thus less +code is generated. + +```cpp +using Set = GpioSet; +Set::setInput(); +``` + +To write and read a set of GPIOs, you need to use an ordered implementation, +which defines the pins from MSB to LSB, left-to-right. You can also check the +number of ports in case your use-case requires atomic reads/writes. + +```cpp +using Port = SoftwareGpioPort; +static_assert(Port::number_of_ports == 1, "Read/write needs to be atomic"); +Port::setOutput(Gpio::OutputType::OpenDrain); +Port::configure(Gpio::InputType::PullUp); +uint8_t nibble = Port::read(); +Port::write(nibble); +``` + +For efficient access you can use a strictly-ordered implementation with a start +pin and width. Note that you can reverse the data order with a negative width. + +```cpp +using Port = GpioPort; +Port::setOutput(); +Port::write(data); + +using ReversePort = GpioPort; +ReversePort::setInput(); +uint8_t data = ReversePort::read(); +``` + +Finally, you can use an empty GPIO implementation in cases where the API +requires a GPIO, but you don't need one, for example, a bit-banged SPI without +MISO pin: + +```cpp +// write only SPI +using SPI = modm::platform::BitBangSpiMaster; +``` + + +## Alternate Function Signals + +To make it easier to connect pins with peripherals, this module implements a +compile-time map of (pin, signal, peripheral) to Alternate Function ID (AF). +Note that you must provide both peripherals and signals to be unambiguous. + +```cpp +GpioConnector::connect(); +``` + +However, it is recommended to wrap this functionality into a separate function +`Driver::connect(config)`, so that additional driver specific pin +configuration can be done: + +```cpp +template< class... Signals > +void Uart1::connect() +{ + Connector = GpioConnector; + Connector::disconnect(); // reset to floating input + + // extract pins from signals + using Rx = Connector::GetSignal; + using Tx = Connector::GetSignal; + // if not found, returns GpioUnused, you can check for this case + static_assert(not Connector::isValid, + "This UART driver requires the Tx signal"); + + // configure both pins + Rx::configure(Gpio::InputType::PullUp); + Tx::configure(Gpio::OutputType::PushPull); + + // connect both pins to alternate functions + // This will static assert if signals do not make sense + Connector::connect(); +} +// Connect these pin signals to Usart1 +Uart1::connect(); +``` + +Note that you may pass a *variable* number of signals to this connect function, +leaving out signals you don't need and adding signals that are not required. + +```cpp +// Connect only one signal +Uart1::connect(); +// Connect more signals than required +Uart1::connect(); +``` diff --git a/src/modm/platform/gpio/stm32/peripherals.hpp.in b/src/modm/platform/gpio/stm32/peripherals.hpp.in index df8a0a20f8..e005ed5efa 100644 --- a/src/modm/platform/gpio/stm32/peripherals.hpp.in +++ b/src/modm/platform/gpio/stm32/peripherals.hpp.in @@ -9,8 +9,7 @@ */ // ---------------------------------------------------------------------------- -#ifndef MODM_STM32_PERIPHERALS_HPP -#define MODM_STM32_PERIPHERALS_HPP +#pragma once namespace modm::platform { @@ -26,5 +25,3 @@ Peripheral }; } - -#endif // MODM_STM32_PERIPHERALS_HPP diff --git a/src/modm/platform/gpio/stm32/pin.hpp.in b/src/modm/platform/gpio/stm32/pin.hpp.in deleted file mode 100644 index 68335efc6d..0000000000 --- a/src/modm/platform/gpio/stm32/pin.hpp.in +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright (c) 2017-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/. - */ -// ---------------------------------------------------------------------------- - -%% set port = gpio["port"] | upper -%% set reg = "GPIO" ~ port -%% set pin = gpio["pin"] - -#ifndef MODM_STM32_GPIO_PIN_{{ port ~ pin }}_HPP -#define MODM_STM32_GPIO_PIN_{{ port ~ pin }}_HPP - -#include "../device.hpp" -#include "base.hpp" -#include "set.hpp" - -namespace modm::platform -{ - -/// @cond -class Gpio{{ port ~ pin }}; -using GpioOutput{{ port ~ pin }} = Gpio{{ port ~ pin }}; -using GpioInput{{ port ~ pin }} = Gpio{{ port ~ pin }}; -/// @endcond - -/// IO class for Pin {{port ~ pin}} -/// @ingroup modm_platform_gpio -class Gpio{{ port ~ pin }} : public Gpio, public ::modm::GpioIO -{ - template - friend class GpioSet; - using PinSet = GpioSet; - friend class Adc; - friend class Adc1; friend class Adc2; - friend class Adc3; friend class Adc4; -public: - using Output = Gpio{{ port ~ pin }}; - using Input = Gpio{{ port ~ pin }}; - using IO = Gpio{{ port ~ pin }}; - using Type = Gpio{{ port ~ pin }}; - static constexpr bool isInverted = false; - static constexpr Port port = Port::{{port}}; ///< Port name - static constexpr uint8_t pin = {{pin}}; ///< Pin number - static constexpr IRQn_Type ExternalInterruptIRQ = EXTI{{ exti_irqn }}_IRQn; - -protected: - /// Bitmask for registers that contain a 1bit value for every pin. - static constexpr uint16_t mask = 0x1 << pin; - /// Bitmask for registers that contain a 2bit value for every pin. - static constexpr uint32_t mask2 = 0x3 << (pin * 2); - /// Port Number. - static constexpr uint8_t port_nr = uint8_t(port); - /// Alternate Function register id. 0 for pin 0-7. 1 for pin 8-15. - static constexpr uint8_t af_id = pin / 8; - /// Alternate Function offset. - static constexpr uint8_t af_offset = (pin * 4) % 32; - /// Alternate Function register mask. - static constexpr uint32_t af_mask = 0xf << af_offset; - -public: - /// @cond - inline static void setAlternateFunction(uint8_t af) { - {{reg}}->AFR[af_id] = ({{reg}}->AFR[af_id] & ~af_mask) | ((af & 0xf) << af_offset); - {{reg}}->MODER = ({{reg}}->MODER & ~mask2) | (i(Mode::AlternateFunction) << (pin * 2)); - } - - /// Enable Analog Mode which is needed to use this pin as an ADC input. - inline static void setAnalogInput() { PinSet::setAnalogInput(); } - /// @endcond - -%% if has_remap - /// Remap this GPIO onto physical pins with selectable GPIO mappings - inline static void remap() { - %% if remap_value - {{remap_reg}} |= {{remap_mask}}; - %% else - {{remap_reg}} &= ~{{remap_mask}}; - %% endif - }; -%% endif - -public: - // GpioOutput - // start documentation inherited - inline static void setOutput() { PinSet::setOutput(); } - inline static void setOutput(bool status) { PinSet::setOutput(status); } - inline static void set() { PinSet::set(); } - inline static void set(bool status) { PinSet::set(status); } - inline static void reset() { PinSet::reset(); } - inline static bool toggle() { - if (isSet()) { reset(); return true; } - else { set(); return false; } - } - inline static bool isSet() { return ({{reg}}->ODR & mask); } - // stop documentation inherited - 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 - // start documentation inherited - inline static void setInput() { PinSet::setInput(); } - inline static bool read() { return ({{reg}}->IDR & mask); } - // end documentation inherited - inline static void configure(InputType type) { PinSet::configure(type); } - inline static void setInput(InputType type) { PinSet::setInput(type); } - // External Interrupts - // Warning: This will disable any previously enabled interrupt which is - // routed to the same interupt line, e.g. PA3 will disable PB3. - // This is a hardware limitation by the STM32 EXTI. - inline static void enableExternalInterrupt() - { - // PA[x], x = 0 .. 3 maps to EXTICR[0] - // PA[x], x = 4 .. 7 maps to EXTICR[1] - // PA[x], x = 8 .. 11 maps to EXTICR[2] - // PA[x], x = 12 .. 15 maps to EXTICR[3] - // => bit3 and bit2 (mask 0x0c) specify the register - // => bit1 and bit0 (mask 0x03) specify the bit position - constexpr uint8_t index = (pin & 0b1100) >> 2; - constexpr uint8_t bit_pos = (pin & 0b0011) << 2; - constexpr uint16_t syscfg_mask = (0b1111) << bit_pos; - constexpr uint16_t syscfg_value = (port_nr & (0b1111)) << bit_pos; -%% if target.family in ["g0"] - EXTI->EXTICR[index] = (EXTI->EXTICR[index] & ~syscfg_mask) | syscfg_value; -%% else - SYSCFG->EXTICR[index] = (SYSCFG->EXTICR[index] & ~syscfg_mask) | syscfg_value; -%% endif - EXTI->IMR{{pf}} |= mask; - } - inline static void disableExternalInterrupt() { EXTI->IMR{{pf}} &= ~mask; } - inline static void enableExternalInterruptVector(const uint32_t priority) - { - NVIC_SetPriority(ExternalInterruptIRQ, priority); - NVIC_EnableIRQ(ExternalInterruptIRQ); - } - inline static void disableExternalInterruptVector() { NVIC_DisableIRQ(ExternalInterruptIRQ); } - inline static void setInputTrigger(const InputTrigger trigger) - { - switch (trigger) - { - case InputTrigger::RisingEdge: - EXTI->RTSR{{pf}} |= mask; - EXTI->FTSR{{pf}} &= ~mask; - break; - case InputTrigger::FallingEdge: - EXTI->RTSR{{pf}} &= ~mask; - EXTI->FTSR{{pf}} |= mask; - break; - case InputTrigger::BothEdges: - EXTI->RTSR{{pf}} |= mask; - EXTI->FTSR{{pf}} |= mask; - break; - } - } -%% if target.family in ["g0"] - inline static bool getExternalInterruptFlag() - { return (EXTI->FPR1 & mask) | (EXTI->RPR1 & mask); } - inline static void acknowledgeExternalInterruptFlag() - { EXTI->FPR1 |= mask; EXTI->RPR1 |= mask; } -%% else - inline static bool getExternalInterruptFlag() { return (EXTI->PR{{pf}} & mask); } - inline static void acknowledgeExternalInterruptFlag() { EXTI->PR{{pf}} = mask; } -%% endif - // GpioIO - // start documentation inherited - inline static Direction getDirection() { - uint32_t mode = ({{reg}}->MODER & mask2); - if (mode == (i(Mode::Input) << pin * 2)) { - return Direction::In; - } - if (mode == (i(Mode::Output) << pin * 2)) { - return Direction::Out; - } - return Direction::Special; - } - // end documentation inherited - inline static void lock() { PinSet::lock(); } - inline static void disconnect() { - setInput(InputType::Floating); - {{reg}}->AFR[af_id] &= ~af_mask; - %% if target["family"] in ["l4"] and target["name"] in ["71", "75", "76", "85", "86"] - {{reg}}->ASCR &= ~mask; - %% endif - - } - -public: -#ifdef __DOXYGEN__ - /// @{ - /// Connect to any software peripheral - using BitBang = GpioSignal; - %% for name, group in signals.items() - /// Connect to {% for sig in group %}{{ sig.driver }}{{ "" if loop.last else " or "}}{% endfor %} - using {{ name }} = GpioSignal; - %% endfor - /// @} -#endif - /// @cond - template< Peripheral peripheral > - struct BitBang { static void connect(); - static_assert( - (peripheral == Peripheral::BitBang), - "Gpio{{ port ~ pin }}::BitBang only connects to software drivers!"); - }; - %% for signal_name, signal_group in signals.items() - template< Peripheral peripheral > - struct {{ signal_name }} { static void connect(); - static_assert( - %% for signal in signal_group - (peripheral == Peripheral::{{ signal.driver }}){% if loop.last %},{% else %} ||{% endif %} - %% endfor - "Gpio{{ port ~ pin }}::{{ signal_name }} only connects to {% for signal in signal_group %}{{signal.driver}}{% if not loop.last %} or {% endif %}{% endfor %}!"); - }; - %% endfor - /// @endcond -private: - template< Peripheral peripheral > - static constexpr int8_t AdcChannel = -1; - template< Peripheral peripheral > - static constexpr int8_t DacChannel = -1; -}; - -/// @cond -template<> -struct Gpio{{ port ~ pin }}::BitBang -{ - using Gpio = Gpio{{ port ~ pin }}; - static constexpr Gpio::Signal Signal = Gpio::Signal::BitBang; - static constexpr int af = -1; - inline static void connect() {} -}; -%% for signal_group in signals.values() - %% for signal in signal_group -template<> -struct Gpio{{ port ~ pin }}::{{ signal.name }} -{ - using Gpio = Gpio{{ port ~ pin }}; - static constexpr Gpio::Signal Signal = Gpio::Signal::{{ signal.name }}; - static constexpr int af = {{ signal.af[0] if signal.af | length else -1 }}; - inline static void - connect() - { - %% if signal.af | length - setAlternateFunction({{ signal.af[0] }}); - %% elif ( signal.driver.startswith("Adc") or signal.driver.startswith("Dac") - or signal.driver.startswith("Comp") ) - disconnect(); - setAnalogInput(); - %% endif - } -}; - %% if signal.driver.startswith("Adc") and signal.name.startswith("In") -template<> -constexpr int8_t -Gpio{{ port ~ pin }}::AdcChannel = {{ signal.name | to_adc_channel }}; - %% endif - - %% if signal.driver.startswith("Dac") and signal.name.startswith("Out") -template<> -constexpr int8_t -Gpio{{ port ~ pin }}::DacChannel = {{ signal.name | to_adc_channel }}; - %% endif - - %% endfor - -%% endfor - - -/// @endcond - -} // namespace modm::platform - -#endif // MODM_STM32_GPIO_PIN_{{ port ~ pin }}_HPP diff --git a/src/modm/platform/gpio/stm32/pin_f1.hpp.in b/src/modm/platform/gpio/stm32/pin_f1.hpp.in deleted file mode 100644 index 469e5d7661..0000000000 --- a/src/modm/platform/gpio/stm32/pin_f1.hpp.in +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright (c) 2017-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/. - */ -// ---------------------------------------------------------------------------- - -%% set port = gpio["port"] | upper -%% set reg = "GPIO" ~ port -%% set pin = gpio["pin"] | int -%% set cr = "CRL" if pin < 8 else "CRH" - -#ifndef MODM_STM32_GPIO_PIN_{{ port ~ pin }}_HPP -#define MODM_STM32_GPIO_PIN_{{ port ~ pin }}_HPP - -#include "../device.hpp" -#include "base.hpp" -#include "set.hpp" - - -namespace modm -{ - -namespace platform -{ - -/// @cond -class Gpio{{ port ~ pin }}; -using GpioOutput{{ port ~ pin }} = Gpio{{ port ~ pin }}; -using GpioInput{{ port ~ pin }} = Gpio{{ port ~ pin }}; -/// @endcond - -/// IO class for Pin {{port ~ pin}} -/// @ingroup modm_platform_gpio -struct Gpio{{ port ~ pin }} : public Gpio, ::modm::GpioIO -{ - template - friend class GpioSet; - using PinSet = GpioSet; - friend class Adc1; friend class Adc2; friend class Adc3; -public: - using Output = Gpio{{ port ~ pin }}; - using Input = Gpio{{ port ~ pin }}; - using IO = Gpio{{ port ~ pin }}; - using Type = Gpio{{ port ~ pin }}; - static constexpr bool isInverted = false; - static constexpr Port port = Port::{{port}}; ///< Port name - static constexpr uint8_t pin = {{pin}}; ///< Pin Number - static constexpr IRQn_Type ExternalInterruptIRQ = EXTI{{ exti_irqn }}_IRQn; - -protected: - /// Bitmask for registers that contain a 1bit value for every pin. - static constexpr uint16_t mask = 0x1 << pin; - static constexpr uint8_t cr_pin = {{ pin }} % 8; - /// Bitmask for the configuration register with a 4bit mask. - static constexpr uint32_t mask4 = 0xf << (cr_pin * 4); - /// Port Number. - static constexpr uint8_t port_nr = static_cast(port); - -public: - /// @cond - /// Enable Analog Mode which is needed to use this pin as an ADC input. - inline static void setAnalogInput() { PinSet::setAnalogInput(); } - /// @endcond - -public: - // GpioOutput - // start documentation inherited - inline static void setOutput() { PinSet::setOutput(); } - inline static void setOutput(bool status) { PinSet::setOutput(status); } - inline static void set() { PinSet::set(); } - inline static void set(bool status) { PinSet::set(status); } - inline static void reset() { PinSet::reset(); } - inline static void toggle() { - if (isSet()) reset(); - else set(); - } - inline static bool isSet() { return ({{reg}}->ODR & mask); } - // stop documentation inherited - 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 - // start documentation inherited - inline static void setInput() { PinSet::setInput(); } - inline static bool read() { return ({{reg}}->IDR & mask); } - // end documentation inherited - inline static void configure(InputType type) { PinSet::configure(type); } - inline static void setInput(InputType type) { PinSet::setInput(type); } - // External Interrupts - // Warning: This will disable any previously enabled interrupt which is - // routed to the same interupt line, e.g. PA3 will disable PB3. - // This is a hardware limitation by the STM32 EXTI. - inline static void enableExternalInterrupt() - { - // PA[x], x = 0 .. 3 maps to EXTICR[0] - // PA[x], x = 4 .. 7 maps to EXTICR[1] - // PA[x], x = 8 .. 11 maps to EXTICR[2] - // PA[x], x = 12 .. 15 maps to EXTICR[3] - // => bit3 and bit2 (mask 0b1100) specify the register - // => bit1 and bit0 (mask 0b0011) specify the bit position - constexpr uint8_t index = (pin & 0b1100) >> 2; - constexpr uint8_t bit_pos = (pin & 0b0011) << 2; - constexpr uint16_t syscfg_mask = (0b1111) << bit_pos; - constexpr uint16_t syscfg_value = (port_nr & (0b1111)) << bit_pos; - AFIO->EXTICR[index] = (AFIO->EXTICR[index] & ~syscfg_mask) | syscfg_value; - EXTI->IMR |= mask; - } - inline static void disableExternalInterrupt() { EXTI->IMR &= ~mask; } - inline static void enableExternalInterruptVector(const uint32_t priority) - { - NVIC_SetPriority(ExternalInterruptIRQ, priority); - NVIC_EnableIRQ(ExternalInterruptIRQ); - } - inline static void disableExternalInterruptVector() { NVIC_DisableIRQ(ExternalInterruptIRQ); } - inline static void - setInputTrigger(const InputTrigger trigger) - { - switch (trigger) - { - case InputTrigger::RisingEdge: - EXTI->RTSR |= mask; - EXTI->FTSR &= ~mask; - break; - case InputTrigger::FallingEdge: - EXTI->RTSR &= ~mask; - EXTI->FTSR |= mask; - break; - case InputTrigger::BothEdges: - EXTI->RTSR |= mask; - EXTI->FTSR |= mask; - break; - } - } - inline static bool getExternalInterruptFlag() { return (EXTI->PR & mask); } - inline static void acknowledgeExternalInterruptFlag() { EXTI->PR = mask; } - // GpioIO - // start documentation inherited - inline static Direction getDirection() { - uint32_t mode = ({{reg}}->{{cr}} & mask4); - if ((mode == 0) or (mode > (uint32_t(0b1001) << (cr_pin * 4)))) - return Direction::Special; - if (mode > 0) - return Direction::Out; - return Direction::In; - } - // end documentation inherited - inline static void lock() { PinSet::lock(); } - inline static void disconnect() { setInput(); }; - -public: -#ifdef __DOXYGEN__ - /// @{ - /// Connect to any software peripheral - using BitBang = GpioSignal; - %% for name, group in signals.items() - /// Connect to {% for sig in group %}{{ sig.driver }}{{ "" if loop.last else " or "}}{% endfor %} - using {{ name }} = GpioSignal; - %% endfor - /// @} -#endif - /// @cond - template< Peripheral peripheral > - struct BitBang { static void connect(); - static_assert( - (peripheral == Peripheral::BitBang), - "Gpio{{ port ~ pin }}::BitBang only connects to software drivers!"); - }; - %% for signal_name, signal_group in signals.items() - template< Peripheral peripheral > - struct {{ signal_name }} { static void connect(); - static_assert( - %% for signal in signal_group - (peripheral == Peripheral::{{ signal.driver }}){% if loop.last %},{% else %} ||{% endif %} - %% endfor - "Gpio{{ port ~ pin }}::{{ signal_name }} only connects to {% for signal in signal_group %}{{signal.driver}}{% if not loop.last %} or {% endif %}{% endfor %}!"); - }; - %% endfor - /// @endcond -private: - template< Peripheral peripheral > - static constexpr int8_t AdcChannel = -1; -}; - -/// @cond -template<> -struct Gpio{{ port ~ pin }}::BitBang -{ - using Gpio = Gpio{{ port ~ pin }}; - static constexpr Gpio::Signal Signal = Gpio::Signal::BitBang; - inline static void connect() {} -}; -%% for _, signal_group in signals.items() - %% for signal in signal_group -template<> -struct Gpio{{ port ~ pin }}::{{ signal.name }} -{ - %% if signal.af | length - static constexpr uint32_t Groups = {% for af in signal.af %}(1UL << {{ af }}){% if not loop.last %} | {% endif %}{% endfor %}; - %% else - static constexpr uint32_t Groups = uint32_t(-1); - %% endif - using Gpio = Gpio{{ port ~ pin }}; - static constexpr Gpio::Signal Signal = Gpio::Signal::{{ signal.name }}; - inline static void - connect() - { - %% if signal.driver.startswith("Adc") - setAnalogInput(); - %% else - uint32_t cr = {{reg}}->{{cr}}; - if (cr & (0b0011UL << (cr_pin * 4))) { - {{reg}}->{{cr}} = cr | (0b1000UL << (cr_pin * 4)); - } - %% endif - } -}; - %% if signal.driver.startswith("Adc") and signal.name.startswith("In") -template<> -constexpr int8_t -Gpio{{ port ~ pin }}::AdcChannel = {{ signal.name | to_adc_channel }}; - %% endif - %% endfor - -%% endfor -/// @endcond - -} // namespace platform - -} // namespace modm - -#endif // MODM_STM32_GPIO_{{ port ~ pin }}_HPP diff --git a/src/modm/platform/gpio/stm32/port.hpp.in b/src/modm/platform/gpio/stm32/port.hpp.in index 5254494334..b1f441a6ef 100644 --- a/src/modm/platform/gpio/stm32/port.hpp.in +++ b/src/modm/platform/gpio/stm32/port.hpp.in @@ -17,13 +17,12 @@ #include #include -namespace modm -{ - -namespace platform +namespace modm::platform { /// @cond +namespace detail +{ template< Gpio::Port port > struct GpioPortInfo; template< class StartGpio, int8_t Width > @@ -31,19 +30,18 @@ struct GpioSetShim { static typename GpioPortInfo::template check check; }; +} // namespace detail /// @endcond /// @ingroup modm_platform_gpio template< class StartGpio, int8_t Width > -class GpioPort : public ::modm::GpioPort/** @cond */, public GpioSetShim /** @endcond */ +class GpioPort : public ::modm::GpioPort/** @cond */, public detail::GpioSetShim /** @endcond */ { - using PinSet = GpioSetShim; + using PinSet = detail::GpioSetShim; public: using PinSet::width; static_assert(width <= 16, "Only a maximum of 16 pins are supported by GpioPort!"); - using PortType = std::conditional_t< (width > 8), - uint16_t, - uint8_t >; + using PortType = std::conditional_t< (width > 8), uint16_t, uint8_t >; static constexpr DataOrder getDataOrder() { return Width > 0 ? GpioPort::DataOrder::Normal : GpioPort::DataOrder::Reversed; } @@ -83,7 +81,8 @@ public: p = bitReverse(uint16_t(uint16_t(data) << StartPinReversed)); else p = uint16_t(data) << StartPin; %% for port, id in ports.items() - if constexpr (mask({{id}})) { p ^= inverted({{id}}); + if constexpr (mask({{id}})) { + p ^= inverted({{id}}); GPIO{{port}}->BSRR = ((~p & mask({{id}})) << 16) | (p & mask({{id}})); } %% endfor @@ -91,13 +90,15 @@ public: }; /// @cond +namespace detail +{ // ------ Port Width Information ------ %% for port in ranges template<> -struct GpioPortInfo +struct GpioPortInfo { - static constexpr int8_t Width = {{port["width"]}}; - static constexpr int8_t StartPin = {{port["start"]}}; + static constexpr int8_t Width = {{port.width}}; + static constexpr int8_t StartPin = {{port.start}}; static constexpr int8_t EndPin = StartPin + Width; template< uint8_t QueryStartPin, int8_t QueryWidth > @@ -107,12 +108,12 @@ struct GpioPortInfo static constexpr bool isReversed = QueryWidth < 0; static constexpr int8_t width = isNormal ? QueryWidth : -QueryWidth; static_assert(isReversed or width <= Width, - "GpioPort Width out of bounds! Maximum is {{port["width"]}}."); + "GpioPort Width out of bounds! Maximum is {{port.width}}."); static_assert(isReversed or (QueryStartPin + QueryWidth <= EndPin), - "GpioPort StartPin + Width out of bounds! Maximum is {{port["start"] + port["width"] - 1}}."); + "GpioPort StartPin + Width out of bounds! Maximum is {{port.start + port.width - 1}}."); static_assert(isNormal or (StartPin <= QueryStartPin + QueryWidth + 1), - "GpioPort StartPin - Width out of bounds! Minimum is {{port["start"]}}."); + "GpioPort StartPin - Width out of bounds! Minimum is {{port.start}}."); }; }; %% endfor @@ -141,10 +142,9 @@ struct GpioSetShim : public GpioSet< %% endfor > { static typename GpioPortInfo::template check check; }; %% endfor +} // namespace detail /// @endcond -} // namespace platform - -} // namespace modm +} // namespace modm::platform #endif // MODM_STM32_GPIO_PORT_HPP diff --git a/src/modm/platform/gpio/stm32/set.hpp.in b/src/modm/platform/gpio/stm32/set.hpp.in index 9b643af7ee..4301c5f0e6 100644 --- a/src/modm/platform/gpio/stm32/set.hpp.in +++ b/src/modm/platform/gpio/stm32/set.hpp.in @@ -9,16 +9,12 @@ */ // ---------------------------------------------------------------------------- -#ifndef MODM_STM32_GPIO_SET_HPP -#define MODM_STM32_GPIO_SET_HPP +#pragma once #include "../device.hpp" #include "base.hpp" -namespace modm -{ - -namespace platform +namespace modm::platform { /// @ingroup modm_platform_gpio @@ -64,6 +60,7 @@ protected: for (const auto &m: masks) r += (m) ? 1 : 0; return r; } + public: static constexpr uint8_t width = sizeof...(Gpios); static constexpr uint8_t number_of_ports = numberOfPorts(); @@ -191,8 +188,8 @@ public: { %% for port, id in ports.items() if constexpr (mask({{id}})) { - uint32_t are_set = (GPIO{{port}}->ODR & mask({{id}})); - uint32_t are_reset = mask({{id}}) ^ are_set; + const uint32_t are_set = (GPIO{{port}}->ODR & mask({{id}})); + const uint32_t are_reset = mask({{id}}) ^ are_set; GPIO{{port}}->BSRR = (are_set << 16) | are_reset; } %% endfor @@ -216,8 +213,4 @@ public: } }; -} // namespace platform - -} // namespace modm - -#endif // MODM_STM32_GPIO_SET_HPP +} // namespace modm::platform diff --git a/src/modm/platform/gpio/stm32/software_port.hpp.in b/src/modm/platform/gpio/stm32/software_port.hpp.in index 093e774063..91e8785151 100644 --- a/src/modm/platform/gpio/stm32/software_port.hpp.in +++ b/src/modm/platform/gpio/stm32/software_port.hpp.in @@ -9,16 +9,12 @@ */ // ---------------------------------------------------------------------------- -#ifndef MODM_STM32_GPIO_SOFTWARE_PORT_HPP -#define MODM_STM32_GPIO_SOFTWARE_PORT_HPP +#pragma once #include "set.hpp" #include -namespace modm -{ - -namespace platform +namespace modm::platform { /** @@ -105,8 +101,4 @@ public: } }; -} // namespace platform - -} // namespace modm - -#endif // MODM_STM32_GPIO_SOFTWARE_PORT_HPP +} // namespace modm::platform diff --git a/src/modm/platform/gpio/stm32/static.hpp.in b/src/modm/platform/gpio/stm32/static.hpp.in new file mode 100644 index 0000000000..ad16e74b93 --- /dev/null +++ b/src/modm/platform/gpio/stm32/static.hpp.in @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2017-2021, 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 + +#include "../device.hpp" +#include "base.hpp" +#include "set.hpp" + +namespace modm::platform +{ + +/// @ingroup modm_platform_gpio +template< class GpioData > +class GpioStatic : public Gpio, public GpioData +{ + template + friend class GpioSet; + using PinSet = GpioSet>; +public: + using Direction = ::modm::Gpio::Direction; + using Type = GpioStatic; + using Output = Type; + using Input = Type; + using IO = Type; + using Data = GpioData; + using GpioData::port; + using GpioData::pin; + using GpioData::ExternalInterruptIRQ; + static constexpr Direction direction = Direction::InOut; + static constexpr bool isInverted = false; + +protected: + /// @cond + // Bitmask for registers that contain a 1bit value for every pin. + static constexpr uint16_t mask = 0x1 << pin; + // Bitmask for registers that contain a 2bit value for every pin. + static constexpr uint32_t mask2 = 0x3 << (pin * 2); +%% if target.family in ["f1"] + // Bitmask for the configuration register with a 4bit mask. + static constexpr uint8_t cr_pin = pin % 8; + static constexpr uint32_t mask4 = 0xf << (cr_pin * 4); +%% endif + // Port Number. + static constexpr uint8_t port_nr = uint8_t(port); + // Alternate Function register id. 0 for pin 0-7. 1 for pin 8-15. + static constexpr uint8_t af_id = pin / 8; + // Alternate Function offset. + static constexpr uint8_t af_offset = (pin * 4) % 32; + // Alternate Function register mask. + static constexpr uint32_t af_mask = 0xf << af_offset; + + static GPIO_TypeDef* gport() + { +%% for port in ports + if constexpr (port == Gpio::Port::{{port}}) return GPIO{{port}}; +%% endfor + return nullptr; + } +%% if target.family in ["f1"] + static volatile uint32_t* gcr() + { + if constexpr (pin < 8) return &gport()->CRL; + return &gport()->CRH; + } +%% endif + /// @endcond + +public: +%% if target.family not in ["f1"] + inline static void setAlternateFunction(uint8_t af) { + gport()->AFR[af_id] = (gport()->AFR[af_id] & ~af_mask) | ((af & 0xf) << af_offset); + gport()->MODER = (gport()->MODER & ~mask2) | (i(Mode::AlternateFunction) << (pin * 2)); + } +%% else + inline static void setAlternateFunction() + { + uint32_t cr = *gcr(); + if (cr & (0b0011UL << (cr_pin * 4))) + *gcr() = cr | (0b1000UL << (cr_pin * 4)); + } +%% endif + /// Enable Analog Mode which is needed to use this pin as an ADC input. + inline static void setAnalogInput() { PinSet::setAnalogInput(); } + +public: + // GpioOutput + inline static void setOutput() { PinSet::setOutput(); } + inline static void setOutput(bool status) { PinSet::setOutput(status); } + inline static void setOutput(OutputType type, OutputSpeed speed = OutputSpeed::MHz50) { PinSet::setOutput(type, speed); } + inline static void configure(OutputType type, OutputSpeed speed = OutputSpeed::MHz50) { PinSet::configure(type, speed); } + + inline static void set() { PinSet::set(); } + inline static void set(bool status) { PinSet::set(status); } + inline static void reset() { PinSet::reset(); } + inline static bool isSet() { return gport()->ODR & mask; } + inline static bool toggle() + { + if (isSet()) { reset(); return true; } + else { set(); return false; } + } + + // GpioInput + inline static void setInput() { PinSet::setInput(); } + inline static void setInput(InputType type) { PinSet::setInput(type); } + inline static void configure(InputType type) { PinSet::configure(type); } + + inline static bool read() { return gport()->IDR & mask; } + + // External Interrupts + // Warning: This will disable any previously enabled interrupt which is + // routed to the same interupt line, e.g. PA3 will disable PB3. + // This is a hardware limitation by the STM32 EXTI. + inline static void enableExternalInterrupt() + { + // PA[x], x = 0 .. 3 maps to EXTICR[0] + // PA[x], x = 4 .. 7 maps to EXTICR[1] + // PA[x], x = 8 .. 11 maps to EXTICR[2] + // PA[x], x = 12 .. 15 maps to EXTICR[3] + // => bit3 and bit2 (mask 0x0c) specify the register + // => bit1 and bit0 (mask 0x03) specify the bit position + constexpr uint8_t index = (pin & 0b1100) >> 2; + constexpr uint8_t bit_pos = (pin & 0b0011) << 2; + constexpr uint16_t syscfg_mask = (0b1111) << bit_pos; + constexpr uint16_t syscfg_value = (port_nr & (0b1111)) << bit_pos; + {{exti_reg}}->EXTICR[index] = ({{exti_reg}}->EXTICR[index] & ~syscfg_mask) | syscfg_value; + EXTI->IMR{{pf}} |= mask; + } + inline static void disableExternalInterrupt() { EXTI->IMR{{pf}} &= ~mask; } + inline static void enableExternalInterruptVector(const uint32_t priority) + { + NVIC_SetPriority(ExternalInterruptIRQ, priority); + NVIC_EnableIRQ(ExternalInterruptIRQ); + } + inline static void disableExternalInterruptVector() { NVIC_DisableIRQ(ExternalInterruptIRQ); } + inline static void setInputTrigger(const InputTrigger trigger) + { + switch (trigger) + { + case InputTrigger::RisingEdge: + EXTI->RTSR{{pf}} |= mask; + EXTI->FTSR{{pf}} &= ~mask; + break; + case InputTrigger::FallingEdge: + EXTI->RTSR{{pf}} &= ~mask; + EXTI->FTSR{{pf}} |= mask; + break; + case InputTrigger::BothEdges: + EXTI->RTSR{{pf}} |= mask; + EXTI->FTSR{{pf}} |= mask; + break; + } + } +%% if target.family in ["g0"] + inline static bool getExternalInterruptFlag() + { return (EXTI->FPR1 & mask) | (EXTI->RPR1 & mask); } + inline static void acknowledgeExternalInterruptFlag() + { EXTI->FPR1 |= mask; EXTI->RPR1 |= mask; } +%% else + inline static bool getExternalInterruptFlag() { return (EXTI->PR{{pf}} & mask); } + inline static void acknowledgeExternalInterruptFlag() { EXTI->PR{{pf}} = mask; } +%% endif + + // GpioIO + inline static Direction getDirection() + { +%% if target.family in ["f1"] + uint32_t mode = (*gcr() & mask4); + if ((mode == 0) or (mode > (uint32_t(0b1001) << (cr_pin * 4)))) + return Direction::Special; + if (mode > 0) + return Direction::Out; + return Direction::In; +%% else + uint32_t mode = (gport()->MODER & mask2); + if (mode == (i(Mode::Input) << pin * 2)) + return Direction::In; + if (mode == (i(Mode::Output) << pin * 2)) + return Direction::Out; + return Direction::Special; +%% endif + } + inline static void lock() { PinSet::lock(); } + inline static void disconnect() + { + setInput(InputType::Floating); +%% if target.family not in ["f1", "l1"] + gport()->AFR[af_id] &= ~af_mask; + %% if target["family"] in ["l4"] and target["name"] in ["71", "75", "76", "85", "86"] + gport()->ASCR &= ~mask; + %% endif +%% endif + } +}; + +} // namespace modm::platform + From 866cbfeaaf693f0e1c1e8c9d1b307c3fce073ba3 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Fri, 24 Sep 2021 18:43:53 +0200 Subject: [PATCH 2/4] [gpio] Adapt library code to Signal changes --- src/modm/driver/pwm/sk6812w.hpp | 2 +- .../adc/at90_tiny_mega/adc_mega.hpp.in | 2 +- .../adc/at90_tiny_mega/adc_tiny.hpp.in | 2 +- src/modm/platform/adc/stm32/adc.hpp.in | 4 +-- src/modm/platform/adc/stm32f0/adc.hpp.in | 4 +-- src/modm/platform/adc/stm32f3/adc.hpp.in | 4 +-- src/modm/platform/can/stm32-fdcan/can.hpp.in | 2 +- src/modm/platform/can/stm32/can.hpp.in | 2 +- src/modm/platform/comp/stm32/comp.hpp.in | 2 +- src/modm/platform/dac/stm32/dac.hpp.in | 2 +- src/modm/platform/dac/stm32/dac_dma.hpp.in | 2 +- src/modm/platform/eth/stm32/eth.hpp | 27 +++---------------- .../i2c/at90_tiny_mega/i2c_master.hpp.in | 2 +- .../i2c/bitbang/bitbang_i2c_master.hpp.in | 2 +- .../i2c/bitbang/bitbang_i2c_master_impl.hpp | 4 +-- .../i2c/stm32-extended/i2c_master.hpp.in | 2 +- src/modm/platform/i2c/stm32/i2c_master.hpp.in | 2 +- .../spi/at90_tiny_mega/spi_master.hpp.in | 2 +- .../uart_spi_master.hpp.in | 2 +- .../spi/bitbang/bitbang_spi_master.hpp | 2 +- .../spi/bitbang/bitbang_spi_master_impl.hpp | 2 +- src/modm/platform/spi/stm32/spi_master.hpp.in | 2 +- .../spi/stm32_uart/uart_spi_master.hpp.in | 2 +- src/modm/platform/timer/stm32/advanced.hpp.in | 2 +- src/modm/platform/timer/stm32/basic.hpp.in | 2 +- .../timer/stm32/general_purpose.hpp.in | 2 +- .../platform/uart/at90_tiny_mega/uart.hpp.in | 2 +- src/modm/platform/uart/stm32/uart.hpp.in | 2 +- src/modm/platform/usb/stm32/usb.hpp.in | 2 +- 29 files changed, 35 insertions(+), 56 deletions(-) diff --git a/src/modm/driver/pwm/sk6812w.hpp b/src/modm/driver/pwm/sk6812w.hpp index d0388fd032..995e409c6f 100644 --- a/src/modm/driver/pwm/sk6812w.hpp +++ b/src/modm/driver/pwm/sk6812w.hpp @@ -55,7 +55,7 @@ class Sk6812w void initialize() { - SpiMaster::template connect(); + SpiMaster::template connect(); SpiMaster::template initialize(); SpiMaster::setDataOrder(SpiMaster::DataOrder::LsbFirst); SpiMaster::Hal::write(uint8_t(0)); diff --git a/src/modm/platform/adc/at90_tiny_mega/adc_mega.hpp.in b/src/modm/platform/adc/at90_tiny_mega/adc_mega.hpp.in index 350ed43904..6eb0b5ee41 100644 --- a/src/modm/platform/adc/at90_tiny_mega/adc_mega.hpp.in +++ b/src/modm/platform/adc/at90_tiny_mega/adc_mega.hpp.in @@ -112,7 +112,7 @@ private: public: // start inherited documentation - template< template class... Signals > + template< class... Signals > static void connect() { diff --git a/src/modm/platform/adc/at90_tiny_mega/adc_tiny.hpp.in b/src/modm/platform/adc/at90_tiny_mega/adc_tiny.hpp.in index 7986a4fc69..7ca656f0a0 100644 --- a/src/modm/platform/adc/at90_tiny_mega/adc_tiny.hpp.in +++ b/src/modm/platform/adc/at90_tiny_mega/adc_tiny.hpp.in @@ -158,7 +158,7 @@ private: public: // start inherited documentation - template< template class... Signals > + template< class... Signals > static void connect() { diff --git a/src/modm/platform/adc/stm32/adc.hpp.in b/src/modm/platform/adc/stm32/adc.hpp.in index f21938f02d..0cfb7da670 100644 --- a/src/modm/platform/adc/stm32/adc.hpp.in +++ b/src/modm/platform/adc/stm32/adc.hpp.in @@ -177,7 +177,7 @@ public: public: // start inherited documentation - template< template class... Signals > + template< class... Signals > static void connect() { @@ -263,7 +263,7 @@ public: static inline constexpr Channel getPinChannel() { - constexpr int8_t channel{Gpio::template AdcChannel}; + constexpr int8_t channel{detail::AdcChannel}; static_assert(channel >= 0, "Adc{{id}} does not have a channel for this pin!"); return Channel(channel); } diff --git a/src/modm/platform/adc/stm32f0/adc.hpp.in b/src/modm/platform/adc/stm32f0/adc.hpp.in index b9a2a8c872..b3f8c75d08 100644 --- a/src/modm/platform/adc/stm32f0/adc.hpp.in +++ b/src/modm/platform/adc/stm32f0/adc.hpp.in @@ -141,7 +141,7 @@ public: public: // start inherited documentation - template< template class... Signals > + template< class... Signals > static void connect() { @@ -271,7 +271,7 @@ public: static inline constexpr Channel getPinChannel() { - constexpr int8_t channel{Gpio::template AdcChannel}; + constexpr int8_t channel{detail::AdcChannel}; static_assert(channel >= 0, "Adc does not have a channel for this pin!"); return Channel(channel); } diff --git a/src/modm/platform/adc/stm32f3/adc.hpp.in b/src/modm/platform/adc/stm32f3/adc.hpp.in index f6463b4339..89b98eaf5c 100644 --- a/src/modm/platform/adc/stm32f3/adc.hpp.in +++ b/src/modm/platform/adc/stm32f3/adc.hpp.in @@ -230,7 +230,7 @@ public: MODM_FLAGS32(InterruptFlag); public: - template< template class... Signals > + template< class... Signals > static void connect() { @@ -336,7 +336,7 @@ public: static inline constexpr Channel getPinChannel() { - constexpr int8_t channel{Gpio::template AdcChannel}; + constexpr int8_t channel{detail::AdcChannel}; static_assert(channel >= 0, "Adc{{id}} does not have a channel for this pin!"); return Channel(channel); } diff --git a/src/modm/platform/can/stm32-fdcan/can.hpp.in b/src/modm/platform/can/stm32-fdcan/can.hpp.in index d0dcdc4d29..17efb3dce9 100644 --- a/src/modm/platform/can/stm32-fdcan/can.hpp.in +++ b/src/modm/platform/can/stm32-fdcan/can.hpp.in @@ -83,7 +83,7 @@ private: ); public: - template< template class... Signals > + template< class... Signals > static void connect(Gpio::InputType inputType = Gpio::InputType::Floating) { diff --git a/src/modm/platform/can/stm32/can.hpp.in b/src/modm/platform/can/stm32/can.hpp.in index 8bfb6e8b45..d32b6e3607 100644 --- a/src/modm/platform/can/stm32/can.hpp.in +++ b/src/modm/platform/can/stm32/can.hpp.in @@ -74,7 +74,7 @@ private: initializeWithPrescaler(uint16_t prescaler, uint8_t bs1, uint8_t bs2, uint32_t interruptPriority, Mode startupMode, bool overwriteOnOverrun); public: - template< template class... Signals > + template< class... Signals > static void connect(Gpio::InputType inputType = Gpio::InputType::Floating) { diff --git a/src/modm/platform/comp/stm32/comp.hpp.in b/src/modm/platform/comp/stm32/comp.hpp.in index 638f838bf1..440871f77d 100644 --- a/src/modm/platform/comp/stm32/comp.hpp.in +++ b/src/modm/platform/comp/stm32/comp.hpp.in @@ -508,7 +508,7 @@ namespace modm::platform public: // start inherited documentation - template< template class... Signals > + template< class... Signals > static void connect() { diff --git a/src/modm/platform/dac/stm32/dac.hpp.in b/src/modm/platform/dac/stm32/dac.hpp.in index bf1478c2cd..26261f4265 100644 --- a/src/modm/platform/dac/stm32/dac.hpp.in +++ b/src/modm/platform/dac/stm32/dac.hpp.in @@ -58,7 +58,7 @@ public: }; %% endif - template< template class... Signals > + template< class... Signals > static void connect() { diff --git a/src/modm/platform/dac/stm32/dac_dma.hpp.in b/src/modm/platform/dac/stm32/dac_dma.hpp.in index 3cb6b1e345..a2fed5d245 100644 --- a/src/modm/platform/dac/stm32/dac_dma.hpp.in +++ b/src/modm/platform/dac/stm32/dac_dma.hpp.in @@ -31,7 +31,7 @@ namespace modm::platform class Dac{{ id }}Dma { public: - template< template class... Signals > + template< class... Signals > static void connect() { diff --git a/src/modm/platform/eth/stm32/eth.hpp b/src/modm/platform/eth/stm32/eth.hpp index ea5a448560..281232176c 100644 --- a/src/modm/platform/eth/stm32/eth.hpp +++ b/src/modm/platform/eth/stm32/eth.hpp @@ -342,34 +342,13 @@ class Eth : public eth using BurstLength_t = modm::Configuration; using RxDmaBurstLength_t = modm::Configuration; - template class... Signals> - struct GpioConfigurator; - template class Signal, template class... Signals> - struct GpioConfigurator - { - static inline void - configure() { - Signal::Gpio::configure(modm::platform::Gpio::OutputType::PushPull, modm::platform::Gpio::OutputSpeed::VeryHigh); - GpioConfigurator::configure(); - } - }; - template - struct GpioConfigurator - { - static inline void - configure() {} - }; - public: - template class... Signals> + template< class... Signals > static void connect() { - using Configurator = GpioConfigurator; - using Connector = GpioConnector; - - Configurator::configure(); - Connector::connect(); + (GpioStatic::configure(Gpio::OutputType::PushPull, Gpio::OutputSpeed::VeryHigh), ...); + GpioConnector::connect(); } template diff --git a/src/modm/platform/i2c/at90_tiny_mega/i2c_master.hpp.in b/src/modm/platform/i2c/at90_tiny_mega/i2c_master.hpp.in index e0208e2416..9d76e87816 100644 --- a/src/modm/platform/i2c/at90_tiny_mega/i2c_master.hpp.in +++ b/src/modm/platform/i2c/at90_tiny_mega/i2c_master.hpp.in @@ -38,7 +38,7 @@ namespace platform class I2cMaster : public ::modm::I2cMaster, I2c { public: - template< template class... Signals, ResetDevices reset = ResetDevices::Standard> + template< class... Signals, ResetDevices reset = ResetDevices::Standard> static void connect(PullUps pullups = PullUps::External) { diff --git a/src/modm/platform/i2c/bitbang/bitbang_i2c_master.hpp.in b/src/modm/platform/i2c/bitbang/bitbang_i2c_master.hpp.in index 8260a70c37..576192cfc3 100644 --- a/src/modm/platform/i2c/bitbang/bitbang_i2c_master.hpp.in +++ b/src/modm/platform/i2c/bitbang/bitbang_i2c_master.hpp.in @@ -48,7 +48,7 @@ class BitBangI2cMaster : public modm::I2cMaster public: // start documentation inherited - template< template class... Signals, ResetDevices reset = ResetDevices::Standard > + template< class... Signals, ResetDevices reset = ResetDevices::Standard > static void connect(PullUps pullups = PullUps::External); diff --git a/src/modm/platform/i2c/bitbang/bitbang_i2c_master_impl.hpp b/src/modm/platform/i2c/bitbang/bitbang_i2c_master_impl.hpp index ae89d68702..a85c8098f3 100644 --- a/src/modm/platform/i2c/bitbang/bitbang_i2c_master_impl.hpp +++ b/src/modm/platform/i2c/bitbang/bitbang_i2c_master_impl.hpp @@ -59,7 +59,7 @@ modm::platform::BitBangI2cMaster::initialize() } template -template< template class... Signals, modm::I2cMaster::ResetDevices reset > +template< class... Signals, modm::I2cMaster::ResetDevices reset > void modm::platform::BitBangI2cMaster::connect(PullUps pullups) { @@ -75,7 +75,7 @@ modm::platform::BitBangI2cMaster::connect(PullUps pullups) SDA::configure(input); SCL::setOutput(SCL::OutputType::OpenDrain); SDA::setOutput(SDA::OutputType::OpenDrain); - if (reset != ResetDevices::NoReset) resetDevices(); + if constexpr (reset != ResetDevices::NoReset) resetDevices(); Connector::connect(); } diff --git a/src/modm/platform/i2c/stm32-extended/i2c_master.hpp.in b/src/modm/platform/i2c/stm32-extended/i2c_master.hpp.in index 7767272ef3..bccdbc9cdc 100644 --- a/src/modm/platform/i2c/stm32-extended/i2c_master.hpp.in +++ b/src/modm/platform/i2c/stm32-extended/i2c_master.hpp.in @@ -67,7 +67,7 @@ private: } public: - template< template class... Signals, ResetDevices reset = ResetDevices::Standard> + template< class... Signals, ResetDevices reset = ResetDevices::Standard> static void connect(PullUps pullups = PullUps::External) { diff --git a/src/modm/platform/i2c/stm32/i2c_master.hpp.in b/src/modm/platform/i2c/stm32/i2c_master.hpp.in index a19b7b03f1..5e984b8bca 100644 --- a/src/modm/platform/i2c/stm32/i2c_master.hpp.in +++ b/src/modm/platform/i2c/stm32/i2c_master.hpp.in @@ -40,7 +40,7 @@ public: static constexpr size_t TransactionBufferSize = {{ options["buffer.transaction"] }}; public: - template< template class... Signals, ResetDevices reset = ResetDevices::Standard> + template static void connect(PullUps pullups = PullUps::External) { diff --git a/src/modm/platform/spi/at90_tiny_mega/spi_master.hpp.in b/src/modm/platform/spi/at90_tiny_mega/spi_master.hpp.in index b07ed978ef..c7339d22be 100644 --- a/src/modm/platform/spi/at90_tiny_mega/spi_master.hpp.in +++ b/src/modm/platform/spi/at90_tiny_mega/spi_master.hpp.in @@ -68,7 +68,7 @@ public: public: // start documentation inherited - template< template class... Signals > + template< class... Signals > static void connect() { diff --git a/src/modm/platform/spi/at90_tiny_mega_uart/uart_spi_master.hpp.in b/src/modm/platform/spi/at90_tiny_mega_uart/uart_spi_master.hpp.in index f88c9f2416..6e23006b36 100644 --- a/src/modm/platform/spi/at90_tiny_mega_uart/uart_spi_master.hpp.in +++ b/src/modm/platform/spi/at90_tiny_mega_uart/uart_spi_master.hpp.in @@ -71,7 +71,7 @@ public: public: // start documentation inherited - template< template class... Signals > + template< class... Signals > static void connect() { diff --git a/src/modm/platform/spi/bitbang/bitbang_spi_master.hpp b/src/modm/platform/spi/bitbang/bitbang_spi_master.hpp index 6d1d25ac16..54db8fbfbc 100644 --- a/src/modm/platform/spi/bitbang/bitbang_spi_master.hpp +++ b/src/modm/platform/spi/bitbang/bitbang_spi_master.hpp @@ -45,7 +45,7 @@ class BitBangSpiMaster : public ::modm::SpiMaster { public: // start documentation inherited - template< template class... Signals > + template< class... Signals > static void connect(); diff --git a/src/modm/platform/spi/bitbang/bitbang_spi_master_impl.hpp b/src/modm/platform/spi/bitbang/bitbang_spi_master_impl.hpp index 5ace139c53..720a45c76d 100644 --- a/src/modm/platform/spi/bitbang/bitbang_spi_master_impl.hpp +++ b/src/modm/platform/spi/bitbang/bitbang_spi_master_impl.hpp @@ -40,7 +40,7 @@ modm::platform::BitBangSpiMaster::configuration(nullptr); // ---------------------------------------------------------------------------- template -template< template class... Signals > +template< class... Signals > void modm::platform::BitBangSpiMaster::connect() { diff --git a/src/modm/platform/spi/stm32/spi_master.hpp.in b/src/modm/platform/spi/stm32/spi_master.hpp.in index 9663b18fe9..94bd6887f4 100644 --- a/src/modm/platform/spi/stm32/spi_master.hpp.in +++ b/src/modm/platform/spi/stm32/spi_master.hpp.in @@ -67,7 +67,7 @@ public: using DataSize = Hal::DataSize; public: - template< template class... Signals > + template< class... Signals > static void connect() { diff --git a/src/modm/platform/spi/stm32_uart/uart_spi_master.hpp.in b/src/modm/platform/spi/stm32_uart/uart_spi_master.hpp.in index 3ce4492b46..e8bdddfaaa 100644 --- a/src/modm/platform/spi/stm32_uart/uart_spi_master.hpp.in +++ b/src/modm/platform/spi/stm32_uart/uart_spi_master.hpp.in @@ -52,7 +52,7 @@ public: }; public: - template< template class... Signals > + template< class... Signals > static void connect() { diff --git a/src/modm/platform/timer/stm32/advanced.hpp.in b/src/modm/platform/timer/stm32/advanced.hpp.in index 12f3ed0c86..3220d40d68 100644 --- a/src/modm/platform/timer/stm32/advanced.hpp.in +++ b/src/modm/platform/timer/stm32/advanced.hpp.in @@ -76,7 +76,7 @@ namespace platform class Timer{{ id }} : public AdvancedControlTimer { public: - template< template class... Signals > + template< class... Signals > static void connect() { diff --git a/src/modm/platform/timer/stm32/basic.hpp.in b/src/modm/platform/timer/stm32/basic.hpp.in index 7b2ebf32be..e8732a294f 100644 --- a/src/modm/platform/timer/stm32/basic.hpp.in +++ b/src/modm/platform/timer/stm32/basic.hpp.in @@ -56,7 +56,7 @@ namespace platform class Timer{{ id }} : public BasicTimer { public: - template< template class... Signals > + template< class... Signals > static void connect() { diff --git a/src/modm/platform/timer/stm32/general_purpose.hpp.in b/src/modm/platform/timer/stm32/general_purpose.hpp.in index 2bdef2faed..74848e1a70 100644 --- a/src/modm/platform/timer/stm32/general_purpose.hpp.in +++ b/src/modm/platform/timer/stm32/general_purpose.hpp.in @@ -116,7 +116,7 @@ public: typedef uint16_t Value; %% endif - template< template class... Signals > + template< class... Signals > static void connect() { diff --git a/src/modm/platform/uart/at90_tiny_mega/uart.hpp.in b/src/modm/platform/uart/at90_tiny_mega/uart.hpp.in index 0862f1891f..22a207f21f 100644 --- a/src/modm/platform/uart/at90_tiny_mega/uart.hpp.in +++ b/src/modm/platform/uart/at90_tiny_mega/uart.hpp.in @@ -45,7 +45,7 @@ public: static constexpr size_t TxBufferSize = {{ options["buffer.tx"] }}; public: - template< template class... Signals > + template< class... Signals > static void connect(Gpio::InputType InputTypeRx = Gpio::InputType::PullUp) { diff --git a/src/modm/platform/uart/stm32/uart.hpp.in b/src/modm/platform/uart/stm32/uart.hpp.in index 41de89079b..7751204d9c 100644 --- a/src/modm/platform/uart/stm32/uart.hpp.in +++ b/src/modm/platform/uart/stm32/uart.hpp.in @@ -51,7 +51,7 @@ public: static constexpr size_t TxBufferSize = {{ options["buffer.tx"] }}; public: - template< template class... Signals > + template< class... Signals > static void connect(Gpio::InputType InputTypeRx = Gpio::InputType::PullUp, Gpio::OutputType OutputTypeTx = Gpio::OutputType::PushPull) diff --git a/src/modm/platform/usb/stm32/usb.hpp.in b/src/modm/platform/usb/stm32/usb.hpp.in index f38966e0ef..3567026b88 100644 --- a/src/modm/platform/usb/stm32/usb.hpp.in +++ b/src/modm/platform/usb/stm32/usb.hpp.in @@ -42,7 +42,7 @@ public: %% endfor } - template< template class... Signals > + template< class... Signals > static void connect() { From 8e8bce6f852cf1a1272778ad64d5a575e0b9b648 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Sat, 18 Sep 2021 01:41:12 +0200 Subject: [PATCH 3/4] [exti] Add dedicated EXTI driver for STM32 --- README.md | 142 +++---- src/modm/platform/extint/sam/module.lb | 2 - src/modm/platform/extint/stm32/exti.cpp.in | 45 ++ src/modm/platform/extint/stm32/exti.hpp.in | 443 ++++++++++++++++++++ src/modm/platform/extint/stm32/module.lb | 105 +++++ src/modm/platform/extint/stm32/module.md | 51 +++ src/modm/platform/gpio/common/unused.hpp.in | 13 +- src/modm/platform/gpio/stm32/base.hpp.in | 8 - src/modm/platform/gpio/stm32/data.hpp.in | 1 - src/modm/platform/gpio/stm32/module.lb | 24 -- src/modm/platform/gpio/stm32/static.hpp.in | 55 --- tools/scripts/generate_hal_matrix.py | 5 +- 12 files changed, 719 insertions(+), 175 deletions(-) create mode 100644 src/modm/platform/extint/stm32/exti.cpp.in create mode 100644 src/modm/platform/extint/stm32/exti.hpp.in create mode 100644 src/modm/platform/extint/stm32/module.lb create mode 100644 src/modm/platform/extint/stm32/module.md diff --git a/README.md b/README.md index 43c37ee9ce..348713298e 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ Here is a table with all device families and the peripheral drivers they support - ✅ Implemented as a software driver in modm. - ○ Available in hardware but missing a software driver in modm. -- ✗ Unavailable in hardware or device with that peripheral not supported by modm. +- ✕ Unavailable in hardware or device with that peripheral not supported by modm. Note that this is a summary overview and your specific device may not have all the peripherals in this table. @@ -146,26 +146,26 @@ Please [discover modm's peripheral drivers for your specific device][discover]. - + - - + + - - + + - +
Comparator - - + + - - + + @@ -173,7 +173,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. - + @@ -193,11 +193,11 @@ Please [discover modm's peripheral drivers for your specific device][discover]. - + - + - +
DMA @@ -213,31 +213,31 @@ Please [discover modm's peripheral drivers for your specific device][discover]. - + - - - + + +
Ethernet - + - + - - + + - - - - - - - - - + + + + + + + + +
External Interrupt @@ -253,31 +253,31 @@ Please [discover modm's peripheral drivers for your specific device][discover]. - - + +
External Memory - + - + - + - - + + - - + + - - - + + +
GPIO @@ -335,29 +335,29 @@ Please [discover modm's peripheral drivers for your specific device][discover]. - - - + + +
Random Generator - - + + - + - + - - + + - - - + + +
SPI @@ -393,11 +393,11 @@ Please [discover modm's peripheral drivers for your specific device][discover]. - - - - - + + + + +
Timer @@ -452,12 +452,12 @@ Please [discover modm's peripheral drivers for your specific device][discover]. - - - - - - + + + + + +
USB @@ -466,7 +466,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. - + @@ -475,9 +475,9 @@ Please [discover modm's peripheral drivers for your specific device][discover]. - - - + + +
diff --git a/src/modm/platform/extint/sam/module.lb b/src/modm/platform/extint/sam/module.lb index 8131fe41a6..ab3272c3de 100644 --- a/src/modm/platform/extint/sam/module.lb +++ b/src/modm/platform/extint/sam/module.lb @@ -10,8 +10,6 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. # ----------------------------------------------------------------------------- -import sys - def init(module): module.name = ":platform:extint" module.description = "External Interrupt" diff --git a/src/modm/platform/extint/stm32/exti.cpp.in b/src/modm/platform/extint/stm32/exti.cpp.in new file mode 100644 index 0000000000..3ab04e0f45 --- /dev/null +++ b/src/modm/platform/extint/stm32/exti.cpp.in @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021, 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/. + */ +// ---------------------------------------------------------------------------- + +#include "exti.hpp" + +namespace modm::platform +{ + +Exti::Handler +Exti::handlers[Exti::Lines] modm_fastdata; + +void Exti::irq(uint32_t mask) +{ + uint32_t flags = EXTI->IMR{{"1" if (extended or separate_flags) else ""}} & mask; + while(flags) + { + const uint8_t lmb = 31ul - __builtin_clz(flags); + auto& fn = handlers[lmb]; + if (fn) fn(lmb); +%% if separate_flags + EXTI->FPR1 = 1ul << lmb; + EXTI->RPR1 = 1ul << lmb; +%% else + EXTI->PR{{"1" if extended else ""}} = 1ul << lmb; +%% endif + flags &= ~(1ul << lmb); + } +} + +%% for irq, range in extimap.items() | sort +MODM_ISR(EXTI{{irq}}) +{ + Exti::irq(Exti::getVectorMaskForLine({{range[0]}})); +} +%% endfor + +} diff --git a/src/modm/platform/extint/stm32/exti.hpp.in b/src/modm/platform/extint/stm32/exti.hpp.in new file mode 100644 index 0000000000..d377969549 --- /dev/null +++ b/src/modm/platform/extint/stm32/exti.hpp.in @@ -0,0 +1,443 @@ +/* + * Copyright (c) 2021, 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 + +#include +#include +#include +#include + +%% if options.with_handlers +#if defined __DOXYGEN__ || !defined MODM_EXTI_HANDLER_STORAGE +/// @ingroup modm_platform_exti +#define MODM_EXTI_HANDLER_STORAGE sizeof(void*) +#endif +%% endif + +namespace modm::platform +{ + +/// @cond +%% for irq in extimap | sort +MODM_ISR_DECL(EXTI{{irq}}); +%% endfor +/// @endcond + +/** + * External Interrupt/Event Controller. + * + * @author Niklas Hauser + * @ingroup modm_platform_exti + */ +class Exti +{ +public: + enum class + Trigger + { + NoEdges, + RisingEdge, + FallingEdge, + BothEdges, + }; + + enum class + Vector + { + LineUnknown = 0, +%% for irq, range in extimap.items() | sort + Line{{range[0]}}{% if (range | length) > 1 %}_{{range[-1]}}{% endif %} = EXTI{{irq}}_IRQn, +%% endfor + }; + + using MaskType = uint{{ 64 if extended else 32 }}_t; +%% if options.with_handlers + using Handler = modm::inplace_function; +%% endif + +%% set pf = "1" if (extended or separate_flags) else "" +%% if options.with_handlers +public: + template< class Pin > + static void + connect(Trigger trigger, Handler&& handler, uint8_t priority=15) + { + disableInterrupts(); + acknowledgeFlags(); + // Now it's safe to copy the handler + handlers[Pin::pin] = handler; + + // Select the source of the exti line + setTriggerSource(); + setTriggers(trigger); + + enableVector(priority); + enableInterrupts(); + } + + template< class Pin > + static void + disconnect() + { + disableInterrupts(); + // Disable the IRQ vector if all pins are disabled + if (not (EXTI->IMR{{pf}} & getVectorMaskForLine(Pin::pin))) + disableVector(); + handlers[Pin::pin] = nullptr; + } +%% endif + +public: +%% set pins = "((1ul" ~ ("l" if extended else "") ~ " << Pins::pin) | ...)" + // - - - TRIGGERS - - - + /// Selects the GPIO port trigger source for EXTI lines 0-{{lines}} + inline static void + setTriggerSource(uint8_t line, Gpio::Port port) + { + const uint8_t index = line >> 2; + const uint8_t bit_pos = (line & 0b0011) << 2; + const uint16_t syscfg_mask = 0xf << bit_pos; + const uint16_t syscfg_value = (uint8_t(port) & 0xf) << bit_pos; + {{exti_reg}}->EXTICR[index] = ({{exti_reg}}->EXTICR[index] & ~syscfg_mask) | syscfg_value; + } + template< class Pin > + static void setTriggerSource() + { + constexpr uint8_t index = Pin::pin >> 2; + constexpr uint8_t bit_pos = (Pin::pin & 0b0011) << 2; + constexpr uint16_t syscfg_mask = 0xf << bit_pos; + constexpr uint16_t syscfg_value = (uint8_t(Pin::port) & 0xf) << bit_pos; + {{exti_reg}}->EXTICR[index] = ({{exti_reg}}->EXTICR[index] & ~syscfg_mask) | syscfg_value; + } + template< class... Pins > + static void setTriggerSources() + { +%% for line in range(lines) + static_assert(((Pins::pin == {{line}} ? 1 : 0) + ...) <= 1, + "Only one GPIO port can trigger line {{line}}!"); +%% endfor + (setTriggerSource(), ...); + } + inline static void + setTrigger(MaskType mask, Trigger trigger) + { + switch (trigger) + { + case Trigger::NoEdges: + EXTI->RTSR{{pf}} &= ~mask; + EXTI->FTSR{{pf}} &= ~mask; +%% if extended + EXTI->RTSR2 &= ~mask >> 32; + EXTI->FTSR2 &= ~mask >> 32; +%% endif + break; + case Trigger::RisingEdge: + EXTI->RTSR{{pf}} |= mask; + EXTI->FTSR{{pf}} &= ~mask; +%% if extended + EXTI->RTSR2 |= mask >> 32; + EXTI->FTSR2 &= ~mask >> 32; +%% endif + break; + case Trigger::FallingEdge: + EXTI->RTSR{{pf}} &= ~mask; + EXTI->FTSR{{pf}} |= mask; +%% if extended + EXTI->RTSR2 &= ~mask >> 32; + EXTI->FTSR2 |= mask >> 32; +%% endif + break; + case Trigger::BothEdges: + EXTI->RTSR{{pf}} |= mask; + EXTI->FTSR{{pf}} |= mask; +%% if extended + EXTI->RTSR2 |= mask >> 32; + EXTI->FTSR2 |= mask >> 32; +%% endif + break; + } + } + template< class... Pins > + static void setTriggers(Trigger trigger) + { setTrigger({{pins}}, trigger); } + + // - - - INTERRUPTS - - - + inline static void + enableInterrupts(MaskType mask) + { + EXTI->IMR{{pf}} |= mask; +%% if extended + EXTI->IMR2 |= mask >> 32; +%% endif + } + template< class... Pins > static void + enableInterrupts() + { EXTI->IMR{{pf}} |= {{pins}}; } + + inline static MaskType + getInterruptEnabled() + { + return {% if extended %}(uint64_t(EXTI->IMR2) << 32) | {% endif %}EXTI->IMR{{pf}}; + } + template< class... Pins > static bool + areInterruptsEnabled() + { return EXTI->IMR{{pf}} & {{pins}}; } + + inline static void + disableInterrupt(MaskType mask) + { + EXTI->IMR{{pf}} &= ~mask; +%% if extended + EXTI->IMR2 &= ~mask >> 32; +%% endif + } + template< class... Pins > static void + disableInterrupts() + { EXTI->IMR{{pf}} &= ~{{pins}}; } + + + // - - - EVENTS - - - + inline static void + enableEvent(MaskType mask) + { + EXTI->EMR{{pf}} |= mask; +%% if extended + EXTI->EMR2 |= mask >> 32; +%% endif + } + template< class... Pins > static void + enableEvents() { EXTI->EMR{{pf}} |= {{pins}}; } + + inline static MaskType + getEventEnabled() + { + return {% if extended %}(uint64_t(EXTI->EMR2) << 32) | {% endif %}EXTI->EMR{{pf}}; + } + template< class... Pins > static void + areEventsEnabled() { return EXTI->EMR{{pf}} & {{pins}}; } + + inline static void + disableEvent(MaskType mask) + { + EXTI->EMR{{pf}} &= ~mask; +%% if extended + EXTI->EMR2 &= ~mask >> 32; +%% endif + } + template< class... Pins > static void + disableEvents() { EXTI->EMR{{pf}} &= ~{{pins}}; } + + // - - - VECTORS - - - + inline static void + enableVector(Vector vector, uint8_t priority) + { + NVIC_SetPriority(IRQn_Type(vector), priority); + NVIC_EnableIRQ(IRQn_Type(vector)); + } + template< class Pin > static void + enableVector(uint8_t priority) { enableVector(getVectorForLine(Pin::pin), priority); } + + inline static void + enableVectors(MaskType mask, uint8_t priority) + { +%% for irq, range in extimap.items() | sort + if (getVectorMaskForLine({{range[0]}}) & mask) enableVector(Vector::Line{{range[0]}}{% if (range | length) > 1 %}_{{range[-1]}}{% endif %}, priority); +%% endfor + } + template< class... Pins > static void + enableVectors(uint8_t priority) + { + constexpr uint32_t mask = {{pins}}; +%% for irq, range in extimap.items() | sort + if constexpr (getVectorMaskForLine({{range[0]}}) & mask) enableVector(Vector::Line{{range[0]}}{% if (range | length) > 1 %}_{{range[-1]}}{% endif %}, priority); +%% endfor + } + + inline static void + disableVector(Vector vector) + { + NVIC_DisableIRQ(IRQn_Type(vector)); + } + template< class Pin > static void + disableVector(uint8_t priority) { disableVector(getVectorForLine(Pin::pin)); } + + inline static void + disableVectors(MaskType mask) + { +%% for irq, range in extimap.items() | sort + if (getVectorMaskForLine({{range[0]}}) & mask) disableVector(Vector::Line{{range[0]}}{% if (range | length) > 1 %}_{{range[-1]}}{% endif %}); +%% endfor + } + template< class... Pins > static void + disableVectors() + { + constexpr uint32_t mask = {{pins}}; +%% for irq, range in extimap.items() | sort + if constexpr (getVectorMaskForLine({{range[0]}}) & mask) disableVector(Vector::Line{{range[0]}}{% if (range | length) > 1 %}_{{range[-1]}}{% endif %}); +%% endfor + } + + // - - - FLAGS - - - +%% if separate_flags + inline static MaskType + getRisingFlags() + { + return {% if extended %}(uint64_t(EXTI->RPR2) << 32) | {% endif %}EXTI->RPR1; + } + template< class... Pins > static bool + areRisingFlagsSet() + { return EXTI->RPR1 & {{pins}}; } + + inline static void + acknowledgeRisingFlags(MaskType mask) + { + EXTI->RPR1 = mask; +%% if extended + EXTI->RPR2 = mask >> 32; +%% endif + } + template< class... Pins > static void + acknowledgeRisingFlags() + { EXTI->RPR1 = {{pins}}; } + + inline static MaskType + getFallingFlags() + { + return {% if extended %}(uint64_t(EXTI->FPR2) << 32) | {% endif %}EXTI->FPR1; + } + template< class... Pins > static bool + areFallingFlagsSet() + { return EXTI->FPR1 & {{pins}}; } + + inline static void + acknowledgeFallingFlags(MaskType mask) + { + EXTI->FPR1 = mask; +%% if extended + EXTI->FPR2 = mask >> 32; +%% endif + } + template< class... Pins > static void + acknowledgeFallingFlags() + { EXTI->FPR1 = {{pins}}; } + + /// Returns rising or falling flags! + inline static MaskType + getFlags() + { return getRisingFlags() | getFallingFlags(); } + template< class... Pins > static bool + areFlagsSet() + { return areRisingFlagsSet() or areFallingFlagsSet(); } + + /// Clears both rising and falling flags! + inline static void + acknowledgeFlags(MaskType mask) + { + acknowledgeRisingFlags(mask); + acknowledgeFallingFlags(mask); + } + template< class... Pins > static void + acknowledgeFlags() + { + acknowledgeRisingFlags(); + acknowledgeFallingFlags(); + } +%% else + inline static MaskType + getFlags() + { +%% if extended + return (uint64_t(EXTI->PR2) << 32) | EXTI->PR1; +%% else + return EXTI->PR; +%% endif + } + template< class... Pins > + static bool areFlagsSet() + { return EXTI->PR{{pf}} & {{pins}}; } + + inline static void + acknowledgeFlags(MaskType mask) + { +%% if extended + EXTI->PR1 = mask; + EXTI->PR2 = mask >> 32; +%% else + EXTI->PR = mask; +%% endif + } + template< class... Pins > + static void acknowledgeFlags() + { EXTI->PR{{pf}} = {{pins}}; } +%% endif + + inline static void + setFlags(MaskType mask) + { + EXTI->SWIER{{pf}} = mask; +%% if extended + EXTI->SWIER2 = mask >> 32; +%% endif + } + template< class... Pins > + static void setFlags() + { EXTI->SWIER{{pf}} = {{pins}}; } + +public: + static constexpr Vector + getVectorForLine(uint8_t line) + { + switch(line) + { +%% for irq, range in extimap.items() | sort + %% for line in range + case {{line}}: + %% endfor + return Vector::Line{{range[0]}}{% if (range | length) > 1 %}_{{range[-1]}}{% endif %}; +%% endfor + } + return Vector::LineUnknown; + } + + static constexpr MaskType + getVectorMaskForLine(uint8_t line) + { + switch(line) + { +%% for irq, range in extimap.items() | sort + %% for line in range + case {{line}}: + %% endfor + %% if (range | length) == 1 + return 1ul << {{ range[0] }}; + %% else + return ((1ul << {{range[-1]+1}}) - 1ul) & ~((1ul << {{range[0]+1}}) - 1ul); + %% endif +%% endfor + } + return 0; + } + +%% if options.with_handlers +protected: + /// @cond + static void irq(uint32_t mask); + static constexpr uint8_t Lines = {{lines}}; + static Handler handlers[Lines]; +%% for irq in extimap | sort + friend void MODM_ISR_NAME(EXTI{{irq}})(); +%% endfor + /// @endcond +%% endif +}; + +} // namespace modm::platform diff --git a/src/modm/platform/extint/stm32/module.lb b/src/modm/platform/extint/stm32/module.lb new file mode 100644 index 0000000000..0223f1942e --- /dev/null +++ b/src/modm/platform/extint/stm32/module.lb @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2021, 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/. +# ----------------------------------------------------------------------------- + +def init(module): + module.name = ":platform:exti" + module.description = FileReader("module.md") + +def prepare(module, options): + module.depends(":cmsis:device", ":platform:gpio", ":utils") + module.add_option( + BooleanOption( + name="with_handlers", + description=descr_with_handlers, + default=True)) + return options[":target"].identifier.platform == "stm32" + +extimap = {} +def validate(env): + # These are all exti possible vectors: 0, 0_1, 1, 15_10, 2, 2_3, 2_TSC, 3, 4, 4_15, 9_5 + all_exti = { + "0": [0], "1": [1], "2": [2], "3": [3], "4": [4], + "0_1": [0,1], + "2_TSC": [2], + "2_3": [2,3], + "4_15": [4,5,6,7,8,9,10,11,12,13,14,15], + "9_5": [5,6,7,8,9], + "15_10": [10,11,12,13,14,15], + } + global extimap + for vec in (v["name"][4:] for v in env[":target"].get_driver("core")["vector"] if "EXTI" in v["name"]): + if vec not in all_exti: + raise ValidateException("Unknown EXTI vector: '{}'".format(vec)) + extimap[vec] = all_exti[vec] + +def build(env): + target = env[":target"].identifier + + separate_flags = target.family in ["g0"] + extended = target.family in ["l4", "g4", "h7"] + if separate_flags: extended = target.name in ["b1", "c1"] + + exti_reg = "SYSCFG" + if target.family in ["g0"]: exti_reg = "EXTI" + if target.family in ["f1"]: exti_reg = "AFIO" + + global extimap + lines = 1 + max(int(g["pin"]) for g in env[":target"].get_driver("gpio")["gpio"]) + env.substitutions = { + "extended": extended, + "separate_flags": separate_flags, + "exti_reg": exti_reg, + "target": target, + "extimap": extimap, + "lines": lines, + "with_handlers": env["with_handlers"], + } + env.outbasepath = "modm/src/modm/platform/exti" + env.template("exti.hpp.in") + if env["with_handlers"]: + env.template("exti.cpp.in") + + +descr_with_handlers = """ +# Use callbacks for GPIO lines + +To simplify the external IRQ management of external GPIO triggers, you can use +the connect method to attach a `void(uint8_t)` callback to EXTI lines 0-15, +which gets passed the triggered line number as an argument: + +```cpp +Exti::connect(Exti::Trigger::FallingEdge, + [count=uint32_t(0)](uint8_t line) mutable +{ + MODM_LOG_INFO << "Line " << line << " triggered " << ++count << " times!" << modm::endl; +}); +// to disable the handler and IRQ later +Exti::disconnect(); +``` + +!!! warning "Duplicate Symbols for EXTI_IRQHandler" + This option internally defines all `MODM_ISR(EXTI*)` IRQs, so you cannot + define them anymore in your application! + +The callback is implemented using `modm::inplace_function`, therefore uses no +heap, but has a fixed storage size of `sizeof(void*)` by default. +You can increase this storage size by defining a new global storage size +`MODM_EXTI_HANDLER_STORAGE=bytes` in your `project.xml`: + +```xml + + + MODM_EXTI_HANDLER_STORAGE=12 + + +``` +""" diff --git a/src/modm/platform/extint/stm32/module.md b/src/modm/platform/extint/stm32/module.md new file mode 100644 index 0000000000..7f7d3e80ec --- /dev/null +++ b/src/modm/platform/extint/stm32/module.md @@ -0,0 +1,51 @@ +# External Interrupt/Event Controller (EXTI) + +This driver provides an API for configuring all EXTI lines via register access. +Note that you need to pass a mask, which allows you to configure multiple EXTI +lines at once. + +A typical use-case is to trigger on multiple GPIO inputs: + +```cpp +// all trigger on both edges +Exti::setTriggers(Exti::Trigger::BothEdges); +// Sets the specific GPIO port per line +Exti::setTriggerSources(); + +// Enables EXTI4 and EXTI9_5 IRQ handlers with the same priority of 10! +Exti::enableVectors(10); +// Sets a different priority for EXTI0 +Exti::enableVector(5); +// Enable all interrupts at the same time +Exti::enableInterrupts(); + +// You must manually define the IRQ handlers but *only* +// if you disable the `with_handlers` option! +MODM_ISR(EXTI0) { Exti::acknowledgeFlags(); } +MODM_ISR(EXTI4) { Exti::acknowledgeFlags(); } +MODM_ISR(EXTI9_5) { Exti::acknowledgeFlags(); } +``` + +Note that you can also use this to configure more than just GPIO trigger +sources, you can configure all other device specific EXTI lines as well. +However, you need to manually configure the NVIC vector and possibly also +configure the sending peripheral separately. + +```cpp +// Configure the RTC peripheral to send the tamper event +// Line 21 is the RTC tamper alert on STM32F4x6 +Exti::setTrigger(1ul << 21, Exti::Trigger::RisingEdge); +Exti::enableInterrupts(1ul << 21); +// Manually enable the RTC_Alarm IRQ +NVIC_SetPriority(RTC_Alarm_IRQn, 10); +NVIC_EnableIRQ(RTC_Alarm_IRQn); +// Different IRQ naming scheme, not EXTI_RTC! +MODM_ISR(RTC_Alarm) {} +``` + +You can also trigger events instead of interrupts to facilitate WFE. + +```cpp +Exti::setTrigger(1ul << 21, Exti::Trigger::FallingEdge); +Exti::enableEvents(1ul << 21); +``` diff --git a/src/modm/platform/gpio/common/unused.hpp.in b/src/modm/platform/gpio/common/unused.hpp.in index 53ebac3552..da203d3beb 100644 --- a/src/modm/platform/gpio/common/unused.hpp.in +++ b/src/modm/platform/gpio/common/unused.hpp.in @@ -95,16 +95,6 @@ public: static void configure(InputType) {} static bool read() { return false; } - // 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; } - /// Reset the interrupt flag in the interrupt routine. - static void acknowledgeExternalInterruptFlag() {} - // GpioIO static Direction getDirection() { return Direction::In; } %% if target.platform in ["stm32"] @@ -121,9 +111,8 @@ public: %% for name in all_signals struct {{ name }} { - using Gpio = GpioUnused; + using Data = detail::DataUnused; static constexpr Gpio::Signal Signal = Gpio::Signal::{{ name }}; - static void connect() {} }; %% endfor }; diff --git a/src/modm/platform/gpio/stm32/base.hpp.in b/src/modm/platform/gpio/stm32/base.hpp.in index 86208b73e0..08abe5f4a1 100644 --- a/src/modm/platform/gpio/stm32/base.hpp.in +++ b/src/modm/platform/gpio/stm32/base.hpp.in @@ -77,14 +77,6 @@ struct Gpio %% endif }; - enum class - InputTrigger - { - RisingEdge, - FallingEdge, - BothEdges, - }; - /// The Port a Gpio Pin is connected to. enum class Port diff --git a/src/modm/platform/gpio/stm32/data.hpp.in b/src/modm/platform/gpio/stm32/data.hpp.in index 5332bf549e..3a31b44795 100644 --- a/src/modm/platform/gpio/stm32/data.hpp.in +++ b/src/modm/platform/gpio/stm32/data.hpp.in @@ -37,7 +37,6 @@ struct DataUnused {}; struct Data{{ port ~ pin }} { static constexpr Gpio::Port port = Gpio::Port::{{port}}; static constexpr uint8_t pin = {{pin}}; - static constexpr IRQn_Type ExternalInterruptIRQ = EXTI{{ gpio.exti_irqn }}_IRQn; %% if gpio.has_remap inline static void remap() { {{gpio.remap_reg}} {% if gpio.remap_value %}|= {% else %}&= ~{% endif %}{{gpio.remap_mask}}; } %% endif diff --git a/src/modm/platform/gpio/stm32/module.lb b/src/modm/platform/gpio/stm32/module.lb index c217404f7f..c1b88deb03 100644 --- a/src/modm/platform/gpio/stm32/module.lb +++ b/src/modm/platform/gpio/stm32/module.lb @@ -195,25 +195,6 @@ def validate(env): bprops["group_map"] = signal_map bprops["remaps"] = driver["remap"] - # These are all exti possible vectors: 0, 0_1, 1, 15_10, 2, 2_3, 2_TSC, 3, 4, 4_15, 9_5 - all_exti = { - "0": [0], "1": [1], "2": [2], "3": [3], "4": [4], - "0_1": [0,1], - "2_TSC": [2], - "2_3": [2,3], - "4_15": [4,5,6,7,8,9,10,11,12,13,14,15], - "9_5": [5,6,7,8,9], - "15_10": [10,11,12,13,14,15], - } - extimap = {} - for vec in [v["name"][4:] for v in device.get_driver("core")["vector"] if "EXTI" in v["name"]]: - if vec not in all_exti: - raise ValidateException("Unknown EXTI vector: '{}'".format(vec)) - for num in all_exti[vec]: - if str(num) in extimap: - raise ValidateException("Pin '{}' already in EXTI map!".format(str(num))) - extimap[str(num)] = vec - # Compute the set of remapped pins remapped_gpios = {} for p in driver["package"][0]["pin"]: @@ -243,7 +224,6 @@ def validate(env): has_remap = key in remapped_gpios bprops[key] = { "gpio": gpio, - "exti_irqn": extimap[gpio["pin"]], "signals": extracted_signals, "has_remap": has_remap, } @@ -272,10 +252,6 @@ def validate(env): all_peripherals.extend([get_driver(r) for r in driver["remap"]]) bprops["all_peripherals"] = sorted(list(set(all_peripherals))) bprops["all_signals"] = sorted(list(set(s["name"] for s in all_signals.values()))) - bprops["pf"] = "1" if device.identifier["family"] in ["l4", "g0", "g4", "h7"] else "" - bprops["exti_reg"] = "SYSCFG" - if device.identifier.family in ["g0"]: bprops["exti_reg"] = "EXTI" - if device.identifier.family in ["f1"]: bprops["exti_reg"] = "AFIO" bprops["gpios"] = [bprops[g["port"] + g["pin"]] for g in driver["gpio"]] # Check the max number of ADC instances diff --git a/src/modm/platform/gpio/stm32/static.hpp.in b/src/modm/platform/gpio/stm32/static.hpp.in index ad16e74b93..518693e8f6 100644 --- a/src/modm/platform/gpio/stm32/static.hpp.in +++ b/src/modm/platform/gpio/stm32/static.hpp.in @@ -34,7 +34,6 @@ public: using Data = GpioData; using GpioData::port; using GpioData::pin; - using GpioData::ExternalInterruptIRQ; static constexpr Direction direction = Direction::InOut; static constexpr bool isInverted = false; @@ -115,60 +114,6 @@ public: inline static bool read() { return gport()->IDR & mask; } - // External Interrupts - // Warning: This will disable any previously enabled interrupt which is - // routed to the same interupt line, e.g. PA3 will disable PB3. - // This is a hardware limitation by the STM32 EXTI. - inline static void enableExternalInterrupt() - { - // PA[x], x = 0 .. 3 maps to EXTICR[0] - // PA[x], x = 4 .. 7 maps to EXTICR[1] - // PA[x], x = 8 .. 11 maps to EXTICR[2] - // PA[x], x = 12 .. 15 maps to EXTICR[3] - // => bit3 and bit2 (mask 0x0c) specify the register - // => bit1 and bit0 (mask 0x03) specify the bit position - constexpr uint8_t index = (pin & 0b1100) >> 2; - constexpr uint8_t bit_pos = (pin & 0b0011) << 2; - constexpr uint16_t syscfg_mask = (0b1111) << bit_pos; - constexpr uint16_t syscfg_value = (port_nr & (0b1111)) << bit_pos; - {{exti_reg}}->EXTICR[index] = ({{exti_reg}}->EXTICR[index] & ~syscfg_mask) | syscfg_value; - EXTI->IMR{{pf}} |= mask; - } - inline static void disableExternalInterrupt() { EXTI->IMR{{pf}} &= ~mask; } - inline static void enableExternalInterruptVector(const uint32_t priority) - { - NVIC_SetPriority(ExternalInterruptIRQ, priority); - NVIC_EnableIRQ(ExternalInterruptIRQ); - } - inline static void disableExternalInterruptVector() { NVIC_DisableIRQ(ExternalInterruptIRQ); } - inline static void setInputTrigger(const InputTrigger trigger) - { - switch (trigger) - { - case InputTrigger::RisingEdge: - EXTI->RTSR{{pf}} |= mask; - EXTI->FTSR{{pf}} &= ~mask; - break; - case InputTrigger::FallingEdge: - EXTI->RTSR{{pf}} &= ~mask; - EXTI->FTSR{{pf}} |= mask; - break; - case InputTrigger::BothEdges: - EXTI->RTSR{{pf}} |= mask; - EXTI->FTSR{{pf}} |= mask; - break; - } - } -%% if target.family in ["g0"] - inline static bool getExternalInterruptFlag() - { return (EXTI->FPR1 & mask) | (EXTI->RPR1 & mask); } - inline static void acknowledgeExternalInterruptFlag() - { EXTI->FPR1 |= mask; EXTI->RPR1 |= mask; } -%% else - inline static bool getExternalInterruptFlag() { return (EXTI->PR{{pf}} & mask); } - inline static void acknowledgeExternalInterruptFlag() { EXTI->PR{{pf}} = mask; } -%% endif - // GpioIO inline static Direction getDirection() { diff --git a/tools/scripts/generate_hal_matrix.py b/tools/scripts/generate_hal_matrix.py index acc92b46bf..84a5cef629 100755 --- a/tools/scripts/generate_hal_matrix.py +++ b/tools/scripts/generate_hal_matrix.py @@ -98,6 +98,7 @@ def hal_get_modules(): "rcc": "System Clock", "gclk": "System Clock", "extint": "External Interrupt", + "exti": "External Interrupt", "fsmc": "External Memory", "flash": "Internal Flash", "timer": "Timer", @@ -108,7 +109,7 @@ def hal_get_modules(): mapping[mname].update(file_cache[module.filename]) # External interrupt is currently part of the GPIO module - if target.platform in ["avr", "stm32"] and "GPIO" in modules: + if target.platform in ["avr"] and "GPIO" in modules: modules.add("External Interrupt") # print(target, dict(modules)) @@ -171,7 +172,7 @@ def hal_create_table(targets, platforms, common_table=False): mapping = targets[1] print(platforms, dict(mapping)) - values = defaultdict(lambda: defaultdict(lambda: "✗")) + values = defaultdict(lambda: defaultdict(lambda: "✕")) for driver in all_drivers: for f, v in modules.items(): # mapping from driver to peripheral From c0aeaae2643d59a24fc74ba5879a0bda764266c1 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Fri, 24 Sep 2021 22:34:18 +0200 Subject: [PATCH 4/4] [exti] Adapt library code to EXTI changes --- examples/nucleo_f411re/radio/lbuild.xml | 1 + examples/nucleo_f411re/radio/radio.hpp | 32 ++++-------- examples/stm32f469_discovery/blink/main.cpp | 6 +++ .../stm32f469_discovery/blink/project.xml | 5 +- .../stm32f469_discovery/touchscreen/main.cpp | 5 +- examples/stm32f4_discovery/exti/main.cpp | 49 +++++-------------- examples/stm32f4_discovery/exti/project.xml | 2 +- src/modm/board/devebox_stm32f4xx/board.hpp | 3 -- src/modm/board/devebox_stm32h750vb/board.hpp | 6 --- src/modm/board/disco_f051r8/board.hpp | 3 -- src/modm/board/disco_f072rb/board.hpp | 11 ----- src/modm/board/disco_f100rb/board.hpp | 3 -- src/modm/board/disco_f303vc/board.hpp | 22 --------- src/modm/board/disco_f407vg/board.hpp | 7 --- src/modm/board/disco_f429zi/board.hpp | 11 ----- src/modm/board/disco_f469ni/board.hpp | 6 --- src/modm/board/disco_f746ng/board.hpp | 3 -- src/modm/board/disco_f769ni/board.hpp | 3 -- src/modm/board/disco_l476vg/board.hpp | 3 -- src/modm/board/nucleo_f103rb/board.hpp | 3 -- src/modm/board/nucleo_f401re/board.hpp | 3 -- src/modm/board/nucleo_f411re/board.hpp | 3 -- src/modm/board/nucleo_f429zi/board.hpp | 3 -- src/modm/board/nucleo_f446re/board.hpp | 3 -- src/modm/board/nucleo_f446ze/board.hpp | 3 -- src/modm/board/nucleo_f746zg/board.hpp | 3 -- src/modm/board/nucleo_f767zi/board.hpp | 3 -- src/modm/board/nucleo_g071rb/board.hpp | 3 -- src/modm/board/nucleo_g431rb/board.hpp | 3 -- src/modm/board/nucleo_g474re/board.hpp | 3 -- src/modm/board/nucleo_h723zg/board.hpp | 3 -- src/modm/board/nucleo_h743zi/board.hpp | 3 -- src/modm/board/nucleo_l496zg-p/board.hpp | 3 -- src/modm/board/stm32_f4ve/board.hpp | 9 +--- src/modm/driver/gpio/gpio_sampler.cpp.in | 11 +---- src/modm/driver/gpio/gpio_sampler.hpp.in | 2 +- src/modm/driver/gpio/gpio_sampler.lb | 3 +- src/modm/driver/gpio/gpio_sampler_impl.hpp.in | 15 +++--- src/modm/driver/pwm/ws2812b.hpp | 2 +- .../one_wire/bitbang/bitbang_master.hpp.in | 2 +- test/config/nucleo-f091.xml | 1 + test/config/nucleo-f103_A.xml | 1 + test/config/nucleo-f103_B.xml | 1 + test/config/nucleo-f401.xml | 1 + test/config/nucleo-f411.xml | 1 + test/config/nucleo-f446.xml | 1 + test/config/nucleo-f446ze.xml | 1 + test/config/nucleo-g474.xml | 1 + test/config/nucleo-l432.xml | 1 + 49 files changed, 62 insertions(+), 214 deletions(-) diff --git a/examples/nucleo_f411re/radio/lbuild.xml b/examples/nucleo_f411re/radio/lbuild.xml index c411b50221..8e2b34d6ac 100644 --- a/examples/nucleo_f411re/radio/lbuild.xml +++ b/examples/nucleo_f411re/radio/lbuild.xml @@ -2,6 +2,7 @@ modm:nucleo-f411re modm:driver:nrf24 + modm:platform:exti modm:platform:spi:2 modm:platform:spi:3 modm:platform:timer:2 diff --git a/examples/nucleo_f411re/radio/radio.hpp b/examples/nucleo_f411re/radio/radio.hpp index 5217049d67..ae18b25f45 100644 --- a/examples/nucleo_f411re/radio/radio.hpp +++ b/examples/nucleo_f411re/radio/radio.hpp @@ -60,15 +60,6 @@ using Nrf1Phy = modm::Nrf24Phy; using Nrf1Config = modm::Nrf24Config; using Nrf1Data = modm::Nrf24Data; - -// This must normally be declared in a .cpp file, NOT a header file -MODM_ISR(EXTI9_5) // From PA9 -{ - Nrf1Irq::acknowledgeExternalInterruptFlag(); - Board::LedD13::toggle(); - Nrf1Data::interruptHandler(); -} - using Nrf2Spi = SpiMaster2; using Nrf2Sck = GpioB13; using Nrf2Mosi = GpioB15; @@ -81,13 +72,6 @@ using Nrf2Phy = modm::Nrf24Phy; using Nrf2Config = modm::Nrf24Config; using Nrf2Data = modm::Nrf24Data; -MODM_ISR(EXTI15_10) // From PB12 -{ - Nrf2Irq::acknowledgeExternalInterruptFlag(); - Board::LedD13::toggle(); - Nrf2Data::interruptHandler(); -} - void inline initializeSpi(uint8_t instances=0b11) { @@ -138,9 +122,11 @@ initializeNrf(uint8_t instances=0b11, uint8_t address1=nrf_address1, uint8_t add Nrf1Config::setCrc(Nrf1Config::Crc::Crc2Byte); Nrf1Irq::setInput(Nrf1Irq::InputType::PullUp); - Nrf1Irq::setInputTrigger(Nrf1Irq::InputTrigger::FallingEdge); - Nrf1Irq::enableExternalInterrupt(); - Nrf1Irq::enableExternalInterruptVector(4); + Exti::connect(Exti::Trigger::FallingEdge, [](uint8_t) + { + Board::LedD13::toggle(); + Nrf1Data::interruptHandler(); + }); } if (instances & 0b10) { @@ -155,8 +141,10 @@ initializeNrf(uint8_t instances=0b11, uint8_t address1=nrf_address1, uint8_t add Nrf2Config::setCrc(Nrf2Config::Crc::Crc2Byte); Nrf2Irq::setInput(Nrf2Irq::InputType::PullUp); - Nrf2Irq::setInputTrigger(Nrf2Irq::InputTrigger::FallingEdge); - Nrf2Irq::enableExternalInterrupt(); - Nrf2Irq::enableExternalInterruptVector(5); + Exti::connect(Exti::Trigger::FallingEdge, [](uint8_t) + { + Board::LedD13::toggle(); + Nrf2Data::interruptHandler(); + }); } } diff --git a/examples/stm32f469_discovery/blink/main.cpp b/examples/stm32f469_discovery/blink/main.cpp index 8aad0e1025..9954d6117d 100644 --- a/examples/stm32f469_discovery/blink/main.cpp +++ b/examples/stm32f469_discovery/blink/main.cpp @@ -37,6 +37,12 @@ main() uint32_t ms_counter{0}; uint32_t us_counter{0}; + Exti::connect(Exti::Trigger::FallingEdge, [count = uint32_t(0)](uint8_t line) mutable + { + count++; + MODM_LOG_INFO << "Button called " << count << " times on line " << line << modm::endl; + }); + while (true) { { diff --git a/examples/stm32f469_discovery/blink/project.xml b/examples/stm32f469_discovery/blink/project.xml index 96fac5c54b..729234da1e 100644 --- a/examples/stm32f469_discovery/blink/project.xml +++ b/examples/stm32f469_discovery/blink/project.xml @@ -4,8 +4,11 @@ - modm:platform:gpio + modm:platform:exti modm:processing:timer modm:build:scons + + MODM_EXTI_HANDLER_STORAGE=12 + diff --git a/examples/stm32f469_discovery/touchscreen/main.cpp b/examples/stm32f469_discovery/touchscreen/main.cpp index 13444a6321..0bd3fb1e86 100644 --- a/examples/stm32f469_discovery/touchscreen/main.cpp +++ b/examples/stm32f469_discovery/touchscreen/main.cpp @@ -38,11 +38,10 @@ class LineDrawer : public modm::pt::Protothread { do { // Wait for either touchscreen interrupt or clear screen button - PT_WAIT_UNTIL(Int::getExternalInterruptFlag() or Button::read()); + PT_WAIT_UNTIL(Int::read() or Button::read()); if (Button::read()) display.clear(); - } while (not Int::getExternalInterruptFlag()); + } while (not Int::read()); - Int::acknowledgeExternalInterruptFlag(); LedRed::set(); PT_CALL(touch.readTouches()); diff --git a/examples/stm32f4_discovery/exti/main.cpp b/examples/stm32f4_discovery/exti/main.cpp index bbe899dfaf..9644e4750b 100644 --- a/examples/stm32f4_discovery/exti/main.cpp +++ b/examples/stm32f4_discovery/exti/main.cpp @@ -26,36 +26,7 @@ #include using namespace Board; - -typedef GpioInputE11 Irq; - - -/* When you choose a different pin you must choose the corresponding - * interrupt handler: (x in A, B, C, D, E, F, G, H, I) - * Px0: EXTI0 - * Px1: EXTI1 - * Px2: EXTI2 - * Px3: EXTI3 - * Px4: EXTI4 - * Px5 to Px9: EXTI9_5 - * Px10 to Px15: EXTI15_10 - */ -MODM_ISR(EXTI0) -{ - Button::acknowledgeExternalInterruptFlag(); - LedBlue::set(); - modm::delay(1ms); - LedBlue::reset(); -} - - -MODM_ISR(EXTI15_10) -{ - Irq::acknowledgeExternalInterruptFlag(); - LedOrange::set(); - modm::delay(1ms); - LedOrange::reset(); -} +using Irq = GpioInputE11; // ---------------------------------------------------------------------------- int @@ -73,15 +44,21 @@ main() // push the button to see the blue led light up Button::setInput(Gpio::InputType::Floating); - Button::setInputTrigger(Button::InputTrigger::RisingEdge); - Button::enableExternalInterrupt(); - Button::enableExternalInterruptVector(14); + Exti::connect