ルネサスの純正開発環境HEWを使って,秋月H8マイコン・マザーのLCDでprintf関数を使う方法
(秋月電子,AKI−H8/3048F−ONEマイコンボードを例にします。クロック25MHzです。)
2012/09/13
matsu 作成
<参考文献>
http://xythos.tokyo-ct.ac.jp/usr/kosaka/web/for_students/H8_3048fone_HEW/H8_3048fone_TNCT_HEW.html
1.開発環境のダウンロードとインストール
(1) ルネサスに登録
http://japan.renesas.com/myrenesas/ に接続して,新規登録でメールアドレスを登録します。
(2) 開発環境ダウンロード
http://japan.renesas.com/products/tools/evaluation_software/downloads.jsp で,「H8SX,H8S,H8コンパイラパッケージ」を探してダウンロードします。2012/09/13の時点で,【無償評価版】H8SX,H8S,H8ファミリ用C/C++コンパイラパッケージ
V.7.00 Release 00 が最新版です。
(3) 開発環境インストール
普通にインストールします。
2.プログラムをH8に転送するアプリ「H8ライター」のインストール
(1) H8ライターダウンロード
yamasan氏のフリーソフトをダウンロードする。http://ym3.plala.jp/yamasan/ に接続して,H8ライター(書き込み制御プログラム含)H8W_*****.zip
(*** KB)をダウンロード。2012/09/13の時点で,H8ライター(書き込み制御プログラム含)H8W_038b4.zip (229
KB) が最新版です。
(2) H8ライターインストール
デスクトップ上で解凍しただけでもかまいませんが,適当な場所にインストールします。Windows 7
64bitの場合,C:\Program Files
(x86) が適当と思われます。ダウンロードしたzipファイルを解凍してできたH8Wフォルダをそのまま,C:\Program Files
(x86) に移動してください。さらに,スタートメニューに登録するとよいでしょう。C:\ProgramData\Microsoft\Windows\Start
Menu\Programs\ を開いて,H8Wフォルダを作り,そこにH8Wフォルダのショートカットや,h8w.exeのショートカットを作っておきます。
(3) H8ライターを制御して一発で書き込む準備
次の内容のファイルを作ります。拡張子は「.cmd」または「.bat」としてください。どちらでも差はありません。下の内容は,「H8/3048BF-ONEマイコン」の場合で記述しています。詳細は,C:\Program
Files
(x86)\H8W\CTRL_MOT にある,「添付書き込み制御ファイル.txt」を参照してください。たとえば,ファイル名を「H8-3048Fone.cmd」として,内容を次のようにします。
rem カレントドライブ・カレントディレクトリへ移動 rem ★フラッシュメモリ書き込みに使用するプログラム echo [AutoPgm] >myAutoPgm.ini |
★の部分は,各自の環境に合わせてください。COMポートはデバイスマネージャを開き,使用しているシリアルポートが,COMの何番であるかを確認します。私の環境ではCOM1でしたので,「1」としています。
やっていることは,h8w.exeを実行する条件をmyAutoPgm.iniに書き込んで,h8w.exeを実行後,myAutoPgm.iniを削除しています。この「goH8-3048Fone.cmd」をデスクトップにでもおいて,HEWで作成した「….mot」ファイルを「goH8-3048Fone.cmd」のアイコンにドラッグ&ドロップすると,マイコンに書き込んでくれます。もちろん,マイコンは書き込みモードにして電源を入れてください。書き込み後はいったん電源を落とし,実行モードにして電源を入れると動作します。
3.HEWによる開発
(1) HEWを起動します。
新規を選択します。すでに作ってあるプロジェクトワークスペースでもかまいませんが,新規の方がわかりやすいでしょう。過去に作ったアプリと類似のものを作るなら,同じプロジェクトワークスペースの方がわかりやすいと思います。
(2) ワークスペース名とプロジェクト名の設定
ワークスペース名は,作業する領域の名前です。どんな名前でもかまいません。プロジェクト名はこれから作成するアプリの名前を指定します。ディレクトリですが,デフォルトの「C:\WorkSpace」の配下だと,バックアップを忘れてしまうことも多いので,Windows
7のバックアップツールで標準的にバックアップしてくれる,マイドキュメント配下の方がよいでしょう。下の例では,マイドキュメントの下にHEWというフォルダを手動で作っておき,「参照」でそのフォルダを指定。ワークスペース名にWorkSpaceLCDを入力。プロジェクト名にはprintf1を指定しました。
(3) CPUのシリーズとタイプの設定
CPUのシリーズとタイプを指定します。「300H」「3048F」です。これはCPUにより異なりますので,シリーズやタイプは各マイコンボードのCPUの種類により変更します。
(300H:AKI-H8/3048,AKI-H8/3052,AKI-H8/3069など,300:AKI-H8/3664など)
(3048f:AKI-H8/3048,AKI-H8/3048f-oneなど)
(4) 動作モードとアドレス空間の設定
アドレス空間のみ1M
byteに変更します。これは,メモリを増設しないモード7でCPUを使用するためです。ライブラリ作成方針は,「コードサイズ優先」でかまいませんが,「スピード優先」にすれば,コードは多少大きくなりますが,速く動作するようになります。
(5) I/Oライブラリ,ヒープメモリ,main()関数生成,等の設定
標準入出力ライブラリは使わないので,「I/Oライブラリ使用」のチェックを外します。プログラム実行中の動的なメモリ確保(mallocやcalloc)も使用しないので,「ヒープメモリ使用」のチェックも外します。なおH'420は1056バイトです。もともとRAMは4kバイトしかありませんので,ヒープメモリを使う場合は注意してください。
開発したプログラムは,C言語で開発するので,「C source
file」を選択します。「I/Oレジスタ定義ファイル」は使用したいので,チェックを入れておきます。「ハードウェアセットアップ関数生成」は,I/Oの初期化をする関数を自動で作っておきますか? という意味ですが,便利なので作って置きましょう。「C/C++
source file」を選択します。「次へ」。
(6) ライブラリの選択
デフォルトで「runtime」,
「new」にチェックが入っていますが,加えて,最低限「string.h」はチェックを入れてください。「ctype.h」,「stdarg.h」,はチェックしなくても,これから紹介するLcdLibは動作してしまいますが,LcdLibのソース中で使っていますのでチェックしておいた方が無難です。なおこれらはあとから,ビルド,Toolchain,標準ライブラリ,カテゴリ:標準ライブラリ,で選択し直すことができます。
これで設定終了です。「完了」します。
(7) 概要表示
そのまま,okしてください。
(7) HEWでの設定
HEWで生成するコードにデバック情報を含むかどうかですが,含むとサイズが大きくなったりします。今回はデバッグ情報は使わないので,「Release」に変更してください。
(7) ハードウェアセットアップ関数生成を選択した人へのTips
hwsetup.cとresetprg.cを開くとわかるのですが,電源オンで最初に実行されるのがresetprg.cなのですが,main()関数に飛び込む前に,hwsetup.cのHardwareSetup()関数を呼び出してくれています。resetprg.cのリマーク文等を取り除いたプログラムを示します。
set_imask_ccr((_UBYTE)1); _INITSCT(); HardwareSetup(); // Use Hardware Setup set_imask_ccr((_UBYTE)0); main(); |
割り込み禁止 初期化済み変数をROMからRAMにコピー ハードウェアセットアップ関数の呼び出し 割り込み許可 main()関数へジャンプ |
#include "iodefine.h"
void initTimer(void)
/*タイマーの初期化*/ void timerStart(unsigned num)/* num= 0-4
*/ void
HardwareSetup(void) } |
コメントは筆者手持ちのハードウェアマニュアルの ページ番号です。無視してください。 |
void initLedP5(void);//LEDのポート5の初期化 void initTimer(void); /*タイマーの初期化*/ void timerStart(unsigned num);//タイマースタート num= 0-4 void timerStop(unsigned num);//タイマーストップ num= 0-4 void HardwareSetup(void);//ポート等の初期化関数 |
プロトタイプ宣言の集まり。 必要に応じて追加していきます。 |
#include "iodefine.h" #include <machine.h> // vector 2 Reserved 途中略 |
←これを忘れないこと |
#include "iodefine.h" void main(void); void main(void) }
|
←忘れないこと。 ←作ったhwsetup.hをインクルード ←割り込み関数でカウントアップしてもらう変数 ←main関数のプロトタイプ宣言。消さない ←main関数の始まり ←タイマーの初期化,割り込み速度のセット等 ←無限ループ ←LEDを1秒周期で点滅 |
#include "iodefine.h" void main(void); void main(void) }
|
←忘れないこと。 ←main関数のプロトタイプ宣言。消さない ←main関数の始まり ←マザーを使うための全初期化 ←LCD画面クリア ←1行目の先頭にカーソル移動 ←Helloを表示 ←2行目の先頭にカーソル移動 ←World!を表示 ←右側LEDを点灯 ←右側LEDを消灯 |
//マザーの種類により次のどれか一つを有効にすること //マイコンの種類により次のどれか一つを有効にすること |
←マザーを選択 ←マイコンを選択 |
#include "iodefine.h" void main(void); void main(void) }
|
←忘れないこと。 ←main関数のプロトタイプ宣言 ←main関数の始まり ←マザーを使うための全初期化 ←LCD画面クリア ←1行目の先頭にカーソル移動 ←Helloを表示 ←2行目の先頭にカーソル移動 ←World!を表示 ←printf出力先をデフォルトのLCDからSCI_1に変更 ←Hyper Terminalに表示 ←Hyper Terminalに1-10を表示 ←printf出力先をLCDに戻す ←右側LEDを点灯 ←右側LEDを消灯 |
(2) LED関連
typedef
enum{RIGHT,LEFT,ALL} led_type;/*←マザーにより内容は異なります*/
void ledon(led_type
sel);
/*LED ON*/
void ledoff(led_type
sel);
/*LED OFF*/
LEDを点灯・消灯させます。AE-H8MBは2個,AKI-H8-USBは4個のLEDが搭載されています。引数にはright, left,
allの他,各LEDに対応するビットを1にした16進数を指定することもできます。
使用例:
ledon(ALL);・・・すべてのLEDを点灯します(AE_H8MB, MD_M52F_48F1,
AKI_H8_USB)。
ledon(0x3);・・・すべてのLEDを点灯します(AE_H8MB,
MD_M52F_48F1)。右側二つのLEDを点灯します(AKI_H8_USB)。
ledon(0xf);・・・すべてのLEDを点灯します。(AKI_H8_USB)
ledon(RIGHT);・・・一番右側のLEDを点灯します。(AE_H8MB,
MD_M52F_48F1, AKI_H8_USB)
ledon(LEFT);・・・一番左側のLEDを点灯します。(AE_H8MB,
MD_M52F_48F1, AKI_H8_USB)
ledon(0x1);・・・一番右側(AKI_H8_USB,
MD_M52F_48F1),左側(AE_H8MB)のLEDを点灯します。
ledon(0x7);・・・右側3つのLEDを点灯します。(AKI_H8_USB)
ledoff(0x1);・・・一番右側(AKI_H8_USB,
MD_M52F_48F1),左側(AE_H8MB)のLEDを消灯します。
(3) 待ちループ
void waitms(unsigned
ms);
/*msec待ちループ*/
void waitus(unsigned
us);
/*μsec*/
LCDやSCI1の初期化のために用意した待ちループです。もちろん汎用に使えます。ms,
usの値は65535まででmsまたはμs単位でwaitします。割り込みがない場合の実測値は,次の通りです。
<3048F16の場合>
waitms(1000);
1.02s
waitms(100); 100.00ms
waitms(10);
10.20ms
waitms(1);
1.00ms
waitms(0);
2.88us
-
waitus(10000); 10ms
waitus(1000);
1000us
waitus(100); 104.00us
waitus(10);
13.20us
waitus(5);
8.40us
waitus(1);
4.50us
waitus(0);
3.50us
<3052F25の場合>
waitms(100);
100.00ms
waitms(10);
10.00ms
waitms(1); 1.00ms
waitms(0); 1.76us
-
waitus(1000); 960.00us
waitus(100);
96.00us
waitus(10);
12.40us
waitus(1);
3.60us
waitus(0);
2.80us
使用例:LCDを左右交互に点滅させます。
#include "iodefine.h"
#include
"hwsetup.h"
#include "LcdLib.h"
void main(void);
void
main(void)
{
init_mother_system();
while(1){
ledon(RIGHT);
ledoff(LEFT);
waitms(500);
ledon(LEFT);
ledoff(RIGHT);
waitms(500);
}
}
(4) 出力デバイス設定関数
typedef enum{ LCD, SCI_1 }
devType;
void seldevice(devType
device); /*出力先の選択*/
typedef
enum{ C16L2, C20L4 } sizeType;
void lcdsize(sizeType size);
/*出力先の選択*/
後述のprintf関数の出力先(LCDユニット:LCD,Hyper
Terminal:SCI_1)を設定します。またLCDのサイズ(16桁2行:C16L2,20桁4行:C20L4)を設定します。上述のinit_mother_system();ではデフォルトで次のように設定されます。必要に応じて変更してください。
seldevice(LCD); /*デフォルト*/
lcdsize(C16L2);
/*デフォルト*/
使用例:Hyper Terminalに"Hello World"を出力します。
#include
"iodefine.h"
#include "hwsetup.h"
#include "LcdLib.h"
void
main(void);
void main(void)
{
init_mother_system();
seldevice(SCI_1);
printf("Hello
World");
while(1);
}
使用例:16桁2行のLCDに"Hello
World"を出力します。
#include "iodefine.h"
#include "hwsetup.h"
#include
"LcdLib.h"
void main(void);
void main(void)
{
init_mother_system();
seldevice(LCD);
lcdsize(C16L2);
clrscr();
printf("Hello World");
while(1);
}
(5) LCD専用関数
void
clrscr(void);
/*LCD画面クリア*/
void gotoxy(unsigned x,unsigned y);
/*LCDカーソル移動*/
次の6つの関数はVer2.2との互換のために定義したマクロです。
void
lcdprintc(char
c);
/*LCD表示,1文字*/
void lcdprints(char
*data);
/*LCD表示,文字列*/
void lcdprintx(unsigned char data);
/*LCD表示,小文字16進*/
void lcdprintX(unsigned char data);
/*LCD表示,大文字16進*/
void lcdprintb(unsigned char data);
/*LCD表示,2進*/
void lcdprintub(unsigned char data);
/*LCD表示,2進上位4ビット*/
void lcdprintlb(unsigned char data);
/*LCD表示,2進下位4ビット*/
clrscr・・・LCD画面をクリアします。
gotoxy・・・LCD上でカーソル移動します。なおカーソルは見えません。
lcdprintc・・・LCDに1文字表示します。
lcdprints・・・LCDに文字列を表示します。文字列中に\nがあっても対応します。
lcdprintx・・・dataを16進数二桁小文字表示します。
lcdprintX・・・dataを16進数二桁大文字表示します。
lcdprintb・・・dataを2進数8桁表示します。
lcdprintub・・・dataの上位4ビットをを2進数4桁表示します。
lcdprintlb・・・dataの下位4ビットをを2進数4桁表示します。
使用例:
#include
"iodefine.h"
#include "hwsetup.h"
#include "LcdLib.h"
void
main(void);
void main(void)
{
init_mother_system();
seldevice(LCD);
lcdsize(C16L2);
clrscr();
gotoxy(0,0);
lcdprints("Hello");
gotoxy(6,0);
lcdprints("World");
gotoxy(0,1);
lcdprints("LCD");
gotoxy(4,1);
lcdprints("Library");
while(1);
}
(6) printf,sprintf, devprintf関数
void printf(const
char *format,...); /*書式付き出力*/
void sprintf(char *buffer,
const char *format,...);
typedef enum{ LCD, SCI_1 } devType;
void
devprintf(devType device,const char
*format,...);/*出力先の選択付きprintf*/
通常のC言語のprintf,
sprintf関数と同じ動作をします。但し,値は返しません。devprintf関数は,一時的に出力デバイスを指定のものに変更して出力します。
なお,char又はunsigned
char型データの2進数表示に関して,%bが拡張されています。指定された印字幅を超える桁数となった場合,通常は印字幅は無視されますが,%bに関しては下位ビットから指定された幅数(桁数)のみの表示となります。下にサンプルをたくさん用意しましたので,確認してください。
書式指定 %[flags][width][.prec][prefix]type
書式指定については,変換指定文字typeに対して,8進数(o),浮動小数点数(f,e,E,g,G),ポインタ(n,p)以外すべてに対応しています。また2進数(b)が拡張されています。精度指定子[.prec]は対応していません。印字幅指定子[width]とフラグ文字[flags]には完全対応しています。エスケープシーケンスは,\\,
\n, \r に対応しています。
使用例:添付のサンプルプログラムtest.c。実行結果は6(7)参照。
#include
"iodefine.h"
#include "hwsetup.h"
#include "LcdLib.h"
void
main(void);
void main(void)
{
unsigned
i;
char s[17];
init_mother_system();
seldevice(LCD);
lcdsize(C16L2);
clrscr();
gotoxy(0,0); printf("Hello!");
for(i=0;i<20;i++)
devprintf(SCI_1,"%5u %04x %016b\n",i,i,i);
gotoxy(0,1);
sprintf(s,"%d year",2002); printf("%s",s);
while(1){
ledon(RIGHT);ledoff(LEFT);waitms(250);
ledon(LEFT);ledoff(RIGHT);waitms(250);
}
}
以下,サンプルです。
<サンプル(Turbo C Ver5.01J と同じ結果になることを確認済)>
プログラム | 実行結果 |
printf("test%s","str"); | [teststr] |
printf("test%5s","str4567890"); | [teststr4567890] |
printf("test%-5s@","str"); | [teststr @] |
printf("test%5s","str"); | [test str] |
printf("test%c",'c'); | [testc] |
printf("test%-5c@",'c'); | [testc @] |
printf("test%5c",'c'); | [test c] |
printf("test%X",(unsigned)35243); | [test89AB] |
printf("test%-#8X@",0x89ab); | [test0X89AB @] |
printf("test%5X",0x89ab); | [test 89AB] |
printf("%lx",4294967295); | [ffffffff] |
printf("%-#14lX@",16777215); | [0XFFFFFF @] |
printf("%#12lx",16777215); | [ 0xffffff] |
printf("%#012lx",0x12341234); | [0x0012341234] |
printf("%u",(unsigned)0xffff); | [65535] |
printf("%-8u@",(unsigned)65535); | [65535 @] |
printf("%8u",(unsigned)65535); | [ 65535] |
printf("%d",32767); | [32767] |
printf("% 8ld@",-32769); | [ -32769@] |
printf("%- 8ld@",32768); | [ 32768 @] |
printf("%-+8ld@",32769); | [+32769 @] |
printf("% -8ld@",32768); | [ 32768 @] |
printf("%+-8ld@",32769); | [+32769 @] |
<サンプル 2進数に関してオリジナル拡張,8bit型(char, unsigned char)のみ>
プログラム | 実行結果 |
printf("%08b",0x5c); | [01011100] |
printf("%8b",0x5c); | [ 1011100] |
printf("%4b",0x5c); | [1100] |
printf("%3b",0x5c); | [100] |
(7) puts,purchar関数
void puts(char *s);
void
putchar(char
c);
通常のC言語のputs関数,putchar関数と同じ動作をします。マクロです。値は返しません。どちらの関数も,上述のseldevice関数で指定されたデバイスに出力します。putsでは改行文字が出力されます。
(8) putc関数
typedef enum{ LCD, SCI_1 }
devType;
void putc(char c,devtype
dev);
通常のC言語のputc関数と同様の動作をします。指定したデバイスに1文字出力します。マクロです。値は返しません。
使用例:1行目に"Start",2行目に"End"を表示。
#include
"iodefine.h"
#include "hwsetup.h"
#include "LcdLib.h"
void
main(void);
void main(void)
{
init_mother_system();
seldevice(LCD);
lcdsize(C16L2);
clrscr();
puts("Start");
putchar('E');
putc('n',LCD);
putc('d',LCD);
while(1);
}
(9) 整数→文字列変換関数
char *ctoa(char value, char
*string, int radix);
/*stringはmax9byte*/
char *uctoa(unsigned char value, char *string, int
radix);/*stringはmax9byte*/
char *itoa(short int value, char *string, int
radix); /*stringはmax17byte*/
char *uitoa(unsigned
short value, char *string, int radix);/*stringはmax17byte*/
char *ltoa(long
value, char *string, int
radix);
/*stringはmax33byte*/
char *ultoa(unsigned long value, char *string, int
radix);/*stringはmax33byte*/
valueをヌル('\0')で終わる文字列に変換して,結果をstring内に格納します。valueはすべて整数型です。radixは,valueを変換する基数を示します。radixは,2以上の値を指定します。valueが負でradixが10であれば,stringの最初の文字はマイナス符号になります。各関数ともstringを指すポインタを返します。
ctoa・・・char型のvalueを基数radixで文字列に変換し,stringに格納します。
uctoa・・・unsigned
char型のvalueを基数radixで文字列に変換し,stringに格納します。
itoa・・・short
int型のvalueを基数radixで文字列に変換し,stringに格納します。
uitoa・・・unsigned
int型のvalueを基数radixで文字列に変換し,stringに格納します。
ltoa・・・long
int型のvalueを基数radixで文字列に変換し,stringに格納します。
ultoa・・・unsigned long
int型のvalueを基数radixで文字列に変換し,stringに格納します。
なお,stringに割り当てられる領域は,最後のヌル('\0')も含めて,返される文字列を格納するのに十分な大きさ(radixが2の時必要となる大きさ)がなければなりません。
ctoa・・・stringの大きさは,最大9バイト必要
uctoa・・・stringの大きさは,最大9バイト必要
itoa・・・stringの大きさは,最大17バイト必要
uitoa・・・stringの大きさは,最大17バイト必要
ltoa・・・stringの大きさは,最大33バイト必要
ultoa・・・stringの大きさは,最大33バイト必要
使用例:unsigned int型のデータを2進数に変換
#include "iodefine.h"
#include
"hwsetup.h"
#include "LcdLib.h"
void main(void);
void
main(void)
{
unsigned num=12345;
char s[17];
init_mother_system();
seldevice(LCD);
lcdsize(C16L2);
clrscr();
gotoxy(0,0);
printf("%16s",uitoa(num,s,2));
gotoxy(0,1);
printf("%08b%08b",(char)(num>>8),(char)num);
while(1);
}
7.謝辞
本ライブラリの開発にあたり,kosaka氏にたくさんアドバイスを頂きました。感謝いたします。
8.バージョンアップ履歴
3.8->4.0
・HEW開発環境に対応,ソースリストの完全公開
3.7->3.8
・AE_H8M8使用時,Port
Bに不正アクセスする場合があったバグをフィクス
3.6->3.7
2002/10/20
・AKI-H8-USB/LANマザー,H8/3052Fに対応。
3.5->3.6
2002/06/23
・sprintfのバグのフィクス(よけいな文字が後ろに出力されることがあった)。
・lcd関係の若干の改良。
3.4->3.5
2002/05/15
・ライブラリの小型化(ルーチンの改良,7160バイト→6282バイト)。
・sprintfの実装。
・マニュアルの訂正。
3.3->3.4
2002/04/18
・マニュアルの訂正。
3.0->3.3
2002/04/18
・printf("%%%3d%%",100);で,[%%100%]と表示されてしまうバグをフィクス。正解は[%100%]。
・電源投入時にマイコンが動作しない可能性があるバグをフィクス。(変数の初期化ミス。)
2.2->3.0
2002/04/01
・20桁×4行のLCDに対応。
・wait関数の改良。
・ANSI準拠のprintf関数を実装。
・LCDユニットだけでなくWindowsのHyper
Terminalへもprintf関数で出力を可能にした。
2.1->2.2 1998/12/27
・小坂氏のレポートを追加。
2.0->2.1 1998/12/25
・リンカの設定ファイル内のSTART設定の解説を追加(readme2.txt)。
1.4->2.0 1998/12/22
・clrscr();
実行直後,gotoxy関数を実行すると表示が不正になるバグをフィクス。
・タクトスイッチがつながっているポート(P4
7/4)の読みとり値が不正になるバグをフィクス。
9.感想・質問・ご要望など問い合わせ先
以下まで必ずメールにてご連絡ください。
松林:
matsu@tokyo-ct.ac.jp