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 16, 2022
1 parent 98a2483 commit 24c4f4f
Show file tree
Hide file tree
Showing 11 changed files with 528 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
40 changes: 40 additions & 0 deletions examples/rp_pico/interrupt/main.cpp
Original file line number Diff line number Diff line change
@@ -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 <modm/board.hpp>

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<GpioInput0>(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<GpioInput1>(Gpio::InputTrigger::HighLevel,
[](Gpio::InputTrigger) { Led::toggle(); });

while (true) {}

return 0;
}
11 changes: 11 additions & 0 deletions examples/rp_pico/interrupt/project.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<library>
<extends>modm:rp-pico</extends>
<options>
<option name="modm:build:build.path">../../../build/rp_pico/interrupt</option>
</options>
<modules>
<module>modm:platform:gpio</module>
<module>modm:platform:irq</module>
<module>modm:build:scons</module>
</modules>
</library>
73 changes: 73 additions & 0 deletions src/modm/platform/extint/rp/int_controller.hpp
Original file line number Diff line number Diff line change
@@ -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 <RP2040.h>

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<IntPriority>(NVIC_GetPriority(type));
}
};

} // namespace modm::platform
94 changes: 94 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,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 <modm/platform/irq/int_handler.hpp>

namespace modm::platform
{

%% if with_gpio
template<>
void
IntHandler<IO_IRQ_BANK0_IRQn, NUM_BANK0_GPIOS>::irqHandler()
{
using PortRegs = Gpio::PortRegs<gpioPort()>;

static_assert(0b1111u == static_cast<uint32_t>(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<Gpio::InputTrigger>(triggers));
if (auto& handler = handlers[pin])
{
handler(static_cast<Gpio::InputTrigger>(triggers));
}
}
}
}
}
}
%%endif

%% if with_qspi
template<>
void
IntHandler<IO_IRQ_QSPI_IRQn, NUM_QSPI_GPIOS>::irqHandler()
{
using PortRegs = Gpio::PortRegs<gpioPort()>;

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

template<IRQn_Type Type, size_t Lines>
IntHandler<Type, Lines>::Handler IntHandler<Type, Lines>::handlers[IntHandler::Lines] modm_fastdata;

%% if with_gpio
MODM_ISR(IO_IRQ_BANK0)
{
IntHandler<IO_IRQ_BANK0_IRQn, NUM_BANK0_GPIOS>::irqHandler();
}
%%endif

%% if with_qspi
MODM_ISR(IO_IRQ_QSPI)
{
IntHandler<IO_IRQ_QSPI_IRQn, NUM_QSPI_GPIOS>::irqHandler();
}
%%endif

} // namespace modm::platform
133 changes: 133 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,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 <modm/architecture/interface/interrupt.hpp>
#include <modm/platform/gpio/base.hpp>
#include <modm/platform/irq/int_controller.hpp>
#include <modm/utils/inplace_function.hpp>

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<IRQn_Type Type, size_t NLines>
class IntHandler
{
public:
static constexpr size_t Lines = NLines;
using Handler =
modm::inplace_function<void(Gpio::InputTrigger), {{handler_storage}}, alignof(void*)>;

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<class Pin>
static void
connect(Gpio::InputTrigger triggers, Handler&& handler)
{
static_assert(Pin::port == gpioPort());

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

handlers[Pin::pin] = handler;

enableInterrupts<Pin>(triggers);
}

template<class Pin>
static void
disconnect()
{
static_assert(Pin::port == gpioPort());

disableInterrupts<Pin>(Gpio::InputTrigger::All);
handlers[Pin::pin] = nullptr;
}

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

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

template<class Pin>
static void
acknowledgeInterrupts(Gpio::InputTrigger triggers)
{
Gpio::PortRegs<Pin::port>::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<Gpio::Port>(-1);
}
};

%% if with_gpio
using GpioIntHandler = IntHandler<IO_IRQ_BANK0_IRQn, NUM_BANK0_GPIOS>;
%%endif
%% if with_qspi
using QspiIntHandler = IntHandler<IO_IRQ_QSPI_IRQn, NUM_QSPI_GPIOS>;
%%endif

} // namespace modm::platform
Loading

0 comments on commit 24c4f4f

Please sign in to comment.