Hatena::ブログ(Diary)

hijouguchiの日記

2010-06-23

外部割り込み

| 14:18 |

 今回は外部割り込みについて。外部割り込みは、タイマ割り込みなどと違ってピンの状態によってかかる割り込みです。これを使うことにより、例えばスイッチの状態変化から、あるいは接続したモジュールから割り込みを書ける事が出来るようになります。


対象マイコン

 全部(のはず) ただし、今回確認しているのはATTiny2313だけです。


外部割り込み概要

 外部割り込みの種類は大まかにINTとPCINTの2種類があります。INTはレジスタによって、割り込みの起こる条件を4種類設定できるのに対し、PCINTでは1種類しかありません。またINTピンは二つくらいしか用意されれませんが、PCINTはたくさん*1用意されているようです。ピンの配置はデータシートを確認しましょう。他にINTピンだとスタンバイモードのAVRを復帰させることが出来ます。


 外部割り込みに登場するレジスタを紹介しましょう。


使用しそうなレジスタ一覧

レジスタdescription
MCUCRINTの割り込みの方法を設定(ATTiny2313等)
GIMSK外部割り込みマスクレジスタ(ATTiny2313等)
GIFR外部割り込みフラグレジスタ(ATTiny2313等)
EICRAINT割り込みコントロールレジスタ(ATMega168等)
EIMSKINT割り込みマスクレジスタ(ATMega168等)
EIFRITN割り込みフラグレジスタ(ATMega168等)
PCICRPCINT割り込みコントロールレジスタ(ATMega168等)
PCIFRPCINTフラグレジスタ(ATMega168等)
PCMSKPCINTピンのマスクレジスタ

 今回登場するレジスタは一見多いように見えますが、Tiny系とMega系でレジスタが若干変わってるので合わせて列挙しただけです。Mega系ではINTとPCINTのレジスタ自体が分かれてます。


 INT、PCINTの割り込みの使い方はほとんど一緒で

などが必要でしょう。もちろんsei()も必要となります。


INT

 INTピンは大抵はINT0、INT1の二つ用意されています。機能は一緒で、それぞれ設定するbitが異なってるくらいです。割り込みの起こる条件はMCUCRあるいはEICRAから設定します。


MCUCR(ATTiny2313等)

bit76543210
MCUCRPUDSM1SESM0ISC11ICS10ISC01ISC00

EICRA(ATMega168等)

bit76543210
EICRA----ISC11ICS10ISC01ISC00

ここで設定するのはISCビットで、INT1はISC11:0から、INT0はISC01:0からそれぞれ設定することになります。


Interrupt Sence Control(ISC)

ISCn1ISCn0description
00INTピンのLレベルで割り込み
01INTピンの論理変化で割り込み
10INTピンの立ち下がりで割り込み
11INTピンの立ち上がりで割り込み

 AVRのスタンバイモードからの復帰で使いたい場合はLレベル割り込み、つまりISCn1:0 = 0とする必要があります。


 次にマスクレジスタです。


GIMSK(ATTiny2313等)

bit76543210
GIMSKINT1INT0PCIE0PCIE2PCIE1---

EIMSK(ATMega168等)

bit76543210
EIMSK------INT1INT0

 ここで使うビットはINT1とINT0のみです。それぞれセットするとせっとした名前のピンの割り込み有効になります。


 最後にピンと割り込みハンドラ名の対応関係です。


割り込みハンドラ名

ピンハンドラ名
INT0INT0_vect
INT1INT1_vect

PCINT

 PCINTはピンの論理変化割り込みしかサポートしていませんが、数が多いのが特徴です。割り込みの方法を設定するレジスタはありません。


PCINT位置(ATTiny2313等)

ピンポートPCIEPCMSK
PCINT7..0PORTBPCIE0PCMSK0
PCINT10..8PORTAPCIE2PCMSK1
PCINT17..11PORTDPCIE1PCMSK2

PCINT位置(ATTiny2313等)

ピンポートPCIEPCMSK
PCINT7..0PORTBPCIE0PCMSK0
PCINT14..8PORTCPCIE1PCMSK1
PCINT23..16PORTDPCIE2PCMSK2

 ポート別にレジスタが割り振られてるみたいなので、ビットの操作はある程度しやすくはなってそうです。

 問題はavr-libcのバージョンによっては?PCINT7..0しかビット名が割り振られてない場合があることです。その場合は使えるピンはPCINT7..0だけになるか、あるいは頑張って使えるようにするかです。ビットの名前も最後の数字がとれてるので注意です。



割り込みを許可するレジスタはGIMSKあるいはPCICRです。


GIMSK(ATTiny2313等)

bit76543210
GIMSKINT1INT0PCIE0PCIE2PCIE1---

PCICR(ATMega168等)

bit76543210
PCICR-----PCIE2PCIE1PCIE0

 使用するビットはPCIE2:0の3つです。PCMSKはそれぞれ使いたいピンに対応するビットをセットします。

 PCINTでは割り込みハンドラ名はPCINT_vectになっています。


サンプルコード

 スイッチ関係を扱うとどうしてもチャタリングが問題になってくるのでチャタリングの心配がいらないようなプログラムにしておきました。


INT(Dフリップフロップっぽいの)

/* ATTiny2313 */                                                                                                                
#include <avr/io.h>
#include <avr/interrupt.h>

ISR(INT0_vect)
{
  /* PD5の状態をPD6に読み込む */
  if((~PIND & (1<<PD5)) == (1<<PD5)) {
    PORTD |=  (1<<PD6);
  } else {
    PORTD &= ~(1<<PD6);
  }
}

int main(void)
{
  /* PD6をL出力、その他をプルアップ有り入力設定 */
  DDRD  =  (1<<PD6);
  PORTD = ~(1<<PD6);


  MCUCR = (1<<ISC01); // INT0立ち下がり
  GIMSK = (1<<INT0);  // INT0の割り込み許可

  sei();
  while(1);
  return 0;
}

PCINT(リセット優先RSフリップフロップっぽいの)

/* ATTiny2313 */
#include <avr/io.h>
#include <avr/interrupt.h>

ISR(PCINT_vect)
{
  /*  
   * PCINT0 でセット、PNINT1でリセット
   * PB0 <=> PCINT0;  PB1 <=> PCINT1
   */

  if((~PINB & (1<<PB0)) == (1<<PB0)) PORTD |=  (1<<PD6);
  if((~PINB & (1<<PB1)) == (1<<PB1)) PORTD &= ~(1<<PD6);
}

int main(void)
{
  /* PD6をL出力設定 */
  DDRD  =  (1<<PD6);
  PORTD = ~(1<<PD6);

  /* PORTBをプルアップ有り入力に設定 */
  DDRB  = 0x00;
  PORTB = 0xFF;

  GIMSK = (1<<PCIE); // PCINT7:0の割り込み許可
  PCMSK = (1<<PCINT0)|(1<<PCINT1); // PCINT0, PCINT1をマスク

  sei();
  while(1);
  return 0;
}

詳しくは

ATTiny2313データシート http://www.atmel.com/dyn/resources/prod_documents/doc8246.pdf

*1:電源用のピン以外すべて?

トラックバック - http://d.hatena.ne.jp/hijouguchi/20100623/1277270332