イチから作って丸ごと学ぶ! H8マイコン道

イチから作って丸ごと学ぶ! H8マイコン道(10)

割り込みテクニックでタイマを使おう

横田 一弘 埼玉県立新座総合技術高等学校 教諭 2010/1/26

- PR -

 それでは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」でなければなりません。

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

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

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

 IENR1を、

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

と定義し、IENTAビットを、

#define IENTA 0x40

と定義します。

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

    IENR1 |= IENTA;

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

図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番地にはスタートアップルーチンのアドレスが設定されているのです。

表2 H8/3664の割り込みベクタテーブル
ベクタ番号
割り込み要因
ベクタアドレス
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): リンカスクリプトと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関数がなければ、スタートアップルーチンを設定すると記述されています。


   
晴子さん
  健一君。割り込みについて理解できた?  
     
     
  はい!
割り込みもC言語で記述できるなんてすごいですね。
 
健一君
     
   
晴子さん
  そうでしょ!
健一君のやる気も出てきたみたいだし……。
それじゃー、いつもの宿題いくわよ〜。
 
     
     
  おっと、携帯から割り込みが……。
で、で、で、では。
お、お、お、お先に失礼しまーす。
 
健一君(笑)
     
   
晴子さん(怒)
  何よ!! ワタシの方が優先順位、高いはずでしょ!
頑張って勉強してるからご褒美にデートしてあげてもいいかなって考えていたのに!
 
     
     
  ドッキーン!!
はいはーい。今回は何でしょうか。
宿題カモーン! 
 
健一君(笑)
     
   
晴子さん(怒)
  まったくもう!  
     

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

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

答え. 解答はこちら(←クリック)


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

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

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

  • 連載バックナンバー
  • 全記事インデックス
  • 組み込み開発トップ
  • MONOistトップ

スキルアップ/キャリアアップ(JOB@IT)

スポンサーからのお知らせ

- PR -
@IT Sepcial

震災関連・復興支援情報

震災関連・復興支援情報
@IT MONOist/EE Times Japan/環境メディアの製造業技術者向け3メディアを中心に、震災関連/復興支援情報を集めました

次世代エンベデッドコーナー

次世代エンベデッド
“次世代”の組み込み機器を開発するエンジニアを支援するコーナー。新潮流・新技術をインタビューやコラム、解説記事で分かりやすく紹介!

Windows Embeddedコーナー

Windows Embedded
Windows Embedded専門コーナー。Windows Embedded StandardやWindows Embedded CEをはじめとする「Windows Embedded」ファミリの最新動向や技術情報をお届けします!!

Androidコーナー

Android
Android専門コーナー。組み込みデバイスへの適用からアプリケーション開発、イベントレポート、ニュースなどAndroidに関するさまざまな技術情報がここに集結!!

@IT MONOist 求人情報

- PR -