【タイマー管理クラス】
このタイマー管理クラスはタイマー0の比較一致割り込みにより動作する。
arduinoなどではタイマー0のOC0A/OC0B出力ピンをPWMとして使用できるが、PWMとして使うとタイマー0の設定が書き換わり、このタイマー管理クラスが誤動作するので注意してほしい。
代表的なボードのPWMとして使ってはいけないピンは次の通り。
1 2 |
UNO(ATmega328) ... D5, D6 LEONARDO(ATmega32u4) ... D3, D11 |
正確なシステム時刻を管理するため比較一致割り込みの一つを使い独自にシステム時刻カウンターを実装し、もう一つの比較一致割り込みはスリープやタイムアウトを管理するためのタイマーとして使用している。また、システム時刻の取得は割り込み処理内などの割り込み禁止状態からの呼び出しでも問題ないよう工夫している。
【概要】
1 |
static void begin(void); |
※タイマー初期設定。内部用なので呼び出してはいけない。
1 |
static uint32_t micros(void); |
arduino互換。システム起動後の経過時間をマイクロ秒で返す。
1 |
static uint32_t millis(void); |
arduino互換。システム起動後の経過時間をミリ秒で返す。
1 |
static void delay(int32_t ms); |
arduino互換。指定ミリ秒の間、スリープする。
1 |
static void delayMicroseconds(int32_t us); |
arduino互換。指定マイクロ秒の間、スリープする。
【ソース・コード】
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 |
/* timer.h - Timer Function Class Copyright (c) 2021 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __TIMER_H #define __TIMER_H #include <stdint.h> #include <stdbool.h> #define WAIT_TIMEOUT 0xFF #define WAIT_OBJECT 0 #define WAIT_INFINITE -1 class Timer : protected Task { private: static void timer0_compb_vect(void); static uint32_t clock(bool us); static void init(int32_t count); static void restart(uint32_t time); static bool wakeup_match(TASK_T *task, uint32_t time); static bool insert_match(TASK_T *task, uint32_t time); friend void timer0_compb_vect(void); protected: static void setup(TASK_T *task, int32_t timeout, Timer *wait); static void cansel(TASK_T *task); virtual void timeout(TASK_T *task) = 0; public: static void begin(void); static uint32_t micros(void); static uint32_t millis(void); static void delay(int32_t ms); static void delayMicroseconds(int32_t us); }; #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 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 211 212 213 214 215 216 |
/* timer.cpp - Timer Function Class Copyright (c) 2021 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <stdint.h> #include <stdbool.h> #include <avr/io.h> #include <util/delay.h> #include <util/atomic.h> #include "avros.h" #define TIMER_PRESCALER 64 #define TIMER_MICROS(ps) ((ps) * 1000000 / F_CPU) #define TIMER_COUNT(us) ((us) / TIMER_MICROS(TIMER_PRESCALER)) #define TIMER_MILLIS TIMER_COUNT(1000) #define TIMER_WAIT_MIN (uint8_t)(120 * F_CPU / 16000000) // minimum limit #define TIMER_WAIT_PTC (uint8_t)( 78 * F_CPU / 16000000) // processing time correction #if defined(TIMSK) #define TIMSK0 TIMSK #endif #if defined(TIFR) #define TIFR0 TIFR #endif static volatile uint32_t _micros; static volatile uint32_t _millis; static uint32_t _ovfcnt; static SEQUE(TASK_T, timer) _queue; #define TIMER_ISR_CLOCK() \ do \ { \ OCR0A += TIMER_MILLIS; \ _micros += 1000; \ _millis += 1; \ } \ while (0) ISR(TIMER0_COMPA_vect) { TIMER_ISR_CLOCK(); } uint32_t Timer::clock(bool us) { uint32_t m; uint8_t c, t; do { // nointerrupts ? if (TIFR0 & _BV(OCF0A)) { TIFR0 |= _BV(OCF0A); TIMER_ISR_CLOCK(); } // read counter t = OCR0A; c = TCNT0; m = us ? _micros : _millis; } while ((t != OCR0A) || (TIFR0 & _BV(OCF0A))); return us ? m + ((uint8_t)(TIMER_MILLIS - 1) - (uint8_t)(t - c)) * TIMER_MICROS(TIMER_PRESCALER) : m; } void timer0_compb_vect(void) { Timer::timer0_compb_vect(); } ISR(TIMER0_COMPB_vect) { timer0_compb_vect(); } bool Timer::wakeup_match(TASK_T *task, uint32_t time) { time = task->x.time - time; if ((int32_t)time > 0) { init(TIMER_COUNT(time)); return false; } if (task->wait) task->wait->timeout(task); task->wcode = WAIT_TIMEOUT; Task::enqueue(task); return true; } void Timer::timer0_compb_vect(void) { if (_ovfcnt > 2) _ovfcnt -= 2; else if (--_ovfcnt) OCR0B += 0x80; else { if (_queue.removeUntil<uint32_t>(wakeup_match, micros())) TIMSK0 &= ~_BV(OCIE0B); Task::switching(); } } void Timer::init(int32_t count) { if (--count < 2) count = 2; _ovfcnt = ((count >> 8) << 1) | ((uint8_t)count & 0x80 ? 1 : 0); if (_ovfcnt) OCR0B = TCNT0 + ((uint8_t)count | 0x80); else { *((uint8_t *)&_ovfcnt) = 1; OCR0B = TCNT0 + (uint8_t)count; } } void Timer::restart(uint32_t time) { init(TIMER_COUNT((int32_t)(time - micros()))); TIFR0 |= _BV(OCF0B); TIMSK0 |= _BV(OCIE0B); } bool Timer::insert_match(TASK_T *task, uint32_t time) { return (int32_t)(task->x.time - time) > 0; } void Timer::setup(TASK_T *task, int32_t timeout, Timer *wait) { if (timeout <= 0) task->wait = nullptr; else { task->wait = wait; task->x.time = micros() + timeout - TIMER_WAIT_PTC; if (_queue.insert<uint32_t>(task, insert_match, task->x.time) == QUEUE_STATUS_FIRST) restart(task->x.time); } } void Timer::cansel(TASK_T *task) { if (task->wait) { task->wait = nullptr; if (_queue.remove(task) == QUEUE_STATUS_FIRST) { TASK_T *task = _queue.head(); if (task) restart(task->x.time); } } } void Timer::begin(void) { TCCR0A = 0; // standard mode. (not pwm mode) OCR0A = TCNT0 + TIMER_MILLIS - 1; TIFR0 |= _BV(OCF0A); TIMSK0 |= _BV(OCIE0A); // start timer. if not started. if ((TCCR0B & (_BV(CS02) | _BV(CS01) | _BV(CS00))) == 0) TCCR0B = _BV(CS01) | _BV(CS00); // 1/64 } uint32_t Timer::micros(void) { return clock(true); } uint32_t Timer::millis(void) { return clock(false); } void Timer::delay(int32_t ms) { delayMicroseconds(ms * 1000); } void Timer::delayMicroseconds(int32_t us) { if (us >= TIMER_WAIT_MIN) { ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { TASK_T *task = Task::dequeue(); if (task) { setup(task, us, nullptr); Task::switching(); } } } else if (us > 0) _delay_loop_2(us * (F_CPU / 4000000)); } |
【関連する投稿】
理想のRTOSを自作する (1)
理想のRTOSを自作する (2)
理想のRTOSを自作する (3)
理想のRTOSを自作する (4)
理想のRTOSを自作する (5)
理想のRTOSを自作する (6)
理想のRTOSを自作する (7)
理想のRTOSを自作する (8)
理想のRTOSを自作する (9)
理想のRTOSを自作する (10)
理想のRTOSを自作する (11)