やっぱり最初はこれよね。
LEDをPD0〜PD7に接続。
本当は抵抗が必要なはずだが、まぁいい。
とりあえずは光ればいい。
以下、ソースコード。
#include <avr/io.h> #include <util/delay.h> void delay_ms( int time ) { // 指定ms分ループ while( time-- ){ _delay_ms( 1 ); } } int main( void ) { DDRD = 0b11111111; // ポートDの方向レジスタを、全て出力に PORTD = 0b00000000; // ポートDの出力レジスタを、全てLowに char count = 0; // 0〜7まで char binc = 1; // インクリメントモードかデクリメントモードか while( 1 ){ PORTD = ( 1 << count ); // ポートDのカウント数位置の出力レジスタのみをHighに if( binc ){ // インクリメントモード count++; if( 7 <= count ){ // 端までいったのでデクリメントモードへ binc = 0; } } else { // デクリメントモード count--; if( 0 >= count ){ // 最初まで戻ったのでインクリメントモードへ binc = 1; } } delay_ms( 50 ); // 50ms待ち } return 0; }
ATmega168Pは、内蔵RC発信器が8MHzとなっていたので、普通にCPUのFrequencyも8MHzだろうと思いこむ。
AVRStudioのProject Optionで、Frequencyを8000000Hzに設定して、F_CPUのdefineを設定したんだけど、実際やってみると、delay_msの待ちが余りに遅い。
具体的に言うと、8倍くらい。
データシートを読み返してみると、ヒューズビットにCKDIV8とかいうのがあって、こいつが0だと、実際には8で割った数がシステムクロックとなるらしい。
(システムクロック前置分周器:目的としては、消費電力節約用?)
ってんで、Frequencyを1000000Hzに設定しなおして、事なきを得る。
デバイスの最高周波数より高い周波数のクロックを入力する場合、前置分周器で押さえてやったりするみたいね。
というわけで、ヒューズビットのCKDIV8を立てて、Frequencyを8000000Hzにしてもちゃんと動きました。
この辺は、電源とかの兼ね合いで変えるべきかね。
AVRWikiにあるように、_delay_ms関数は、システムクロック数によって、待てる時間が異なり、しかもその時間は結構短い。
なので、1ms指定で指定ms分ループをしている。
本当は、1ms指定じゃなくもっと大きい数で少ない回数回す方が、誤差は少ないはず。
そのシステムクロックで回せる最大数以上だったら最大数、未満だったらその値を指定するのが一番いいような気はする。
しかし、未満で変数を渡す場合は、以下の問題がある。
_delay_ms関数は結局、周波数の定数F_CPUを参照して、それをもとにループ回数を決め、ループを回しているだけらしい。
このため、定数を_delay_msに渡す場合は、コンパイラが最適化し、ループ回数が決定している。
けど、変数を渡すようなコードを書くと、doubleに変換した引数とF_CPUの計算ルーチンが入るため、コードが膨らむ。
手軽に使えるけど、色々考えなければいけないようだ。
タイマーを使ってみようかとも思ったけど、分周比との兼ね合いを考えると、あらかじめ待つ時間がわかってるような場合じゃないと、使いにくい。
結局、今のやり方が、とりあえずは一番お手頃な感じ。