ピン入力と割り込みの基礎をまとめておく。
ATtiny13を使うが、他のCPUでもほぼ同じである。(ATtiny10はピン数が少なくより制約が大きいが、tiny13のTimer0は8ビットであるのに対し、tiny10のTimer0は16ビットであるといった違いもある。次回はタイマー割り込みを考えている。)
もっとも簡単なのはスイッチ入力であるが、ガイガーカウンタのようなパルス入力も原理は同じである。
機械的なスイッチではバウンスの処理が必要であるが、ここでは省略する。(数msec程度の間にON,OFFを繰り返しが起こるので、割り込みを検出してから短い時間は無効にするなど、工夫が必要)
スイッチを押すとLOW、はなすとHIGHになるようにする回路を考える。スイッチの片側はVCCに10kΩの抵抗を介してプルアップし、スイッチの反対側はGNDに落とす。プルアップされた側をPB1と接続して、スイッチを押すとPB0のLEDが点灯するようにする。
ATtiny13
1 | PB5/RESET | 8 | VCC |
---|---|---|---|
2 | PB3 | 7 | PB2 |
3 | PB4 | 6 | PB1/INT0 |
4 | GND | 5 | PB0 |
#include <avr/io.h> void led_on() { PORTB |= (1 << PB0); } void led_off() { PORTB &= ~(1 << PB0); } int main() { DDRB |= (1 << PB0); led_off(); while(1) { if (bit_is_set(PINB, 1)) { led_off(); } else { led_on(); } } }
bit_is_setはavr/sfr_defs.hで定義されていて、
#define bit_is_set(sfr, bit) (_SFR_BYTE(sfr) & _BV(bit))
であり、bit_is_set(PINB, 1)は
PINB & (1<<PB1)
と同じで、PINBの1ビット目が1であれば、trueである。
これが普通のやりかたであるが、ずっと変化がないときに毎回ピンの状態をチェックしているのは無駄に思える。
マイコンにはハードウェア割り込みという機能があるので、これを使う方法で書き直してみる。
INT0による割り込み
tiny13では外部割り込みはPB1にINT0が割り当てられている。上で入力にPB1を使ったのはこのためである。
割り込みに関するレジスタは
MCUCR,GIMSK
である。INT0に割り込みがあったときに呼ばれるルーチンは
ISR(INT0_vect) { }
に書く。interrupt.hをインクルードする必要がある。
MCUCR: MCU Control Register
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
MCUCR | – | PUID | SE | SM1 | SM0 | – | ISC01 | ISC00 |
ISC01 | ISC00 | |
---|---|---|
0 | 0 | INT0がLOWのとき割り込みが発生 |
0 | 1 | INT0に任意の変化で割り込みが発生 |
1 | 0 | INT0にfalling edge(HIGH→LOW)で割り込みが発生 |
1 | 1 | INT0にrising edge(LOW→HIGH)で割り込みが発生 |
GIMSK: General Interrupt Mask Register
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
GIMSK | – | INT0 | PCIE | – | – | – | – | – |
INT0=1のとき、割り込みが有効
(PCIEはINT0以外のPCINT5..0を有効にするときに使う)
以下はINT0=PB1でfalling edge(HIGHからLOWへの変化)の割り込みがあると、
10msだけLEDをONにするだけのコード。whileループでは何もしないことに注意する。
#include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> ISR(INT0_vect) { PORTB |= (1<<PB0); _delay_ms(10); PORTB &= ~(1<<PB0); } int main() { DDRB |= (1<<PB0); MCUCR |= (1<<ISC01); // falling edge of INT0 GIMSK |= (1<<INT0); // enable INT0 interrupt sei(); // enable global interrupts while(1) { } return 0; }
tiny13を2個用意して、1個は単なる発振(Lチカ)にしておき、その出力をもう1個のtinyのINT0に入力するという風にして、ISC01|ISC00=00,01,10,11の違いをLEDで視覚的に見ることができるので、比較してみるとわかりやすいと思う。
コメントを残す