徒然日記 電子工作編でタグ「gcc」が付けられているもの
昨日、完全 ROM 化してエージングをかけていると不具合発生。15分程度動かしていると、強制的に’under'状態になり、表示がちらつく感じ。
調べてみると、プログラム内部の基準クロックにしていた、TimerA の割り込みで 31.25ms ごとに更新される 16bit のフリーランカウンタの扱いに問題があった。
16bit の符号無し整数でボローが出たときの処理が、x86 の gcc と違うような気がするのだが気のせいか?
-----
一晩エージングしたが、特に問題はないよう。
調べてみると、プログラム内部の基準クロックにしていた、TimerA の割り込みで 31.25ms ごとに更新される 16bit のフリーランカウンタの扱いに問題があった。
16bit の符号無し整数でボローが出たときの処理が、x86 の gcc と違うような気がするのだが気のせいか?
-----
一晩エージングしたが、特に問題はないよう。
- ごく希に、一瞬だけ 'under' を表示する
- data セグメントの定数にうまくアクセスできない

リセットスイッチ、FLASH 書き込みモード切替スイッチ、電圧測定用の分圧回路を組み込む。イグニッションパルスの入力回路は、ブレッドボードを使ってカットアンドトライをしたが、手持ちに丁度いいコンデンサがなかったので保留。
電圧計測の AD変換はあっさり動いたので組み込むことに。簡単なオープニング画面を表示させて、いよいよ完全 ROM 化。
Makefile に rom.mot を作るルールを追加、rom から起動するプログラム用のスタートアップルーチンとリンカスクリプトを持ってきて、コンパイル。
リンカスクリプトの変更(1,2)を忘れていて多少手こずったが、CPU へのヘキサの書き込みは一発で動く。
先に ROM 化したプログラムとあわせてヘキサが2本(H8OS も入れると3本)、メモリマップも奇々怪々。機会があれば一本化したほうがいいかも。
あとは、注文した LCD をケースに入れて、ステラから信号を引っ張り出して...。
----
エージングしていたんだが、表示がおかしい。正常な信号が入っているのに under と判断しているようだ。
やっぱり、簡単にはいかないか。

今日はここまで 30分。のこり 30分は...。結局コード圧縮に勤しむ。全部の関数を main に入れたり、2進->10進変換に H8OS API を使ったりで 256byte ほど圧縮。
あとは、正常動作、回転数高すぎ、回転数低すぎ、エンジン停止の状態を作ればプログラムは一応完成。作ること自体は難しくないのだが、メモリが足りない...。
RAM 上で動作チェックはやめて、いきなり ROM で動かすことにしようか?
-----
ちなみに、自分で作った H8+GCC 向けの「吐き出されるオブジェクトの小さい2進->10進変換」のソースはこちら。10分かそこらで作ったたいしたコードではないのだが、記念に残しておこう (^^;
short i,ptr,n,m;
ptr = 19;
for (i = 0;i < 4;i++) {
// while(x > 0) {
n = x / 10;
m = x - n * 10;
LcdRam[ptr--] = m + '0';
x = n;
}
今日は LCD アクセスルーチンの ROM 化。の前に、昨日作業を終えてから気がついた TimerW アクセスルーチンの<del>バグ</del>不具合を直して動作確認。
そのあと、LCD アクセスルーチンを ROM 化し、テストプログラムを書いたのだが、あっという間にコードが膨れ上がり RAM 容量の壁にぶち当たる。
----
このプロジェクトを始めてから、早一月半。LCD アクセスルーチンのファイルのタイムスタンプは丁度一ヶ月前。いくらなんでも2ヶ月以内に完成させたいと、今日の作業時間は2時間。
予想外のところに時間を吸い取られていく...。
----
H8+gcc は異様にコード効率が悪い。
struct sLcdFont {
unsigned char code;
unsigned char *dat;
};
void lcd_font_set_up(void)
{
int i;
struct sLcdFont buf;
for (i = 0;i < 6;i++) {
buf.code = i;
buf.dat = font[i];
func_tbl[SetLcdFont]((int)&buf);
}
}
SetLcdFont は定数。
この関数が 92byte。
void put_disp(unsigned short x)
{
disp_bar(x);
disp_number(x);
func_tbl[RewriteLcd](0);
}
RewriteLcd は定数
この関数が 50byte
ちなみにコンパイルオプションは -O。引数のレジスタ渡しとかしないのだろうか?
開発中のコードに使える RAM 領域は 1kbyte 強ある。これだけあれば何とかなると思っていたのだが、全然足りなくなってきた。
そのあと、LCD アクセスルーチンを ROM 化し、テストプログラムを書いたのだが、あっという間にコードが膨れ上がり RAM 容量の壁にぶち当たる。
----
このプロジェクトを始めてから、早一月半。LCD アクセスルーチンのファイルのタイムスタンプは丁度一ヶ月前。いくらなんでも2ヶ月以内に完成させたいと、今日の作業時間は2時間。
予想外のところに時間を吸い取られていく...。
----
H8+gcc は異様にコード効率が悪い。
struct sLcdFont {
unsigned char code;
unsigned char *dat;
};
void lcd_font_set_up(void)
{
int i;
struct sLcdFont buf;
for (i = 0;i < 6;i++) {
buf.code = i;
buf.dat = font[i];
func_tbl[SetLcdFont]((int)&buf);
}
}
SetLcdFont は定数。
この関数が 92byte。
void put_disp(unsigned short x)
{
disp_bar(x);
disp_number(x);
func_tbl[RewriteLcd](0);
}
RewriteLcd は定数
この関数が 50byte
ちなみにコンパイルオプションは -O。引数のレジスタ渡しとかしないのだろうか?
開発中のコードに使える RAM 領域は 1kbyte 強ある。これだけあれば何とかなると思っていたのだが、全然足りなくなってきた。
昨日に引き続いて TimerW アクセスルーチンを ROM化。測定した周期の読み出しは
func_tbl[GetPeriodW](0)
なのだが、print の引数に
func_tbl[GetPeriodW]
と書いてしまって、アドレスが返ってきてしまい一瞬あせる。すぐにアドレスが返っていると気づいて事なきを得たが、気がつかなければかなりハマるところだった。
-----
「関数のポインタの配列へのポインタ」の値を読み出す部分だが、気になったので見直しをかけて、結局以下の形にした。
#define func_tbl_ptr (*(int (***)(int))0x5800)
もう何がなんだか...。ちなみに、
#define func_tbl_ptr (*(int (**[])(int))0x5800)
は、読み出した値を代入するところで形が合わないとワーニングになった。
-----
今日はここで時間切れ。明日は LCD アクセスルーチンの ROM化だ。
func_tbl[GetPeriodW](0)
なのだが、print の引数に
func_tbl[GetPeriodW]
と書いてしまって、アドレスが返ってきてしまい一瞬あせる。すぐにアドレスが返っていると気づいて事なきを得たが、気がつかなければかなりハマるところだった。
-----
「関数のポインタの配列へのポインタ」の値を読み出す部分だが、気になったので見直しをかけて、結局以下の形にした。
#define func_tbl_ptr (*(int (***)(int))0x5800)
もう何がなんだか...。ちなみに、
#define func_tbl_ptr (*(int (**[])(int))0x5800)
は、読み出した値を代入するところで形が合わないとワーニングになった。
-----
今日はここで時間切れ。明日は LCD アクセスルーチンの ROM化だ。
昨日作った ROM化した TimerA アクセスルーチンを使うためのプログラムを作る。
「関数のポインタの配列へのポインタ」をアクセスするのに悩んだが、なんとか一発で動く。
ROM にある関数のアクセスはこんな感じ。
enum {dummy,InitTimerA,GetTimerA_Counter};
#define func_tbl_ptr (int (**)(int))(*(volatile unsigned long *)0x5800)
int (**func_tbl)(int);
func_tbl = func_tbl_ptr;
func_tbl[InitTimerA](0);
このあと、二つの *.mot ファイルを CPU の FLASH に書き込むのに手間取るが(結局二本の *.mot をエディターで結合した)、ROM 化した関数のアクセス・動作は問題なく成功。
明日は TIimerW アクセスルーチンの ROM 化だ。
「関数のポインタの配列へのポインタ」をアクセスするのに悩んだが、なんとか一発で動く。
ROM にある関数のアクセスはこんな感じ。
enum {dummy,InitTimerA,GetTimerA_Counter};
#define func_tbl_ptr (int (**)(int))(*(volatile unsigned long *)0x5800)
int (**func_tbl)(int);
func_tbl = func_tbl_ptr;
func_tbl[InitTimerA](0);
このあと、二つの *.mot ファイルを CPU の FLASH に書き込むのに手間取るが(結局二本の *.mot をエディターで結合した)、ROM 化した関数のアクセス・動作は問題なく成功。
明日は TIimerW アクセスルーチンの ROM 化だ。
プログラム全体のサイズが H8OS/3664 の RAM 領域で開発できる範囲を大きく超えることが明らかになったので、今まで作ったルーチンを少しずつ ROM 化していくことにした。
最初はサイズが小さく機能も少ない TimerA 割り込み関連の処理から。H8OS 本体のリンカスクリプトを見ながら専用のリンカスクリプトを作る。関数ベクタテーブルの ROM 化に手間取ったがとりあえずそれらしい *.mot ファイルが完成。
しかし、こういうのは最近の組み込みの範疇に入るのだろうか?いや、入るのだろうが、おそらく「プリミティブ」なごく一部な領域になるのだろう。
今日はここで time up。明日は呼び出す側のプログラム作成、ROM への書き込み、そして debug だ。
----
関数へのポインタの配列を定数化にするのに、少し悩む。
const int (*func_tbl[])(int) = {main,...};
だと、const int を返す関数のポインタへの配列になってしまう。
int (* const func_tbl[])(int) = {main,...};
正解はこちら。やっぱりポインタは難関だ。
----
gcc は(同じファイルにない)外部関数のアドレスを引っ張ってこれる。以前使っていた 8/16bit の C コンパイラではこれが出来なくて苦労した記憶がある。
----
今日、ヘキサファイルを眺めていて気付いたが、H8 の gcc はポインタが 4byte もある。コードがでかくなるわけだ。
昨日少し調べたが、H8 CPU は 16bit レジスタが 16本もある。なるほど、C で割り込みハンドラを書くとスタックを馬鹿食いたくさん使うわけだ。
最初はサイズが小さく機能も少ない TimerA 割り込み関連の処理から。H8OS 本体のリンカスクリプトを見ながら専用のリンカスクリプトを作る。関数ベクタテーブルの ROM 化に手間取ったがとりあえずそれらしい *.mot ファイルが完成。
しかし、こういうのは最近の組み込みの範疇に入るのだろうか?いや、入るのだろうが、おそらく「プリミティブ」なごく一部な領域になるのだろう。
今日はここで time up。明日は呼び出す側のプログラム作成、ROM への書き込み、そして debug だ。
----
関数へのポインタの配列を定数化にするのに、少し悩む。
const int (*func_tbl[])(int) = {main,...};
だと、const int を返す関数のポインタへの配列になってしまう。
int (* const func_tbl[])(int) = {main,...};
正解はこちら。やっぱりポインタは難関だ。
----
gcc は(同じファイルにない)外部関数のアドレスを引っ張ってこれる。以前使っていた 8/16bit の C コンパイラではこれが出来なくて苦労した記憶がある。
----
今日、ヘキサファイルを眺めていて気付いたが、H8 の gcc はポインタが 4byte もある。コードがでかくなるわけだ。
昨日少し調べたが、H8 CPU は 16bit レジスタが 16本もある。なるほど、C で割り込みハンドラを書くとスタックを
昨日に引き続いて、タイマW のソースにタイマA を組み込むとうまく動かない不具合の調査。色々やっていくうちに、スタックがコードを食い荒らしているのではなかろうかと考え H8OS のメモリダンプやらメモリクリアを使って見て行くと、確かにスタックがコードまでは行かなくても bbs 領域まで侵食した跡が見える。
割り込みハンドラの終わり方を間違ってスタックを食い続けているか、いや、ハンドラの管理は H8OS だし。CPU の data sheet を見ても特殊な「お約束」があるようにも見えないし...。
あれこれやった結果、割り込みを使うとその分スタック使用量が増えて、H8OS のユーザーRAM 領域にまで入り込むらしいことがわかった。これなら使わないソースを組み込んだだけで動作がおかしくなるのも納得がいく。
今日はここまでで 20分のオーバー。つまらないことで一日半も使ったと思うと悔しいが、2時間と思えば、そうでもない。
-----
これ以上は RAM 上での開発は無理。ここから先は毎回フラッシュに焼いていかなくてはならない。CPU の書き込み制限 100回以内に完成させられるか?
----
それにしても、H8 のメモリ利用率は、8086/Z80 に比べて異様に悪い気がする。気のせいなのか、いまどきの CPU はこんなものなのか?
割り込みハンドラの終わり方を間違ってスタックを食い続けているか、いや、ハンドラの管理は H8OS だし。CPU の data sheet を見ても特殊な「お約束」があるようにも見えないし...。
あれこれやった結果、割り込みを使うとその分スタック使用量が増えて、H8OS のユーザーRAM 領域にまで入り込むらしいことがわかった。これなら使わないソースを組み込んだだけで動作がおかしくなるのも納得がいく。
今日はここまでで 20分のオーバー。つまらないことで一日半も使ったと思うと悔しいが、2時間と思えば、そうでもない。
-----
これ以上は RAM 上での開発は無理。ここから先は毎回フラッシュに焼いていかなくてはならない。CPU の書き込み制限 100回以内に完成させられるか?
----
それにしても、H8 のメモリ利用率は、8086/Z80 に比べて異様に悪い気がする。気のせいなのか、いまどきの CPU はこんなものなのか?

ここは手こずると思っていたのでうれしい。
時間が余ったので何をしようかと悩んだが、周期測定テストプログラム(タイマW使用)にタイマA テストプログラムを合体させることにした。が、ここでハマる。
タイマA 関連を別ファイルにしてコンパイルしたのだが、動かず。まぁ、割り込み関係だし、とタイマA 関連の関数をコメントアウトして周期測定プログラムを元に戻してコンパイルするも動かず。まさか...。Makefile からタイマA のソースを外すと動く。リンカの問題?タイマA のソースを周期測定プログラムに持ってきて...、それでも同じ。コードが入るだけで(呼び出さなくても)動かない。なぜ??
今日はこれで 10分オーバー。
人間、運の総量は決まっているのだろうか、やっぱり。
今日は実際の動作をイメージした LCD表示ルーチンの作成。バーグラフ表示は出来ているし、自前の 10進変換を作れば良いだけのはずだったのだが...。
現在、表示の時間待ちに H8OS の sleep を使っているのだが、最短の待ち時間が少々長い。ソースを見ると タイマー V を使っているので、パラメータを変更して入力クロックを 1/128 から 1/32 にしようとレジスタを直接アクセスしたのだが...、動かない。2-30分ほどやってみたが原因不明。
ここでハマって入るわけにもいかないので、途中であきらめ 10進変換ルーチンを作成開始。と、「メモリが足りない」のメッセージが。どうやら 変数÷変数 を実行すると演算ライブラリがリンクされて 200byte 程度コードが増えるようだ。変数÷定数だとリンクされないのだが...。
アルゴリズムを変え、使っていない LCD アクセスの関数削除したりして何とか動くようになったが、ゼロサプレスが入りそうもない。
予想外のところに時間をとられて今日は1時間オーバー。
-----
表示周りはとりあえずここまでにして、明日からは周期測定部に入ろう。まずは車両エミュレーターの作成か。
現在、表示の時間待ちに H8OS の sleep を使っているのだが、最短の待ち時間が少々長い。ソースを見ると タイマー V を使っているので、パラメータを変更して入力クロックを 1/128 から 1/32 にしようとレジスタを直接アクセスしたのだが...、動かない。2-30分ほどやってみたが原因不明。
ここでハマって入るわけにもいかないので、途中であきらめ 10進変換ルーチンを作成開始。と、「メモリが足りない」のメッセージが。どうやら 変数÷変数 を実行すると演算ライブラリがリンクされて 200byte 程度コードが増えるようだ。変数÷定数だとリンクされないのだが...。
アルゴリズムを変え、使っていない LCD アクセスの関数削除したりして何とか動くようになったが、ゼロサプレスが入りそうもない。
予想外のところに時間をとられて今日は1時間オーバー。
-----
表示周りはとりあえずここまでにして、明日からは周期測定部に入ろう。まずは車両エミュレーターの作成か。
今日はコード圧縮の続き。やっている途中で LCD の初期化に失敗する理由がわかった。時間待ちの関数の中の for のダミーループが最適化で無くなっていた orz。for で使う変数を volatile 宣言し、時定数を調整するとオリジナルのソースで問題なく表示できるようになった。本当はオシロを引っ張り出してきて波形を見ながら調整したほうが良いのだが、仕事じゃないし、良いことにしよう (^^;
IO レジスタのアクセスに構造体を使うのをやめてコードの圧縮も完了。H8 はメモリ間接のアドレッシングで論理演算が出来ないらしく、結構手間取る。ソースはかなり見づらくなったが、1bit 操作するだけで 16byte も使われてはかなわない。
結局 1K 弱のコードから 100byte 程度削った。
切のいいところまでやってしまって、今日も 30分の時間オーバー。
明日は表示部を仮fix 予定。次はいよいよカウンターを使ってパルス間の周期測定だ。
-----
こういうことをするのなら、アセンブラを使いたいところだが、H8 のアセンブラは未経験。手を広げすぎてプロジェクト自体が収束しなくては本末転倒。
IO レジスタのアクセスに構造体を使うのをやめてコードの圧縮も完了。H8 はメモリ間接のアドレッシングで論理演算が出来ないらしく、結構手間取る。ソースはかなり見づらくなったが、1bit 操作するだけで 16byte も使われてはかなわない。
結局 1K 弱のコードから 100byte 程度削った。
切のいいところまでやってしまって、今日も 30分の時間オーバー。
明日は表示部を仮fix 予定。次はいよいよカウンターを使ってパルス間の周期測定だ。
-----
こういうことをするのなら、アセンブラを使いたいところだが、H8 のアセンブラは未経験。手を広げすぎてプロジェクト自体が収束しなくては本末転倒。
昨日作ったソースを見直してから動かしてみると...、動かない (; ;)。見直しをかけると、CGRAM に送るデータをコマンド(RS=0)として送っていた。そこを直すと無事動いた。フォントもこちらでイメージしたとおり。
と、ここで不具合発見。自前のプログラムから直で LCD をアクセスするとうまく表示されない。一度 H8OS を使って LCD 表示をしてからだとうまくいく。初期化ルーチンに問題があるのか...。
それはそれとして、今日の残り時間はコードを短くする検討に入る。
と、ここで不具合発見。自前のプログラムから直で LCD をアクセスするとうまく表示されない。一度 H8OS を使って LCD 表示をしてからだとうまくいく。初期化ルーチンに問題があるのか...。
それはそれとして、今日の残り時間はコードを短くする検討に入る。
P4:CGRAM アクセス成功の続きを読む
予定を変更して、
1 はほどなく終わったが、2 がうまくいかない 現 ->旧のファイル共有は出来たのだが...。あきらめて 3 に取り掛かり、.mot ファイルが出来上がったところでタイムアップ。
----
明日は今日作った .mot ファイルの動作確認から
- 旧メインマシン(現iTunesサーバー)に VNC サーバーをインストール
- 旧メインマシンから現メインマシンへファイル共有できるようにする
- H8OS の API を使って、単にシリアルポートからメッセージを出力するだけのプログラムを動作させる
1 はほどなく終わったが、2 がうまくいかない 現 ->旧のファイル共有は出来たのだが...。あきらめて 3 に取り掛かり、.mot ファイルが出来上がったところでタイムアップ。
----
明日は今日作った .mot ファイルの動作確認から
昨日作った LCD表示プログラムを H8/OS 上のコマンドインタープリターで動くようにあれこれやってみる。
この環境は何年か前に使ったことがあるのだが、そのときは H8/OS のページにあるLinux 上で動くコンパイラを使ったので割とすんなり行ったのだが、今回は GDL と一緒にインストールした Windows 版の gcc をコマンドラインから使うのでいろいろうまくいかない。
H8/OS のサンプルプログラムの Makefile と リンカスクリプトを使ったがそのままではコンパイルできない。
あれこれやって、結局
Makefile は h8300-hms-* -> h8300-elf-*
ram3664.x は OUTPUT_FORMAT("coff-h8300") ->OUTPUT_FORMAT("elf32-h8300")
ぐらいでそれらしいヘキサ (*.MOT)ファイルを吐くようになった。
ちなみにコードサイズ自体は 0x324 byte 。H8/OS + コマンドインタープリター環境ではユーザーの使える RAM サイズは 1.5KByte 程度なので、当分はなんとかなるだろう。
----
このあとの工程は、
H/W 作成
H8/OS をインストールさせてコマンドインタープリタの動作確認
H8/OS の API を使って LCD 接続確認
自作 LCD アクセスルーチンのテスト・デバッグ
パルス幅検出部のコーディング
車両エミュレーターの作成
車両エミュレータを使ってのテスト
全体のコーディング
車両エミュレータを使ってのテスト
実車両を使って実パルスを測定
パルス入力部分の設計・実装
実車両に積んでテスト
....ずいぶんあるなぁ。一日一時間ずつやってるんだけど、何ヶ月かかるか...。
この環境は何年か前に使ったことがあるのだが、そのときは H8/OS のページにあるLinux 上で動くコンパイラを使ったので割とすんなり行ったのだが、今回は GDL と一緒にインストールした Windows 版の gcc をコマンドラインから使うのでいろいろうまくいかない。
H8/OS のサンプルプログラムの Makefile と リンカスクリプトを使ったがそのままではコンパイルできない。
あれこれやって、結局
Makefile は h8300-hms-* -> h8300-elf-*
ram3664.x は OUTPUT_FORMAT("coff-h8300") ->OUTPUT_FORMAT("elf32-h8300")
ぐらいでそれらしいヘキサ (*.MOT)ファイルを吐くようになった。
ちなみにコードサイズ自体は 0x324 byte 。H8/OS + コマンドインタープリター環境ではユーザーの使える RAM サイズは 1.5KByte 程度なので、当分はなんとかなるだろう。
----
このあとの工程は、
H/W 作成
H8/OS をインストールさせてコマンドインタープリタの動作確認
H8/OS の API を使って LCD 接続確認
自作 LCD アクセスルーチンのテスト・デバッグ
パルス幅検出部のコーディング
車両エミュレーターの作成
車両エミュレータを使ってのテスト
全体のコーディング
車両エミュレータを使ってのテスト
実車両を使って実パルスを測定
パルス入力部分の設計・実装
実車両に積んでテスト
....ずいぶんあるなぁ。一日一時間ずつやってるんだけど、何ヶ月かかるか...。