ArduinoのattachInterrupt()によりピン割り込みは可能であるが、さらに、ウェイクアップさせたり、イベント出力することも出来たりする割り込み管理ライブラリを作成してみた。
XIAOは全てのピンが割り込みに対応しているが、D4ピンは、NMIであるため割り込み禁止状態、或いは、他の割り込み実行中であっても割り込みが発生してしまうことに注意。普通の割り込みと同じ感覚で使ってしまうとバグること間違いなしなため特別な事情がないかぎりは使わないほうが良いだろう。割り込みを禁止してコードの命令サイクルだけでタイミングをとるプログラムも当然バグることになる。
なお、ArduinoのattachInterruptとは割り込みハンドラが重複するため排他的にしか利用できない。
【メソッドの概要】
1 |
void begin(void) |
ライブラリの初期化を行う。
1 |
void end(void) |
ライブラリを終了する。
1 |
bool config(uint32_t pin, EXTINT_SENSE sense, bool filter = false) |
ピンの割り込み設定を行う。
1 |
bool interrupt(uint32_t pin, EXTINT_CALLBACK cb) |
割り込みを制御する。
1 |
bool wakeup(uint32_t pin, bool enable) |
ウェイクアップを制御する。
1 |
EVENTSYS_GEN eventOutput(uint32_t pin, bool enable) |
イベント出力を制御する。成功時は、イベント・ジェネレータIDを返し、失敗時はEVENTSYS_GEN_NONEを返す。
【サンプル・スケッチ】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#include "extint.h" void callback() { IOBUS::toggleOutput(IOBUS_PIN_13_LED); } void setup() { // XIAO D9 ピンから割り込みを発生させる IOBUS::pinMode(EXTINT_PIN_D9, INPUT_PULLUP); GClock::begin(); ExtInt::begin(); ExtInt::config(EXTINT_PIN_D9, EXTINT_SENSE_FALL); ExtInt::interrupt(EXTINT_PIN_D9, callback); } void loop() { } |
【修正履歴】
2020-05-16
イベント出力も出来ますと言っておきながら、イベント連携に必要なイベント・ジェネレータIDを返していなかったためeventOutput()の戻り値としてイベント・ジェネレータIDを返すように仕様変更。
【ライブラリ】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
/* extint.h - External Interrupt Library for Microchip ATSAMD21 (Cortex®-M0+) Copyright (c) 2020 Sasapea's Lab. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __EXTINT_H #define __EXTINT_H #include <stdint.h> #include <stdbool.h> #include <sam.h> #include "gclock.h" #include "eventsys.h" #include "IOBUS.h" // // Port Define for Seeeduino XIAO // #ifdef SEEED_XIAO_M0 #define EXTINT_PIN_D0 EXTINT_PIN_PA02 #define EXTINT_PIN_D1 EXTINT_PIN_PA04 #define EXTINT_PIN_D2 EXTINT_PIN_PA10 #define EXTINT_PIN_D3 EXTINT_PIN_PA11 #define EXTINT_PIN_D4 EXTINT_PIN_PA08 // NMI #define EXTINT_PIN_D5 EXTINT_PIN_PA09 #define EXTINT_PIN_D6 EXTINT_PIN_PB08 #define EXTINT_PIN_D7 EXTINT_PIN_PB09 #define EXTINT_PIN_D8 EXTINT_PIN_PA07 #define EXTINT_PIN_D9 EXTINT_PIN_PA05 #define EXTINT_PIN_D10 EXTINT_PIN_PA06 #endif #define EXTINT_PIN_PA00 IOBUS_PIN('A', 0) #define EXTINT_PIN_PA01 IOBUS_PIN('A', 1) #define EXTINT_PIN_PA02 IOBUS_PIN('A', 2) #define EXTINT_PIN_PA03 IOBUS_PIN('A', 3) #define EXTINT_PIN_PA04 IOBUS_PIN('A', 4) #define EXTINT_PIN_PA05 IOBUS_PIN('A', 5) #define EXTINT_PIN_PA06 IOBUS_PIN('A', 6) #define EXTINT_PIN_PA07 IOBUS_PIN('A', 7) #define EXTINT_PIN_PA08 IOBUS_PIN('A', 8) // NMI #define EXTINT_PIN_PA09 IOBUS_PIN('A', 9) #define EXTINT_PIN_PA10 IOBUS_PIN('A', 10) #define EXTINT_PIN_PA11 IOBUS_PIN('A', 11) #define EXTINT_PIN_PA12 IOBUS_PIN('A', 12) #define EXTINT_PIN_PA13 IOBUS_PIN('A', 13) #define EXTINT_PIN_PA14 IOBUS_PIN('A', 14) #define EXTINT_PIN_PA15 IOBUS_PIN('A', 15) #define EXTINT_PIN_PA16 IOBUS_PIN('A', 16) #define EXTINT_PIN_PA17 IOBUS_PIN('A', 17) #define EXTINT_PIN_PA18 IOBUS_PIN('A', 18) #define EXTINT_PIN_PA19 IOBUS_PIN('A', 19) #define EXTINT_PIN_PA20 IOBUS_PIN('A', 20) #define EXTINT_PIN_PA21 IOBUS_PIN('A', 21) #define EXTINT_PIN_PA22 IOBUS_PIN('A', 22) #define EXTINT_PIN_PA23 IOBUS_PIN('A', 23) #define EXTINT_PIN_PA24 IOBUS_PIN('A', 24) #define EXTINT_PIN_PA25 IOBUS_PIN('A', 25) #define EXTINT_PIN_PA27 IOBUS_PIN('A', 27) #define EXTINT_PIN_PA28 IOBUS_PIN('A', 28) #define EXTINT_PIN_PA30 IOBUS_PIN('A', 30) #define EXTINT_PIN_PA31 IOBUS_PIN('A', 31) #define EXTINT_PIN_PB00 IOBUS_PIN('B', 0) #define EXTINT_PIN_PB01 IOBUS_PIN('B', 1) #define EXTINT_PIN_PB02 IOBUS_PIN('B', 2) #define EXTINT_PIN_PB03 IOBUS_PIN('B', 3) #define EXTINT_PIN_PB04 IOBUS_PIN('B', 4) #define EXTINT_PIN_PB05 IOBUS_PIN('B', 5) #define EXTINT_PIN_PB06 IOBUS_PIN('B', 6) #define EXTINT_PIN_PB07 IOBUS_PIN('B', 7) #define EXTINT_PIN_PB08 IOBUS_PIN('B', 8) #define EXTINT_PIN_PB09 IOBUS_PIN('B', 9) #define EXTINT_PIN_PB10 IOBUS_PIN('B', 10) #define EXTINT_PIN_PB11 IOBUS_PIN('B', 11) #define EXTINT_PIN_PB12 IOBUS_PIN('B', 12) #define EXTINT_PIN_PB13 IOBUS_PIN('B', 13) #define EXTINT_PIN_PB14 IOBUS_PIN('B', 14) #define EXTINT_PIN_PB15 IOBUS_PIN('B', 15) #define EXTINT_PIN_PB16 IOBUS_PIN('B', 16) #define EXTINT_PIN_PB17 IOBUS_PIN('B', 17) #define EXTINT_PIN_PB22 IOBUS_PIN('B', 22) #define EXTINT_PIN_PB23 IOBUS_PIN('B', 23) #define EXTINT_PIN_PB30 IOBUS_PIN('B', 30) #define EXTINT_PIN_PB31 IOBUS_PIN('B', 31) typedef enum { EXTINT_SENSE_NONE = EIC_CONFIG_SENSE0_NONE_Val, EXTINT_SENSE_RISE = EIC_CONFIG_SENSE0_RISE_Val, EXTINT_SENSE_FALL = EIC_CONFIG_SENSE0_FALL_Val, EXTINT_SENSE_BOTH = EIC_CONFIG_SENSE0_BOTH_Val, EXTINT_SENSE_HIGH = EIC_CONFIG_SENSE0_HIGH_Val, EXTINT_SENSE_LOW = EIC_CONFIG_SENSE0_LOW_Val, } EXTINT_SENSE; typedef void (*EXTINT_CALLBACK)(void); class ExtInt { private: static uint8_t extintno(uint32_t pin); static void init(void); static void setCallback(uint8_t no, EXTINT_CALLBACK cb); static void setNMICTRL(uint8_t value); static uint8_t getNMICTRL(void); static void ctrl(uint8_t value) { EIC->CTRL.reg = value; while (EIC->STATUS.bit.SYNCBUSY || EIC->CTRL.bit.SWRST) continue; } static void controlGCLK(EXTINT_SENSE sense, bool filter) { if (filter || ((sense >= EXTINT_SENSE_RISE) && (sense <= EXTINT_SENSE_BOTH))) GClock::control(GCLOCK_ID_EIC, true); } public: static void begin(void) { PM->APBAMASK.bit.EIC_ = 1; ctrl(EIC_CTRL_SWRST); ctrl(EIC_CTRL_ENABLE); NVIC_EnableIRQ(EIC_IRQn); NVIC_SetPriority(EIC_IRQn, 0); init(); } static void end(void) { NVIC_DisableIRQ(EIC_IRQn); ctrl(EIC_CTRL_SWRST); GClock::control(GCLOCK_ID_EIC, false); } static bool config(uint32_t pin, EXTINT_SENSE sense, bool filter = false) { uint8_t no = extintno(pin); if (no > EIC_EXTINT_NUM) return false; if (sense == EXTINT_SENSE_NONE) filter = false; IOBUS::multiplexing(pin, sense != EXTINT_SENSE_NONE ? IOBUS_PMUX_A : IOBUS_PMUX_DISABLE); controlGCLK(sense, filter); if (no == EIC_EXTINT_NUM) setNMICTRL((filter ? EIC_NMICTRL_NMIFILTEN : 0) | EIC_NMICTRL_NMISENSE(sense)); else EIC->CONFIG[no >> 3].reg = ((filter ? EIC_CONFIG_FILTEN0 : 0) | EIC_CONFIG_SENSE0(sense)) << ((no & 7) << 2); return true; } static bool interrupt(uint32_t pin, EXTINT_CALLBACK cb) { uint8_t no = extintno(pin); if (no > EIC_EXTINT_NUM) return false; setCallback(no, cb); if (no == EIC_EXTINT_NUM) EIC->NMICTRL.reg = (cb ? getNMICTRL() : EIC_NMICTRL_NMISENSE_NONE); else if (cb) EIC->INTENSET.reg = EIC_INTENSET_EXTINT(1 << no); else EIC->INTENCLR.reg = EIC_INTENCLR_EXTINT(1 << no); return true; } static bool wakeup(uint32_t pin, bool enable) { uint8_t no = extintno(pin); if (no >= EIC_EXTINT_NUM) return false; if (enable) EIC->WAKEUP.reg |= EIC_WAKEUP_WAKEUPEN(1 << no); else EIC->WAKEUP.reg &= ~EIC_WAKEUP_WAKEUPEN(1 << no); return true; } static EVENTSYS_GEN eventOutput(uint32_t pin, bool enable) { uint8_t no = extintno(pin); if (no >= EIC_EXTINT_NUM) return EVENTSYS_GEN_NONE; if (enable) EIC->EVCTRL.reg |= EIC_EVCTRL_EXTINTEO(1 << no); else EIC->EVCTRL.reg &= ~EIC_EVCTRL_EXTINTEO(1 << no); return (EVENTSYS_GEN)(EVENTSYS_GEN_EIC_EXTINT_0 + no); } }; #endif |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
/* extint.cpp - External Interrupt Library for Microchip ATSAMD21 (Cortex®-M0+) Copyright (c) 2020 Sasapea's Lab. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <string.h> #include "extint.h" #define NONE 0xFF #define _NMI EIC_EXTINT_NUM // for SAMD21GJ static const uint8_t EXTINTn[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, _NMI, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x0C, 0x0D, NONE, 0x0F, 0x08, NONE, 0x0A, 0x0B, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00, 0x01, NONE, NONE, NONE, NONE, 0x06, 0x07, NONE, NONE, NONE, NONE, NONE, NONE, 0x0E, 0x0F }; static EXTINT_CALLBACK _callback[EIC_EXTINT_NUM + 1]; static uint8_t _nmictrl; void NMI_Handler(void) { if (EIC->NMIFLAG.bit.NMI) { EIC->NMIFLAG.reg = EIC_NMIFLAG_NMI; if (_callback[_NMI]) _callback[_NMI](); } } void EIC_Handler(void) { uint32_t b = 1; for (uint32_t i = 0; (i < EIC_EXTINT_NUM) && (EIC->INTFLAG.reg & EIC_INTFLAG_MASK); ++i, b <<= 1) { if (EIC->INTFLAG.reg & b) { EIC->INTFLAG.reg = b; if (_callback[i]) _callback[i](); } } } uint8_t ExtInt::extintno(uint32_t pin) { return pin < sizeof(EXTINTn) ? EXTINTn[pin] : NONE; } void ExtInt::init(void) { _nmictrl = EIC_NMICTRL_NMISENSE_NONE; memset(_callback, 0, sizeof(_callback)); } void ExtInt::setCallback(uint8_t no, EXTINT_CALLBACK cb) { _callback[no] = cb; } void ExtInt::setNMICTRL(uint8_t value) { _nmictrl = value; } uint8_t ExtInt::getNMICTRL(void) { return _nmictrl; } |
【参照ライブラリ】
Seeeduino XIAO (ATSAMD21G18) のGPIO速度 (IOBUS.h)
Seeeduino XIAO (ATSAMD21G18) のGCLKライブラリ (gclock.h/gclock.cpp)
Seeeduino XIAO (ATSAMD21G18) のEVSYSライブラリ (eventsys.h/eventsys.cpp)