組み込み用コンパイラは、次のデータがROM領域に配置されるようコード生成を行います。
これら初期値を持つ変数や定数データはROM領域に配置されるため、プログラムから書き換えることができません。プログラム実行中に、これらを書き換える必要がある場合は、スタートアップルーチンで、ROM領域のデータをRAM領域へ転送しておきます(図4)。
リスト2は、そのプログラミング例です。
#define ROMstartAddr ((unsigned char *)0x00400000) #define ROMendAddr ((unsigned char *)0x004000FF) #define RAMstartAddr ((unsigned char *)0x00410000) void ROMtoRAM(void) { unsigned char *rom, *ram; for(rom=ROMstartAddr,ram=RAMstartAddr; rom<=ROMendAddr; rom++, ram++) *ram=*rom; }
しかし、ROM領域からRAM領域へ初期値データを転送するプログラムは、その分だけメモリが余分に必要となります。このため、特別な理由がない限り、このようなプログラミングスタイルは取らないようにするべきだと筆者は考えます。
プログラムで初期化されていない外部変数は、システムに電源を入れた直後やリセット後には、不定な値を持っています。スタートアップルーチンでは、これを特定の値でクリア(初期化)します(図5)。
リスト3は、424000H番地から42400FH番地までを0クリアするプログラムです。
#define startAddr ((unsigned char *)0x00424000) #define endAddr ((unsigned char *)0x0042400F) void init_clr(void) { unsigned char *p; for(p=startAddr; p<=endAddr; p++) *p=0x0; }
組み込みシステムではI/Oポートにさまざまな周辺機器が接続されています。また、H8マイコンに限らず多くのマイコンには、割り込みコントローラやタイマなど、CPU内蔵レジスタが存在しています。スタートアップルーチンでは、これらの周辺機器や内蔵レジスタが動作可能になるように初期化を行います。特にマルチプレクスされた端子は、その端子が信号線として使われるのかI/Oポートとして使われるのか、I/Oポートとして使われる場合は、それが入力か出力かをスタートアップルーチンで設定します。
本連載で使っているH8/3048Fマイコンには、10本の入出力ポート(ポート1、2、3、4、5、6、8、9、A、B)と1本の入力専用ポート(ポート7)があり、各ポートは兼用端子となっています。各ポートは、入出力を制御するデータディレクションレジスタ(DDR)と出力データを格納するデータレジスタ(DR)から構成されています。リスト4は、マルチプレクスされたポート0を汎用I/Oポートとして設定し、さらに出力ポートに設定するためのプログラムです。
void initPort(void) { *P0MD=0x01; // ポート0を汎用I/Oポートに設定: // ポートモードレジスタ(P0MD:0) *P0DIR=0xff; // ポート0を出力ポートに設定: // ポート入出力制御レジスタ(P0DIR:0-7) }
スタックの設定や各種初期化が終了したら、最初に実行されるアプリケーションプログラムをスタートアップルーチンから呼び出します。
WindowsやLinuxなどが搭載されたパソコンで動作するプログラム(ネイティブ環境)の場合、プログラムの最初の関数名は、int型のmainに限定されています。なぜint型のmainが最初の関数かというと、通常ネイティブ環境のスタートアップルーチンで最初に呼び出す関数がmainとなっているためです。これに対して組み込みシステムでは、スタートアップルーチンが呼び出す最初の関数はmainに限定されず、プログラマが自由に決めることができます。
Copyright © ITmedia, Inc. All Rights Reserved.