|
○ はじめに
ATmega8535 が備えているタイマ/カウンタについては、LECTURE 6 タイマ/カウンタについて にて簡単に説明してみました。ここでは、そのうちのタイマ/カウンタ0を使って LED を点滅させてみたいと思います。
○ 関連レジスタ
タイマ/カウンタ0関連のレジスタを以下に紹介します。マクロについては、avr-libc で使用できる定義名を掲載しています。また、日本語のレジスタ名とビット名は、英語の定義名称を適当に訳してます。
- タイマ/カウンタ0制御レジスタ(TCCR0)
ビット番号 7 6 5 4 3 2 1 0 マクロ FOC0 WGM00 COM01 COM00 WGM01 CS02 CS01 CS00 初期値 0 0 0 0 0 0 0 0 各ビットの説明を以下に書きます。
ビット名 内容 FOC0(OC0強制出力) 動作種別が標準動作および比較一致タイマ/カウンタクリアの場合に、1 を設定することで強制的に比較一致を発生させます。割り込みやタイマ/カウンタのクリアは行われず、OC0 ピンの出力のみ変更になる様です。自分は使い所がよく分りませんが… WGM00(波形生成モード) 後述の WGM01 と併せて動作種別(LECTURE 6 タイマ/カウンタについてを参照)を設定します。 WGM01 WGM00 動作種別 0 0 標準動作 0 1 位相基準PWM 1 0 比較一致タイマ/カウンタクリア 1 1 高速PWM COM01/COM00(比較0出力モード) OC0 ピン(PDIP パッケージでは 4 ピン)の出力方式を設定します。 - WGM00 = 0 の場合(標準動作、CTC動作)
COM01 COM00 内容 0 0 OC0 には出力されない 0 1 比較一致において反転 1 0 比較一致において 0(Low) 1 1 比較一致において 1(High)
- WGM00 = 1、WGM01 = 1 の場合(高速PWM)
COM01 COM00 内容 0 0 OC0 には出力されない 0 1 予約 1 0 比較一致で 0(Low)、TCNT0 = 0 で 1(High) 1 1 比較一致で 1(High)、TCNT0 = 0 で 0(Low)
- WGM00 = 1、WGM01 = 0 の場合(位相基準PWM)
COM01 COM00 内容 0 0 OC0 には出力されない 0 1 予約 1 0 TCNT0 上昇時の比較一致で 0(Low)、下降時の比較一致で 1(High) 1 1 TCNT0 上昇時の比較一致で 1(High)、下降時の比較一致で 0(Low)
WGM01(波形生成モード) WGM00 の説明を参照 CS02/CS01/CS00(クロック選択) タイマ/カウンタ0で使用するクロックのシステムクロックに対する分周比、あるいは外部クロックの使用を以下の組み合わせで設定します。 CS02 CS01 CS00 内容 0 0 0 タイマ/カウンタ0停止 0 0 1 分周比無し 0 1 0 CK/8 0 1 1 CK/64 1 0 0 CK/256 1 0 1 CK/1024 1 1 0 外部クロック。T0 ピン(PDIP パッケージでは 1 ピン)の立ち下がりエッジ 1 1 1 外部クロック。T0 ピン(PDIP パッケージでは 1 ピン)の立ち上がりエッジ
- WGM00 = 0 の場合(標準動作、CTC動作)
- タイマ/カウンタ0カウンタ(TCNT0)
8ビットのカウンタ値を格納するレジスタです。
ビット番号 7 6 5 4 3 2 1 0 マクロ - - - - - - - - 初期値 0 0 0 0 0 0 0 0
- タイマ/カウンタ0比較レジスタ(OCR0)
TCNT0 の値との比較に使われる 8 ビットレジスタです。
ビット番号 7 6 5 4 3 2 1 0 マクロ - - - - - - - - 初期値 0 0 0 0 0 0 0 0
- タイマ/カウンタ割り込みマスクレジスタ(TIMSK)(タイマ/カウンタ間共通)※タイマ/カウンタ0に関係有るのは OCIE0 / TOIE0 です。
ビット番号 7 6 5 4 3 2 1 0 マクロ OCIE2 TOIE2 TICIE1 OCIE1A OCIE1B TOIE1 OCIE0 TOIE0 初期値 0 0 0 0 0 0 0 0 各ビットの説明を以下に書きます。
ビット名 内容 OCIE0(比較一致フラグ割り込み許可) 1 を設定すると、比較一致割り込みが有効になります。 TOIE0(オーバーフロー割り込み許可) 1 を設定すると、オーバーフロー割り込みが有効になります。
- タイマ/カウンタ割り込み要求フラグレジスタ(TIFR)(タイマ/カウンタ間共通)※タイマ/カウンタ0に関係有るのは OCF0 / TOV0 です。
ビット番号 7 6 5 4 3 2 1 0 マクロ OCF2 TOV2 ICF1 OCF1A OCF1B TOV1 OCF0 TOV0 初期値 0 0 0 0 0 0 0 0 各ビットの説明を以下に書きます。
ビット名 内容 OCF0(比較一致フラグ) TCNT0 と OCR0 の値が一致した場合に 1 が設定されます。割り込み処理時に自動的に 0 となる他、1 を書き込むことで 0 となります。 TOV0(オーバーフローフラグ) TCNT0 でのオーバーフロー発生時に 1 が設定されます。割り込み処理時に自動的に 0 となる他、1 を書き込むことで 0 となります。
○ 回路図
LECTURE 1 ATmega8535 および使用環境について に掲載してある全体回路図に対して、点滅させる LED 部分を赤枠で囲んだ図をこちらに置いておきます。…とは言っても、LECTURE 2 LED を点滅させる と全く同じなのでそちらの図へのリンクです。
○ サンプルプログラムと説明
タイマ/カウンタ0を使って LED を点滅させるサンプルプログラムを 07timer0.tar.gz からダウンロードできます。はじめに のページから、全てのサンプルプログラムを 固めた tar ファイルをダウンロードすることもできます。
中には
- Makefile
- util.h
- util.c
- timer0.h
- timer0.c
- main-ov.c
- main-comp.c
が格納されています。util.h/util.c については LECTURE 4 A/D 変換でボリューム(可変抵抗)値を読む の内容と同じで Makefile もほとんど変わらないので、それ以外について以下に載せます。なお、以下に掲載しているソースコードには修正 BSD ライセンス条項を含めていませんが、ソースコード配布は修正 BSD ライセンスに従います(はじめに のページの免責事項を参照してください)。
これまでにヒューズビットの設定を行ったことが一度も無い場合は、まず最初に make fuse を実行してください。fuse ターゲットについては、LECTURE 2 LED を点滅させる のサンプルプログラムの項を参照してください。
make 実行後、オーバーフロー割り込みを使用したプログラムをアップロードする場合は make uisp-ov を実行してください。また、比較一致割り込みを使用したプログラムをアップロードする場合は make uisp-comp を実行してください。実装方法が異なるだけで動作内容は同じです。
LED が約 500ms 毎に(1 秒間に 2 回程)ON/OFF を繰り返します。
こちらは LED 点滅の動画です(10秒。YouTube に接続します)。
- timer0.h
1 #ifndef ___TIMER0_H_INCLUDED_ 2 #define ___TIMER0_H_INCLUDED_ 3 4 #include <avr/io.h> 5 #include <inttypes.h> 6 7 8 /* タイマークロック選択用マクロ */ 9 #define TIMER0_STOP 0x00 10 #define TIMER0_CK (_BV(CS00)) 11 #define TIMER0_CK_8 (_BV(CS01)) 12 #define TIMER0_CK_64 (_BV(CS01) | _BV(CS00)) 13 #define TIMER0_CK_256 (_BV(CS02)) 14 #define TIMER0_CK_1024 (_BV(CS02) | _BV(CS00)) 15 16 /* 比較出力モード選択用マクロ */ 17 #define COM0_NONE 0x00 18 #define COM0_TOGGLE (_BV(COM00)) 19 #define COM0_HIGH_FIRST (_BV(COM01)) 20 #define COM0_LOW_FIRST (_BV(COM01) | _BV(COM00)) 21 22 /* 波形生成種別選択用マクロ */ 23 #define WGM0_NORMAL 0x00 24 #define WGM0_PWM (_BV(WGM00)) 25 #define WGM0_CTC (_BV(WGM01)) 26 #define WGM0_FAST_PWM (_BV(WGM01) | _BV(WGM00)) 27 28 #define timer0_ov_enable() { TIMSK |= _BV(TOIE0); } 29 #define timer0_ov_disable() { TIMSK &= ~_BV(TOIE0); } 30 #define timer0_comp_enable() { TIMSK |= _BV(OCIE0); } 31 #define timer0_comp_disable() { TIMSK &= ~_BV(OCIE0); } 32 #define timer0_clear_com_mode() { TCCR0 &= ~(_BV(COM01) | _BV(COM00)); } 33 #define timer0_clear_wave_mode() { TCCR0 &= ~(_BV(WGM01) | _BV(WGM00)); } 34 35 #define timer0_stop()\ 36 { TCCR0 &= ~(_BV(CS02) | _BV(CS01) | _BV(CS00)); } 37 38 #define timer0_set_com_mode(mode) {\ 39 timer0_clear_com_mode();\ 40 TCCR0 |= (mode) & (_BV(COM01) | _BV(COM00));\ 41 } 42 43 #define timer0_set_wave_mode(mode) {\ 44 timer0_clear_wave_mode();\ 45 TCCR0 |= (mode) & (_BV(WGM01) | _BV(WGM00));\ 46 } 47 48 #define timer0_start(clock) {\ 49 timer0_stop();\ 50 TCCR0 |= (clock) & (_BV(CS02) | _BV(CS01) | _BV(CS00));\ 51 } 52 53 /* 54 * TCNT0 に設定するカウンタ値を指定する。 55 */ 56 static inline void 57 timer0_set_count(uint8_t count) 58 { 59 TCNT0 = count; 60 } 61 62 63 /* 64 * OCR0 に設定するカウンタ値を指定する。 65 */ 66 static inline void 67 timer0_set_ocr0(uint8_t ocr0) 68 { 69 OCR0 = ocr0; 70 } 71 72 73 void timer0_ov_set_interrupt(void (*func)(void)); 74 void timer0_comp_set_interrupt(void (*func)(void)); 75 76 77 #endif 78 79 /* End of timer0.h */
- 8 〜 14 行目
タイマ/カウンタ0 用のクロック指定を楽にできる様にマクロを定義しています。TCCR0 レジスタの CS02-00 ビットに対応するビットを _BV() マクロで設定しています。本マクロは 後述の timer0_start() マクロ実行時に指定します。
- 16 〜 20 行目
比較出力モードを楽に指定できる様にマクロを定義しています。TCCR0 レジスタの COM01-00 ビットに対応するビットを _BV() マクロで設定しています。本マクロは後述の timer0_set_com_mode() マクロ実行時に指定します。
- 22 〜 26 行目
波形生成種別を楽に指定できる様にマクロを定義しています。TCCR0 レジスタの WGM01-00 ビットに対応するビットを _BV() マクロで設定しています。本マクロは後述の timer0_set_wave_mode() マクロ実行時に指定します。
- 28 〜 51 行目
記述が楽になる様に、あるいは分りやすくなる様にいくつかのマクロを定義しています。
timer0_ov_enable()/timer0_ov_disable() マクロは、TIMSK レジスタの TOIE0 ビットを操作することでオーバーフロー割り込みの有効化/無効化を行います。
timer0_comp_enable()/timer0_comp_disable() マクロは、TIMSK レジスタの OCIE0 ビットを操作することで比較一致割り込みの有効化/無効化を行います。
timer0_clear_com_mode() マクロは、TCCR0 レジスタの COM01-00 ビットをクリアします。
timer0_clear_wave_mode() マクロは、TCCR0 レジスタの WGM01-00 ビットをクリアします。
timer0_stop() マクロは、TCCR0 レジスタの CS02-00 ビットをクリア(クロック無し) することでタイマ/カウンタ0 を停止します。
timer0_set_com_mode() マクロは、timer0_clear_com_mode() で TCCR0 レジスタの COM01-00 ビットをクリアした後に、指定されたクロック設定を COM01-00 ビットに設定します。
timer0_set_wave_mode() マクロは、timer0_clear_wave_mode() で TCCR0 レジスタの WGM01-00 ビットをクリアした後に、指定された波形生成種別を WGM01-00 ビットに設定します。
timer0_start() マクロは、timer0_stop() でタイマ/カウンタ0 を停止した後に、 指定されたクロック設定を TCCR0 レジスタの CS02-00 ビットに設定することでタイマ/カウンタ0 を開始します。
- 53 〜 70 行目
こちらは型チェックを有効にしたかったので、マクロではなく inline 関数としました。
timer0_set_count() 関数は、指定された 8 ビット値を TCNT0 に書き込みます。 TCNT0 の初期値を設定したり、割り込み等のタイミングでカウンタ値を変えたい場合に使います。 また、timer0_set_ocr0() 関数は、指定された 8 ビット値を OCR0 に書き込みます。 この値は比較一致に使用されます。
- timer0.c
1 #include <avr/interrupt.h> 2 #include "timer0.h" 3 4 5 static void (*timer0_ov_interrupt_handler)(void); 6 static void (*timer0_comp_interrupt_handler)(void); 7 8 9 /* 10 * オーバーフロー割り込みハンドラ 11 */ 12 ISR(TIMER0_OVF_vect) 13 { 14 timer0_ov_interrupt_handler(); 15 } 16 17 18 /* 19 * オーバーフロー時に割り込みハンドラ内で実行するユーザ関数を設定する。 20 */ 21 void 22 timer0_ov_set_interrupt(void (*func)(void)) 23 { 24 timer0_ov_interrupt_handler = func; 25 } 26 27 28 /* 29 * 比較一致割り込みハンドラ 30 */ 31 ISR(TIMER0_COMP_vect) 32 { 33 timer0_comp_interrupt_handler(); 34 } 35 36 37 /* 38 * 比較一致時に割り込みハンドラ内で実行するユーザ関数を設定する。 39 */ 40 void 41 timer0_comp_set_interrupt(void (*func)(void)) 42 { 43 timer0_comp_interrupt_handler = func; 44 } 45 46 /* End of timer0.c */
- 9 〜 15 行目
オーバーフロー割り込みを有効にした場合に使用する、割り込みハンドラを記述しています。 ATmega8535 のタイマ/カウンタ0 オーバーフロー割り込みを表すマクロは TIMER0_OVF_vect です。 実際には、後述する timer0_ov_set_interrupt() で登録されたユーザ関数を実行する様に実装しています。
- 18 〜 25 行目 (timer0_ov_set_interrupt())
前述のオーバーフロー割り込みハンドラ内で実際に実行する、ユーザ関数を登録します。
- 28 〜 34 行目
比較一致割り込みを有効にした場合に使用する、割り込みハンドラを記述しています。 ATmega8535 のタイマ/カウンタ0 比較一致割り込みを表すマクロは TIMER0_COMP_vect です。 実際には、後述する timer0_comp_set_interrupt() で登録されたユーザ関数を実行する様に実装しています。
- 37 〜 44 行目 (timer0_comp_set_interrupt())
前述の比較一致割り込みハンドラ内で実際に実行する、ユーザ関数を登録します。
- main-ov.c(オーバーフロー割り込み使用時の main)
1 #include <avr/interrupt.h> 2 #include <avr/io.h> 3 #include <avr/sleep.h> 4 5 #include "timer0.h" 6 #include "util.h" 7 8 9 static void timer0_ov_interrupt(void); 10 11 12 int 13 main(void) 14 { 15 /* PB0 ピンのみ出力設定 */ 16 DDRB |= _BV(DDB0); 17 pullup(); 18 19 timer0_ov_enable(); 20 timer0_ov_set_interrupt(timer0_ov_interrupt); 21 timer0_set_count(0); 22 23 sei(); 24 25 /* (8MHz/256)/256 = 約122Hz */ 26 timer0_start(TIMER0_CK_256); 27 28 while (1){ 29 sleep_mode(); 30 } 31 32 return 0; /* 警告を避けるため */ 33 } 34 35 36 static void 37 timer0_ov_interrupt(void) 38 { 39 static int count = 0; 40 41 /* 42 * LED の状態変更周波数を約 2Hz (500ms) とする。 43 * 割り込み周期が約 122Hz なので 61回毎に状態を変える。 44 * 45 * ON-----OFF-----ON-----... 46 * 500ms 500ms 47 */ 48 if (++ count != 61) { 49 return; 50 } 51 52 count = 0; 53 PORTB ^= _BV(PB0); 54 } 55 56 /* End of main-ov.c */
本ファイルは、オーバーフロー割り込みを使う場合の main() を記述しています。
- 12 〜 33 行目 (main())
- pullup() 前に PORTB0 のみ出力方向に設定
- pullup() (pullup.h、PORTB0 以外の全入力ポートの内部プルアップを有効化)
- timer0_ov_enable() (timer0.h、オーバーフロー割り込み有効化)
- timer0_ov_set_interrupt() (timer0.h、オーバーフロー割り込み時に実行するユーザ関数の登録)
- timer0_set_count() (timer0.h、TCNT0 の初期値を設定)
を行った後、sei() (avr/interrupt.h) を実行してシステムの割り込みを有効化しています。
その後に timer0_start() を実行してタイマ/カウンタ0 の動作を開始します。 timer0_start() に TIMER0_CK_256 を指定しているため、オーバーフロー割り込みの周期は約 122Hz ((8MHz/256)/256)となります。
以後 sleep_mode() にてアイドル動作へ移行し、LED の点滅処理はオーバーフロー割り込みハンドラに任せます。
- 36 〜 54 行目 (timer0_ov_interrupt())
オーバーフロー割り込み時に実行されるユーザ関数です。
static 変数である count で割り込み回数を数え、61 回に一度 PORTB0 の値を反転します。 割り込み周期が約 122Hz であるため、反転周期は約 122/61 = 2 Hz となります。
- main-comp.c(比較一致割り込み使用時の main)
1 #include <avr/interrupt.h> 2 #include <avr/io.h> 3 #include <avr/sleep.h> 4 5 #include "timer0.h" 6 #include "util.h" 7 8 9 static void timer0_comp_interrupt(void); 10 11 12 int 13 main(void) 14 { 15 /* PB0 ピンのみ出力設定 */ 16 DDRB |= _BV(DDB0); 17 pullup(); 18 19 timer0_comp_enable(); 20 timer0_comp_set_interrupt(timer0_comp_interrupt); 21 timer0_set_wave_mode(WGM0_CTC); 22 timer0_set_count(0); 23 timer0_set_ocr0(127); 24 25 sei(); 26 27 /* (8MHz/256)/128 = 約244Hz */ 28 timer0_start(TIMER0_CK_256); 29 30 while (1){ 31 sleep_mode(); 32 } 33 34 return 0; /* 警告を避けるため */ 35 } 36 37 38 static void 39 timer0_comp_interrupt(void) 40 { 41 static int count = 0; 42 43 /* 44 * LED の状態変更周波数を約 2Hz (500ms) とする。 45 * 割り込み周期が約 244Hz なので 122回毎に状態を変える。 46 * 47 * ON-----OFF-----ON-----... 48 * 500ms 500ms 49 */ 50 if (++ count != 122) { 51 return; 52 } 53 54 count = 0; 55 PORTB ^= _BV(PB0); 56 } 57 58 /* End of main-comp.c */
本ファイルは、比較一致割り込みを使う場合の main() を記述しています。
- 12 〜 35 行目 (main())
- pullup() 前に PORTB0 のみ出力方向に設定
- pullup() (pullup.h、PORTB0 以外の全入力ポートの内部プルアップを有効化)
- timer0_comp_enable() (timer0.h、比較一致割り込み有効化)
- timer0_comp_set_interrupt() (timer0.h、比較一致割り込み時に実行するユーザ関数の登録)
- timer0_set_wave_mode() (timer0.h、波形生成種別を設定)
- timer0_set_count() (timer0.h、TCNT0 の初期値を設定)
- timer0_set_ocr0() (timer0.h、OCR0 の値を設定)
を行った後、sei() (avr/interrupt.h) を実行してシステムの割り込みを有効化しています。
この初期化フェーズで、timer0_set_wave_mode(WGM0_CTC) によって波形生成種別を "比較一致タイマ/カウンタクリア(CTC)"に設定しています。これにより TCNT0 と OCR0 が一致した時点で TCNT0 をクリアし、0 からカウントを再開する動作を選択しています。
その後に timer0_start() を実行してタイマ/カウンタ0 の動作を開始します。 timer0_start() に TIMER0_CK_256 を指定しており、かつ OCR0 の値によりカウンタ最大値は 127 となるため、比較一致割り込みの周期は約 244Hz ((8MHz/256)/128)となります。
以後 sleep_mode() にてアイドル動作へ移行し、LED の点滅処理は比較一致割り込みハンドラに任せます。
- 38 〜 56 行目 (timer0_comp_interrupt())
比較一致割り込み時に実行されるユーザ関数です。
static 変数である count で割り込み回数を数え、122 回に一度 PORTB0 の値を反転します。 割り込み周期が約 244Hz であるため、反転周期は約 244/122 = 2 Hz となります。
以上で解説は終わりです。次回はタイマ/カウンタ1を使って同じことをやってみたいと思います。