ブレッドボードラジオAVRマイコン入門

BASCOM-AVR 外部割り込み(その2)

 外部割り込みプログラムの続きです。スイッチのチョイ押しプログラムなどの実験しました。マイコン回路は「 その1」と同じものを使用します。

1. スイッチを押すたびにLEDの状態が反転する

 スイッチをチョンと押すたびにLEDが点灯・消灯を繰り返しすプログラムです。

 プログラムファイル int2a.bas

1 $regfile = "attiny2313.dat" ATtiny2313を使用する。
2 $crystal = 1000000 クロック周波数を1MHzに設定。
3
4 Config Portb = Output ポートBを出力に設定する。
5 Config Portd = Input ポートDを入力に設定する。
6 Portd = 127 PD0〜PD6をプルアップする。
7 Dim Flag As Bit ビット型変数「Flag」を使用する。
8
9 On Int0 Int0_int 割込みが起きたら「Int0_int」を実行する。
10 Enable Interrupts 割込み全般を許可する。
11 Enable Int0 Int0割込みを許可する。
12
13 Do Doループの範囲ここから。
14  If Flag = 1 Then 条件分岐。「Flag=1」ならば、
15  Toggle Portb.0 PB0の出力を反転する。
16  Waitms 10 10ミリ秒間そのまま。
17  End If 条件分岐プログラムここまで。
18  Flag = 0 「Flag」を「0」にする。
19 Loop Doループの範囲ここまで。13行目へ戻る。
20
21 End メインプログラム終わり。
22
23 Int0_int: 割込みプログラム「Int0_int」ここから。
24  Flag = 1 「Flag」を「1」にする。
25 Return 割込みプログラム「Int0_int」ここまで。

 このプログラムではスイッチのバウンス対策が必要ですが、その方法がわかりません。今回は16行目に「Waitms 10」と書いてみました。いったん「Flag=1」になると10ミリ秒間はそのままです。その間にバウンスによって新たな割り込みが起こっても反応しません。10ミリ秒たつと「Flag」が「0」に戻るので、割り込みが可能になります。なお、スイッチをずっと押し続けると、LEDが1秒くらいの周期でゆっくり点滅します。

2. スイッチを押すと点滅開始、もう1回押すと点滅停止

 スイッチをチョンと押すとLEDが点滅を開始し、もう1回チョンと押すと点滅を停止するプログラムです。点滅を停止するときは、スイッチを押した瞬間の状態のままで停止します。入力スイッチプログラムのページにも書きましたが、こういう動作をするプログラムはできそうでなかなかできません。今回も少し問題が残っています。

 プログラムファイル int2b.bas

1 $regfile = "attiny2313.dat" ATtiny2313を使用する。
2 $crystal = 1000000 クロック周波数を1MHzに設定。
3
4 Config Portb = Output ポートBを出力に設定する。
5 Config Portd = Input ポートDを入力に設定する。
6 Portd = 127 PD0〜PD6をプルアップする。
7 Dim Flag As Bit ビット型変数「Flag」を使用する。
8
9 On Int0 Int0_int 割込みが起きたら「Int0_int」を実行する。
10 Enable Interrupts 割込み全般を許可する。
11 Enable Int0 Int0割込みを許可する。
12
13 Do Doループの範囲ここから。
14  If Flag = 1 Then 条件分岐。「Flag=1」ならば、
15  Toggle Portb.0 PB0の出力を反転する。
16  Waitms 500 500ミリ秒間そのまま。
17  End If 条件分岐プログラムここまで。
18  Flag = 0 「Flag」を「0」にする。
19 Loop Doループの範囲ここまで。13行目へ戻る。
20
21 End メインプログラム終わり。
22
23 Int0_int: 割込みプログラム「Int0_int」ここから。
24  Toggle Flag 「Flag」の値を反転させる。
25  Waitms 250 250ミリ秒間そのまま。
26 Return 割込みプログラム「Int0_int」ここまで。

 このプログラムでは割り込みが起きるたびに「Flag」の値が「1」になったり「0」になったりします。「Flag=1」ならLEDが点滅します。「Flag=0」のときは何も指示がないのでLEDは直前の状態のままになります。25行目「Waitms 250」はスイッチのバウンス対策ですが、数値の選定は微妙です。「Waitms 250」と書くと、スイッチを押してから250ミリ秒経過しないとLEDの状態が切り替わりません。もっと短くしたいのですが、たとえば「Waitms 100」と書いた場合、もしスイッチが100ミリ秒間以上押されると割り込みが2回起こり、いったん「1」になった変数がまた「0」に戻ってしまいます。

 上記のプログラムでは、LEDが点滅を停止するときは直前の状態のままで止まりますが、条件分岐プログラムの中に「Else Portb.0 = 0」と書けば、必ず消灯して停止します。逆に「Else Portb.0 = 1」と書けば点灯した状態で停止します。

3. ポート出力のオンオフで割り込みを発生させる

 入力スイッチを使う代わりにプログラムでINT0(PD2)の状態を変化させ、これによって割り込みを発生させるプログラムです。こういうのは「ソフトウェア割込み」と言うそうです。INT0端子の状態がわかりやすいように、ここにもLEDをつなぎました。

 第1図

 プログラムファイル int2b.bas

1 $regfile = "attiny2313.dat" ATtiny2313を使用する。
2 $crystal = 1000000 クロック周波数を1MHzに設定。
3
4 Config Portb = Output ポートBを出力に設定する。
5 Config Portd = Output ポートDを出力に設定する。
6 Config Int0 = Falling 立下りエッジで割込みを発生させる。
7 On Int0 Int0_int 割込みが起きたら「Int0_int」を実行する。
8 Enable Interrupts 割込み全般を許可する。
9 Enable Int0 Int0割込みを許可する。
10
11 Do Doループの範囲ここから。
12  Toggle Portd.2 PD2の出力を反転する。
13  Waitms 500 500ミリ秒間そのまま。
14 Loop Doループの範囲ここまで。11行目へ戻る。
15
16 End メインプログラム終わり。
17
18 Int0_int: 割込みプログラム「Int0_int」ここから。
19  Toggle Portb.0 PB0の出力を反転させる。
20 Return 割込みプログラム「Int0_int」ここまで。

 INT0(PD2)を1秒周期でオンオフするため、ポートDを出力に設定します。6行目「Config Int0 = Falling」は立下りエッジ、つまりINT0の状態が「1」から「0」に変わる瞬間に割り込みを発生させるという設定です。立ち上がりエッジで割込みをかける場合は「Config Int0 = Rising」と書きます。これまでのプログラムでは何も設定しませんでしたが、そういう場合は「Config Int0 = Low Level」、つまりINT0が「0」になっている間はずっと連続して割込みが起こるという設定になります。

 メインプログラムでは500ミリ秒ごとにPD2出力を反転させます。これによりLED1が1秒周期で点滅します。PD2出力が「1」から「0」に変わるとき、つまりLED1が消灯する瞬間に割り込みが発生し、PB0(LED0)の状態が反転します。LED1とLED0の点滅の関係は上の図右のようになります。もし「Config Int0 = Rising」と書けば、逆にLED1が点灯する瞬間にLED0の状態が変化します。

4. Int0とInt1を併用する

 ATtiny2313にはInt0とInt1という2つの外部割込み機能があります。両者を併用して、スイッチが早く押された方のLEDだけが点灯するプログラムを作りました。テレビのクイズ番組の早押しボタンのようなイメージです。回路図は下記の通りです。Int0(6番ピン)とInt1(7番ピン)にそれぞれ割り込み用のスイッチを付けます。リセット端子(1番ピン)にもスイッチをつなぎます。プログラムの途中で一時的に割り込みを禁止する命令「Disable Int0 (Int1)」を使いました。

 第2図

 プログラムファイル int2d.bas

1 $regfile = "attiny2313.dat" ATtiny2313を使用する。
2 $crystal = 1000000 クロック周波数を1MHzに設定。
3
4 Config Portb = Output ポートBを出力に設定する。
5 Config Portd = Input ポートDを入力に設定する。
6 Portd = 127 PD0〜PD6をプルアップする。
7 Dim Flag0 As Bit ビット型変数「Flag0」を使用する。
8 Dim Flag1 As Bit ビット型変数「Flag1」を使用する。
9
10 On Int0 Int0_int Int0割込みが起きたら「Int0_int」を実行する。
11 On Int1 Int1_int Int1割込みが起きたら「Int1_int」を実行する。
12 Enable Interrupts 割込み全般を許可する。
13 Enable Int0 Int0割込みを許可する。
14 Enable Int1 Int1割込みを許可する。
15
16 Do Doループの範囲ここから。
17  If Flag0 = 1 Then 条件分岐1。「Flag0=1」ならば、
18   Portb.0 = 1 PB0を「1」にする。LED0が点灯。
19   Disable Int1 Int1割込みを禁止する。
20  End If 条件分岐1ここまで。
21  If Flag1 = 1 Then 条件分岐2。「Flag1=1」ならば、
22   Portb.1 = 1 PB1を「1」にする。LED1が点灯。
23   Disable Int0 Int0割込みを禁止する。
24  End If 条件分岐2ここまで。
25 Loop Doループの範囲ここまで。16行目へ戻る。
26
27 End メインプログラム終わり。
28
29 Int0_int: 割込みプログラム「Int0_int」ここから。
30  Flag0 = 1 「Flag0」の値を「1」にする。
31 Return 割込みプログラム「Int0_int」ここまで。
32
33 Int1_int: 割込みプログラム「Int1_int」ここから。
34  Flag1 = 1 「Flag1」の値を「1」にする。
35 Return 割込みプログラム「Int1_int」ここまで。

 スイッチS0を押すとInt0割込みが起こり、「Flag0」の値が「1」になってLED0が点灯します。スイッチS1を押すとInt1割込みが起こり、「Flag1」の値が「1」になってLED1が点灯します。 Int0割込みが起きると、LED0を点灯するとともにInt1割込みを禁止します。19行目「Disable Int1」がそのための記述です。これにより、いったんLED0が点灯したら、その後でS1を押してもLED1は点灯しません。逆に、LED1が点灯するとInt0割込みが禁止されるので、S0を押しても反応しなくなります。LEDを消灯するときはリセットスイッチ(S2)を押します。