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

BASCOM-AVR 入力スイッチ(その2)

 LEDの連続点滅と入力スイッチを組み合わせたプログラムを試してみました。マイコン回路は「入力スイッチテスト用回路」を使用します。

1. スイッチを押している間だけLEDが点滅する

 入力スイッチが押されている間だけ、LED0が0.5秒間隔で点滅するプログラムです。スイッチが押されていないときはLEDは消灯しています。図に描くと下のような感じです。

 第1図

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

1 $regfile = "at26def.dat" ATtiny26Lを使用する。
2 $crystal = 1000000 クロック周波数を1MHzに設定。
3 Config Porta = Output ポートAを出力に設定する。
4 Config Portb = Input ポートBを入力に設定する。
5 Portb = 1 PB0をプルアップする。
6 Do Doループの範囲ここから。
7  If Pinb.0 = 0 Then 条件分岐のプログラムここから。
もし「Pinb.0 = 0」ならば8,9行目のプログラムを実行。
8  Toggle Porta.0 PA0が「0」なら「1」に、「1」なら「0」にする。
9  Waitms 500 0.5秒間そのまま。
10  Else そうでなければ(「Pinb.0 = 1」ならば)、
次行のプログラムを実行。
11  Porta = 0 ポートAを「0」にする。LED0が消灯。
12  End If 条件分岐のプログラムここまで。
13 Loop Doループの範囲ここまで。6行目へ戻る
14 End 終わり。

 入力(Pinb.0)はプログラムでプルアップされていますので、スイッチが押されていないときは「Pinb.0 = 1」です。これは7行目「If Pinb.0 = 0 Then」の条件に合わないのでプログラムは10行目「Else」へ飛び、11行目「Porta = 0」が実行されます。つまりスイッチが押されていないときはLED0は消灯したままです。

 スイッチが押されると「Pinb.0 = 0」になります。この場合は8行目「Toggle Porta.0」、9行目「Waitms 500」が実行されます。初期状態でLEDは消灯していますから、ここで0.5秒間点灯することになります。13行目「Loop」で6行目「Do」へ戻ります。ループ2巡目でまた7行目「If Pinb.0 = 0 Then」まで来たとき、まだ「Pinb.0 = 0」(スイッチが押されたまま)ならば、今度はLEDが0.5秒間消灯します。これの繰り返しで、スイッチが押されている限りLEDは点滅を続けます。

 押していたスイッチを放すと点滅は止まりますが、放した瞬間に止まるのではなく、9行目「Waitms 500」が実行され、ループの次の回でプログラムが7行目「If Pinb.0 = 0 Then」まで来た時点で止まります(正確に言えば11行目「Porta = 0」が実行された時点で止まる)。したがって、たとえばLEDが点灯した瞬間にスイッチを放した場合、LEDが消えるまでに0.5秒かかります。

2. スイッチを押している間だけ点滅が止まる

 上記とは逆に、普段はLEDが点滅していて、入力スイッチを押している間だけ点滅が止まるプログラムです。

 第2図

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

1 $regfile = "at26def.dat" ATtiny26Lを使用する。
2 $crystal = 1000000 クロック周波数を1MHzに設定。
3 Config Porta = Output ポートAを出力に設定する。
4 Config Portb = Input ポートBを入力に設定する。
5 Portb = 1 PB0をプルアップする。
6 Do Doループの範囲ここから。
7  If Pinb.0 = 1 Then 条件分岐のプログラムここから。
もし「Pinb.0 = 1」ならば8,9行目のプログラムを実行。
8  Toggle Porta.0 PA0が「0」なら「1」に、「1」なら「0」にする。
9  Waitms 500 0.5秒間そのまま。
10  Else そうでなければ(「Pinb.0 = 0」ならば)、
次行のプログラムを実行。
11  Porta = 0 ポートAを「0」にする。LED0が消灯。
12  End If 条件分岐のプログラムここまで。
13 Loop Doループの範囲ここまで。6行目へ戻る
14 End 終わり。

 前のプログラムとの違いは、7行目が「If Pinb.0 = 1 Then」に変わっているところだけです。たった1文字の違いでまったく逆の動作になります。

 7行目を「If Pinb.0 = 0 Then」にしたままで、「Then」以下のプログラム(8,9行目)と「Else」以下のプログラム(11行目)を入れ替えても同じ動作をさせることができます。

3. スイッチを押した瞬間の状態を保持

 上記「input2b.bas」で10行目「Else」と11行目「Porta = 0」を書かなかった場合は、スイッチを押した瞬間の状態がそのまま保持されます。つまり、スイッチを押した瞬間にLEDが点灯していれば点灯したまま、消灯していれば消灯したままになります。スイッチを押している間中そのままで、スイッチを放せば元通り点滅を再開します。下記のプログラムでは点滅間隔を50msと短くしています。「コイントス」的な遊びに使えそうです。

 第3図

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

1 $regfile = "at26def.dat" ATtiny26Lを使用する。
2 $crystal = 1000000 クロック周波数を1MHzに設定。
3 Config Porta = Output ポートAを出力に設定する。
4 Config Portb = Input ポートBを入力に設定する。
5 Portb = 1 PB0をプルアップする。
6 Do Doループの範囲ここから。
7  If Pinb.0 = 1 Then 条件分岐のプログラムここから。
もし「Pinb.0 = 1」ならば8,9行目のプログラムを実行。
8  Toggle Porta.0 PA0が「0」なら「1」に、「1」なら「0」にする。
9  Waitms 50 50ミリ秒間そのまま。
10  End If 条件分岐のプログラムここまで。
11 Loop Doループの範囲ここまで。6行目へ戻る
12 End 終わり。

 実は、こういう動作になるということはあれこれやっているうちに偶然発見しました。なので後付けの説明になりますが、「Else」命令がないということは「Pinb.0 = 0」のときに「何もしない」ということになるのだと思います。だから1回前のループの時点での「Toggle Porta.0」の結果のまま時間だけが過ぎていくのだというふうに考えています。

 最初にやったプログラム「input2a.bas」でも、10行目「Else」と11行目「Porta = 0」を削除すると同じような動作にすることができます。スイッチを押すとLEDが点滅を始めますが、LEDが点灯している瞬間にスイッチを放せば点灯したまま、LEDが消灯している瞬間にスイッチを放せば消灯したままになります。再びスイッチを押せば点滅を再開します。

4. LEDの順次点灯を途中で止める

 8個のLEDがLED0から順に1個ずつ点灯していくプログラムで、入力スイッチを押している間だけ順次点灯が止んでスイッチを押した瞬間に点灯していたLEDだけが点灯し続けるというプログラムです。押していたスイッチを放すと順次点灯を再開します。点灯間隔を短くして、「光の流れを止める」という感じにしてみました。

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

1 $regfile = "at26def.dat" ATtiny26Lを使用する。
2 $crystal = 1000000 クロック周波数を1MHzに設定。
3 Config Porta = Output ポートAを出力に設定する。
4 Config Portb = Input ポートBを入力に設定する。
5 Portb = 1 PB0をプルアップする。
6 Porta = 1 ポートAを「1」にする。LED0が点灯。
7 Do Doループの範囲ここから。
8  If Pinb.0 = 1 Then 条件分岐のプログラムここから。
もし「Pinb.0 = 1」ならば次行のプログラムを実行。
9  Rotate Porta, Left ポートAの出力を1ビット左に回転させる。
10  Waitms 50 50ミリ秒間そのまま。
11  End If 条件分岐のプログラムここまで。
12 Loop Doループの範囲ここまで。7行目へ戻る
13 End 終わり。

 このプログラムの場合、最初にLEDを1個点灯しておく必要があります。6行目「Porta = 1」はそのためのものです。点灯しておくLEDはどれでも良いので、ここは「Porta = 2」(LED1が点灯)でも「Porta = 4」(LED2が点灯)でもかまいません。5行目と6行目は書式が同じですが、入力に指定したときと出力に指定したときとでは意味が違ってきます。