以前からArduinoのタイマー割り込み処理ってなぜこんな複雑なんだろうと思っていた。
調べてみるとタイマーは高速PWMモードで16MHzの場合で1024usという中途半端な時間で割り込みが発生するようになっている。
millis()の戻り値はミリ秒であるため補正するために約42ms毎に1ms補正しているようだ。ということは補正時にmillis()の戻り値が1飛ぶことになる...millis()の戻り値が1msずつ変化すること期待しているアプリにとっては想定外のことが起こる可能がありそうだ。ちなみに8MHzでは2msずつ変化し補正時のみ4ms変化することになるようだ。CTCモードなら補正いらないのにな...こんなんでいいのか?
micros()は補正なしのオーバーフローカウンター値から計算して求めているので問題ないが当然ながら補正しているmillis()と補正していないmicros()では時間の進み方が異なってしまう。
[2020-02-26]
再度確認してみたところmicros()が補正してないというのは私の勘違いでした。すいません...
【millis()の戻り値が飛んでしまうことを確認するプログラム】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
void millis_travel(void) { unsigned long m, _millis[50]; _millis[0] = millis(); for (uint8_t i = 0; i < sizeof(_millis) / sizeof(_millis[0]); ) { m = millis(); if (_millis[i] != m) _millis[++i] = m; } for (uint8_t i = 0; i < sizeof(_millis) / sizeof(_millis[0]); ++i) { if (i && (m + 1 != _millis[i])) Serial.print("[travel]--> "); Serial.println(m = _millis[i]); } while (1) continue; } |
【millis()の戻り値が飛んでしまうことを確認するプログラムの出力】
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 |
1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 [travel]--> 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 |
【参考: wiring.cのタイマー割り込み処理】
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 |
// the prescaler is set so that timer0 ticks every 64 clock cycles, and the // the overflow handler is called every 256 ticks. #define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(64 * 256)) // the whole number of milliseconds per timer0 overflow #define MILLIS_INC (MICROSECONDS_PER_TIMER0_OVERFLOW / 1000) // the fractional number of milliseconds per timer0 overflow. we shift right // by three to fit these numbers into a byte. (for the clock speeds we care // about - 8 and 16 MHz - this doesn't lose precision.) #define FRACT_INC ((MICROSECONDS_PER_TIMER0_OVERFLOW % 1000) >> 3) #define FRACT_MAX (1000 >> 3) volatile unsigned long timer0_overflow_count = 0; volatile unsigned long timer0_millis = 0; static unsigned char timer0_fract = 0; #if defined(TIM0_OVF_vect) ISR(TIM0_OVF_vect) #else ISR(TIMER0_OVF_vect) #endif { // copy these to local variables so they can be stored in registers // (volatile variables must be read from memory on every access) unsigned long m = timer0_millis; unsigned char f = timer0_fract; m += MILLIS_INC; f += FRACT_INC; if (f >= FRACT_MAX) { f -= FRACT_MAX; m += 1; } timer0_fract = f; timer0_millis = m; timer0_overflow_count++; } |