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/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/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 @@
../../../build/stm32f469_discovery/blink
- 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(Exti::Trigger::RisingEdge, [](uint8_t)
+ {
+ LedBlue::set();
+ modm::delay(1ms);
+ LedBlue::reset();
+ });
// pull pin E11 low to see the orange led light up
Irq::setInput(Gpio::InputType::PullUp);
- Irq::setInputTrigger(Irq::InputTrigger::BothEdges);
- Irq::enableExternalInterrupt();
- Irq::enableExternalInterruptVector(14);
+ Exti::connect(Exti::Trigger::BothEdges, [](uint8_t)
+ {
+ LedOrange::set();
+ modm::delay(1ms);
+ LedOrange::reset();
+ });
while (true)
{
diff --git a/examples/stm32f4_discovery/exti/project.xml b/examples/stm32f4_discovery/exti/project.xml
index ca189b80d2..243d0e34a6 100644
--- a/examples/stm32f4_discovery/exti/project.xml
+++ b/examples/stm32f4_discovery/exti/project.xml
@@ -5,7 +5,7 @@
modm:architecture:interrupt
- modm:platform:gpio
+ modm:platform:exti
modm:build:scons
diff --git a/src/modm/board/devebox_stm32f4xx/board.hpp b/src/modm/board/devebox_stm32f4xx/board.hpp
index b9f8d336f1..4a3d55fdb8 100644
--- a/src/modm/board/devebox_stm32f4xx/board.hpp
+++ b/src/modm/board/devebox_stm32f4xx/board.hpp
@@ -137,9 +137,6 @@ initialize()
LedGreen::setOutput(modm::Gpio::Low);
Button::setInput(Gpio::InputType::PullDown);
- Button::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- Button::enableExternalInterrupt();
-// Button::enableExternalInterruptVector(12);
}
diff --git a/src/modm/board/devebox_stm32h750vb/board.hpp b/src/modm/board/devebox_stm32h750vb/board.hpp
index cf38c9c104..56fe997341 100644
--- a/src/modm/board/devebox_stm32h750vb/board.hpp
+++ b/src/modm/board/devebox_stm32h750vb/board.hpp
@@ -158,14 +158,8 @@ initialize()
SysTickTimer::initialize();
LedGreen::setOutput(modm::Gpio::Low);
-
ButtonK1::setInput(Gpio::InputType::PullUp);
- ButtonK1::setInputTrigger(Gpio::InputTrigger::FallingEdge);
- ButtonK1::enableExternalInterrupt();
-
ButtonK2::setInput(Gpio::InputType::PullUp);
- ButtonK2::setInputTrigger(Gpio::InputTrigger::FallingEdge);
- ButtonK2::enableExternalInterrupt();
}
}
diff --git a/src/modm/board/disco_f051r8/board.hpp b/src/modm/board/disco_f051r8/board.hpp
index 6b9ca3a7a8..4b7f40d0ef 100644
--- a/src/modm/board/disco_f051r8/board.hpp
+++ b/src/modm/board/disco_f051r8/board.hpp
@@ -75,9 +75,6 @@ initialize()
LedBlue::setOutput(modm::Gpio::Low);
Button::setInput();
- Button::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- Button::enableExternalInterrupt();
-// Button::enableExternalInterruptVector(12);
}
} // namespace Board
diff --git a/src/modm/board/disco_f072rb/board.hpp b/src/modm/board/disco_f072rb/board.hpp
index 2f922f91fb..7982474be5 100644
--- a/src/modm/board/disco_f072rb/board.hpp
+++ b/src/modm/board/disco_f072rb/board.hpp
@@ -125,9 +125,6 @@ initialize()
LedRight::setOutput(modm::Gpio::Low);
Button::setInput();
- Button::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- Button::enableExternalInterrupt();
-// Button::enableExternalInterruptVector(12);
}
@@ -135,15 +132,7 @@ inline void
initializeL3g()
{
l3g::Int1::setInput();
- l3g::Int1::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- l3g::Int1::enableExternalInterrupt();
-// l3g::Int1::enableExternalInterruptVector(12);
-
l3g::Int2::setInput();
- l3g::Int2::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- l3g::Int2::enableExternalInterrupt();
-// l3g::Int2::enableExternalInterruptVector(12);
-
l3g::Cs::setOutput(modm::Gpio::High);
l3g::SpiMaster::connect();
diff --git a/src/modm/board/disco_f100rb/board.hpp b/src/modm/board/disco_f100rb/board.hpp
index 602b9ff482..481fab31af 100644
--- a/src/modm/board/disco_f100rb/board.hpp
+++ b/src/modm/board/disco_f100rb/board.hpp
@@ -110,9 +110,6 @@ initialize()
LedBlue::setOutput(modm::Gpio::Low);
Button::setInput();
- Button::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- Button::enableExternalInterrupt();
-// Button::enableExternalInterruptVector(12);
}
}
diff --git a/src/modm/board/disco_f303vc/board.hpp b/src/modm/board/disco_f303vc/board.hpp
index 60e7cd8df9..c736b45337 100644
--- a/src/modm/board/disco_f303vc/board.hpp
+++ b/src/modm/board/disco_f303vc/board.hpp
@@ -171,9 +171,6 @@ initialize()
Leds::setOutput(modm::Gpio::Low);
Button::setInput();
- Button::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- Button::enableExternalInterrupt();
-// Button::enableExternalInterruptVector(12);
}
@@ -181,15 +178,7 @@ inline void
initializeL3g()
{
l3g::Int1::setInput();
- l3g::Int1::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- l3g::Int1::enableExternalInterrupt();
-// l3g::Int1::enableExternalInterruptVector(12);
-
l3g::Int2::setInput();
- l3g::Int2::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- l3g::Int2::enableExternalInterrupt();
-// l3g::Int2::enableExternalInterruptVector(12);
-
l3g::Cs::setOutput(modm::Gpio::High);
l3g::SpiMaster::connect();
@@ -202,19 +191,8 @@ inline void
initializeLsm3()
{
lsm3::Int1::setInput();
- lsm3::Int1::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- lsm3::Int1::enableExternalInterrupt();
-// lsm3::Int1::enableExternalInterruptVector(12);
-
lsm3::Int2::setInput();
- lsm3::Int2::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- lsm3::Int2::enableExternalInterrupt();
-// lsm3::Int2::enableExternalInterruptVector(12);
-
lsm3::Drdy::setInput();
- lsm3::Drdy::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- lsm3::Drdy::enableExternalInterrupt();
-// lsm3::Drdy::enableExternalInterruptVector(12);
lsm3::I2cMaster::connect();
lsm3::I2cMaster::initialize();
diff --git a/src/modm/board/disco_f407vg/board.hpp b/src/modm/board/disco_f407vg/board.hpp
index e99e4af4ec..8d46a81047 100644
--- a/src/modm/board/disco_f407vg/board.hpp
+++ b/src/modm/board/disco_f407vg/board.hpp
@@ -178,19 +178,12 @@ initialize()
Leds::setOutput(modm::Gpio::Low);
Button::setInput();
- Button::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- Button::enableExternalInterrupt();
-// Button::enableExternalInterruptVector(12);
}
inline void
initializeLis3()
{
lis3::Int::setInput();
- lis3::Int::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- lis3::Int::enableExternalInterrupt();
-// lis3::Int::enableExternalInterruptVector(12);
-
lis3::Cs::setOutput(modm::Gpio::High);
lis3::SpiMaster::connect();
diff --git a/src/modm/board/disco_f429zi/board.hpp b/src/modm/board/disco_f429zi/board.hpp
index 8aa34ae700..fbab281297 100644
--- a/src/modm/board/disco_f429zi/board.hpp
+++ b/src/modm/board/disco_f429zi/board.hpp
@@ -228,9 +228,6 @@ initialize()
LedRed::setOutput(modm::Gpio::Low);
Button::setInput();
- Button::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- Button::enableExternalInterrupt();
-// Button::enableExternalInterruptVector(12);
}
@@ -238,15 +235,7 @@ inline void
initializeL3g()
{
l3g::Int1::setInput();
- l3g::Int1::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- l3g::Int1::enableExternalInterrupt();
-// l3g::Int1::enableExternalInterruptVector(12);
-
l3g::Int2::setInput();
- l3g::Int2::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- l3g::Int2::enableExternalInterrupt();
-// l3g::Int2::enableExternalInterruptVector(12);
-
l3g::Cs::setOutput(modm::Gpio::High);
l3g::SpiMaster::connect();
diff --git a/src/modm/board/disco_f469ni/board.hpp b/src/modm/board/disco_f469ni/board.hpp
index 008663aa19..cd8cd04d66 100644
--- a/src/modm/board/disco_f469ni/board.hpp
+++ b/src/modm/board/disco_f469ni/board.hpp
@@ -192,9 +192,6 @@ inline void
initializeTouchscreen()
{
ft6::Int::setInput();
- ft6::Int::setInputTrigger(Gpio::InputTrigger::FallingEdge);
- ft6::Int::enableExternalInterrupt();
-// ft6::Int::enableExternalInterruptVector(12);
ft6::I2cMaster::connect();
ft6::I2cMaster::initialize();
@@ -228,9 +225,6 @@ initialize()
LedOrange::setOutput(modm::Gpio::Low);
Button::setInput();
- Button::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- Button::enableExternalInterrupt();
-// Button::enableExternalInterruptVector(12);
}
inline void
diff --git a/src/modm/board/disco_f746ng/board.hpp b/src/modm/board/disco_f746ng/board.hpp
index 4990d2a50a..19a8931171 100644
--- a/src/modm/board/disco_f746ng/board.hpp
+++ b/src/modm/board/disco_f746ng/board.hpp
@@ -189,9 +189,6 @@ initialize()
stlink::Uart::initialize();
Button::setInput();
- Button::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- Button::enableExternalInterrupt();
-// Button::enableExternalInterruptVector(12);
// Disable Backlight
GpioK3::setOutput(modm::Gpio::Low);
diff --git a/src/modm/board/disco_f769ni/board.hpp b/src/modm/board/disco_f769ni/board.hpp
index fb85f34e64..9d2b321197 100644
--- a/src/modm/board/disco_f769ni/board.hpp
+++ b/src/modm/board/disco_f769ni/board.hpp
@@ -157,9 +157,6 @@ initialize()
stlink::Uart::initialize();
Button::setInput();
- Button::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- Button::enableExternalInterrupt();
-// Button::enableExternalInterruptVector(12);
}
}
diff --git a/src/modm/board/disco_l476vg/board.hpp b/src/modm/board/disco_l476vg/board.hpp
index 4e5d6cc83b..ea704fab93 100644
--- a/src/modm/board/disco_l476vg/board.hpp
+++ b/src/modm/board/disco_l476vg/board.hpp
@@ -117,9 +117,6 @@ initialize()
LedRed::setOutput(modm::Gpio::Low);
Button::setInput();
- Button::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- Button::enableExternalInterrupt();
-// Button::enableExternalInterruptVector(12);
}
/// You must take out the LCD screen and close SB24 and SB25 for USB to work
diff --git a/src/modm/board/nucleo_f103rb/board.hpp b/src/modm/board/nucleo_f103rb/board.hpp
index 5e61ed68a2..86f01c9805 100644
--- a/src/modm/board/nucleo_f103rb/board.hpp
+++ b/src/modm/board/nucleo_f103rb/board.hpp
@@ -115,9 +115,6 @@ initialize()
stlink::Uart::initialize();
Button::setInput();
- Button::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- Button::enableExternalInterrupt();
-// Button::enableExternalInterruptVector(12);
}
}
diff --git a/src/modm/board/nucleo_f401re/board.hpp b/src/modm/board/nucleo_f401re/board.hpp
index d0a0f373e2..2aeb4f06da 100644
--- a/src/modm/board/nucleo_f401re/board.hpp
+++ b/src/modm/board/nucleo_f401re/board.hpp
@@ -113,9 +113,6 @@ initialize()
stlink::Uart::initialize();
Button::setInput();
- Button::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- Button::enableExternalInterrupt();
-// Button::enableExternalInterruptVector(12);
}
}
diff --git a/src/modm/board/nucleo_f411re/board.hpp b/src/modm/board/nucleo_f411re/board.hpp
index 1a9d18f6b2..c242a5534a 100644
--- a/src/modm/board/nucleo_f411re/board.hpp
+++ b/src/modm/board/nucleo_f411re/board.hpp
@@ -115,9 +115,6 @@ initialize()
stlink::Uart::initialize();
Button::setInput();
- Button::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- Button::enableExternalInterrupt();
-// Button::enableExternalInterruptVector(12);
}
}
diff --git a/src/modm/board/nucleo_f429zi/board.hpp b/src/modm/board/nucleo_f429zi/board.hpp
index f304f11d5d..6a82659cb0 100644
--- a/src/modm/board/nucleo_f429zi/board.hpp
+++ b/src/modm/board/nucleo_f429zi/board.hpp
@@ -147,9 +147,6 @@ initialize()
LedRed::setOutput(modm::Gpio::Low);
Button::setInput();
- Button::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- Button::enableExternalInterrupt();
-// Button::enableExternalInterruptVector(12);
}
inline void
diff --git a/src/modm/board/nucleo_f446re/board.hpp b/src/modm/board/nucleo_f446re/board.hpp
index f77dd554b8..fe3522bb41 100644
--- a/src/modm/board/nucleo_f446re/board.hpp
+++ b/src/modm/board/nucleo_f446re/board.hpp
@@ -122,9 +122,6 @@ initialize()
stlink::Uart::initialize();
Button::setInput();
- Button::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- Button::enableExternalInterrupt();
-// Button::enableExternalInterruptVector(12);
}
}
diff --git a/src/modm/board/nucleo_f446ze/board.hpp b/src/modm/board/nucleo_f446ze/board.hpp
index 0dbe672176..65f364a569 100644
--- a/src/modm/board/nucleo_f446ze/board.hpp
+++ b/src/modm/board/nucleo_f446ze/board.hpp
@@ -149,9 +149,6 @@ initialize()
LedRed::setOutput(modm::Gpio::Low);
Button::setInput();
- Button::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- Button::enableExternalInterrupt();
- // Button::enableExternalInterruptVector(12);
}
inline void
diff --git a/src/modm/board/nucleo_f746zg/board.hpp b/src/modm/board/nucleo_f746zg/board.hpp
index b37ff23276..469821b6ad 100755
--- a/src/modm/board/nucleo_f746zg/board.hpp
+++ b/src/modm/board/nucleo_f746zg/board.hpp
@@ -142,9 +142,6 @@ initialize()
LedRed::setOutput(modm::Gpio::Low);
Button::setInput();
- Button::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- Button::enableExternalInterrupt();
-// Button::enableExternalInterruptVector(12);
}
}
diff --git a/src/modm/board/nucleo_f767zi/board.hpp b/src/modm/board/nucleo_f767zi/board.hpp
index 00ae3e6879..0657d685a3 100755
--- a/src/modm/board/nucleo_f767zi/board.hpp
+++ b/src/modm/board/nucleo_f767zi/board.hpp
@@ -141,9 +141,6 @@ initialize()
LedRed::setOutput(modm::Gpio::Low);
Button::setInput();
- Button::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- Button::enableExternalInterrupt();
-// Button::enableExternalInterruptVector(12);
}
}
diff --git a/src/modm/board/nucleo_g071rb/board.hpp b/src/modm/board/nucleo_g071rb/board.hpp
index 35d84d8523..003ba23d19 100644
--- a/src/modm/board/nucleo_g071rb/board.hpp
+++ b/src/modm/board/nucleo_g071rb/board.hpp
@@ -152,9 +152,6 @@ initialize()
stlink::Uart::initialize();
Button::setInput();
- Button::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- Button::enableExternalInterrupt();
-// Button::enableExternalInterruptVector(12);
}
}
diff --git a/src/modm/board/nucleo_g431rb/board.hpp b/src/modm/board/nucleo_g431rb/board.hpp
index 24d47bdf5a..d0c40dcf93 100644
--- a/src/modm/board/nucleo_g431rb/board.hpp
+++ b/src/modm/board/nucleo_g431rb/board.hpp
@@ -132,9 +132,6 @@ initialize()
stlink::Uart::initialize();
Button::setInput();
- Button::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- Button::enableExternalInterrupt();
-// Button::enableExternalInterruptVector(12);
}
}
diff --git a/src/modm/board/nucleo_g474re/board.hpp b/src/modm/board/nucleo_g474re/board.hpp
index 4db895135e..1fe8e61486 100644
--- a/src/modm/board/nucleo_g474re/board.hpp
+++ b/src/modm/board/nucleo_g474re/board.hpp
@@ -146,9 +146,6 @@ initialize()
stlink::Uart::initialize();
Button::setInput();
- Button::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- Button::enableExternalInterrupt();
-// Button::enableExternalInterruptVector(12);
}
}
diff --git a/src/modm/board/nucleo_h723zg/board.hpp b/src/modm/board/nucleo_h723zg/board.hpp
index 34a5d646bc..44a1d65d37 100644
--- a/src/modm/board/nucleo_h723zg/board.hpp
+++ b/src/modm/board/nucleo_h723zg/board.hpp
@@ -173,9 +173,6 @@ initialize()
LedRed::setOutput(modm::Gpio::Low);
Button::setInput();
- Button::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- Button::enableExternalInterrupt();
-// Button::enableExternalInterruptVector(12);
}
}
diff --git a/src/modm/board/nucleo_h743zi/board.hpp b/src/modm/board/nucleo_h743zi/board.hpp
index f2d708b40b..5a1c1cf1ab 100644
--- a/src/modm/board/nucleo_h743zi/board.hpp
+++ b/src/modm/board/nucleo_h743zi/board.hpp
@@ -185,9 +185,6 @@ initialize()
LedRed::setOutput(modm::Gpio::Low);
Button::setInput();
- Button::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- Button::enableExternalInterrupt();
-// Button::enableExternalInterruptVector(12);
}
/// FIXME: USB does not work on this board.
diff --git a/src/modm/board/nucleo_l496zg-p/board.hpp b/src/modm/board/nucleo_l496zg-p/board.hpp
index 69813ca10b..9a3b9bfbb1 100644
--- a/src/modm/board/nucleo_l496zg-p/board.hpp
+++ b/src/modm/board/nucleo_l496zg-p/board.hpp
@@ -162,9 +162,6 @@ initialize()
LedRed::setOutput(modm::Gpio::Low);
Button::setInput();
-// Button::setInputTrigger(Gpio::InputTrigger::RisingEdge);
-// Button::enableExternalInterrupt();
-// Button::enableExternalInterruptVector(12);
}
inline void
diff --git a/src/modm/board/stm32_f4ve/board.hpp b/src/modm/board/stm32_f4ve/board.hpp
index e6a7b5b9fe..53c129e45d 100644
--- a/src/modm/board/stm32_f4ve/board.hpp
+++ b/src/modm/board/stm32_f4ve/board.hpp
@@ -234,16 +234,9 @@ initialize()
LedGreen3::setOutput(modm::Gpio::Low);
Button::setInput();
- Button::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- Button::enableExternalInterrupt();
-
ButtonK0::setInput();
- ButtonK0::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- // ButtonK0::enableExternalInterrupt();
-
ButtonK1::setInput();
- ButtonK1::setInputTrigger(Gpio::InputTrigger::RisingEdge);
- // ButtonK1::enableExternalInterrupt();
+
Usart1::connect();
Usart1::initialize();
}
diff --git a/src/modm/driver/gpio/gpio_sampler.cpp.in b/src/modm/driver/gpio/gpio_sampler.cpp.in
index 6b292383c3..2bb17d5106 100644
--- a/src/modm/driver/gpio/gpio_sampler.cpp.in
+++ b/src/modm/driver/gpio/gpio_sampler.cpp.in
@@ -106,21 +106,14 @@ GpioSampler::Channel::operator[](size_t index) const
bool
GpioSampler::Channel::read(size_t index) const
{
- return ((*this)[index] > 0);
+ return ((*this)[index] & 1);
}
GpioSampler::Type
GpioSampler::Channel::diff(size_t index) const
{
if (index == 0) return 0;
- Type s0 = (*this)[index - 1];
- Type s1 = (*this)[index];
- // abs
- uint32_t t0 = (s0 > 0) ? s0 : -s0;
- uint32_t t1 = (s1 > 0) ? s1 : -s1;
- // Fix overflow issues
- if (t1 < t0) t1 |= (1ul << 31);
- return t1 - t0;
+ return (*this)[index - 1] - (*this)[index];
}
} // namespace modm::platform
diff --git a/src/modm/driver/gpio/gpio_sampler.hpp.in b/src/modm/driver/gpio/gpio_sampler.hpp.in
index 738e06428f..1ef505aeff 100644
--- a/src/modm/driver/gpio/gpio_sampler.hpp.in
+++ b/src/modm/driver/gpio/gpio_sampler.hpp.in
@@ -100,7 +100,7 @@ protected:
static void reset(Interrupt vector);
static void setHandler(Interrupt vector, void(*handler)());
static inline Type getTime() {
- return Type(DWT->CYCCNT & ~(1ul << 31));
+ return Type(DWT->CYCCNT & ~1);
}
};
diff --git a/src/modm/driver/gpio/gpio_sampler.lb b/src/modm/driver/gpio/gpio_sampler.lb
index 6710e3e785..eee1cf3f2f 100644
--- a/src/modm/driver/gpio/gpio_sampler.lb
+++ b/src/modm/driver/gpio/gpio_sampler.lb
@@ -26,6 +26,7 @@ def prepare(module, options):
module.depends(
":platform:gpio",
+ ":platform:exti",
":platform:core",
":architecture:interrupt")
return True
@@ -49,7 +50,7 @@ def build(env):
env.substitutions = {
"extis": extis,
- "vectors_location": env.get(":platform:core:vector_table_location", "rom")
+ "vectors_location": env.get(":platform:core:vector_table_location", "rom"),
}
env.outbasepath = "modm/src/modm/driver"
env.template("gpio_sampler.cpp.in")
diff --git a/src/modm/driver/gpio/gpio_sampler_impl.hpp.in b/src/modm/driver/gpio/gpio_sampler_impl.hpp.in
index 1945fbc832..3cb81501a9 100644
--- a/src/modm/driver/gpio/gpio_sampler_impl.hpp.in
+++ b/src/modm/driver/gpio/gpio_sampler_impl.hpp.in
@@ -16,6 +16,7 @@
#include
#include
#include
+#include
namespace modm
{
@@ -63,12 +64,12 @@ GpioSampler::sampleGpio(Channel *data, Type time)
{
if constexpr (Gpio::pin == pin)
{
- if ((pin_count == 1) or Gpio::getExternalInterruptFlag())
+ if ((pin_count == 1) or (platform::Exti::areFlagsSet()))
{
- if (not Gpio::read()) time = -time;
+ if (Gpio::read()) time |= 1;
constexpr size_t channel = channels - sizeof...(Gpios) - 1;
data[channel].add(time);
- Gpio::acknowledgeExternalInterruptFlag();
+ platform::Exti::acknowledgeFlags();
}
}
sampleGpio(data, time);
@@ -92,11 +93,11 @@ GpioSampler::Create(size_t max_samples)
[](Handle &h) {
// prime the data logger
const Type time = getTime();
- const Type start[] = {(Gpios::read() ? time : -time)...};
+ const Type start[] = {(Gpios::read() ? (time | 1) : time)...};
h.set_start_time(start);
},
[]() {
- (Gpios::disableExternalInterrupt(), ...);
+ platform::Exti::disableInterrupts();
// only reset the vectors that contain at least one GPIO
%% for vector in extis
if constexpr (pin_count_{{vector | lower}}) reset(Interrupt::{{ vector | capitalize }});
@@ -117,9 +118,9 @@ GpioSampler::Create(size_t max_samples)
}
%% endfor
// trigger on both edges
- (Gpios::setInputTrigger(Gpios::InputTrigger::BothEdges), ...);
+ platform::Exti::setTriggers(platform::Exti::Trigger::BothEdges);
// enabled all GPIO interrupts
- (Gpios::enableExternalInterrupt(), ...);
+ platform::Exti::enableInterrupts();
// prime the logger with initial measurement
handle.restart();
return handle;
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/driver/pwm/ws2812b.hpp b/src/modm/driver/pwm/ws2812b.hpp
index a867718cac..0f064f712d 100644
--- a/src/modm/driver/pwm/ws2812b.hpp
+++ b/src/modm/driver/pwm/ws2812b.hpp
@@ -55,7 +55,7 @@ class Ws2812b
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 36640f4240..5661548fca 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
@@ -120,7 +120,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 1bbe04f908..dd919fc2be 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
@@ -166,7 +166,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 37ad23d346..267f7f32c5 100644
--- a/src/modm/platform/adc/stm32f0/adc.hpp.in
+++ b/src/modm/platform/adc/stm32f0/adc.hpp.in
@@ -142,7 +142,7 @@ public:
public:
// start inherited documentation
- template< template class... Signals >
+ template< class... Signals >
static void
connect()
{
@@ -272,7 +272,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 1eaff1f94c..8836e9a760 100644
--- a/src/modm/platform/adc/stm32f3/adc.hpp.in
+++ b/src/modm/platform/adc/stm32f3/adc.hpp.in
@@ -235,7 +235,7 @@ public:
MODM_FLAGS32(InterruptFlag);
public:
- template< template class... Signals >
+ template< class... Signals >
static void
connect()
{
@@ -341,7 +341,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/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/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 64%
rename from src/modm/platform/gpio/stm32/unused.hpp.in
rename to src/modm/platform/gpio/common/unused.hpp.in
index c37b665c16..da203d3beb 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,78 +52,69 @@ 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) {}
- // 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() {}
+ static void configure(InputType) {}
+ static bool read() { return false; }
// 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;
+ using Data = detail::DataUnused;
static constexpr Gpio::Signal Signal = Gpio::Signal::{{ name }};
- 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/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/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..3a31b44795
--- /dev/null
+++ b/src/modm/platform/gpio/stm32/data.hpp.in
@@ -0,0 +1,76 @@
+/*
+ * 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}};
+%% 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..c1b88deb03 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,25 +193,7 @@ def validate(env):
sig["instance"] = remap["instance"]
signal_map[key].append(sig)
bprops["group_map"] = signal_map
-
- # 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
+ bprops["remaps"] = driver["remap"]
# Compute the set of remapped pins
remapped_gpios = {}
@@ -242,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,
}
@@ -271,7 +252,7 @@ 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["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 +260,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