AVR用ソースをlinux用にコンパイルしてprintfデバッグ

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で指定している時間の合計なので、他処理のことを考慮していません。ここからさらに引く必要があります。
次回はこんな面倒なことをしなくて済む、タイマーデバイスを使おうと思います。

Leave a Reply

Your email address will not be published. Required fields are marked *

CAPTCHA