diff --git a/README.md b/README.md
index d1913692a0..fe258d2518 100644
--- a/README.md
+++ b/README.md
@@ -369,6 +369,28 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✕ |
✕ |
+IRQ |
+✕ |
+✕ |
+✕ |
+✕ |
+✕ |
+✕ |
+✕ |
+✕ |
+✕ |
+✕ |
+✕ |
+✕ |
+✕ |
+✕ |
+✕ |
+✕ |
+✅ |
+✕ |
+✕ |
+✕ |
+
Random Generator |
✕ |
✕ |
diff --git a/examples/rp_pico/interrupt/main.cpp b/examples/rp_pico/interrupt/main.cpp
new file mode 100644
index 0000000000..5f9b0e45b1
--- /dev/null
+++ b/examples/rp_pico/interrupt/main.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2022, Nikolay Semenov
+ *
+ * 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
+
+int
+main()
+{
+ Board::initialize();
+
+ using Led = Board::LedGreen;
+ Led::setOutput();
+ Led::set();
+
+ GpioIntHandler::enable();
+
+ // Powers on the LED on the low->high transition, and off on high->low.
+ GpioInput0::setInput();
+ GpioIntHandler::connect(Gpio::InputTrigger::BothEdges,
+ [](Gpio::InputTrigger triggers) {
+ Led::set(!!(triggers & Gpio::InputTrigger::RisingEdge));
+ });
+
+ // Toggles LED each time gpio input is at the high level.
+ GpioInput1::setInput(Gpio::InputType::PullDown);
+ GpioIntHandler::connect(Gpio::InputTrigger::HighLevel,
+ [](Gpio::InputTrigger) { Led::toggle(); });
+
+ while (true) {}
+
+ return 0;
+}
diff --git a/examples/rp_pico/interrupt/project.xml b/examples/rp_pico/interrupt/project.xml
new file mode 100644
index 0000000000..f41621481e
--- /dev/null
+++ b/examples/rp_pico/interrupt/project.xml
@@ -0,0 +1,11 @@
+
+ modm:rp-pico
+
+
+
+
+ modm:platform:gpio
+ modm:platform:irq
+ modm:build:scons
+
+
diff --git a/src/modm/platform/extint/rp/int_controller.hpp b/src/modm/platform/extint/rp/int_controller.hpp
new file mode 100644
index 0000000000..0d85eab6c2
--- /dev/null
+++ b/src/modm/platform/extint/rp/int_controller.hpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2022, Nikolay Semenov
+ *
+ * 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
+
+namespace modm::platform
+{
+
+/**
+ * Priority level of 0-192 in steps of 64 for each interrupt.
+ * A higher level corresponds to a lower priority,
+ * so level 0 is the highest programmable interrupt priority.
+ * ...
+ * The processor implements only bits[7:6] of each field, bits [5:0] read as zero and ignore writes.
+ * This means writing 255 to a priority register saves value 192 to the register.
+ *
+ * https://developer.arm.com/documentation/dui0662/b/Cortex-M0--Peripherals/Nested-Vectored-Interrupt-Controller
+ * https://developer.arm.com/documentation/dui0662/b/Cortex-M0--Peripherals/Nested-Vectored-Interrupt-Controller/Interrupt-Priority-Registers
+ *
+ * @ingroup modm_platform_irq
+ */
+enum IntPriority : uint8_t
+{
+ Highest = 0x00,
+ Default = 0x80,
+ Lowest = 0xff,
+};
+
+/**
+ * Interrupt Controller
+ *
+ * @ingroup modm_platform_irq
+ */
+class IntController
+{
+public:
+ static void
+ enable(IRQn_Type type)
+ {
+ NVIC_ClearPendingIRQ(type);
+ NVIC_EnableIRQ(type);
+ }
+
+ static void
+ disable(IRQn_Type type)
+ {
+ NVIC_DisableIRQ(type);
+ }
+
+ static void
+ setPriority(IRQn_Type type, IntPriority priority)
+ {
+ NVIC_SetPriority(type, priority);
+ }
+
+ static IntPriority
+ getPriority(IRQn_Type type)
+ {
+ return static_cast(NVIC_GetPriority(type));
+ }
+};
+
+} // namespace modm::platform
\ No newline at end of file
diff --git a/src/modm/platform/extint/rp/int_handler.cpp.in b/src/modm/platform/extint/rp/int_handler.cpp.in
new file mode 100644
index 0000000000..fddaf6e165
--- /dev/null
+++ b/src/modm/platform/extint/rp/int_handler.cpp.in
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2022, Nikolay Semenov
+ *
+ * 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
+
+namespace modm::platform
+{
+
+%% if with_gpio
+template<>
+void
+IntHandler::irqHandler()
+{
+ using PortRegs = Gpio::PortRegs;
+
+ static_assert(0b1111u == static_cast(Gpio::InputTrigger::All));
+
+ // TODO: check if multicore enabled
+ auto& proc_irq_ctrl = sio_hw->cpuid ? iobank0_hw->proc1_irq_ctrl : iobank0_hw->proc0_irq_ctrl;
+
+ for (size_t group = 0; group < Lines / 8; ++group)
+ {
+ if (uint32_t int_status = proc_irq_ctrl.ints[group])
+ {
+ for (uint8_t pin = group * 8; int_status; ++pin, int_status >>= 4)
+ {
+ if (uint32_t triggers = int_status & 0b1111u)
+ {
+ PortRegs::acknowledge_irq(pin, static_cast(triggers));
+ if (auto& handler = handlers[pin])
+ {
+ handler(static_cast(triggers));
+ }
+ }
+ }
+ }
+ }
+}
+%%endif
+
+%% if with_qspi
+template<>
+void
+IntHandler::irqHandler()
+{
+ using PortRegs = Gpio::PortRegs;
+
+ static_assert(Lines <= 8);
+
+ // TODO: check if multicore enabled
+ auto& proc_irq_ctrl = sio_hw->cpuid ? ioqspi_hw->proc1_qspi_ctrl : ioqspi_hw->proc0_qspi_ctrl;
+
+ uint32_t int_status = proc_irq_ctrl.ints;
+
+ for (uint8_t pin = 0; int_status; ++pin, int_status >>= 4)
+ {
+ if (uint32_t triggers = int_status & 0b1111u)
+ {
+ PortRegs::acknowledge_irq(pin, static_cast(triggers));
+ if (auto& handler = handlers[pin])
+ {
+ handler(static_cast(triggers));
+ }
+ }
+ }
+}
+%%endif
+
+template
+IntHandler::Handler IntHandler::handlers[IntHandler::Lines] modm_fastdata;
+
+%% if with_gpio
+MODM_ISR(IO_IRQ_BANK0)
+{
+ IntHandler::irqHandler();
+}
+%%endif
+
+%% if with_qspi
+MODM_ISR(IO_IRQ_QSPI)
+{
+ IntHandler::irqHandler();
+}
+%%endif
+
+} // namespace modm::platform
\ No newline at end of file
diff --git a/src/modm/platform/extint/rp/int_handler.hpp.in b/src/modm/platform/extint/rp/int_handler.hpp.in
new file mode 100644
index 0000000000..ef44fdf374
--- /dev/null
+++ b/src/modm/platform/extint/rp/int_handler.hpp.in
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2022, Nikolay Semenov
+ *
+ * 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
+
+namespace modm::platform
+{
+%% if with_gpio
+MODM_ISR_DECL(IO_IRQ_BANK0);
+%%endif
+%% if with_qspi
+MODM_ISR_DECL(IO_IRQ_QSPI);
+%%endif
+
+/**
+ * Interrupt Handler
+ *
+ * @ingroup modm_platform_irq
+ */
+template
+class IntHandler
+{
+public:
+ static constexpr size_t Lines = NLines;
+ using Handler =
+ modm::inplace_function;
+
+public:
+ static void
+ enable(IntPriority priority = IntPriority::Default)
+ {
+ if (priority != IntController::getPriority(Type))
+ {
+ IntController::setPriority(Type, priority);
+ }
+ IntController::enable(Type);
+ }
+
+ static void
+ disable()
+ {
+ IntController::disable(Type);
+ }
+
+ template
+ static void
+ connect(Gpio::InputTrigger triggers, Handler&& handler)
+ {
+ static_assert(Pin::port == gpioPort());
+
+ disableInterrupts(Gpio::InputTrigger::All);
+ acknowledgeInterrupts(Gpio::InputTrigger::All);
+
+ handlers[Pin::pin] = handler;
+
+ enableInterrupts(triggers);
+ }
+
+ template
+ static void
+ disconnect()
+ {
+ static_assert(Pin::port == gpioPort());
+
+ disableInterrupts(Gpio::InputTrigger::All);
+ handlers[Pin::pin] = nullptr;
+ }
+
+private:
+ template
+ static void
+ enableInterrupts(Gpio::InputTrigger triggers)
+ {
+ Gpio::PortRegs::enable_irq(Pin::pin, triggers);
+ }
+
+ template
+ static void
+ disableInterrupts(Gpio::InputTrigger triggers)
+ {
+ Gpio::PortRegs::disable_irq(Pin::pin, triggers);
+ }
+
+ template
+ static void
+ acknowledgeInterrupts(Gpio::InputTrigger triggers)
+ {
+ Gpio::PortRegs::acknowledge_irq(Pin::pin, triggers);
+ }
+
+ static void
+ irqHandler();
+ friend void MODM_ISR_NAME(IO_IRQ_BANK0)();
+ friend void MODM_ISR_NAME(IO_IRQ_QSPI)();
+
+ // In the current implementation we do not allow handlers
+ // for the same line (pin) for more than a single core.
+ static Handler handlers[Lines];
+
+ static constexpr Gpio::Port
+ gpioPort()
+ {
+%% if with_gpio
+ if constexpr (Type == IO_IRQ_BANK0_IRQn) { return Gpio::Port::Bank0; }
+%%endif
+%% if with_qspi
+ if constexpr (Type == IO_IRQ_QSPI_IRQn) { return Gpio::Port::Qspi; }
+%%endif
+ return static_cast(-1);
+ }
+};
+
+%% if with_gpio
+using GpioIntHandler = IntHandler;
+%%endif
+%% if with_qspi
+using QspiIntHandler = IntHandler;
+%%endif
+
+} // namespace modm::platform
\ No newline at end of file
diff --git a/src/modm/platform/extint/rp/module.lb b/src/modm/platform/extint/rp/module.lb
new file mode 100644
index 0000000000..f81ad66590
--- /dev/null
+++ b/src/modm/platform/extint/rp/module.lb
@@ -0,0 +1,58 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2022, Nikolay Semenov
+#
+# 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:irq"
+ module.description = FileReader("module.md")
+
+def prepare(module, options):
+ module.depends(
+ ":cmsis:device",
+ ":platform:gpio")
+ module.add_option(
+ BooleanOption(
+ name="gpio",
+ description="Enable IRQ support for GPIO",
+ default=True))
+ module.add_option(
+ BooleanOption(
+ name="qspi",
+ description="Enable IRQ support for QSPI",
+ default=False))
+ module.add_option(
+ StringOption(
+ name="handler.storage",
+ description="Size of storage for the handler's implementation",
+ default="sizeof(void*)"))
+
+ return options[":target"].identifier.platform == "rp"
+
+def validate(env):
+ if not any([env.get("gpio"), env.has_module("qspi")]):
+ raise ValidateException("At least one IRQ type must be enabled!")
+
+def build(env):
+ multicore_enabled = env.has_module(":platform:multicore")
+
+ with_gpio = env.get("gpio", True)
+ with_qspi = env.get("qspi", False)
+ handler_storage = env.get("handler.storage", "sizeof(void*)")
+
+ env.substitutions = {
+ "multicore_enabled": multicore_enabled,
+ "with_gpio": with_gpio,
+ "with_qspi": with_qspi,
+ "handler_storage": handler_storage,
+ }
+ env.outbasepath = "modm/src/modm/platform/irq"
+ env.copy("int_controller.hpp")
+ env.template("int_handler.hpp.in")
+ env.template("int_handler.cpp.in")
diff --git a/src/modm/platform/extint/rp/module.md b/src/modm/platform/extint/rp/module.md
new file mode 100644
index 0000000000..3636615a2c
--- /dev/null
+++ b/src/modm/platform/extint/rp/module.md
@@ -0,0 +1,45 @@
+# External Interrupt Handler
+
+This driver provides an API for configuring all IRQ lines via register access.
+Note that you need to pass a mask, which allows you to configure multiple IRQ
+lines at once.
+
+```cpp
+GpioIntHandler::enable();
+
+// Powers on the LED on the low->high transition, and off on high->low.
+GpioInput0::setInput();
+GpioIntHandler::connect(Gpio::InputTrigger::BothEdges,
+ [](Gpio::InputTrigger triggers) {
+ Led::set(!!(triggers & Gpio::InputTrigger::RisingEdge));
+ });
+
+// Toggles LED each time gpio input is at the high level.
+GpioInput1::setInput(Gpio::InputType::PullDown);
+GpioIntHandler::connect(Gpio::InputTrigger::HighLevel,
+ [](Gpio::InputTrigger) { Led::toggle(); });
+```
+
+## Multicore mode
+
+Each core can register callbacks in the same IntHandler,
+but for the different pins (current implementation's constraint).
+
+Also, enable/disable and connect/disconnect calls
+affect the NVIC of the executing core only.
+
+## Callbacks
+
+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 in your `project.xml` options:
+
+```xml
+
+
+
+
+
+
+
+```
\ No newline at end of file
diff --git a/src/modm/platform/gpio/rp/base.hpp.in b/src/modm/platform/gpio/rp/base.hpp.in
index d2a38ebda2..a0614197d0 100644
--- a/src/modm/platform/gpio/rp/base.hpp.in
+++ b/src/modm/platform/gpio/rp/base.hpp.in
@@ -35,6 +35,19 @@ struct Gpio
PullDown = 0x2, ///< pull-down on input
};
+ enum class
+ InputTrigger : uint32_t
+ {
+ None = 0x00u,
+ LowLevel = 0x01u,
+ HighLevel = 0x02u,
+ BothLevels = LowLevel | HighLevel,
+ FallingEdge = 0x04u,
+ RisingEdge = 0x08u,
+ BothEdges = FallingEdge | RisingEdge,
+ All = BothLevels | BothEdges,
+ };
+
enum class
OutputType
{
@@ -47,6 +60,7 @@ struct Gpio
Slow = 0,
Fast = 1,
};
+
enum class
DriveStrength
{
@@ -55,6 +69,7 @@ struct Gpio
mA_8 = 2,
mA_12 = 3,
};
+
enum class
SlewRate : uint8_t
{
@@ -133,8 +148,52 @@ struct Gpio::PortRegs
PADS_BANK0_GPIO0_SLEWFAST_BITS
);
}
+ static void enable_irq(uint8_t pin, InputTrigger triggers)
+ {
+ uint32_t value = static_cast(triggers) << 4 * {{intreg_pin_mask_shift[port]}};
+%% if multicore_enabled
+ auto& proc_irq_ctrl = sio_hw->cpuid ? io{{port | lower}}_hw->proc1_{{intreg_ctrl_names[port]}} : io{{port | lower}}_hw->proc0_{{intreg_ctrl_names[port]}};
+%% else
+ auto& proc_irq_ctrl = io{{port | lower}}_hw->proc0_{{intreg_ctrl_names[port]}};
+%% endif
+ hw_set_bits(&proc_irq_ctrl.inte{{intreg_pin_access[port]}}, value);
+ }
+ static void disable_irq(uint8_t pin, InputTrigger triggers)
+ {
+ uint32_t value = static_cast(triggers) << 4 * {{intreg_pin_mask_shift[port]}};
+%% if multicore_enabled
+ auto& proc_irq_ctrl = sio_hw->cpuid ? io{{port | lower}}_hw->proc1_{{intreg_ctrl_names[port]}} : io{{port | lower}}_hw->proc0_{{intreg_ctrl_names[port]}};
+%% else
+ auto& proc_irq_ctrl = io{{port | lower}}_hw->proc0_{{intreg_ctrl_names[port]}};
+%% endif
+ hw_clear_bits(&proc_irq_ctrl.inte{{intreg_pin_access[port]}}, value);
+ }
+ static void acknowledge_irq(uint8_t pin, InputTrigger triggers)
+ {
+ uint32_t value = static_cast(triggers) << 4 * {{intreg_pin_mask_shift[port]}};
+ io{{port | lower}}_hw->intr{{intreg_pin_access[port]}} = value;
+ }
};
+
%% endfor
/// @endcond
+inline constexpr Gpio::InputTrigger operator|(Gpio::InputTrigger a, Gpio::InputTrigger b)
+{
+ using enum_ut = std::underlying_type_t;
+ return static_cast(static_cast(a) | static_cast(b));
+}
+
+inline constexpr Gpio::InputTrigger operator&(Gpio::InputTrigger a, Gpio::InputTrigger b)
+{
+ using enum_ut = std::underlying_type_t;
+ return static_cast(static_cast(a) & static_cast(b));
+}
+
+inline constexpr bool operator!(Gpio::InputTrigger it)
+{
+ using enum_ut = std::underlying_type_t;
+ return !static_cast(it);
+}
+
} // namespace modm::platform
diff --git a/src/modm/platform/gpio/rp/module.lb b/src/modm/platform/gpio/rp/module.lb
index 6ae1fa611b..823d6c3771 100644
--- a/src/modm/platform/gpio/rp/module.lb
+++ b/src/modm/platform/gpio/rp/module.lb
@@ -54,6 +54,18 @@ def build(env):
"Bank0": "bank0",
"Qspi": "_qspi"
},
+ "intreg_ctrl_names": {
+ "Bank0": "irq_ctrl",
+ "Qspi": "qspi_ctrl"
+ },
+ "intreg_pin_access": {
+ "Bank0": "[pin / 8]",
+ "Qspi": ""
+ },
+ "intreg_pin_mask_shift": {
+ "Bank0": "(pin % 8)",
+ "Qspi": "pin"
+ },
"port_width": 32
}
subs["ports"] = OrderedDict([(k, i) for i, k in enumerate([p["name"].capitalize() for p in subs["ranges"]])])
@@ -86,6 +98,7 @@ def build(env):
subs["target"] = device.identifier
subs["all_signals"] = all_signals
subs["gpios"] = [subs[gpio["name"].capitalize()] for gpio in driver["gpio"]]
+ subs["multicore_enabled"] = env.has_module(":platform:multicore")
env.substitutions = subs
env.outbasepath = "modm/src/modm/platform/gpio"