Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[rp2040] Implement IRQ handlers for GPIO and QSPI #848

Merged
merged 1 commit into from
Apr 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}
10 changes: 10 additions & 0 deletions examples/rp_pico/interrupt/project.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<library>
<extends>modm:rp-pico</extends>
<options>
<option name="modm:build:build.path">../../../build/rp_pico/interrupt</option>
</options>
<modules>
<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