Skip to content

Commit

Permalink
[extint] Add external interrupt support for SAM
Browse files Browse the repository at this point in the history
  • Loading branch information
henrikssn authored and salkinium committed Jul 20, 2020
1 parent bad229a commit cbce428
Show file tree
Hide file tree
Showing 9 changed files with 296 additions and 4 deletions.
41 changes: 41 additions & 0 deletions examples/samd/interrupt/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2020, Erik Henriksson
*
* 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>

using namespace Board;
using namespace std::chrono_literals;

static volatile bool blink = true;

void
isr() {
blink = !blink;
// Kids, please don't do serial logging in ISRs...
MODM_LOG_DEBUG << "blink: " << (blink ? "true" : "false") << modm::endl;
}

int
main()
{
Board::initialize();
ExternalInterrupt::initialize();
ExtInt<3>::initialize(&isr);
ExtInt<3>::connectPin<D12>();
while (1) {
if (blink) {
Led::toggle();
} else {
Led::set(0);
}
modm::delay(100ms);
}
return 0;
}
10 changes: 10 additions & 0 deletions examples/samd/interrupt/project.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<library>
<extends>modm:feather-m0</extends>
<options>
<option name="modm:build:build.path">../../../build/samd/interrupt</option>
</options>
<modules>
<module>modm:build:scons</module>
<module>modm:platform:extint</module>
</modules>
</library>
58 changes: 58 additions & 0 deletions src/modm/platform/extint/sam/extint.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright (c) 2020, Erik Henriksson
*
* 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 "extint.hpp"

void isr();

namespace modm
{
namespace platform
{

void
ExternalInterrupt::initialize(ClockGenerator clockGen, int priority) {
NVIC_DisableIRQ(EIC_IRQn);
NVIC_ClearPendingIRQ(EIC_IRQn);
NVIC_SetPriority(EIC_IRQn, priority);
NVIC_EnableIRQ(EIC_IRQn);

GenericClockController::connect<ClockPeripheral::Eic>(clockGen);

// Enable EIC
EIC->CTRL.bit.ENABLE = 1;
int waitCycles = 2048;
while (EIC->STATUS.bit.SYNCBUSY == 1 && --waitCycles);
}

std::array<std::function<void()>, 16> ExternalInterrupt::handlers_ = {};

// FIXME: Figure out if it is worth using function pointers here instead
// to get rid of std::function overhead.
MODM_ISR(EIC) {
uint32_t int_flag = EIC->INTFLAG.reg;
uint32_t flags = int_flag << 16;
auto handler = ExternalInterrupt::handlers_.end() - 1;
do {
uint8_t leading_zeros = __builtin_clz(flags);
handler -= leading_zeros;
flags <<= leading_zeros;
if (flags & 1u << 31 && *handler) {
(*handler)();
flags &= ~(1u << 31);
}
} while (flags);
EIC->INTFLAG.reg |= int_flag;
}


} // namespace platform
} // namespace modm
95 changes: 95 additions & 0 deletions src/modm/platform/extint/sam/extint.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Copyright (c) 2020, Erik Henriksson
*
* 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 <functional>
#include <modm/architecture/interface/interrupt.hpp>
#include <modm/platform/clock/gclk.hpp>
#include <modm/platform/device.hpp>
#include <modm/platform/gpio/base.hpp>

#pragma once

namespace modm
{

namespace platform
{

MODM_ISR_DECL(EIC);

/**
* External Interrupt handler for SAMD devices.
*
* @author Erik Henriksson
* @ingroup modm_platform_extint
*/
class ExternalInterrupt {
friend void EIC_IRQHandler(void);
public:
/**
* Initializes the External Interrupt handler.
*
* @param clockGen
* The clock generator to use for the peripheral. If any interrupts are to
* be used to akeup the CPU from standby mode, make sure this clock is
* actually running in standby. Defaults to external 32.768kHz crystal osc.
*/
static void initialize(
ClockGenerator clockGen = ClockGenerator::ExternalCrystal32K,
int priority = (1ul << __NVIC_PRIO_BITS) - 1ul);

protected:
static std::array<std::function<void()>, 16> handlers_;
};

/**
* External Interrupt instance for SAMD devices.
*
* @author Erik Henriksson
* @ingroup modm_platform_extint
*/
template<int instance> class ExtInt : private ExternalInterrupt
{
public:
/**
* Initializes the External Interrupt instance.
*
* @param handler
* Function that will be called for any interrupts.
* @param trigger
* Specifies the edge detection trigger to use (default is rising edge).
* @param wakeupEnabled
* If true (default), allows the CPU to wakeup from interrupt
* from this instance.
*/
static void initialize(
std::function<void()> handler,
Gpio::InputTrigger trigger = Gpio::InputTrigger::RisingEdge,
bool wakeupEnabled = true);

/**
* Connects a GPIO pin to this External Interrupt instance.
*
* @tparam Pin
* The GPIO pin to connect this instance to.
*/
template<class Pin>
static void
connectPin() {
Pin::template connectInterrupt<instance>();
}
};

} // namespace platform

} // namespace modm

#include "extint_impl.hpp"
39 changes: 39 additions & 0 deletions src/modm/platform/extint/sam/extint_impl.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2020, Erik Henriksson
*
* 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

namespace modm
{
namespace platform
{

template<int instance>
void
ExtInt<instance>::initialize(
std::function<void()> handler,
Gpio::InputTrigger trigger,
bool wakeupEnabled) {
handlers_[instance] = handler;
if (wakeupEnabled) {
EIC->WAKEUP.reg |= EIC_WAKEUP_WAKEUPEN(1u << instance);
} else {
EIC->WAKEUP.reg &= ~EIC_WAKEUP_WAKEUPEN(1u << instance);
}
constexpr int sensePos = instance*EIC_CONFIG_SENSE1_Pos;
EIC->CONFIG[instance & 0x8].reg &= EIC_CONFIG_SENSE0_Msk << sensePos;
EIC->CONFIG[instance & 0x8].reg |= uint32_t(trigger) << sensePos;
EIC->INTENSET.vec.EXTINT |= 1u << instance;
}


} // namespace platform
} // namespace modm
31 changes: 31 additions & 0 deletions src/modm/platform/extint/sam/module.lb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (c) 2020, Erik Henriksson
#
# 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/.
# -----------------------------------------------------------------------------

import sys

def init(module):
module.name = ":platform:extint"
module.description = "External Interrupt"

def prepare(module, options):
if options[":target"].identifier.platform != "sam":
return False

module.depends(":cmsis:device", ":platform:clock", ":platform:gpio")

return True

def build(env):
env.outbasepath = "modm/src/modm/platform/extint"
env.copy("extint.hpp")
env.copy("extint_impl.hpp")
env.copy("extint.cpp")
8 changes: 5 additions & 3 deletions src/modm/platform/gpio/sam/base.hpp.in
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,11 @@ struct Gpio
enum class
InputTrigger
{
RisingEdge,
FallingEdge,
BothEdges,
RisingEdge = EIC_CONFIG_SENSE0_RISE_Val,
FallingEdge = EIC_CONFIG_SENSE0_FALL_Val,
BothEdges = EIC_CONFIG_SENSE0_BOTH_Val,
High = EIC_CONFIG_SENSE0_HIGH_Val,
Low = EIC_CONFIG_SENSE0_LOW_Val,
};

/// The Port a Gpio Pin is connected to.
Expand Down
4 changes: 3 additions & 1 deletion src/modm/platform/gpio/sam/module.lb
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,11 @@ def validate(env):
all_signals.update(extracted_signals)
signal_names = sorted(list(set(s["name"] for s in extracted_signals.values())))
extracted_signals = OrderedDict([(name, sorted([s for s in extracted_signals.values() if s["name"] == name], key=lambda si:si["name"])) for name in signal_names])
extints = sorted({"pin": signal["name"][6:], "function": signal["function"]} for signal in raw_signals if signal["name"].startswith("extint"))
bprops[key] = {
"gpio": gpio,
"signals": extracted_signals
"signals": extracted_signals,
"extints": extints
}

all_peripherals = [s["driver"] for s in all_signals.values()]
Expand Down
14 changes: 14 additions & 0 deletions src/modm/platform/gpio/sam/pin.hpp.in
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class Gpio{{ port ~ pin }} : public Gpio, public ::modm::GpioIO
friend class Adc;
friend class Adc1; friend class Adc2;
friend class Adc3; friend class Adc4;
template<int instance> friend class ExtInt;
public:
using Output = Gpio{{ port ~ pin }};
using Input = Gpio{{ port ~ pin }};
Expand Down Expand Up @@ -124,6 +125,19 @@ public:
"Gpio{{ port ~ pin }}::{{ signal_name }} only connects to {% for signal in signal_group %}{{signal.driver}}{% if not loop.last %} or {% endif %}{% endfor %}!");
};
%% endfor

protected:
%% for extint in extints
template< int instance >
inline static void connectInterrupt() {
static_assert(
instance == {{ extint.pin }},
"Gpio{{ port ~ pin }} only connects to ExtInt<{{ extint.pin }}>");
PORT->Group[uint32_t(port)].PINCFG[uint32_t(pin)].bit.PMUXEN = true;
PORT->Group[uint32_t(port)].PMUX[uint32_t(pin) >> 1].reg =
PORT_PMUX_{{ gpio.pmux }}_{{ extint.function | upper }};
}
%% endfor
};

/// @cond
Expand Down

0 comments on commit cbce428

Please sign in to comment.