Skip to content

Commit

Permalink
[rp2040] Implement IRQ handlers for GPIO and QSPI
Browse files Browse the repository at this point in the history
Based on discussion #847
  • Loading branch information
cocasema committed Apr 18, 2022
1 parent 98a2483 commit 28100f8
Show file tree
Hide file tree
Showing 10 changed files with 512 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
<td align="center">✅</td>
<td align="center">○</td>
<td align="center">○</td>
<td align="center"></td>
<td align="center"></td>
<td align="center">✅</td>
<td align="center">✅</td>
<td align="center">✅</td>
Expand Down
38 changes: 38 additions & 0 deletions examples/rp_pico/interrupt/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* 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 <modm/board.hpp>

int
main()
{
Board::initialize();

using Led = Board::LedGreen;
Led::setOutput();
Led::set();

// Powers on the LED on the low->high transition, and off on high->low.
GpioInput0::setInput();
IntHandler::connect<GpioInput0>(Gpio::InputTrigger::BothEdges,
[](Gpio::InputTrigger_t triggers) {
Led::set(!!(triggers & Gpio::InputTrigger::RisingEdge));
});

// Toggles LED each time gpio input is at the high level.
GpioInput1::setInput(Gpio::InputType::PullDown);
IntHandler::connect<GpioInput1>(Gpio::InputTrigger::HighLevel,
[](Gpio::InputTrigger_t) { Led::toggle(); });

while (true) {}

return 0;
}
12 changes: 12 additions & 0 deletions examples/rp_pico/interrupt/project.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<library>
<extends>modm:rp-pico</extends>
<options>
<option name="modm:build:build.path">../../../build/rp_pico/interrupt</option>
<option name="modm:platform:extint:qspi">true</option>
</options>
<modules>
<module>modm:platform:gpio</module>
<module>modm:platform:extint</module>
<module>modm:build:scons</module>
</modules>
</library>
98 changes: 98 additions & 0 deletions src/modm/platform/extint/rp/int_handler.cpp.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* 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 <modm/platform/extint/int_handler.hpp>

namespace modm::platform
{

%% if with_bank0
void
IntHandler::irqBank0Handler()
{
using PortRegs = Gpio::PortRegs<Gpio::Port::Bank0>;

static_assert(0b1111u == static_cast<uint32_t>(Gpio::InputTrigger::All));

%% if multicore_enabled
auto& proc_irq_ctrl = sio_hw->cpuid ? iobank0_hw->proc1_irq_ctrl : iobank0_hw->proc0_irq_ctrl;
%% else
auto& proc_irq_ctrl = iobank0_hw->proc0_irq_ctrl;
%% endif

for (size_t group = 0; group < NUM_BANK0_GPIOS / 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<Gpio::InputTrigger>(triggers));
if (auto& handler = bank0Handlers[pin])
{
handler(static_cast<Gpio::InputTrigger>(triggers));
}
}
}
}
}
}

%% endif

%% if with_qspi
void
IntHandler::irqQspiHandler()
{
using PortRegs = Gpio::PortRegs<Gpio::Port::Qspi>;

static_assert(NUM_QSPI_GPIOS <= 8);
static_assert(0b1111u == static_cast<uint32_t>(Gpio::InputTrigger::All));

%% if multicore_enabled
auto& proc_irq_ctrl = sio_hw->cpuid ? ioqspi_hw->proc1_qspi_ctrl : ioqspi_hw->proc0_qspi_ctrl;
%% else
auto& proc_irq_ctrl = ioqspi_hw->proc0_qspi_ctrl;
%% endif

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<Gpio::InputTrigger>(triggers));
if (auto& handler = qspiHandlers[pin])
{
handler(static_cast<Gpio::InputTrigger>(triggers));
}
}
}
}

%% endif

%% for type in types
IntHandler::Handler
IntHandler::{{type}}Handlers[NUM_{{type | upper}}_GPIOS] modm_fastdata;

%% endfor

%% for type in types
MODM_ISR(IO_IRQ_{{type | upper}})
{
IntHandler::irq{{type | capitalize}}Handler();
}

%% endfor

} // namespace modm::platform
145 changes: 145 additions & 0 deletions src/modm/platform/extint/rp/int_handler.hpp.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/*
* 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 <modm/architecture/interface/interrupt.hpp>
#include <modm/platform/extint/int_priority.hpp>
#include <modm/platform/gpio/base.hpp>
#include <modm/utils/inplace_function.hpp>

#include <RP2040.h>

#if defined __DOXYGEN__ || !defined MODM_EXTINT_HANDLER_STORAGE
/// @ingroup modm_platform_extint
#define MODM_EXTINT_HANDLER_STORAGE sizeof(void*)
#endif

namespace modm::platform
{

%% for type in types
MODM_ISR_DECL(IO_IRQ_{{type | upper}});
%% endfor

/**
* Interrupt Handler
*
* @ingroup modm_platform_extint
*/
class IntHandler
{
public:
using Handler = modm::inplace_function<void(Gpio::InputTrigger_t), MODM_EXTINT_HANDLER_STORAGE, alignof(void*)>;

public:
static void
enable(IRQn_Type type, IntPriority priority = IntPriority::Default)
{
if (!NVIC_GetEnableIRQ(type))
{
if (priority != static_cast<IntPriority>(NVIC_GetPriority(type)))
{
NVIC_SetPriority(type, priority);
}
NVIC_ClearPendingIRQ(type);
NVIC_EnableIRQ(type);
}
}

static void
disable(IRQn_Type type)
{
NVIC_DisableIRQ(type);
}

template<class Pin>
static void
connect(Gpio::InputTrigger_t triggers, Handler&& handler)
{
constexpr const auto type = irqType<Pin::port>();
static_assert(0 <= type, "Support for this Pin's Port is not enabled!");

enable(type);

disableInterrupts<Pin>(Gpio::InputTrigger::All);
acknowledgeInterrupts<Pin>(Gpio::InputTrigger::All);

irqHandler<Pin::port>(Pin::pin) = handler;

enableInterrupts<Pin>(triggers);
}

template<class Pin>
static void
disconnect()
{
static_assert(0 <= irqType<Pin::port>(), "Support for this Pin's Port is not enabled!");

disableInterrupts<Pin>(Gpio::InputTrigger::All);
irqHandler<Pin::port>(Pin::pin) = nullptr;
}

private:
template<class Pin>
static void
enableInterrupts(Gpio::InputTrigger_t triggers)
{
Gpio::PortRegs<Pin::port>::enable_irq(Pin::pin, triggers);
}

template<class Pin>
static void
disableInterrupts(Gpio::InputTrigger_t triggers)
{
Gpio::PortRegs<Pin::port>::disable_irq(Pin::pin, triggers);
}

template<class Pin>
static void
acknowledgeInterrupts(Gpio::InputTrigger_t triggers)
{
Gpio::PortRegs<Pin::port>::acknowledge_irq(Pin::pin, triggers);
}

%% for type in types
static void
irq{{type | capitalize}}Handler();
friend void MODM_ISR_NAME(IO_IRQ_{{type | upper}})();

// In the current implementation we do not allow handlers
// for the same line (pin) for more than a single core.
static Handler {{type}}Handlers[NUM_{{type | upper}}_GPIOS];

%% endfor

template <Gpio::Port port>
static constexpr IRQn_Type
irqType()
{
%% for type in types
if constexpr (port == Gpio::Port::{{type | capitalize}}) { return IO_IRQ_{{type | upper}}_IRQn; }
%% endfor
return static_cast<IRQn_Type>(-99);
}

template <Gpio::Port port>
static constexpr Handler&
irqHandler(uint8_t pin)
{
%% for type in types
if constexpr (port == Gpio::Port::{{type | capitalize}}) { return {{type}}Handlers[pin]; }
%% endfor
return *static_cast<Handler*>(nullptr);
}
};

} // namespace modm::platform
39 changes: 39 additions & 0 deletions src/modm/platform/extint/rp/int_priority.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* 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 <cstdint>

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_extint
*/
enum IntPriority : uint8_t
{
Highest = 0x00,
Default = 0x80,
Lowest = 0xff,
};

} // namespace modm::platform
Loading

0 comments on commit 28100f8

Please sign in to comment.