ATtiny85のPCINT(ピンチェンジ割り込み)機能を使ってみよう

こんにちは、くのへ@MasazaneKunoheです。

今日はATtiny85の「PCINT(ピンチェンジ割込み)」機能について紹介します!

こちらの記事で「外部割込み」について紹介しましたが、外部割込み機能は7番ピン(PB2ピン)しか持っていません。

その7番ピン(PB2ピン)はI2C通信のSCLピンの機能もあるので、外部割込みとI2C通信を同時に使うことはできません。

でもどうしても使いたい、、、
そんな時は「ピンチェンジ割込み」と言う機能を使いましょう!

ピンチェンジ割込みと言うのは、その名のごとく、「普通のピンを割込みピンにチェンジさせる」というものです。

今回は7番ピン以外に割込み機能を付与する「ピンチェンジ割込み」について紹介します。

紹介する内容

今回は「ピンチェンジ割込み」の機能を紹介します。

「割込み」という機能は、前回の記事を参照ください。
ざっくりいえば、以下の図のように、プログラムの実行中に別のプログラムを割り込ませることを言います。(↓イメージ図)

この記事では、前回の記事と同様に、音楽を奏でるこちらの記事のコードを改造し、loopの実行中にLEDをオン・オフさせる処理を「ピンチェンジ割込み」を使って割り込ませます。

やり方(概要)

ATtiny85ではピンを全てピンチェンジ割込み(PCINT)ピンとして使うことが出来ます。
次の図のようにPCINTピン番号はPBピン番号と同じになっているので、覚えやすいですね。

ちなみに、外部割込みピンである7ピン(PB2ピン)が何故かPCINT2の機能を持っています。
何故だろ、、、(別に要らなくない?)

あと、ResetピンはPCINT5としての機能があります。が、Resetピンを通常ピンとして使おうとするのはかなり難しいと思います(ATtiny85の紹介記事参照)。

さて、ATtiny85でピンチェンジ割込みを使う場合は、ライブラリ「PinChangeInterrupt.h」を使います。
PinChangeInterruptライブラリを使うと、前回の記事の外部割込みと殆ど同じようにプログラミングできます。

この記事では以下の順番で解説します。

1. 回路を作ります
2. PinChangeInterruptライブラリをインストールします
3. ピンチェンジ割込みを行うプログラムをATtiny85に書き込みます

詳細

①回路を作ります。

前回と同じような以下回路を組みます。
前回の記事と異なる点は、スイッチとプルダウン抵抗が2番ピン(PB3ピン)に接続されているところです。

これを回路図で書くとこのような回路です。

この図の通り、2番ピン(PB2)をPCINT2ピンとし、このピンの電圧をスイッチをおして変化させ、それを検知させて割込み機能を動かします。

②PinChangeInterruptライブラリをインストールします

ArduinoIDEを開き、「ツール」⇒「ライブラリを管理」を開きます。

そして、検索バーに「PinChangeInterrupt」と書いて検索しましょう。
PinChangeInterruptライブラリが登場するはずです。これをインストールしてください。

これでライブラリのインストールはばっちりです。(2023年3月時点でバージョンは1.2.9でした)

③ピンチェンジ割込みを行うプログラムをATtiny85に書き込みます。

上記の準備が出来たら、ATtiny85にピンチェンジ割込みを行わせるコードをプログラミングします。

前回と同じくこちらの記事に記載したおさかな天国を奏でる次のスケッチを改造します。

//ピンの設定
#define soundPin 4 //D4ピン(3番ピン)を音の出力ピンとする

//音階データ

//おさかな天国
int scale[] = {
  783,783,783,783,783,783,783,880,987,0,0,0,0,0,880,987,
  1046,0,1046,0,1046,1046,0,987,880,0,0,0,0,0,0,0,

  739,739,739,739,739,739,739,783,880,0,0,0,0,0,880,880,
  783,0,783,0,783,783,0,659,783,0,0,0,0,0,0,0
};

//音の長さ
int time[] ={
  250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250,
  250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250,
  250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250,
  250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250
};

//配列の長さ
int scaleLen = sizeof(scale) / sizeof(int);

//loop用のカウンター
int i=0;


void setup() {
  // put your setup code here, to run once:

  pinMode(soundPin, OUTPUT);

}

void loop() {
  // put your main code here, to run repeatedly:

  SoundMusic();

}

void SoundMusic()
{
  if (i < scaleLen){
    tone(soundPin, scale[i], time[i] * 0.9);  //
    delay(time[i]);
    i++;
  }else{
    //i = 0;
  }   
}

このコードを次のように加筆します。

#include "PinChangeInterrupt.h"


//ピンの設定
#define soundPin 4 //D4ピン(3番ピン)を音の出力ピンとする

//音階データ

//おさかな天国
int scale[] = {
  783,783,783,783,783,783,783,880,987,0,0,0,0,0,880,987,
  1046,0,1046,0,1046,1046,0,987,880,0,0,0,0,0,0,0,

  739,739,739,739,739,739,739,783,880,0,0,0,0,0,880,880,
  783,0,783,0,783,783,0,659,783,0,0,0,0,0,0,0
};

//音の長さ
int time[] ={
  250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250,
  250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250,
  250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250,
  250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250
};

//配列の長さ
int scaleLen = sizeof(scale) / sizeof(int);

//loop用のカウンター
int i=0;

//LEDピン
#define LED_Pin 0

void setup() {
  // put your setup code here, to run once:

  pinMode(soundPin, OUTPUT);
  pinMode(LED_Pin,OUTPUT);

  attachPCINT(digitalPinToPCINT(3),Interruption, CHANGE);

}

void loop() {
  // put your main code here, to run repeatedly:

  SoundMusic();

}

void SoundMusic()
{
  if (i < scaleLen){
    tone(soundPin, scale[i], time[i] * 0.9);  //
    delay(time[i]);
    i++;
  }else{
    //i = 0;
  }   
}

void Interruption()
{
  digitalWrite(LED_Pin, !digitalRead(LED_Pin));
}

これをATtiny85に書き込んで、回路に組み込み、実行してみましょう。
音楽が流れている最中(Loop中)にLEDがオンオフします。
プログラム実行中にInterruption()という別の処理を割り込ませることが出来ました!!

こちらの動画↓がピンチェンジ割込みで音楽を鳴らしているところに割り込んでLEDを光らせているところです。

なお、後述しますがピンチェンジ割込みは「CHANGE」イベントしか使えません。
つまり「割込みピンの電圧が変化した時」しか設定できません。
そのため、この動画の通り、ボタンを押した瞬間の電圧上昇と、ボタンを離す瞬間の電圧降下のそれぞれのタイミングで、Interruption()が動作してON・OFFしています。

コード解説

ピンチェンジ割込みを使う方法は以下の通りです。

1) setupの中にattachPCINTを記載する

サンプルコードの以下の部分です。

attachPCINT(digitalPinToPCINT(3),Interruption, CHANGE);

attachPCINTはピンチェンジ割込み機能をONにするための命令です。
attachPCINT(第1引数, 第2引数, 第3引数)の形で記載し、それぞれの引数は次の通りです。

第1引数:割込みピンの指定。digitalPinToPCINT(PBピン番号)と記載する。指定したPBピンが「ピンチェンジ割込みピン」となる
第2引数:動かすサブプロシージャ
第3引数:割込みピンの電圧変化イベント。ピンチェンジ割込みの場合はCHANGEのみ使用可能

今回のサンプルコードの場合は、「2番ピンPB3ピンをPCINT3ピンに設定し、そのピンの電圧が変化(CHANGE)した時に、Interruptionというサブプロシージャを動かす」と言う命令になっています。

第3引数の電圧変化イベントは、ピンチェンジ割込みの場合は、以下の候補のうち、CHANGEのみを使用することができます。(外部割込みならどれでも使える。こちらの記事参照

  • LOW : LOWレベルで割り込みを発生させます。
  • CHANGE : 信号が変化したときに割り込みを発生させます。
  • RIGING : 信号が立ち上がりエッジである場合に割り込みを発生させます。
  • FALLING : 信号が立ち下がりエッジである場合に割り込みを発生させます。
  • HIGH : HIGHレベルで割り込みを発生させます。

上述の動画のように、ボタンを押して割り込ませる場合、CHANGEイベントだとボタンを押した時だけではなく、離した時にも動作するので気を付けてください。

2) 割込み時に動かすサブプロシージャInterruptionを記載する

サンプルコードの以下の部分です。これも前の記事と一緒です。

void Interruption()
{
  digitalWrite(LED_Pin, !digitalRead(LED_Pin));
}

このサンプルコードはLEDピンをON/OFFさせるコードにしていますが、もっと複雑な処理でも構いません。
割込み時に処置したい内容をここにコーディングすると、ピンチェンジ割込ピンとして設定したピンが電圧変化した時に動作します。

これで割込みヨシ!!


今日は「ATtiny85のピンチェンジ割込み」の使い方を紹介しました。

PinChangeInterruptライブラリを使うと外部割込みと殆ど同じようにプログラムすることが出来ます。
ただし、CHANGEイベントしか判定できないので、そこだけは注意が要りますね~

ではまた別の記事で会いましょう。
ではでは~

🦅バサバサ~