マイコンで何かやろうと思ったら、やっぱり
どうしても使いたくなるのは正確な時間制御。
timer機能ですね。
MEGA168にはtimerが0〜2の計3つ登載されてます。
0と2が8ビットタイマー。1が16ビットタイマー。
使い方も機能もそれぞれ大体同じなんですが、
timer1にはキャプチャー機能が、timer2には
外部パルスクロック入力機能があるという違い
はあります。
まぁ、MEGA168ならどのtimerでもノーマルモード、
CTCモードもあるし、タイマーカウントの結果を
ピン出力するいわゆるPWMも各timerで使用する
ことが出来ます。
とはいえ、arduinoなのでハードウェアの隅から
隅まで使い尽くすということより、簡単な
スケッチで動くということが重要なので、
それらの機能を全部使える必要はないでしょう。
標準の組み込みライブラリにはtimer割り込み関係の
モノはありませんでした。(PWMだけみたい)
というわけで、まずは先人のスケッチを
探してみます。ありました。↓これ。
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1167000525
Q&Aの途中のモノのようです。思ったように
動いていないようです。
ソースが古いのかarduinoIDEがバージョンアップ
してしまったのかわかりませんが、コンパイルすると
内部レジスターファイルの名称などが未定義と
出ちゃうので、山根さんの本を参考に動作する
ように修正してみました。
(後日訂正:コンパイルが通らなかったのは、多分
mega8用のスケッチだからだと思われます)
↓こんな感じです。
#include <avr/interrupt.h>
#include <avr/io.h>
#define INIT_TIMER_COUNT 6
#define RESET_TIMER2 TCNT2 = INIT_TIMER_COUNT
int ledPin = 3; //digital3 (PD3)
int swPin = 9; //digital9 (PB1)
int int_counter = 0;
volatile int second = 0;
int oldSecond = 0;
long starttime = 0;
// Aruino runs at 16 Mhz, so we have 1000 Overflows per second...
// 1/ ((16000000 / 64) / 256) = 1 / 1000
ISR(TIMER2_OVF_vect) {
RESET_TIMER2;
int_counter += 1;
if (int_counter == 1000) {
second+=1;
int_counter = 0;
}
};
void setup() {
pinMode(ledPin, OUTPUT); // set the digital pin as output
pinMode(swPin, INPUT); // set the digital pin as input
digitalWrite(swPin, HIGH); //set swPin internal pull-up
Serial.begin(9600);
Serial.println("Initializing timerinterrupt");
//Timer2 Settings: Timer Prescaler /64,
TCCR2B |= (1<<CS22); // turn on CS22 bit
TCCR2B &= ~((1<<CS21) | (1<<CS20)); // turn off CS21 and CS20 bits
// Use normal mode
TCCR2A &= ~((1<<WGM21) | (1<<WGM20)); // turn off WGM21 and WGM20 bits
// Use internal clock - external clock not used in Arduino
ASSR |= (0<<AS2);
TIMSK2 |= (1<<TOIE2) | (0<<OCIE2A); //Timer2 Overflow Interrupt Enable
RESET_TIMER2;
sei();
starttime = millis();
}
void loop() {
if (oldSecond != second) {
Serial.print(second);
Serial.print(". ->");
Serial.print(millis() - starttime);
Serial.println(".");
digitalWrite(ledPin, LOW);
delay(100);
digitalWrite(ledPin, HIGH);
oldSecond = second;
}
if (digitalRead(swPin) == LOW){
second = 0;
}
}
(後日訂正:#include文やビットシフト演算子の
不等号記号は本来半角なのですが、半角不等号は
htmlタグに認識されるのか、削除されてしまう
ようなので、全角に置き換えてます。半角に
読み直してください)
1秒ごとにLEDが光って、シリアル経由で
PCに秒数を表示します。
うーん、arduinoっぽく無いなぁ。
殆どgccのプログラムそのままですわ。
内部レジスタファイルをビット単位で操作
しているので、gccのプログラムと
変わりありません…
(それに、元からの間違えが数箇所残ったまま
です。ゼロと論理和をとっても何も変わらない
はずだよなぁ…ASSR |= (0<<AS2);とか。
普通なら、AS2のビットを0にするなら
論理積取らなきゃでしょう。まぁ、ひとまず
放って置きます)
ちなみに、出力用LEDはreduino-nano用に
合わせてデジタル3番にしています。適宜
13番に修正して使ってください。
あと、sw3(PB1ピン)のボタンを有効に
してあり、押すと秒数が0にクリアされます。
PB1の内部プルアップを有効にしています。
ボタン押下でLOW入力です。
なおreduino-nanoをお使いの方へ。sw3は
↓この写真でいう緑のボタンです。
(黄色はリセットなので、実験中は押し間違え
しないように…)
arduinoなのに、こんなにゴチャゴチャした
プログラムってねぇ…スケッチじゃないよ…。
と思っていたら、arduinoのサイトのlibraryの
ページ↓
http://www.arduino.cc/en/Reference/Libraries
に「MsTimer2」というライブラリが公開されて
いました。
このライブラリはOfficial Librariesではなく
Contributed Librariesなんですが、簡単に
使えるので便利、便利。
このライブラリを使ってみた結果と、そもそも
なぜみなさんtimer2を好んで使っているのかに
ついても調べてみました。
調べたら、それなりに理由があってtimer2のようです。
ある程度納得の行く理由が見つかりました。
この辺のことについてはまた後で書きます。

2