From cbce42883c3ac74071ef560aeb048ec3d4bef4f6 Mon Sep 17 00:00:00 2001 From: Erik Henriksson Date: Mon, 20 Jul 2020 12:18:22 +0200 Subject: [PATCH] [extint] Add external interrupt support for SAM --- examples/samd/interrupt/main.cpp | 41 +++++++++ examples/samd/interrupt/project.xml | 10 +++ src/modm/platform/extint/sam/extint.cpp | 58 ++++++++++++ src/modm/platform/extint/sam/extint.hpp | 95 ++++++++++++++++++++ src/modm/platform/extint/sam/extint_impl.hpp | 39 ++++++++ src/modm/platform/extint/sam/module.lb | 31 +++++++ src/modm/platform/gpio/sam/base.hpp.in | 8 +- src/modm/platform/gpio/sam/module.lb | 4 +- src/modm/platform/gpio/sam/pin.hpp.in | 14 +++ 9 files changed, 296 insertions(+), 4 deletions(-) create mode 100644 examples/samd/interrupt/main.cpp create mode 100644 examples/samd/interrupt/project.xml create mode 100644 src/modm/platform/extint/sam/extint.cpp create mode 100644 src/modm/platform/extint/sam/extint.hpp create mode 100644 src/modm/platform/extint/sam/extint_impl.hpp create mode 100644 src/modm/platform/extint/sam/module.lb diff --git a/examples/samd/interrupt/main.cpp b/examples/samd/interrupt/main.cpp new file mode 100644 index 0000000000..481f0afd50 --- /dev/null +++ b/examples/samd/interrupt/main.cpp @@ -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 + +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(); + while (1) { + if (blink) { + Led::toggle(); + } else { + Led::set(0); + } + modm::delay(100ms); + } + return 0; +} diff --git a/examples/samd/interrupt/project.xml b/examples/samd/interrupt/project.xml new file mode 100644 index 0000000000..1c53a440cb --- /dev/null +++ b/examples/samd/interrupt/project.xml @@ -0,0 +1,10 @@ + + modm:feather-m0 + + + + + modm:build:scons + modm:platform:extint + + diff --git a/src/modm/platform/extint/sam/extint.cpp b/src/modm/platform/extint/sam/extint.cpp new file mode 100644 index 0000000000..fbe51d70d6 --- /dev/null +++ b/src/modm/platform/extint/sam/extint.cpp @@ -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(clockGen); + + // Enable EIC + EIC->CTRL.bit.ENABLE = 1; + int waitCycles = 2048; + while (EIC->STATUS.bit.SYNCBUSY == 1 && --waitCycles); +} + +std::array, 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 diff --git a/src/modm/platform/extint/sam/extint.hpp b/src/modm/platform/extint/sam/extint.hpp new file mode 100644 index 0000000000..8e5e36b371 --- /dev/null +++ b/src/modm/platform/extint/sam/extint.hpp @@ -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 +#include +#include +#include +#include + +#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, 16> handlers_; +}; + +/** + * External Interrupt instance for SAMD devices. + * + * @author Erik Henriksson + * @ingroup modm_platform_extint + */ +template 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 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 + static void + connectPin() { + Pin::template connectInterrupt(); + } +}; + +} // namespace platform + +} // namespace modm + +#include "extint_impl.hpp" \ No newline at end of file diff --git a/src/modm/platform/extint/sam/extint_impl.hpp b/src/modm/platform/extint/sam/extint_impl.hpp new file mode 100644 index 0000000000..c9ccd0a35d --- /dev/null +++ b/src/modm/platform/extint/sam/extint_impl.hpp @@ -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 +void +ExtInt::initialize( + std::function 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 diff --git a/src/modm/platform/extint/sam/module.lb b/src/modm/platform/extint/sam/module.lb new file mode 100644 index 0000000000..1ae2501fb9 --- /dev/null +++ b/src/modm/platform/extint/sam/module.lb @@ -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") diff --git a/src/modm/platform/gpio/sam/base.hpp.in b/src/modm/platform/gpio/sam/base.hpp.in index da7cebec29..83c5baffe1 100644 --- a/src/modm/platform/gpio/sam/base.hpp.in +++ b/src/modm/platform/gpio/sam/base.hpp.in @@ -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. diff --git a/src/modm/platform/gpio/sam/module.lb b/src/modm/platform/gpio/sam/module.lb index 39deee0e8e..15e2ab7e06 100644 --- a/src/modm/platform/gpio/sam/module.lb +++ b/src/modm/platform/gpio/sam/module.lb @@ -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()] diff --git a/src/modm/platform/gpio/sam/pin.hpp.in b/src/modm/platform/gpio/sam/pin.hpp.in index f56bd61416..81770a5e1c 100644 --- a/src/modm/platform/gpio/sam/pin.hpp.in +++ b/src/modm/platform/gpio/sam/pin.hpp.in @@ -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 friend class ExtInt; public: using Output = Gpio{{ port ~ pin }}; using Input = Gpio{{ port ~ pin }}; @@ -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