H8-300Hのソフト開発をMacOSX上でやろう、の日記

←前へ
インデックス
次へ→


- LCD表示


後のデバッグをやりやすくするために、AKI-LANについているLCDに文字を表示するルーチンを作っておこう。
AKI-LANボードには、SEIKO社 (SUNLIKE社かも?)製のSC1602BSというLCDモジュールがついている。5x7Dotのキャラクタジェネレータ付きで、11本または7本の制御線でASCII文字を表示できる。
AKI-LANでは、H8のPORTBの6本(+R/W線)を使ってコントロールできる。

    LCDpin   LED       H8pin
     DB4     LED0      PB0
     DB5     LED1      PB1
     DB6     LED2      PB2
     DB7     LED3      PB3
     RS                PB4
     E                 PB7

LCDを初期化して表示するルーチンは、次の通り。

lcd.c を見る


このモジュール用のヘッダは、次の通り。

lcd.h を見る


んで、テストプログラムは次の通り。

File: test3.c
#include "lcd.h"
#include "3052.h"

int main()
{
  int pa;
  char str[16];

  PA.DDR = 0x00;
  PB.DDR = 0xff;
  PB.DR.BYTE = 0x0f;

  LCDInit();
  LCDDisplay("Hello my boy!",0);
  while(1){
    pa = PA.DR.BYTE&0x0f;
    str[0] = 'P';
    str[1] = 'A';
    str[2] = '=';
    str[3] = '0'+(pa^0x0f);
    str[4] = 0;
    LCDDisplay(str,1);
    PB.DR.BYTE = pa;
  }
  return 0;
}


Makefileは次の通り。

Makefile を見る

(h8mon用である)

% make
% h8load test3.mot

で、無事、"Hello my boy!"がLCDに表示された。また、PAのスイッチを押すと、2行目の表示が変わる。


- Newlibを使う


GCCをコンパイルした時にNewlib(標準Cライブラリセット)もコンパイルしたが、ここまではまだ使っていない。ちゃんと動くか実験しよう。

先ほどのtest3.cをちょっとだけ変えて、次のようにする。

File: test4.c
#include "lcd.h"
#include "3052.h"
#include <stdio.h>

int main()
{
  int pa;
  char str[16];

  PA.DDR = 0x00;
  PB.DDR = 0xff;
  PB.DR.BYTE = 0x0f;

  LCDInit();
  LCDDisplay("Hello my boy!",0);
  while(1){
    pa = PA.DR.BYTE&0x0f;
    sprintf(str,"PA=%1X",0x0f&~pa);
    LCDDisplay(str,1);
    PB.DR.BYTE = pa;
  }
  return 0;
}


要点は、sprintfを使っている所である。Makefileは、"test3"を"test4"に変えただけ。

% make
/Users/shibuya/work/h8/bin/h8300-hms-gcc -c -mh -mrelax -mint32 -I/Users/shibuya/work/h8/h8300-hms/include -Wall -g test4.c
/Users/shibuya/work/h8/bin/h8300-hms-gcc -c -mh -mrelax -mint32 -I/Users/shibuya/work/h8/h8300-hms/include -Wall -g lcd.c
/Users/shibuya/work/h8/bin/h8300-hms-as -o crt0.o crt0.S
/Users/shibuya/work/h8/bin/h8300-hms-ld -o test4.out -mh8300h --relax --script h8300h_mon.x test4.o lcd.o crt0.o -Map test4.out.map -L/Users/shibuya/work/h8/h8300-hms/lib/h8300h/int32 -lc -L/Users/shibuya/work/h8/lib/gcc/h8300-hms/3.4.3/h8300h/int32 -lgcc 
/Users/shibuya/work/h8/h8300-hms/lib/h8300h/int32/libc.a(sbrk.o)(.text+0x1c): In function `_sbrk':
sbrk.c:17: undefined reference to `end'
make: *** [test4.out] Error 1

というわけで、sbrk関数がendというシンボルを使っていた。そういえば、リンカスクリプト(h8300h.x)を作った時に、_endの定義を消していたっけ。^_^;;
改良したリンカスクリプトは次の通り。

h8300h_mon.x を見る


変更点は、
       _bss_end = . ;
の直後に
           _end = .;
を加えただけである。

こんどは、コンパイル成功。
では、ダウンロードして実行してみよう。

% h8load test4.mot

.....しかし、ダウンロードが途中で止まって先に進まなくなった。
どうやら、I-Oデータのシリアルドライバは書き出しバッファがあふれるとwriteが停止してしまうようだ。
仕方がないので、h8loadをちょっと改造する。

h8load.c を見る


3840バイト送信する毎に1秒休んでいる。(38400bpsで3840バイト送信するのに約1秒かかる。)

% make; sudo cp h8load /usr/local/bin

で、先ほどの作業に戻る。

% h8load test4.mot

今度は無事送信完了。動作もOKである。

ところで、sprintfを使うだけで、いったいどれだけのコードがライブラリからくっついてきたのだろうか。

test3.out.mapとtest4.out.mapを見比べてみる。

test3.out.map を見る

test4.out.map を見る


"Linker script and memory map"の項を探して、各セクションのサイズを見比べてもらいたい。

Linker script and memory map
        test3              test4
section address    size    address    size
.text   0x00220000 0x40c   0x00220000 0xa178
.data   0x0022040c 0x0     0x0022a178 0x828
.bss    0x0x22040c 0x0     0x0022a9a0 0x48

感想は、おまかせする。(^o^;)


ちなみに、このプログラムをROMに書き込んでもちゃんと動作するかどうか、実験してみる。
変更点は、Makefileの"h8300h_mon.x"を"h8300h.x"に変えるだけ。(h8300h.xの方も_endのシンボル宣言を書き加えてある。)

% make clean;make
% h8write test4.mot
(しばらく書き込み中。。。)

で、AKI-LANをMode6で起動。無事動作していた。


- NewlibのI/Oルーチン


せっかくなので、NewlibのI/Oルーチンを実装してみよう。
libgcc.aの中には、_write, _readなどの関数があり、printfなどのファイルアクセス関数を呼ぶと最終的にはそれらが呼び出されている。
H3/800H用のNewlibの場合、MRIというシミュレータ用に、trap命令を発行するようになっているようだ。("newlib-1.13.0/newlib/libc/sys/h8300hms/write.S"などを参照。)
ライブラリ外に指定したオブジェクトにそれらの関数があると、libgcc.a内の_write関数などはリンクされなくなり、オブジェクト内の関数が用いられる。
そこで、次のモジュールを作る。

File: iostub.c
/* I/O stub routines for newlib */

#include "lcd_dev.h"
#include <sys/types.h>
#include <sys/stat.h>

ssize_t _write(int fd, const void *data, size_t n)
{
  switch(fd){
  case 1: /* stdout */
  case 2: /* stderr */
    return lcd_write(fd,data,n);
  }
  return -1;
}

ssize_t _read(int fd, void *buf, size_t n)
{
  return -1;
}

int _fstat(int fd, struct stat *sb)
{
  switch(fd){
  case 1: /* stdout */
  case 2: /* stderr */
    return lcd_fstat(fd,sb);
  }
  return -1;
}


stdoutとstderrへの書き出しは、lcd_write関数を使うようにするというものである。
printfなどは内部でfstatも使っているようなので、これも用意する。
lcd_write関数は、先ほどのlcd.cを書き換えて、次のように作った。

lcd_dev.h を見る

lcd_dev.c を見る


動作の概要は、16文字まで表示をし、それを超える分はカット。改行が来ると行を上に送るというものである。

これを試すテストプログラムは次の通り。

File: test5.c
#include "lcd.h"
#include "3052.h"
#include <stdio.h>

int main()
{
  unsigned char pa,panew;

  PA.DDR = 0x00;
  PB.DDR = 0xff;
  PB.DR.BYTE = 0x0f;
  printf("Muy bien!\n");
  pa = 0;
  while(1){
    panew = (~PA.DR.BYTE)&0x0f;
    if(pa != panew){
      pa = panew;
      printf("PA=%1X\n",pa);
    }
    PB.DR.BYTE = (~pa)&0x0f;
  }
  return 0;
}


PORT-Aのキーの状態が変わる度にprintfをしている。

Makefileは次のようになる。

Makefile を見る


コンパイルしてh8loadで送り込んで実行。
OK。


←前へ
インデックス
次へ→
Last updated: 2005/4/24 14:14
Copyright (C) 2005 by SHIBUYA K.
All Rights Reserved.