Arduinoの機能だけなら利用価値はないかもしれないが、CPUが持つ機能をフル活用するには多種多様なクロック管理が必要となってくるためペリフェラル・クロックを管理するためのGCLKライブラリを作ってみた。
ペリフェラルとジェネレータの割当管理を行い、クロック・ジェネレータを追加したりクロックをポート出力することが可能だ。
【XIAOがセットアップするクロック・ソースとジェネレータ】
1 2 3 4 |
GCLOCK_SRC_DFLL48M --> GCLOCK_GEN_MAIN (48MHz DFLL[XOSC32K]) GCLOCK_SRC_XOSC32K --> GCLOCK_GEN_XOSC32K (32KHz XTAL) GCLOCK_SRC_OSCULP32K --> GCLOCK_GEN_OSCULP32K (32KHz RC 超低電力) GCLOCK_SRC_OSC8M --> GCLOCK_GEN_OSC8M ( 8MHz RC) |
※上記以外のクロックが必要な場合はセットアップ済みのクロック・ソースから新しいクロック・ジェネレータを追加することができる。
【メソッドの概要】
1 |
void begin(void) |
規定のジェネレータをGCLOCK_GEN_GCLK0(48MHz)で初期化する。
1 |
void select(GCLOCK_ID id, GCLOCK_GEN gen) |
ペリフェラル(id)に接続するジェネレータ(gen)を指定する。
1 |
void control(GCLOCK_ID id, bool enable) |
ペリフェラル(id)へのクロック出力(enable)を制御。他のライブラリやアプリが利用する。
1 |
GCLOCK_GEN config(GCLOCK_SRC src, uint8_t divider = 0, bool divsel = false, bool idc = false, GCLOCK_GEN reqid = GCLOCK_GEN_NONE) |
同じソース(src)で同じクロック(divider and divsel)を持つジェネレータがあればそのジェネレータを、ない場合は新たにジェネレータをセットアップし返す。失敗した場合はGCLOCK_GEN_NONEを返す。なお、ソース(src)自体のセットアップは行なわないことに注意すべし。
1 |
bool output(uint32_t pin, GCLOCK_SRC src, uint8_t divider = 0, bool divsel = false, bool idc = false, bool runstdby = false, bool oov = false) |
指定ポート(pin)にクロック(src and divider and divsel)を出力する。XIAOではD2/D3ポートのみ利用可能。
【サンプル・スケッチ】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#include "gclock.h" void setup() { GClock::begin(); // 32KHzクロックをWDTに供給 GClock::select(GCLOCK_ID_WDT, GCLOCK_GEN_OSCULP32K); GClock::control(GCLOCK_ID_WDT, true); // 32KHzクロックをポート出力 GClock::output(GCLOCK_PIN_D2, GCLOCK_SRC_OSCULP32K); } void loop() { } |
【修正履歴】
2020-05-18
begin()にWDT/RTC用のジェネレータ初期設定を追加。control()が完璧に間違っていたので修正。
【ライブラリ】
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 |
/* gclock.h - GCLK 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 __GCLOCK_H #define __GCLOCK_H #include <stdint.h> #include <stdbool.h> #include <sam.h> // // Port Define for Seeeduino XIAO // #ifdef SEEED_XIAO_M0 #define GCLOCK_PIN_D2 GCLOCK_PIN_PA10 #define GCLOCK_PIN_D3 GCLOCK_PIN_PA11 #endif #define GCLOCK_PIN_PA10 IOBUS_PIN('A', 10) #define GCLOCK_PIN_PA11 IOBUS_PIN('A', 11) #define GCLOCK_PIN_PA14 IOBUS_PIN('A', 14) #define GCLOCK_PIN_PA15 IOBUS_PIN('A', 15) #define GCLOCK_PIN_PA16 IOBUS_PIN('A', 16) #define GCLOCK_PIN_PA17 IOBUS_PIN('A', 17) #define GCLOCK_PIN_PA20 IOBUS_PIN('A', 20) #define GCLOCK_PIN_PA21 IOBUS_PIN('A', 21) #define GCLOCK_PIN_PA22 IOBUS_PIN('A', 22) #define GCLOCK_PIN_PA23 IOBUS_PIN('A', 23) #define GCLOCK_PIN_PA27 IOBUS_PIN('A', 27) #define GCLOCK_PIN_PA28 IOBUS_PIN('A', 28) #define GCLOCK_PIN_PA30 IOBUS_PIN('A', 30) #define GCLOCK_PIN_PB10 IOBUS_PIN('B', 10) #define GCLOCK_PIN_PB11 IOBUS_PIN('B', 11) #define GCLOCK_PIN_PB12 IOBUS_PIN('B', 12) #define GCLOCK_PIN_PB13 IOBUS_PIN('B', 13) #define GCLOCK_PIN_PB14 IOBUS_PIN('B', 14) #define GCLOCK_PIN_PB15 IOBUS_PIN('B', 15) #define GCLOCK_PIN_PB16 IOBUS_PIN('B', 16) #define GCLOCK_PIN_PB17 IOBUS_PIN('B', 17) #define GCLOCK_PIN_PB22 IOBUS_PIN('B', 22) #define GCLOCK_PIN_PB23 IOBUS_PIN('B', 23) typedef enum { GCLOCK_ID_DFLL48 = GCLK_CLKCTRL_ID_DFLL48_Val, GCLOCK_ID_FDPLL = GCLK_CLKCTRL_ID_FDPLL_Val, GCLOCK_ID_FDPLL32K = GCLK_CLKCTRL_ID_FDPLL32K_Val, GCLOCK_ID_WDT = GCLK_CLKCTRL_ID_WDT_Val, GCLOCK_ID_RTC = GCLK_CLKCTRL_ID_RTC_Val, GCLOCK_ID_EIC = GCLK_CLKCTRL_ID_EIC_Val, GCLOCK_ID_USB = GCLK_CLKCTRL_ID_USB_Val, GCLOCK_ID_EVSYS_0 = GCLK_CLKCTRL_ID_EVSYS_0_Val, GCLOCK_ID_EVSYS_1 = GCLK_CLKCTRL_ID_EVSYS_1_Val, GCLOCK_ID_EVSYS_2 = GCLK_CLKCTRL_ID_EVSYS_2_Val, GCLOCK_ID_EVSYS_3 = GCLK_CLKCTRL_ID_EVSYS_3_Val, GCLOCK_ID_EVSYS_4 = GCLK_CLKCTRL_ID_EVSYS_4_Val, GCLOCK_ID_EVSYS_5 = GCLK_CLKCTRL_ID_EVSYS_5_Val, GCLOCK_ID_EVSYS_6 = GCLK_CLKCTRL_ID_EVSYS_6_Val, GCLOCK_ID_EVSYS_7 = GCLK_CLKCTRL_ID_EVSYS_7_Val, GCLOCK_ID_EVSYS_8 = GCLK_CLKCTRL_ID_EVSYS_8_Val, GCLOCK_ID_EVSYS_9 = GCLK_CLKCTRL_ID_EVSYS_9_Val, GCLOCK_ID_EVSYS_10 = GCLK_CLKCTRL_ID_EVSYS_10_Val, GCLOCK_ID_EVSYS_11 = GCLK_CLKCTRL_ID_EVSYS_11_Val, GCLOCK_ID_SERCOMX_SLOW = GCLK_CLKCTRL_ID_SERCOMX_SLOW_Val, GCLOCK_ID_SERCOM0_CORE = GCLK_CLKCTRL_ID_SERCOM0_CORE_Val, GCLOCK_ID_SERCOM1_CORE = GCLK_CLKCTRL_ID_SERCOM1_CORE_Val, GCLOCK_ID_SERCOM2_CORE = GCLK_CLKCTRL_ID_SERCOM2_CORE_Val, GCLOCK_ID_SERCOM3_CORE = GCLK_CLKCTRL_ID_SERCOM3_CORE_Val, GCLOCK_ID_SERCOM4_CORE = GCLK_CLKCTRL_ID_SERCOM4_CORE_Val, GCLOCK_ID_SERCOM5_CORE = GCLK_CLKCTRL_ID_SERCOM5_CORE_Val, GCLOCK_ID_TCC0_TCC1 = GCLK_CLKCTRL_ID_TCC0_TCC1_Val, GCLOCK_ID_TCC2_TC3 = GCLK_CLKCTRL_ID_TCC2_TC3_Val, GCLOCK_ID_TC4_TC5 = GCLK_CLKCTRL_ID_TC4_TC5_Val, GCLOCK_ID_TC6_TC7 = GCLK_CLKCTRL_ID_TC6_TC7_Val, GCLOCK_ID_ADC = GCLK_CLKCTRL_ID_ADC_Val, GCLOCK_ID_AC_DIG = GCLK_CLKCTRL_ID_AC_DIG_Val, GCLOCK_ID_AC_ANA = GCLK_CLKCTRL_ID_AC_ANA_Val, GCLOCK_ID_DAC = GCLK_CLKCTRL_ID_DAC_Val, GCLOCK_ID_PTC = GCLK_CLKCTRL_ID_PTC_Val, GCLOCK_ID_I2S_0 = GCLK_CLKCTRL_ID_I2S_0_Val, GCLOCK_ID_I2S_1 = GCLK_CLKCTRL_ID_I2S_1_Val, } GCLOCK_ID; typedef enum { GCLOCK_GEN_GCLK0 = GCLK_CLKCTRL_GEN_GCLK0_Val, // 8 division factor bits - DIV[ 7:0] GCLOCK_GEN_GCLK1 = GCLK_CLKCTRL_GEN_GCLK1_Val, // 16 division factor bits - DIV[15:0] GCLOCK_GEN_GCLK2 = GCLK_CLKCTRL_GEN_GCLK2_Val, // 5 division factor bits - DIV[ 4:0] GCLOCK_GEN_GCLK3 = GCLK_CLKCTRL_GEN_GCLK3_Val, // 8 division factor bits - DIV[ 7:0] GCLOCK_GEN_GCLK4 = GCLK_CLKCTRL_GEN_GCLK4_Val, // 8 division factor bits - DIV[ 7:0] GCLOCK_GEN_GCLK5 = GCLK_CLKCTRL_GEN_GCLK5_Val, // 8 division factor bits - DIV[ 7:0] GCLOCK_GEN_GCLK6 = GCLK_CLKCTRL_GEN_GCLK6_Val, // 8 division factor bits - DIV[ 7:0] GCLOCK_GEN_GCLK7 = GCLK_CLKCTRL_GEN_GCLK7_Val, // 8 division factor bits - DIV[ 7:0] GCLOCK_GEN_NONE = GCLOCK_GEN_GCLK7 + 1, // // for Seeeduino XIAO (see. startup.c) // GCLOCK_GEN_MAIN = GCLK_CLKCTRL_GEN_GCLK0_Val, GCLOCK_GEN_XOSC32K = GCLK_CLKCTRL_GEN_GCLK1_Val, GCLOCK_GEN_OSCULP32K = GCLK_CLKCTRL_GEN_GCLK2_Val, GCLOCK_GEN_OSC8M = GCLK_CLKCTRL_GEN_GCLK3_Val, } GCLOCK_GEN; typedef enum { GCLOCK_SRC_XOSC = GCLK_GENCTRL_SRC_XOSC_Val, GCLOCK_SRC_GCLKIN = GCLK_GENCTRL_SRC_GCLKIN_Val, GCLOCK_SRC_GCLKGEN1 = GCLK_GENCTRL_SRC_GCLKGEN1_Val, // GCLOCK_GEN_XOSC32K-> GCLOCK_SRC_OSCULP32K = GCLK_GENCTRL_SRC_OSCULP32K_Val, // ->GCLOCK_GEN_OSCULP32K GCLOCK_SRC_OSC32K = GCLK_GENCTRL_SRC_OSC32K_Val, GCLOCK_SRC_XOSC32K = GCLK_GENCTRL_SRC_XOSC32K_Val, // ->GCLOCK_GEN_XOSC32K GCLOCK_SRC_OSC8M = GCLK_GENCTRL_SRC_OSC8M_Val, // ->GCLOCK_GEN_OSC8M GCLOCK_SRC_DFLL48M = GCLK_GENCTRL_SRC_DFLL48M_Val, // ->GCLOCK_GEN_MAIN GCLOCK_SRC_FDPLL = GCLK_GENCTRL_SRC_FDPLL_Val, GCLOCK_SRC_NONE = GCLOCK_SRC_FDPLL + 1, } GCLOCK_SRC; class GClock { public: static void begin(void); static void select(GCLOCK_ID id, GCLOCK_GEN gen); static void control(GCLOCK_ID id, bool enable); static GCLOCK_GEN config(GCLOCK_SRC src, uint8_t divider = 0, bool divsel = false, bool idc = false, GCLOCK_GEN reqid = GCLOCK_GEN_NONE); static bool output(uint32_t pin, GCLOCK_SRC src, uint8_t divider = 0, bool divsel = false, bool idc = false, bool runstdby = false, bool oov = false); }; #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 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 |
/* gclock.cpp - GCLK 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 "gclock.h" #include "IOBUS.h" #define DIVIDER(sel, div) (uint8_t)((sel) ? 1 << ((div) + 1) : ((div) == 0 ? 1 : (div))) #define NONE 0xFF // for SAMD21GJ static const uint8_t GCLK_IO[] = { NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, 0x04, 0x05, NONE, NONE, 0x00, 0x01, 0x02, 0x03, NONE, NONE, 0x04, 0x05, 0x06, 0x07, NONE, NONE, NONE, 0x00, 0x00, NONE, 0x00, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, NONE, NONE, NONE, NONE, 0x00, 0x01, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, }; static GCLOCK_GEN _gen[GCLOCK_ID_I2S_1 - GCLOCK_ID_DFLL48 + 1]; static void sync(void) { while (GCLK->STATUS.bit.SYNCBUSY) continue; } void GClock::begin(void) { for (uint32_t i = 0; i < sizeof(_gen) / sizeof(_gen[0]); ++i) _gen[i] = GCLOCK_GEN_GCLK0; select(GCLOCK_ID_WDT, GCLOCK_GEN_OSCULP32K); select(GCLOCK_ID_RTC, GCLOCK_GEN_XOSC32K ); } void GClock::select(GCLOCK_ID id, GCLOCK_GEN gen) { _gen[id] = gen; } void GClock::control(GCLOCK_ID id, bool enable) { GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(id) | GCLK_CLKCTRL_GEN(_gen[id]) | (enable ? GCLK_CLKCTRL_CLKEN : 0); sync(); } GCLOCK_GEN GClock::config(GCLOCK_SRC src, uint8_t divider, bool divsel, bool idc, GCLOCK_GEN reqid) { GCLOCK_GEN id = GCLOCK_GEN_NONE; for (uint32_t i = GCLOCK_GEN_GCLK0; i <= GCLOCK_GEN_GCLK7; ++i) { if ((reqid != GCLOCK_GEN_NONE) && (reqid != (GCLOCK_GEN)i)) continue; *(volatile uint8_t *)&GCLK->GENCTRL.reg = i; if (GCLK->GENCTRL.bit.GENEN) { if (GCLK->GENCTRL.bit.SRC == src) { *(volatile uint8_t *)&GCLK->GENDIV.reg = i; if (DIVIDER(GCLK->GENCTRL.bit.DIVSEL, GCLK->GENDIV.bit.DIV) == DIVIDER(divsel, divider)) { if (idc) { GCLK->GENCTRL.bit.IDC = 1; sync(); } return (GCLOCK_GEN)i; } } } else if (id == GCLOCK_GEN_NONE) id = (GCLOCK_GEN)i; } if (id != GCLOCK_GEN_NONE) { GCLK->GENDIV.reg = GCLK_GENDIV_ID(id) | GCLK_GENDIV_DIV(divider); sync(); GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(id) | GCLK_GENCTRL_SRC(src) | GCLK_GENCTRL_GENEN | (idc ? GCLK_GENCTRL_IDC : 0) | (divsel ? GCLK_GENCTRL_DIVSEL : 0); sync(); } return id; } bool GClock::output(uint32_t pin, GCLOCK_SRC src, uint8_t divider, bool divsel, bool idc, bool runstdby, bool oov) { GCLOCK_GEN id = (GCLOCK_GEN)(pin < sizeof(GCLK_IO) ? GCLK_IO[pin] : NONE); if (id == NONE) return false; if (src == GCLOCK_SRC_NONE) { IOBUS::pinMode(pin, DISABLE); *(volatile uint8_t *)&GCLK->GENCTRL.reg = id; GCLK->GENCTRL.bit.OE = 0; sync(); } else { if ((id = config(src, divider, divsel, idc, id)) == GCLOCK_GEN_NONE) return false; *(volatile uint8_t *)&GCLK->GENCTRL.reg = id; GCLK->GENCTRL.reg |= GCLK_GENCTRL_OE | (oov ? GCLK_GENCTRL_OOV : 0) | (runstdby ? GCLK_GENCTRL_RUNSTDBY : 0); sync(); IOBUS::pinMode(pin, OUTPUT, true); IOBUS::multiplexing(pin, IOBUS_PMUX_H); } return true; } |