Arduinoで外部(ピン変化)割り込み

 Arduinoで外部割り込み(ピン変化割り込み)をライブラリを使わないで行う方法です。以前Timer割込みを使うためにいろいろ調べていたついでにピン変化割込みについても調べたのでその内容をまとめておきます。

スポンサーリンク
スポンサーリンク

概要

 Timerによる時間割込みはその名の通り、ある決められた時間間隔でプログラムに割込み処理を発生させます。

 対して、外部割込み(ピン変化割込み)とは割り当てられたピンがHIGH⇒LOWもしくはLOW⇒HIGHなどと変化したときに割込み処理を発生させるというものです。

 例えばボタンが押されたらLEDを点灯するといったソースを考えてみます。

 ボタンのON/OFFをメインループ内で確認する場合、いつボタンが押されるのか分からない状態を定期的(周期的)にチェックするため、どうしてもボタンが押されてから反応するまでにタイムラグが発生してしまいます。はやい周期でチェックできれば問題ありませんが、いろいろ処理が増えてくるとだんだん無理が生じてきます。

 そこでピン変化割込みを利用します。ボタンが押されたことをピン変化で感知し、変化した瞬間に割込み処理が発生するため、押された瞬間に処理を行うことができます。

 でArduinoIDEにはやはり便利な関数 attachInterrupt()が準備されていて、この関数使用すれば簡単に外部割込みを使用することができます。しかしこの関数、ピン割り当てが2pin/3pinの2つしかできません(UNOの場合)。

 レジスタを直接操作すればもう少し柔軟にピン割り当てができるので今回はIDE関数(ライブラリ)を使わない方法で外部割込みを行います。

外部割込みの設定

 外部割込みの方法と、ピン変化割込みの大きく二つがあります。

  外部割込みは attachInterrupt()関数 を使った方法で使用されてます。また割込みタイミング(変化、立上がり、立下り)を指定して割込み処理を発生させることができます。のでこちらの方法で行うならば attachInterrupt()関数 使えば良いかと思います。ただArduinoでいうD2、D3の2ピンにしか割り当てられません。

  ピン変化割込みはほとんどのピンに割り当てることが可能です。ですが割込みのタイミングを設定するこはできず、ピンロジックが変化したら割込みが発生します。 レジスタを直接いじる必要がありますが、IDE関数では通常設定できないピンにも割り当てることができるので少し幅が広がります。

外部割込み(INT0、INT1)

 割込みを発生させるピンを許可にして、割込み発生のタイミングを指定すれば完了です。複数ピンの割込みに比べて高速に処理できるようです。

外部割込み許可(有効化)レジスタ

 有効化はEIMSKレジスタ(External Interrupt Mask Register)で行います。

bit76543210
EIMSK -  -  -  -  -  - INT1INT0
arduino対応pin -  -  -  -  -  - D3D2

 有効化は下位2bitで行います。対応するbitを「1」にすると各々のピン割込みが有効になります。

割込み制御レジスタ

 割込みのタイミングの設定はEICRAレジスタ(External Interrupt Control Register A)で行います。割込みタイミングをピン変化、立上がり、立下りなどで発生するよう設定できます。

bit76543210
EICRA  -   -   -   - ISC11ISC10ISC01ISC00

 ISC11/10でINT1(D3)をISC01/00でINT0(D2)を設定します。

ISC*1ISC*0割込み発生条件(タイミング)
00Lowレベルで発生
01論理変化で発生
10ピンレベルの下降端で発生
11ピンレベルの上昇端で発生

 それぞれのピンで発生条件を指定できます。

 必要最低限の設定は以上です。ただ先ほども少し触れましたが単独ピンの割込みはIDE関数 attachInterrupt() が準備されているため特に拘りがなければそちら使えば良いかと思います。

スポンサーリンク

ピン変化割込み(PCINT)

 もう一つの割込みは複数ピンをグループ毎にして割込みを処理するものです。Arduinoのほとんどのピンに割り当てることができますが、割込み処理の細かいタイミングは指定できません。ピン変化にのみ対応します。IDE関数では通常設定できないピンにも割り当てることができるので少し幅が広がります。

複数ピンの割込み許可レジスタ

 8ピン毎に3つのグループに分けられていてグループ毎に割込み許可の設定を行います。まずグループ毎(8ピン)をまとめて許可、その後さらにピン毎に個別に許可する感じです。

 各グループはそのままポート毎に分けられてます。

PCIE0

ポートPB7 PB6 PB5 PB4 PB3 PB2PB1PB0
ArduinoピンD13D12D11D10D9D8

PCIE1

ポートPC7 PC6 PC5 PC4 PC3 PC2PC1PC0
ArduinoピンRESETA5A4A3A2A1A0

PCIE2

ポートPD7 PD6 PD5 PD4 PD3 PD2PD1PD0
ArduinoピンD7D6D5D4D3D2D1D0

 でまずこのグループ毎に割込みを許可します。割込み許可はPCICRレジスタ(Pin Change Interrupt Control Register)で行います。

bit76543210
PCICR -  -  -  -  -  PCIE2PCIE1PCIE0

 許可は下位3bitで行います。対応するbitを「1」にすると各々のピングループの割込みが有効になります。 例えばPCIE0を許可する場合、0bit目を「1」にします。

  PCICR  |= B00000001;

とかもしくは、

  PCICR  |= (1 << PCIE0);

 こんな感じで記述します。後者をちょくちょく見かけますが、わからなければ前者の記述でも問題ありません。これで許可した複数ピンの何れかが変化したときに割込み処理が発生します。

 さらに個別ピン毎に割込み許可を行うにはPCMSKレジスタ(Pin Change Enable Mask)で行います。

bit 7  6  5  4  3  2  1  0 
PCMSK2D7D6D5D4D3D2D1D0
PCMSK1RESETA5A4A3A2A1A0
PCMSK0D13D12D11D10D9D8

 対応するI/OピンをArduinoピンで表してます。レジスタは先ほどと同じでポート毎になってます。

 例えばPCIE0で「D8」ピンを個別に許可する場合は、

  PCMSK0 |= B00000001;

 こんな感じで対応するレジスタの対応するbitを「1」にします。もしくは

  PCMSK0 |= (1 << PCINT0);

 こんな記述でもO.Kです。対応するbit名など詳細はデータシートを確認して下さい。

 最低限必要な事前設定は以上です。これで許可したピンが変化したときに割込みが発生するようになります。

割込み時に呼ばれる関数

 割込みが発生したときに呼ばれる関数(ISR)です。

  シンボル        発生元      
INT0_vectD2ピンの外部割込みが発生
INT1_vectD3ピンの外部割込みが発生
PCINT0_vectPCIE0のピン変化が発生
PCINT1_vectPCIE1のピン変化が発生
PCINT2_vectPCIE2のピン変化が発生

 例えばD8ピン(PCINT0群)のピン変化割込みが発生したときは

ISR(PCINT0_vect) {
  //ここに処理を記述
}

 こんな感じの関数が呼び出されます。

 基本的にはIDEの関数使用していれば特に問題ないと思いますが、D2、D3ピン以外で外部割込み使いたい場合とかはこんな感じでレジスタ操作する必要があります。

コメント