トップページ H8 2 タイマー割り込みを使ったLED点滅

2 タイマー割り込みを使ったLED点滅
前回LEDの点灯プログラムを作成しました。これはPBポートにLED点灯の値を書き込むだけの簡単な処理です。しかし、今回は点滅です。点灯と消灯のデータを交互に書き換えることが必要です。
ではどうやって実現したらいいのでしょう?簡単に思いつくことは、

while(1)
{
  LED点灯;
  一定時間待つ;
  LE消灯;
  一定時間待つ;
}

一定時間待つ
{
   for(i = 0; i < 1234; i++)
   {
   }
}
こんな感じで実現できます。しかし、これでいいのでしょうか?CPUが待ち時間のために処理を奪われてしまいます。CPUが常に時間を監視しているわけです。これを人間の仕事にたとえれば、呼び出し音のならない電話で仕事をしているようなものです。電話が来るかどうか絶えず電話機を見ているようなものです。電話機を見ているからほかの仕事ができません。実際はいろいろな仕事をしている最中に電話機の呼び出し音がなることで仕事を中断して電話での対応をします。このように電話機の呼び出し音に相当する技術が割り込み処理です。仕事中に電話機の呼び出し音で割り込まれ、電話が終わってもとの仕事に復帰する。割り込み技術は1冊の本位になるくらいの分量があります。

LEDの点滅プログラムはタイマー割り込みをつかいます。これは学校などでチャイムがなったら勉強して次にチャイムが鳴ったら休み時間、といったイメージです。LEDの点滅では、
チャイム鳴って、
現在点灯なら消灯
現在消灯なら点灯
といった処理になります。

タイマー割り込みはITU(Integrated Timer Unit)のオーバーフロー割り込みを使って実現します。
最初に、start.asmに割り込みのベクターアドレスを書きます。
      
.SECTION ITU0,CODE,LOCATE=H'000060 .DATA.L NotUsed .DATA.L NotUsed .DATA.L _ITU_OVI_0 _ITU_OVI_0は全レジスタを退避し、 InterruptITUを呼び出し、 全レジスタを復旧してrteでもどります。 _ITU_OVI_0: push.l er0 push.l er1 push.l er2 push.l er3 push.l er4 push.l er5 push.l er6 jsr @_InterruptITU0 pop.l er6 pop.l er5 pop.l er4 pop.l er3 pop.l er2 pop.l er1 pop.l er0 rte
InterruptITU0がCで記述する割り込みの関数です。

CでのMyOS.cにおいて
最初にInitITU()で割り込みに必要なレジスタの設定をします。
タイマーは1つカウント時間は1/25MHz秒です。
とりあえずLEDでの点滅では1msで時間があれば十分なの1msごとに割り込みを発生させます。
タイマーはオーバーフローで割り込みが発生します。最大値は0xffffです。0xffff - 25000で1msのタイマー値になります

次にタイマー割り込みの本体InterruptITU0()はオーバーフロー割り込みのフラグをクリアしています。これをやらないと次の割り込みが発生しないので重要事項です。あとは時間を見てLEDの点灯・消灯を処理しています。g_nITU0は1msの時間をカウントするカウンターです。この例では1秒ごとに点灯。・消灯を繰り返します。

MyOSでは無限ループの中で何もしていないです。つまり、LEDの点滅のため常に時間を監視するという大変な仕事から解放されたことになります。
/*
        H8042 ファームウェア
                2007/1/12 Dyna System
*/
#include "3052.h"

/* start内に定義 */
extern  void EnableInterrupt(void);
extern  void DisableInterrupt(void);

int g_nITU0;
unsigned char g_cON;/*LED on = 1, off = 0 */

void InitPort()
{
        PB_DDR = 0xff;                          /* bit7..0 out  */
        PB_DR |= 0xff;
}

void InitITU()
{
        TSTR = 0x01;    /* timer 0 enable */
        TSNC = 0;
        TMDR = 0x0;
        TFCR = 0x0;
        TOER = 0x0;
        TOCR = 0xff;

        TCR0 = 0x00;    /* 分周なし */
        TIOR0 = 0x88;
        TIER0 = 0x04;/* オーバーフロー割り込み許可 */
        TCNT0 = 0xffff - 25000;/* 1 msec interval */
        GRA0 = 0;
        GRB0 = 0;
        g_nITU0 = 1000;
        g_cON = 0;
}


void InterruptITU0()
{
        TSR0 &= 0xfb;
        TCNT0 = 0xffff - 25000;/* 1 msec interval */
        g_nITU0--;
        if(g_nITU0 == 0)
        {
                g_nITU0 = 1000;
                if(g_cON == 0)
                {
                        g_cON = 1;
                        PB_DR &= 0x0e;
                }
                else
                {
                        g_cON = 0;
                        PB_DR |= 0x01;
                }
        }
}

void MyOS()
{
        InitPort();
        InitITU();
        EnableInterrupt();
        while(1)
        {
        }
}


サンプルプログラムのダウンロード