| 割り込み処理中で操作される変数の定義 | ||
| sbi/cbi関数 | ||
| 割り込み処理の中で、グローバル変数の値を変えるようなプログラムを組んだときは注意が必要。 たとえば |
uint16_t timer_count;
timer_start(msec);
while(timer_count > 0);
|
| というプログラムを書いたとする。 グローバル変数timer_countの値はtimer_startルーチンの中で設定され、同時にタイマ割り込みが有効になる。timer_countの値はタイマ割り込みルーチンの中で減算される。 whileループはtimer_countがゼロになった時にループを脱出するように思えるが、実際にはそうはならない。 何故かはコンパイル結果を見ればすぐ分かる。 |
|
| timer_countの値をレジスタにロードして、あとはレジスタに対してゼロチェックを行っている。 timer_countの値が割り込み中で変化しても、それはレジスタには反映されないので、この条件チェックは永久に成立しない事になる。 これを避けるためには、timer_count変数の宣言にvolatileを付け加えれば良い。 |
|
| 変更後のコンパイル結果はこうなる。 |
|
| 一見すると変わってないように見えるが、ラベルの位置が違うので、これなら毎回timer_countの値がチェックされる。 |
| AVRアセンブラではビット制御命令sbi/cbiはI/Oポートレジスタにしか使用できず、制御レジスタに対して使おうとするとアセンブルエラーになってしまうが、AVR-GCCのsbi/cbi関数は制御レジスタに対しても使用することができる。 |
sbi(PORTD, 1);
sbi(TIMSK, 1);
|
| 実際にはsbi命令にコンパイルされるのは前者だけで、後者は複数の命令で同じ動作を行うようなオブジェクトが出力される。 コンパイル結果を見れば違いは一目瞭然。 |
|
| 制御レジスタに対するsbi/cbi関数は、ポートの値を読み込んでからビットをセット(またはクリア)して、同じポートへ出力するという命令にコンパイルされている。 |