普段はスリープして,ボタンによる割り込みで起動するLCDディスプレイをArduinoで作ろうとした際に困ったことと対処法をログしておきます.
使用機材
- Arduino Uno
- I2C液晶(秋月電子)
- I2Cレベル変換
- Unoの出力が5Vで,I2C液晶が3.3VのI2C利用のため.
- I2C液晶用ライブラリ "I2CLiquidCrystal"
- 参考: ストロベリー・リナックス/秋月電子のI2C液晶/OLEDほかをArduinoで使う
配線
使用機能について
attachInterrupt( digitalPinToInterrupt(pinNo) , func , mode )
digitalピン入力より割り込み処理し,関数を実行します.
- pinNo: digitalの使用ピン番号.Arduino Unoでは2か3しか使えない.
- func: 割り込みで実行する関数
- mode: 割り込み処理するデジタル入力の種類.
- RISING
- FALLING
- CHANGE
問題点
ボタンを押したらLCDの表示を変えようと思うと,一見以下のコードを書いたらよさそうに思います1
誤例.ino
#include <I2CLiquidCrystal.h>
#include <mglcd.h>
I2CLiquidCrystal lcd;
void func() {
lcd.print("intterupted");
}
void setup() {
lcd.begin(16,2);
pinMode(2,INPUT);
attachInterrupt(digitalPinToInterrupt(2),func,FALLING);
interrupts();
}
void loop() {
}
しかしボタンを押してもLCDには何も表示されません.
##対処
I2Cのライブラリ(wire.h)は割り込みを用いており,割り込み内で割り込み発動,みたいなことになり,ハングしているのでは,と考えています.
対処としては,状態変数を用いてfunc()外でlcdインスタンスをいじるようにすれば問題ないようです.
正.ino
#include <I2CLiquidCrystal.h>
#include <mglcd.h>
I2CLiquidCrystal lcd;
volatile bool state=false;
void func() {
state = true;
}
void setup() {
lcd.begin(16,2);
pinMode(2,INPUT);
attachInterrupt(digitalPinToInterrupt(2),func,FALLING);
}
void loop() {
if(state){
lcd.print("interrupted");
}
delay(100);
}
I2Cが割り込みを用いているため,どこかでnointerrupts()
関数で割り込み禁止しても同様に誤動作します.
-
他のコードではよく見る
interrupt.h
をインクルードしていませんが,Arduino.h
ですでに記述されているので実際不要です(Arduino 1.6現在). ↩