最近、毎日何か作ってるような気もするが今度はRTCライブラリを作って見た。モード2専用ではあるがモード0でもほぼ同じことができそう。でも、毎分とか毎時とか毎日とか毎月とか毎年とかのカレンダーでアラーム設定できるのはモード2だけだ。
RTCの年の値は0-63であるため、基準年を1900年にすると1963年までしか対応できないので基準年を2000年とした。これで2000-2063年まで対応できる。
C言語ランタイムのtime_t型は64-bit型になっているようなので2038年問題はないが2063年問題は残ることになる。が、若い人は別にしてあと43年も対応できれば十分だろう。
ちなみにデバッグ中にクロック・レジスタのリードに毎回1ms前後の時間がかかることを発見。それを解決するのに少々手間取ってしまった。リード同期がとれていない状態でクロック・レジスタをリードしてしまうとリード同期がとれるまでCPUが止まってしまうらしい。リード・リクエスト(RREQ)とリード・コンティニュー(RCONT)を同時設定することでリード同期が完璧に決まる。一度、同期が取れればCPUが止まることはないようだ。
【概要】
1 |
bool begin(bool clearOnMatch = false) |
ライブラリを初期化する。
1 |
void end(void) |
ライブラリを終了する。
1 |
void interrupt(RTCLOCK_CALLBACK cb) |
割り込みを設定する。
1 |
EVENTSYS_GEN eventOutput(RTCLOCK_EVOSEL evsel, bool enable) |
イベント出力の設定をする。
1 |
void frequencyCorrection(int8_t value) |
RTCクロック(1.024KHz)の999424クロック毎の補正量を符号付で指定する。
1 |
void debugControl(bool dbgrun) |
デバッグ中の動作を設定する。
1 |
time_t time(void) |
時刻を取得する。C言語のtime()と互換で1900/1/1からの秒数を返す。
1 |
void update(time_t time) |
時刻を設定する。
1 |
void alarm(RTCLOCK_ALARM mask, time_t time = 0) |
アラームを設定する。
【サンプルスケッチ】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#include “rtclock.h” void setup() { struct tm now; now.tm_sec = 0; now.tm_min = 0; now.tm_hour = 0; now.tm_mday = 1; now.tm_mon = 1 - 1; now.tm_year = 2020 - 1900; RTClock::begin(); RTClock::update(mktime(&now)); } void loop() { RTClock::time(); } |
【修正】
2020-05-24
evetsys.hのインクルードとRTCLOCK_EVOSEL型を追加。eventOutput()を仕様変更しEVENTSYS_GEN型を返すように変更。
【ライブラリ】
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 |
/* rtclock.h - Real Time Clock 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 __RTCLOCK_H #define __RTCLOCK_H #include <stdint.h> #include <stdbool.h> #include <string.h> #include <time.h> #include "gclock.h" #include "eventsys.h" typedef enum { RTCLOCK_EVOSEL_PER_0, RTCLOCK_EVOSEL_PER_1, RTCLOCK_EVOSEL_PER_2, RTCLOCK_EVOSEL_PER_3, RTCLOCK_EVOSEL_PER_4, RTCLOCK_EVOSEL_PER_5, RTCLOCK_EVOSEL_PER_6, RTCLOCK_EVOSEL_PER_7, RTCLOCK_EVOSEL_OVF, RTCLOCK_EVOSEL_ALARM, } RTCLOCK_EVOSEL; typedef enum { RTCLOCK_ALARM_DISABLE = 0x0, // Alarm Disabled RTCLOCK_ALARM_SS = 0x1, // Match seconds only RTCLOCK_ALARM_MMSS = 0x2, // Match seconds and minutes only RTCLOCK_ALARM_HHMMSS = 0x3, // Match seconds, minutes, and hours only RTCLOCK_ALARM_DDHHMMSS = 0x4, // Match seconds, minutes, hours, and days only RTCLOCK_ALARM_MMDDHHMMSS = 0x5, // Match seconds, minutes, hours, days, and months only RTCLOCK_ALARM_YYMMDDHHMMSS = 0x6, // Match seconds, minutes, hours, days, months, and years } RTCLOCK_ALARM; typedef enum { RTCLOCK_INT_ALARM, RTCLOCK_INT_OVF, } RTCLOCK_INT; typedef void (*RTCLOCK_CALLBACK)(RTCLOCK_INT); class RTClock { private: static void setCallback(RTCLOCK_CALLBACK cb); static void sync(void) { while (RTC->MODE2.CTRL.bit.SWRST || RTC->MODE2.STATUS.bit.SYNCBUSY) continue; } static bool enableGCLK(void) { // GCLK = 32.768KHz / (1 << (4 + 1)) = 1.024KHz GCLOCK_GEN gen = GClock::config(GCLOCK_SRC_XOSC32K, 4, true); if (gen == GCLOCK_GEN_NONE) return false; GClock::select(GCLOCK_ID_RTC, gen); GClock::control(GCLOCK_ID_RTC, true); return true; } public: static bool begin(bool clearOnMatch = false) { if (!enableGCLK()) return false; RTC->MODE2.CTRL.reg = RTC_MODE2_CTRL_SWRST; sync(); RTC->MODE2.CTRL.reg = RTC_MODE2_CTRL_ENABLE | RTC_MODE2_CTRL_MODE_CLOCK | (clearOnMatch ? RTC_MODE2_CTRL_MATCHCLR : 0) | RTC_MODE2_CTRL_PRESCALER_DIV1024; sync(); NVIC_EnableIRQ(RTC_IRQn); NVIC_SetPriority(RTC_IRQn, 0); RTC->MODE2.READREQ.reg = RTC_READREQ_RREQ | RTC_READREQ_RCONT; sync(); return true; } static void end(void) { if (RTC->MODE2.CTRL.bit.ENABLE) { NVIC_DisableIRQ(RTC_IRQn); RTC->MODE2.CTRL.reg = RTC_MODE0_CTRL_SWRST; sync(); GClock::control(GCLOCK_ID_RTC, false); } } static void interrupt(RTCLOCK_CALLBACK cb) { setCallback(cb); if (cb) RTC->MODE2.INTENSET.reg = RTC_MODE2_INTENSET_ALARM0 | RTC_MODE2_INTENSET_OVF; else RTC->MODE2.INTENCLR.reg = RTC_MODE2_INTENCLR_ALARM0 | RTC_MODE2_INTENCLR_OVF; } static EVENTSYS_GEN eventOutput(RTCLOCK_EVOSEL evsel, bool enable) { switch (evsel) { case RTCLOCK_EVOSEL_PER_0: RTC->MODE2.EVCTRL.bit.PEREO0 = enable; return EVENTSYS_GEN_RTC_PER_0; case RTCLOCK_EVOSEL_PER_1: RTC->MODE2.EVCTRL.bit.PEREO1 = enable; return EVENTSYS_GEN_RTC_PER_1; case RTCLOCK_EVOSEL_PER_2: RTC->MODE2.EVCTRL.bit.PEREO2 = enable; return EVENTSYS_GEN_RTC_PER_2; case RTCLOCK_EVOSEL_PER_3: RTC->MODE2.EVCTRL.bit.PEREO3 = enable; return EVENTSYS_GEN_RTC_PER_3; case RTCLOCK_EVOSEL_PER_4: RTC->MODE2.EVCTRL.bit.PEREO4 = enable; return EVENTSYS_GEN_RTC_PER_4; case RTCLOCK_EVOSEL_PER_5: RTC->MODE2.EVCTRL.bit.PEREO5 = enable; return EVENTSYS_GEN_RTC_PER_5; case RTCLOCK_EVOSEL_PER_6: RTC->MODE2.EVCTRL.bit.PEREO6 = enable; return EVENTSYS_GEN_RTC_PER_6; case RTCLOCK_EVOSEL_PER_7: RTC->MODE2.EVCTRL.bit.PEREO7 = enable; return EVENTSYS_GEN_RTC_PER_7; case RTCLOCK_EVOSEL_OVF : RTC->MODE2.EVCTRL.bit.OVFEO = enable; return EVENTSYS_GEN_RTC_OVF; case RTCLOCK_EVOSEL_ALARM: RTC->MODE2.EVCTRL.bit.ALARMEO0 = enable; return EVENTSYS_GEN_RTC_CMP_0; } return EVENTSYS_GEN_NONE; } static void debugControl(bool dbgrun) { RTC->MODE2.DBGCTRL.reg = (dbgrun ? RTC_DBGCTRL_DBGRUN : 0); } static void frequencyCorrection(int8_t value) { RTC->MODE2.FREQCORR.reg = value; sync(); } static time_t time(void) { RTC_MODE2_CLOCK_Type c; struct tm t; memset(&t, 0, sizeof(t)); c.reg = RTC->MODE2.CLOCK.reg; t.tm_sec = c.bit.SECOND; t.tm_min = c.bit.MINUTE; t.tm_hour = c.bit.HOUR; t.tm_mday = c.bit.DAY; t.tm_mon = c.bit.MONTH - 1; t.tm_year = c.bit.YEAR + 100; return mktime(&t); } static void update(time_t time) { RTC_MODE2_CLOCK_Type c; struct tm t; localtime_r(&time, &t); c.bit.SECOND = t.tm_sec; c.bit.MINUTE = t.tm_min; c.bit.HOUR = t.tm_hour; c.bit.DAY = t.tm_mday; c.bit.MONTH = t.tm_mon + 1; c.bit.YEAR = t.tm_year % 100; RTC->MODE2.CLOCK.reg = c.reg; sync(); RTC->MODE2.READREQ.reg = RTC_READREQ_RREQ | RTC_READREQ_RCONT; sync(); } static void alarm(RTCLOCK_ALARM mask, time_t time = 0) { if (mask != RTCLOCK_ALARM_DISABLE) { RTC_MODE2_ALARM_Type c; struct tm t; localtime_r(&time, &t); c.bit.SECOND = t.tm_sec; c.bit.MINUTE = t.tm_min; c.bit.HOUR = t.tm_hour; c.bit.DAY = t.tm_mday; c.bit.MONTH = t.tm_mon + 1; c.bit.YEAR = t.tm_year % 100; RTC->MODE2.Mode2Alarm[0].ALARM.reg = c.reg; sync(); } RTC->MODE2.Mode2Alarm[0].MASK.reg = RTC_MODE2_MASK_SEL(mask); sync(); } }; #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 |
/* rtclock.cpp - Real Time Clock 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 "rtclock.h" static RTCLOCK_CALLBACK _callback; void RTC_Handler(void) { if (RTC->MODE2.INTFLAG.bit.ALARM0) { RTC->MODE2.INTFLAG.bit.ALARM0 = 1; if (_callback) _callback(RTCLOCK_INT_ALARM); } if (RTC->MODE2.INTFLAG.bit.OVF) { RTC->MODE2.INTFLAG.bit.OVF = 1; if (_callback) _callback(RTCLOCK_INT_OVF); } } void RTClock::setCallback(RTCLOCK_CALLBACK cb) { _callback = cb; } |
【参照ライブラリ】
Seeeduino XIAO (ATSAMD21G18) のGCLKライブラリ (gclock.h/gclock.cpp)
Seeeduino XIAO (ATSAMD21G18) のEVSYSライブラリ (eventsys.h/eventsys.cpp)