【AVR】_delay_ms()がうまく動かない?

AVRで工作していると、1秒待って欲しいって思うときにディレイ関数を使いたくなります。そのノリで使っていたら、100msと指定していたのに10秒以上待たされるという現象が起きました。なぜでしょう。

まずはじめに疑ったのは、クロック周波数です。_delay_ms()関数では指定された時間とクロック周波数から計算されたサイクル数、待つという実装がされています。つまり、クロック周波数が違えば指定した時間通りに動いてくれません。

というわけでクロック周波数を確認しました。筆者が今回使っていたAVRはATmega328Pです。内蔵RC発信器からの8MHzで動作させています。Hzで書くと8,000,000Hzになります。周波数の指定は、F_CPUで定義することでできます。この場合は、

[code lang="cpp" highlight="1"]

define F_CPU 8000000UL

int main(void){ _delay_ms(100);

return 0; }

[/code]

このように8,000,000Hzである、と宣言します。最後のULはunsigned Long型の定数であると指定するためのものです。8,000,000という値はintの範囲(-32768~32767)を超えているので、これがないと溢れてしまいます。 今回は正しく定義されていたので別の問題である、ということで考えうる他の原因を考えました。

次に疑ったのは、_delay_ms()関数が引数として取りうる値が制限されていることです。調べてみると過去のバージョンのことで当てはまりませんので割愛します。

最終的に考えたことは、delay_ms()関数は使ってはいけないのではないかということです。つまり、delay_ms()関数を使っても正確に測ることは不可能な場合があるということです。 それは割り込みを使った時です。

考えてみればわかるのですが、時間から求めた回数だけ「何もしない」を繰り返しているわけです。その間に割り込みが発生してしまえばおかしくなるのはすぐに分かります。

というわけで、原因が割り込みであるというところまでは確認をしていませんが、LEDのダイナミック・ドライブでタイマー割り込みを積極的に利用しているのでこれが原因であるということで間違いないと思います。どのよう解決するかはまたどこかで書こうと思います。