2012/05/13

HEWでprintfを使えるようにする

今回は,printfの実装を行いました。
プログラムでprintfといえば,画面に文字や変数の値を表示することが多いですが,マイコンの場合はシリアル通信でPCなどにデータを送るようにします。ターミナルソフトでこのデータを見れば,デバッグに使えるわけです。
(SHには,便利なエミュレータやデバッガがあるらしい?のですが,私は未だにprintfデバッグです^^;)

これまでは自作のprintfを使っていたのですが,今回はHEWのstdio.hで最初から定義されているprintfを使うことにしました。Renesas純正ですから,きっとSH向けに最適化されているだろうと期待してみたり。
プログラム中...
RenesasのFAQユーザーズマニュアルC/C++コンパイラ,アセンブラ,最適化リンケージエディタ ユーザーズマニュアル,p.168あたり)を読みながら格闘すること早半日・・・なんとか実装できました。以下に手順を示します。

なお,HEWとコンパイラパッケージのバージョンは,
  • C/C++ compiler package for SuperH RISC engine family V.9.02 Release 00 (Evaluation Version)
  • High-performance Embedded Workshop Upgrade 4.09.00
ターゲットマイコンはSH7125です。

1.新規ワークスペース作成

まず,HEWの「ファイル」-「新規ワークスペース」から新しいワークスペースを作成します。生成ファイルの設定画面で,「I/Oライブラリ使用」にチェックを入れてください。これによって,標準入出力に必要な関数のほとんどをHEWが用意してくれます。また,「ハードウェアセットアップ関数生成」で「C/C++ source file」を指定すると,パワーオンリセット後のI/Oの初期化関数(HardwareSetup関数)の雛形を生成してくれます。
「I/Oライブラリ使用」にチェック,「ハードウェアセットアップ関数生成」は「C/C++ source file」を指定

2.シリアル通信のための関数作成

ワークスペースができたら,プロジェクトから「lowsrc.src」を削除します。このファイルには,デバッガ用の低水準入出力関数「charget」と「charput」が記述されています。従って,この「charget」と「charput」をシリアル通信の関数に置き換えれば,シリアルポートに対してprintfが使えるようになります。今回作成した関数を下表に示します。割り込みは使わない,最も単純なものです。シリアル通信の初期化関数(sio_init)も作成します。

void sio_init(void){
    STB.CR3.BIT._SCI1=0; // SCIスタンバイ解除
    SCI1.SCSCR.BYTE=0x00; // 送受信割り込み禁止、送受信禁止
    PFC.PACRL1.BYTE.H=0x10; // PA3をRXD1に設定
    PFC.PACRL2.BYTE.L=0x01; // PA4をTXD1に設定
    SCI1.SCSMR.BYTE=0x00; // 通信フォーマット、クロックソースを選択
    SCI1.SCBRR=19; // ビットレート BRR=(25000000[Hz]/(32*38400[bps])) - 1 => 19
    SCI1.SCSCR.BIT.TE=1;   // トランスミットイネーブル:送信動作を許可
    SCI1.SCSCR.BIT.RE=1;   // レシーブイネーブル:受信動作を許可
}

void charput(char c){
    while (SCI1.SCSSR.BIT.TDRE==0);
    SCI1.SCSSR.BIT.TDRE=0;
    SCI1.SCTDR=c;
}

char charget(void){
    char c;
    while ((SCI1.SCSSR.BYTE & 0x40)==0);
    c=SCI1.SCRDR;
    SCI1.SCSSR.BYTE=SCI1.SCSSR.BYTE & 0xBF;
    return(c);
}

追記(2012/10/26):↑のcharget()はエラー処理を省略しており,受信エラー発生時に無限ループになってしまいます。常にデータが送られてくるような用途では,エラー処理を追加することをオススメします。修正版のchargetはこちら

3.シリアル通信初期化関数の呼び出し設定

パワーオンリセット後に,シリアル通信の初期化関数が呼ばれるように設定します。「ハードウェアセットアップ関数生成」を指示しておくと,「hwsetup.c」というファイルが生成され,その中に「HardwareSetup」関数があります。この関数が,パワーオンリセット後に自動的に呼ばれますので,この中でシリアル通信の初期化関数を呼び出すよう変更します。

追記(2012/8/30):sio_init()は,printfを使う前ならば,どこで呼び出してもよいです。HardwareSetup以外でも,mainでもOKです。(HardwareSetupはmainより前に実行されるため,ここに初期化処理をまとめておくと便利です

void HardwareSetup(void)
{
    sio_init();
}

4.完成!

以上で設定は完了です。これで,思う存分にprintfできます。例えば,下表のようにすると・・・

void main(void)
{
    int a=1;
    double b=1;

    a+=1;
    b=3.141592;

    printf("abcdefghijklmnopqrstuvwxyz\r\n",0);
    printf("%d\r\n",a);
    printf("%lf\r\n",b);
}

こんな感じ↓で出力されます。
使ってみて気付いたのですが,このprintfは必ず第二引数まで指定しなければならないようです。
文字列のみをprintfする場合,上の表のように,第二引数に適当な値を指定してください。(第二引数を指定しないと何も出力されません)
TeraTermで確認した結果
手順さえ分かってしまえば,割と簡単にprintfを使えるようになります。ぜひ試してみてください。

余談ですが,現在のプログラムサイズは21.6 kByteでした。SH7125のROMは128 kByteなので,既に20%弱を使ってしまっています。
もう少し削りたいところですね。

それでは-~)ノ~~

0 件のコメント:

コメントを投稿