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

[extint] Adds external interrupt support for SAM #436

Merged
merged 3 commits into from
Jul 20, 2020
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
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>();
salkinium marked this conversation as resolved.
Show resolved Hide resolved
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>
2 changes: 1 addition & 1 deletion src/modm/board/feather_m0/board.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ struct SystemClock
};

using LoggerDevice = modm::IODeviceWrapper< Uart0, modm::IOBuffer::BlockIfFull >;
// using Leds = SoftwareGpioPort< LedD13 >;
using Leds = SoftwareGpioPort< Led >;

inline void
initialize()
Expand Down
2 changes: 1 addition & 1 deletion src/modm/board/feather_m0/module.lb
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def build(env):
env.outbasepath = "modm/src/modm/board"
env.substitutions = {
"with_logger": True,
"with_assert": False,
"with_assert": True,
}
env.template("../board.cpp.in", "board.cpp")
env.copy('.')
Expand Down
6 changes: 4 additions & 2 deletions src/modm/platform/clock/sam/gclk.cpp.in
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ modm::platform::GenericClockController::initExternalCrystal(
// Enable external crystal.
SYSCTRL->XOSC32K.reg =
SYSCTRL_XOSC32K_STARTUP( 0x6u ) |
SYSCTRL_XOSC32K_XTALEN | SYSCTRL_XOSC32K_EN32K;
SYSCTRL_XOSC32K_XTALEN |
SYSCTRL_XOSC32K_RUNSTDBY |
SYSCTRL_XOSC32K_EN32K;
// separate call, as described in chapter 15.6.3
SYSCTRL->XOSC32K.bit.ENABLE = 1;
while (!SYSCTRL->PCLKSR.bit.XOSC32KRDY and --waitCycles);
Expand Down Expand Up @@ -94,7 +96,7 @@ modm::platform::GenericClockController::initDFLL48MHz(
!(SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLLCKC) ||
!(SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLLCKF));
// Wait for synchronization.
while (!SYSCTRL->PCLKSR.bit.DFLLRDY and --waitCycles)
while (!SYSCTRL->PCLKSR.bit.DFLLRDY and --waitCycles);
return waitCycles;
}

Expand Down
65 changes: 57 additions & 8 deletions src/modm/platform/clock/sam/gclk.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,59 @@ ClockSource : uint32_t
DFLL48M = GCLK_GENCTRL_SRC_DFLL48M_Val
};

enum class
ClockGenerator : uint32_t
{
System = 0,
ExternalCrystal32K = 1,
ULP32K = 2,
Internal8M = 3,
};

enum class ClockPeripheral : uint32_t {
Dfll48 = GCLK_CLKCTRL_ID_DFLL48_Val,
Fdpll = GCLK_CLKCTRL_ID_FDPLL_Val,
Fdpll32K = GCLK_CLKCTRL_ID_FDPLL32K_Val,
Wdt = GCLK_CLKCTRL_ID_WDT_Val,
Rtc = GCLK_CLKCTRL_ID_RTC_Val,
Eic = GCLK_CLKCTRL_ID_EIC_Val,
Usb = GCLK_CLKCTRL_ID_USB_Val,
Evsys0 = GCLK_CLKCTRL_ID_EVSYS_0_Val,
Evsys1 = GCLK_CLKCTRL_ID_EVSYS_1_Val,
Evsys2 = GCLK_CLKCTRL_ID_EVSYS_2_Val,
Evsys3 = GCLK_CLKCTRL_ID_EVSYS_3_Val,
Evsys4 = GCLK_CLKCTRL_ID_EVSYS_4_Val,
Evsys5 = GCLK_CLKCTRL_ID_EVSYS_5_Val,
Evsys6 = GCLK_CLKCTRL_ID_EVSYS_6_Val,
Evsys7 = GCLK_CLKCTRL_ID_EVSYS_7_Val,
Evsys8 = GCLK_CLKCTRL_ID_EVSYS_8_Val,
Evsys9 = GCLK_CLKCTRL_ID_EVSYS_9_Val,
Evsys10 = GCLK_CLKCTRL_ID_EVSYS_10_Val,
Evsys11 = GCLK_CLKCTRL_ID_EVSYS_11_Val,
SercomXSlow = GCLK_CLKCTRL_ID_SERCOMX_SLOW_Val,
Sercom0 = GCLK_CLKCTRL_ID_SERCOM0_CORE_Val,
Sercom1 = GCLK_CLKCTRL_ID_SERCOM1_CORE_Val,
Sercom2 = GCLK_CLKCTRL_ID_SERCOM2_CORE_Val,
Sercom3 = GCLK_CLKCTRL_ID_SERCOM3_CORE_Val,
Sercom4 = GCLK_CLKCTRL_ID_SERCOM4_CORE_Val,
Sercom5 = GCLK_CLKCTRL_ID_SERCOM5_CORE_Val,
Tcc0 = GCLK_CLKCTRL_ID_TCC0_TCC1_Val,
Tcc1 = GCLK_CLKCTRL_ID_TCC0_TCC1_Val,
Tcc2 = GCLK_CLKCTRL_ID_TCC2_TC3_Val,
Tc3 = GCLK_CLKCTRL_ID_TCC2_TC3_Val,
Tc4 = GCLK_CLKCTRL_ID_TC4_TC5_Val,
Tc5 = GCLK_CLKCTRL_ID_TC4_TC5_Val,
Tc6 = GCLK_CLKCTRL_ID_TC6_TC7_Val,
Tc7 = GCLK_CLKCTRL_ID_TC6_TC7_Val,
Adc = GCLK_CLKCTRL_ID_ADC_Val,
AcDig = GCLK_CLKCTRL_ID_AC_DIG_Val,
AcAna = GCLK_CLKCTRL_ID_AC_ANA_Val,
Dac = GCLK_CLKCTRL_ID_DAC_Val,
Ptc = GCLK_CLKCTRL_ID_PTC_Val,
I2s0 = GCLK_CLKCTRL_ID_I2S_0_Val,
I2s1 = GCLK_CLKCTRL_ID_I2S_1_Val
};

/**
* Clock management
*
Expand Down Expand Up @@ -55,15 +108,11 @@ class GenericClockController
ClockSource source = ClockSource::OSC8M,
uint32_t waitCycles = 2048);

template< ClockPeripheral peripheral >
static void
connect(ClockGenerator clockGen);

private:
enum class
ClockGenerator : uint32_t
{
System = 0,
ExternalCrystal32K = 1,
ULP32K = 2,
Internal8M = 3,
};

enum class
ClockMux : uint32_t
Expand Down
10 changes: 10 additions & 0 deletions src/modm/platform/clock/sam/gclk_impl.hpp.in
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,15 @@ GenericClockController::setFlashLatency()
return Core_Hz;
}

template< ClockPeripheral peripheral >
void
GenericClockController::connect(ClockGenerator clockGen)
{
GCLK->CLKCTRL.reg =
GCLK_CLKCTRL_CLKEN |
GCLK_CLKCTRL_GEN(uint32_t(clockGen)) |
GCLK_CLKCTRL_ID(uint32_t(peripheral));
}

}

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;
salkinium marked this conversation as resolved.
Show resolved Hide resolved
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
Loading