「AVRマイコンでキャラクタLCDに時間を表示する(その2)」で外部クロックに変更して精度を上げたはずが、またもやずれていました。デバッグするべく、AVRのソースをlinux用に改造したいと思います。
まずは、makefileから変更します。gdbでデバッグすることも考慮して、-gをつけたコマンドを12,13行目に追加しました。また、デバッグ用として-DオプションでDEBUGという文字列を定義しておきます。
# cat -n makefile 1 hello: hello.c makefile 2 avr-gcc -O2 -mmcu=attiny2313 -DF_CPU=1000000UL -c -o hello.o hello.c 3 avr-gcc -O2 -mmcu=attiny2313 hello.o -o hello 4 avr-objcopy -j .text -j .data -O ihex hello hello.hex 5 6 write: 7 avrdude -p t2313 -c linuxgpio -U flash:w:hello.hex 8 9 clean: 10 rm -f hello.o hello.hex hello 11 12 debug: hello.c makefile 13 gcc -g -DDEBUG hello.c
次にソース本体であるhello.cを修正します。AVR用にヘッダが2つ呼ばれていますが、これを削除します。
# head -2 hello.c #include <avr/io.h> #include <util/delay.h>
削除したヘッダの代わりに、stdio.hをインクルードして、printfを使えるようにしておきます。
このようになりました。
# head -6 hello.c #ifndef DEBUG # include <avr/io.h> # include <util/delay.h> #else # include <stdio.h> #endif
AVR用のヘッダがインクルードされなくなったので、PORTBやDDRやらの定義も使えなくなりました。
ビルドが通れば良いので、変数で代用します。以下のように定義を追加しました。
unsigned char DDRB; unsigned char PORTB;
後は、_delay関数です。実際にはマクロですが、関数として実装します。
引数で指定した数値をprintfで表示する関数とします。以下のようになりました。
void _delay_us(int us) { printf("%d us\n",us); } void _delay_ms(int ms) { printf("%d ms\n",ms); }
hello.cの修正ができたので、ビルドしてみます。
# make debug gcc -g -DDEBUG hello.c # ls a.out hello.c makefile #
linuxの実行ファイルとして、a.outが生成されました。
実行してみましょう。
# ./a.out > result
しばらくしたら、CTRL+Cで停止します。行数を確認。
# wc -l result 4897631 result
ちょっと多かったですね。
今回は時間のデバッグなので、while(1)中にある_delayが合計で1秒になっているかどうかの確認なので、目印となる998msを基準に抽出してみます。
# grep -n 998 result | head 105:998 ms 168:998 ms 231:998 ms 294:998 ms 357:998 ms 420:998 ms 483:998 ms 546:998 ms 609:998 ms 672:998 ms #
この中の一周期を適当にピックアップします。295行目から357行目としましょう。
# sed -n '295,357p' result 1 us 1 us 1 us 1 us 40 us 2 ms 1 us 1 us 40 us 1 us 1 us 40 us 40 us 1 us 1 us 40 us 1 us 1 us 40 us 40 us 1 us 1 us 40 us 1 us 1 us 40 us 40 us 1 us 1 us 40 us 1 us 1 us 40 us 40 us 1 us 1 us 40 us 1 us 1 us 40 us 40 us 1 us 1 us 40 us 1 us 1 us 40 us 40 us 1 us 1 us 40 us 1 us 1 us 40 us 40 us 1 us 1 us 40 us 1 us 1 us 40 us 40 us 998 ms
usとmsが混在して見難いので、単位を合わせます。すべてusに変換します。
# !! | sed 's/ ms/000 us/' sed -n '295,357p' result | sed 's/ ms/000 us/' 1 us 1 us 1 us 1 us 40 us 2000 us 1 us 1 us 40 us 1 us 1 us 40 us 40 us 1 us 1 us 40 us 1 us 1 us 40 us 40 us 1 us 1 us 40 us 1 us 1 us 40 us 40 us 1 us 1 us 40 us 1 us 1 us 40 us 40 us 1 us 1 us 40 us 1 us 1 us 40 us 40 us 1 us 1 us 40 us 1 us 1 us 40 us 40 us 1 us 1 us 40 us 1 us 1 us 40 us 40 us 1 us 1 us 40 us 1 us 1 us 40 us 40 us 998000 us #
awkで合計を計算します。
# sed -n '295,357p' result | sed 's/ ms/000 us/' | awk '{sum+=$1}END{print sum}' 1001036 #
1.036ミリ秒ずれています。
時計をより正確にするには、998msとしていたdelayを、998000us – 1036us = 996964usとすれば良いことになります。ただし、delayで指定している時間の合計なので、他処理のことを考慮していません。ここからさらに引く必要があります。
次回はこんな面倒なことをしなくて済む、タイマーデバイスを使おうと思います。
コメント