検索
連載

割り込みテクニックでタイマを使おうイチから作って丸ごと学ぶ! H8マイコン道(10)(3/3 ページ)

制御プログラミングで欠かせない割り込み処理。今回は、C言語によるタイマA割り込みプログラムについて詳しく解説する。

Share
Tweet
LINE
Hatena
前のページへ |       

 それではC言語によるタイマA割り込みプログラムを考えてみます。

 C言語による割り込み処理ルーチンは、関数と同じ形式でコーディングします。割り込み処理ルーチンは、引数も戻り値もないので、


void 割り込み処理ルーチン名(void)
{
    ……
} 

となります。

 しかし、割り込み処理ルーチンは特別なコードが必要なので、

void int_tima(void) __attribute__((interrupt_handler)); 

のように、「__attribute__((interrupt_handler))」を付けてプロトタイプ宣言します。するとCコンパイラは普通の関数と区別して、割り込み処理ルーチンとしてのコンパイルを行います。割り込み処理ルーチンの宣言は、Cコンパイラによって異なります。この方法はH8マイコン用GCC特有のものなので注意してください。

 また、割り込み要求を0クリアするのは、割り込み処理ルーチンの仕事です。

    IRR1 &= ~IRRTA; 

で、割り込み要求を解除してください。

 次に、割り込み許可について説明します。

 図6にH8マイコンの割り込みの仕組みを示します。

 H8マイコンの周辺機能には、「割り込み要求フラグ」と「割り込みイネーブルビット」があります。割り込みイネーブルビットが「1」のとき、割り込み要求ビットが「1」にセットされると、CPUに対して割り込みが要求されます。割り込みイネーブルビットが「0」ならば、割り込み禁止です。つまり、割り込みイネーブルビットを「1」にセットしないと、割り込みは発生しません。

 周辺機能からの割り込み要求は、割り込みコントローラで制御され、CPUに通知されます。

 CPUの「コンディションコードレジスタ(CCR)」のIビットが「0」のとき、CPUは割り込みを受け付けます。Iビットが「1」のときは割り込みが保留されます。つまり、割り込み処理を実行するには、Iビットが「0」でなければなりません。

H8マイコンの内部割り込み
図6 H8マイコンの内部割り込み

 それでは、タイマAの割り込みを許可しましょう。

 IENTAビットは、「割り込みイネーブルレジスタ1(IENR1)」の第6ビットにあります(図7)。

 IENR1を、

#define IENR1 (*(volatile unsigned char *)0xfff4) 

と定義し、IENTAビットを、

#define IENTA 0x40 

と定義します。

 IENTAビットに「1」をセットするには、

    IENR1 |= IENTA; 

とします。これで、タイマA割り込みが発生するようになります。

割り込みイネーブルレジスタ1(IENR1)
図7 割り込みイネーブルレジスタ1(IENR1)

 次に、CCRのIビットを操作します。

 メモリ空間に配置されたレジスタは、ポインタによりC言語で操作できますが、CPUのレジスタはC言語で操作できません。このような場合、アセンブリ命令を直接記述します。

 多くのマイコン用Cコンパイラでは、C言語の中にアセンブリ命令を組み込めるように言語仕様が拡張されています。

 CPUのIビットを0クリアし、CPUが割り込みを受け取るようにするには、

    asm("andc.b #0x3f,ccr"); 

とアセンブリ命令を実行させます。

 同じように、Iビットをセットし割り込みを保留させるには、

    asm("orc.b #0xc0,ccr"); 

とします。

 プログラムは次のようになります(ソースコード3)。

/*  1秒間隔でカウント表示する。
 *
 */
#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 IRRTA 0x40
#define IENTA 0x40
 
void int_tima(void) __attribute__((interrupt_handler));
 
unsigned char LED[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
int count;
 
int main(void)
{
    asm("orc.b #0xc0,ccr");     /* 割り込み禁止 */
    PDR5 = 0xfe;
    PCR5 = 0x03;
    PCR8 = 0xff;
    TMA = 0x18;
    IRR1 &= ~IRRTA;
    IENR1 |= IENTA;
    asm("andc.b #0x3f,ccr");    /* 割り込み許可 */
 
    count = 0;
    PDR8 = LED[count];
    for (;;) ;
    return 0;
}
 
void int_tima(void)
{
    IRR1 &= ~IRRTA;
    if (count < 9)
        count++;
    else
        count = 0;
    PDR8 = LED[count];
} 
ソースコード3

  • ソースコード3のダウンロード(seccnt2.lzh

コラム(1):割り込みベクタテーブル

 H8マイコンは、割り込み要求に応じた処理を呼び出すために、「割り込みベクタテーブル(Interrupt Vector Table)」でアドレス管理しています。H8マイコンの割り込みベクタテーブルは0番地に配置されています。

 割り込みベクタテーブルには、割り込み処理ルーチンのスタートアドレスが格納されています。その内容を表2に示します。

 もし、タイマA割り込みが発生したならば、CPUは「0x0026」番地のデータをスタートアドレスとして、タイマA割り込み処理ルーチンに飛んでいきます。H8マイコンがリセットした場合も同様で、CPUは0番地のデータを「プログラムカウンタ(PC)」にセットし、そこから最初のプログラムの実行がはじまります。つまり、0番地にはスタートアップルーチンのアドレスが設定されているのです。

ベクタ番号 割り込み要因 ベクタアドレス
0 リセット 0x0000
1〜6 システム予約 0x0002〜
7 外部割り込み(NMI) 0x000E
8〜11 トラップ命令 0x0010〜
12 アドレスブレーク 0x0018
13 スリープ命令の実行による直接遷移 0x001A
14〜18 外部割り込み(IRQ0〜IRQ3,WKP) 0x001C〜
19 タイマA割り込み 0x0026
20 システム予約 0x0028
21 タイマW割り込み 0x002A
22 タイマV割り込み 0x002C
23 SCI割り込み 0x002E
24 IIC割り込み 0x0030
25 A/D変換器割り込み 0x0032
表2 H8/3664の割り込みベクタテーブル

コラム(2):リンカスクリプトとC言語の割り込み処理ルーチン

 H8用GCCにおいても、割り込みベクタテーブルは、きちんと管理されています。

 リンカスクリプト「3664.x」ファイルをテキストエディタで開くと、

SECTIONS {
.vectors 0 : {
        SHORT(ABSOLUTE(_start)) /* 0 : Reset */
        SHORT(ABSOLUTE(_start)) /* 1 : */
        SHORT(ABSOLUTE(_start)) /* 2 : */
        SHORT(ABSOLUTE(_start)) /* 3 : */
        SHORT(ABSOLUTE(_start)) /* 4 : */
        SHORT(ABSOLUTE(_start)) /* 5 : */
        SHORT(ABSOLUTE(_start)) /* 6 : */
        SHORT(DEFINED(_int_nmi)?ABSOLUTE(_int_nmi):
ABSOLUTE(_start)) /* 7 : NMI */
        SHORT(DEFINED(_int_trap0)?ABSOLUTE(_int_trap0):
ABSOLUTE(_start)) /* 8 : trap0 */
        SHORT(DEFINED(_int_trap1)?ABSOLUTE(_int_trap1):
ABSOLUTE(_start)) /* 9 : trap1 */
        SHORT(DEFINED(_int_trap2)?ABSOLUTE(_int_trap2):
ABSOLUTE(_start)) /* 10 : trap2 */
        SHORT(DEFINED(_int_trap3)?ABSOLUTE(_int_trap3):
ABSOLUTE(_start)) /* 11 : trap3 */
        SHORT(ABSOLUTE(_start)) /* 12 : */
        SHORT(ABSOLUTE(_start)) /* 13 : */
        SHORT(DEFINED(_int_irq0)?ABSOLUTE(_int_irq0):
ABSOLUTE(_start)) /* 14 : IRQ0 */
        SHORT(DEFINED(_int_irq1)?ABSOLUTE(_int_irq0):
ABSOLUTE(_start)) /* 15 : IRQ1 */
        SHORT(DEFINED(_int_irq2)?ABSOLUTE(_int_irq0):
ABSOLUTE(_start)) /* 16 : IRQ2 */
        SHORT(DEFINED(_int_irq3)?ABSOLUTE(_int_irq0):
ABSOLUTE(_start)) /* 17 : IRQ3 */
        SHORT(ABSOLUTE(_start)) /* 18 : */
        SHORT(DEFINED(_int_tima)?ABSOLUTE(_int_tima):
ABSOLUTE(_start)) /* 19 : Timer A */
        SHORT(ABSOLUTE(_start)) /* 20 : */
        SHORT(DEFINED(_int_timw)?ABSOLUTE(_int_timw):
ABSOLUTE(_start)) /* 21 : Timer W */
        SHORT(DEFINED(_int_timv)?ABSOLUTE(_int_timv):
ABSOLUTE(_start)) /* 22 : Timer V */
        SHORT(DEFINED(_int_sci3)?ABSOLUTE(_int_sci3):
ABSOLUTE(_start)) /* 23 : SCI3 */
        SHORT(DEFINED(_int_iic)?ABSOLUTE(_int_iic):
ABSOLUTE(_start)) /* 24 : IIC */
        SHORT(DEFINED(_int_adi)?ABSOLUTE(_int_adi):
ABSOLUTE(_start)) /* 25 : A/D */
        FILL(0xff)
        } > rom
※レイアウトの都合上改行して表示している個所があります。

と記述された部分があります。実は、これが割り込みベクタテーブルの記述です。

 例えばタイマA割り込みの場合、そのベクタ番号は「19」なので、その19行目を見ます。すると、

SHORT(DEFINED(_int_tima)?ABSOLUTE(_int_tima):
ABSOLUTE(_start)) /* 19 : Timer A */ 
※レイアウトの都合上改行して表示している個所があります。

と記述されているので、タイマAの割り込み処理ルーチンの名前は「int_tima」であると分かるのです。そこに、もしint_tima関数があるならば、ベクタアドレスにint_tima関数を設定し、int_tima関数がなければ、スタートアップルーチンを設定すると記述されています。


photo

健一君。割り込みについて理解できた?


photo

はい!


割り込みもC言語で記述できるなんてすごいですね。


photo

そうでしょ!


健一君のやる気も出てきたみたいだし……。


それじゃー、いつもの宿題いくわよ〜。


photo

おっと、携帯から割り込みが……。


で、で、で、では。


お、お、お、お先に失礼しまーす。


photo

何よ!! ワタシの方が優先順位、高いはずでしょ!


頑張って勉強してるからご褒美にデートしてあげてもいいかなって考えていたのに!


photo

ドッキーン!!


はいはーい。今回は何でしょうか。


宿題カモーン!


photo

まったくもう!



晴子さんからの宿題(4)

0秒から59秒までカウントするプログラムを作ってね。左右2つの7セグメントLEDを使って、2けた同時に表示できないとダメよ!!





 今回はH8マイコンの周辺機能の1つ、タイマAを使ってみました。タイマAはとてもシンプルで使い方も簡単です。マイコンのタイマは種類も豊富で、もっと多くの機能を持っています。ですから、実際にはさらに複雑なプログラムになることでしょう。

 また今回は、割り込み処理についても解説しました。制御プログラミングで、割り込み処理は必修といえます。リアルタイムOSのように、割り込み処理を隠蔽(いんぺい)したり、処理プログラム間の協調動作をサポートするソフトウェアもありますが、今回はその基礎となる技術です。

 次回のテーマは「7セグメントLEDのダイナミック点灯」です。お楽しみに!(次回に続く)


Copyright © ITmedia, Inc. All Rights Reserved.

前のページへ |       
ページトップに戻る