――大規模で複雑なプログラムを作るとき、すべての処理を1つのファイルに記述するこはありません。プログラムを機能によって分類し、複数のモジュールからプログラムが構成されるように作成します。それではプログラムをモジュール化してみましょう。
晴子さんは、プログラムを「main.c」「h8tiny.h」「interrupu.c」の3つに分けました(晴子さんのプログラム「7seg2.lzh」)。それでは個々のプログラムを解説しましょう。
main.cには、main関数のみが記述されています。ここではH8マイコンの周辺機能を初期化し、さらにカウント変数をクリアして無限ループに入ります。仕事はすべて割り込みで行われるからです。
いままで紹介したプログラムでは、プログラムの先頭部分でレジスタの宣言などを行っていましたが、main.cでは、
#include “h8tiny.h”
と1行で済ませています。
#includeはソースを差し込む指定で、ここでh8tiny.hファイルの内容が差し込まれるのです。
#include “h8tiny.h” int count1, count10; int main(void) { asm("orc.b #0xc0,ccr"); /* 割り込み禁止 */ /* タイマAの初期化 */ TMA = 0x18; IRR1 &= ~IRRTA; IENR1 |= IENTA; /* タイマVの初期化 */ TCSRV = 0; TCORA = 124; TCRV1 = 0xe3; TCRV0 = 0x4b; /* ポートの初期化 */ PCR5 = 0x03; PCR8 = 0xff; asm("andc.b #0x3f,ccr"); /* 割り込み許可 */ count1 = 0; count10 = 0; for (;;) ; return 0; }
それでは、h8tiny.hを見てみましょう。
h8tiny.hは、次のようにレジスタの宣言などが含まれています。このようにレジスタ宣言をヘッダファイルで定義しておけば、後はインクルードするだけで共通に利用できます。
#define PCR5 (*((volatile unsigned char *)0xffe8)) #define PDR5 (*((volatile unsigned char *)0xffd8)) #define PCR8 (*((volatile unsigned char *)0xffeb)) #define PDR8 (*((volatile unsigned char *)0xffdb)) #define TMA (*(volatile unsigned char *)0xffa6) #define IRR1 (*(volatile unsigned char *)0xfff6) #define IENR1 (*(volatile unsigned char *)0xfff4) #define TCRV0 (*(volatile unsigned char *)0xffa0) #define TCSRV (*(volatile unsigned char *)0xffa1) #define TCORA (*(volatile unsigned char *)0xffa2) #define TCORB (*(volatile unsigned char *)0xffa3) #define TCRV1 (*(volatile unsigned char *)0xffa5) #define IRRTA 0x40 #define IENTA 0x40 #define CMFA 0x40 #define LED1 0x01 #define LED2 0x02
interrupu.cには、割り込みベクタが書かれています。ここでもH8マイコンの周辺機能を使いますが、レジスタの定義はh8tiny.hをインクルードするだけです。
カウント変数count1とcount10の宣言で、
extern int count1, count10;
と、extern宣言していますが、これは外部の変数を使うという意味です。main.cにある変数count1とcount10を使うので、interrupu.cではextern宣言しているわけです。
#include "h8tiny.h" void int_tima(void) __attribute__((interrupt_handler)); void int_timv(void) __attribute__((interrupt_handler)); extern int count1, count10; void int_tima(void) { IRR1 &= ~IRRTA; if (count1 < 9) count1++; else { count1 = 0; if (count10 < 9) count10++; else count10 = 0; } } void int_timv(void) { static unsigned char LED[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; static int tagle = 0; if (TCSRV & CMFA) { TCSRV &= ~CMFA; if (tagle) { PDR5 = ~LED2; PDR8 = LED[count1]; tagle = 0; } else { PDR5 = ~LED1; PDR8 = LED[count10]; tagle = 1; } } else TCSRV = 0; }
最後に、プログラムの実行方法を説明します。
まず、スタートアップ・ルーチンをアセンブルします。
$ h8300-elf-gcc -c crt0.s
そして、main.cをコンパイルします。
$ h8300-elf-gcc -c -mh -mn main.c
さらに、interrupt.cをコンパイルします。
$ h8300-elf-gcc -c -mh -mn interrupt.c
最後にリンクして、実行ファイルを作りましょう。
$ h8300-elf-gcc -o swcount.bin -T 3664.x -nostdlib -mh -mn crt0.o main.o interrupt.o
いかがでしょうか。ソースファイルの数だけコンパイルし、最後にリンクすればよいのです。意外と簡単ですよね。
なるほど、割り込みも奥が深いなぁ。
そうね。
制御のプログラムってイベント処理が多いから。
ところで晴子さん。
宿題がちゃんとできたご褒美(デート)、いつにしましょうか?
えっ、ワタシいつそんな約束したかしら。
あっ、いけない。今日買い物頼まれているんだったわ。
健一君にはいつもどおり宿題を用意しといたから、ワタシ帰るね。
バイバーイ。
えっー!!
H8Tiny-USBから通信で「Hello world」を送ってね。パソコンに通信ソフトをインストールしないとダメよ!!
前回に引き続き、タイマ割り込みをテーマに、7セグメントLEDのダイナミック点灯を行いました。これでH8Tiny-USBのハードウェアをすべて使いこなしたことに……。といいたいところですが、まだ「シリアル通信」が残っていますね。
H8Tiny-USBとパソコンをつなぐUSBケーブルは、電源の供給、プログラムのダウンロードのほか、H8Tiny-USBとの通信に使うことができます。というわけで、次回(ついに完結!!)は、シリアル通信によるprintf関数を作ってみましょう。お楽しみに!(次回に続く)
Copyright © ITmedia, Inc. All Rights Reserved.