AKI-H8/3052で遊ぶ ~ printfデバッグ編

やっぱりパソコン上でのデバッグだけでは効率が悪い場合も多いと思います.そこで,出来たら便利だと思うのがprintfデバッグです.

printfのサイズ

gccの標準ライブラリのprintfは巨大すぎてノーマルなH8上では動きません.SRAMでも繋いでメモリを拡張する手もありますが,ここはコンパクトな簡易printfを作ります.(C言語が使えるという人には,可変長引数くらい何も説明せずともOKですよね?)

簡易printf関数

////////////////////////////////////////////////////////////////////////////
//               H8用標準出力ライブラリ h8stdio.h

#define NONE 0
#define LCD 1
#define SCI 2

#ifndef STDOUT
// stdout setting for debug (use NONE when release)
#define STDOUT SCI // LCD , SCI or NONE
#endif

// 標準関数置き換え
#define printf __printf
#define puts __puts
#define putchar __putchar


#if STDOUT == LCD
////////////////////////////////////////////////////////////////////////////
//               LCD

#define LCD_PORT P3
#define LCD_SIG_RS 0x10
#define LCD_SIG_E  0x20
#define LCD_CMD_CLEAR 0x01

void lcd_wait(void)
{
    volatile int t=1000; while(t--);
}

// 8ビット出力
void lcd_out(unsigned char d)
{
    LCD_PORT.DR.BYTE = d | LCD_SIG_E; // output and enable E
    //lcd_wait(); // 無いと駄目?
    LCD_PORT.DR.BYTE = d & ~LCD_SIG_E; // output and disable E
    lcd_wait();
}

// LCD初期化
void lcd_init(void)
{
    // 3回 00000011を出力
    lcd_out(0x03);
    lcd_out(0x03);
    lcd_out(0x03);

    // ファンクションセット
    lcd_out(0x02); // 4ビットモードに
    lcd_out(0x2);
    lcd_out(0x8);

    // 00001100 表示オン/カーソルOFF/ブリンクOFF
    lcd_out(0x0);
    lcd_out(0xc);

    // 00000110 エントリーモードセット
    lcd_out(0x0);
    lcd_out(0x6);
}

// データ出力
void lcd_write(unsigned char d)
{
    lcd_out((d>>4)&0x0f | LCD_SIG_RS);  // 上位4ビット
    lcd_wait();
    lcd_out(d&0x0f | LCD_SIG_RS);       // 下位4ビット
    lcd_wait();
}

// 表示クリア
void lcd_clear(void)
{
    LCD_PORT.DR.BYTE = 0;
    lcd_out(0);
    lcd_out(LCD_CMD_CLEAR);
    lcd_wait();
}


void lcd_setpos(int x,int y)
{
    y &= 0x1; // 2行のみ
    LCD_PORT.DR.BYTE = 0;
    lcd_out(0x08 | (y<<2) );
    lcd_out(x);
    lcd_wait();
}


static int lcd_line = 0;
static int lcd_clearflag = 0;

// char送信
void __putchar(char c) {
    if (c=='\n') {
        lcd_line++;
        lcd_clearflag = 1;
    } else if (c=='\f') {
        lcd_clear();
        lcd_line = 0;
    } else {
        if (lcd_clearflag) {
            lcd_setpos(0,lcd_line);
            lcd_clearflag = 17;
            while (--lcd_clearflag) lcd_write(' ');
            lcd_setpos(0,lcd_line);
        }
        lcd_write((unsigned char)c);
    }
}


#elif STDOUT == SCI

////////////////////////////////////////////////////////////////////////////
//               シリアルポート

// ビットレート設定リスト(25MHz時)
typedef enum {
    BR2400   = 80,   //n=1,SMRの設定
    BR4800   = 162,  //n=0,SMRの設定,以下すべて0
    BR9600   = 80,
    BR19200  = 40,
    BR38400  = 19,
    BR57600  = 13,
    BR115200 = 5,
} BaudRate;

//SCI初期化
void sci_init(BaudRate b)
{
    int i;
    SCI1.SCR.BYTE = SCI1.SMR.BYTE = 0;// 初期値は0
    SCI1.BRR = b;                     // ビットレート
    for (i = 0; i <4000; i++) ;       // 待つ
    SCI1.SCR.BYTE = 0x70;             // 受信割り込み許可,送受信許可
    SCI1.SSR.BYTE;                    // ダミーリード
    SCI1.SSR.BYTE = 0x80;             // エラーフラグクリア
}

// char送信
void __putchar(char c)
{
    if (c=='\n') __putchar('\r');  // 改行はCRLF
    while (!SCI1.SSR.BIT.TDRE);     // 送信を待つ
    SCI1.TDR = c;
    SCI1.SSR.BIT.TDRE = 0;          // TDREクリア
}


// 受信割り込み
void int_rxi1(void)
{
    // とりあえず,エコーバック
    char rd;
    rd = SCI1.RDR;         // 受信データ
    SCI1.SSR.BIT.RDRF = 0; // RDRFクリア
    __putchar(rd);       // エコーバック
}

// エラー割り込み
void int_eri1(void)
{
    SCI1.SSR.BYTE &= 0x80;            //エラーなら エラーフラグクリア
}


#else

#define __putchar(s) 0

#endif

#if STDOUT == NONE
#    define __puts(s) 0
#    define __put_int(d) 0
#    define __printf 
#    define std_init() 
#else

#include <stdarg.h>

// 文字列送信
void __puts(char *s) {
    while (*s) putchar(*s++);
}

// 数値送信
void __put_int(long d)
{
    char s[8];
    int l=0,sgf=0;
    s[sizeof(s)-1]='\0';
    if (d<0) {
        d=-d;
        sgf=1;
    }
    do{
        s[sizeof(s)-2-l]=d%10+'0';
        l++;
    } while(d/=10);
    if (sgf) {
        s[sizeof(s)-2-l]='-';
        l++;
    }
    __puts(s+(sizeof(s)-1-l));
}

// フォーマット出力
void __printf(char *fmt, ...)
{
    va_list argptr;

    va_start(argptr, fmt);

    for(; *fmt; fmt++){
        if (*fmt=='%') {
            fmt++;
            switch(*fmt){
            case 'd':
                __put_int(va_arg(argptr, int));
            break;
            case 's':
                puts(va_arg(argptr, char *));
            break;
            }
        } else {
            putchar(*fmt);
        }
    }
    va_end(argptr);
}

void std_init()
{
#if STDOUT == SCI
    sci_init(BR38400);   //SCI初期化 38400bps
#elif STDOUT == LCD
    P3.DDR = 0xff;  // LCD port
    lcd_wait();
    lcd_init();
    putchar('\f'); // clear
#endif
}

#endif

使い方

サイズが大きくなるので,printfは%dと%sしか使えません.必用なときに実装してください.


#include <3052.h>

#define STDOUT 1 // 0:NONE 1:LCD 2:SCI
#include "h8stdio.h"

int main()
{
    std_init();
    EI; // enable interrupt


    puts("Stand by ready!\n");
    printf("int:%d str:%s",1234,"STRING");

    for(;;);
}

終わりに

printfデバッグに頼りすぎるのもどうかと思いますが,かなり楽になります.

この文書の履歴

Copyright © 瓶詰堂 all rights reserved.