小さなチップはどうやってボタンを読むのか
洗濯機やサーモスタット、ゲームのコントローラーの中には、画面もキーボードもない小さなチップが入っています。チップは、いくつかの数を書いたり読んだりするだけで、ボタンに気づき、明かりをつけ、モーターの強さを調節しなければなりません。このレッスンでは、Python でシミュレートした架空のチップを使い、その数が変わるところを見ます。
このレッスンが終わると
メモ
レジスタを2進数で読み、ビットを立てる、下ろす、反転させるとどうなるかを説明できる。
キーワード
- レジスタ:チップの中にある、番号のついた小さな記憶。ここでは 8 ビットの幅。
- ビット:0 か 1。ビット 0 は右端のもの。
- マスク:選んだビットだけを取り出したり変えたりするための数。
- ピン:チップの金属の足。高い(1)か低い(0)。
- PWM:パルス幅変調。ピンをとても速くオンとオフにし、オンの時間の割合で効き方の強さを決める。
- 割り込み:チップをいったん止めて、ハンドラーを実行させる信号。
はじめに:もう知っていること
メモ
頭の中で答えてください。1. 2進数の 1010 はふつうの数でいくつですか。2. 8 ビットには何通りの値が入りますか。3. Python の print は何をしますか。答え: 10。256通りで、0 から 255。値を画面に表示する。
始める前に
メモ
レッスンの上のバーにある ラボを開始 を押し、動いていると表示されるまで待ちます。コンテナはひとつ、jupyter だけで、ターミナルからは docker exec jupyter でその中の Python を使います。これはコンテナの中でコマンドをひとつ実行する命令です。チップは架空のチップのシミュレーターで、本物のボードではありません。本物のチップは、レジスタも名前もきまりも違い、ずっと多くあります。JupyterLab のリンクを押すと、ポート 8888 の画面が開きます。下の 実行 ボタンは、それぞれのコマンドをラボのターミナルに、同じセッションの中で順番に入力します。様子を見たいときや自分で入力したいときは、バーの ターミナルを開く(新しいタブ) を押します。
チップはレジスタの集まり
ls で作業フォルダーを一覧します。mcu.py がチップ、start.ipynb がノートブック、steps がスクリプトです。最初のスクリプトはチップをリセットし、すべてのレジスタを2進数、16進数、ふつうの数で表示します。レジスタは7つあります。DDR(どのピンが出力か)、PORT(出力が出す値)、PIN(ピンが読む値)、TCCR(タイマーの制御)、TCNT(タイマーのカウント)、OCR(比較値)、IFR(割り込みフラグ)です。リセットの直後は、7つとも 0b00000000 です。
docker exec jupyter lsここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
docker exec jupyter python3 steps/registers.pyここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
ビットはマスクで変える
スクリプトは PORT を 0b00001010 にして始めます。マスクとの OR はビットを立てて、0b00001011 になります。マスクの反対との AND はビットを下ろして、0b00001001 になります。XOR はビットを反転させて、0b00000001 になり、同じ反転をもう一度すると 0b00001001 に戻ります。そのたびに変わるのは選んだビットだけで、それがマスクの役目です。
docker exec jupyter python3 steps/bits.pyここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
ピンは外から見えるビット
DDR の 1 は、そのピンを出力にします。スクリプトはピン 0 を出力にして、PORT のビット 0 に 1 を書き込み、PIN は 0b00000001 と読み戻します。それから 8 ティックのあいだ、ティックごとにビットを反転させて #.#.#.#. と表示します。点滅する明かりで、# はオン、. はオフです。
docker exec jupyter python3 steps/pins.pyここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
確認
ヒント
ピンが出力かどうかを決めるのはどのレジスタですか。PORT が 1 にした出力ピンを、PIN はどう表示しますか。答え: DDR。1 です。出力ピンは、チップが出している値を示すからです。
PWM は周期のうち一部だけオンになるピン
タイマーは 0 から 15 まで数えて、また最初に戻ります。つまり1周期は 16 ティックです。TCNT が OCR より小さいあいだ、ピン 5 は高くなります。スクリプトは OCR を 4、8、12 にして、それぞれ1周期分を表示します。オンが4ティック、オフが12ティックなら 25 パーセント、8 と 8 なら 50 パーセント、オンが12ティック、オフが4ティックなら 75 パーセントです。この割合がデューティ比で、本物のチップも、オンとオフだけで明かりを暗くしたりモーターを遅くしたりできます。
docker exec jupyter python3 steps/pwm.pyここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
ボタンはバウンスする。だから聞き方が大事
本物のボタンは、0 から 1 へ一度できれいに変わるのではなく、ちらつきます。最初のスクリプトは、ティックごとにピンをポーリングして読みます。1回押しただけで、ティック 2 と 4 に立ち上がりのエッジを2つ見て、2回と数えます。2つ目のスクリプトは、チップに割り込みフラグを立てさせ、ハンドラーを呼ばせます。ハンドラーは、同じ2つのエッジで、ティック 2 と 4 に2回呼ばれます。そのあと、ハンドラーが、最後に残した呼び出しから 3 ティック以内の呼び出しを無視するようにすると、1回の押下は1回と数えられます。
docker exec jupyter python3 steps/poll.pyここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
docker exec jupyter python3 steps/interrupt.pyここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
よくある思い込み
注意
割り込みを使えばバウンスがなくなる、と思う人は多いです。そうではありません。ハンドラーはエッジのたびに動き、1回の押下で2回動きました。割り込みは、チップがずっと問い合わせ続けなくてよくなるというだけです。バウンスは信号の中に残っていて、コードで対処しなければなりません。これはシミュレーターだということも覚えておいてください。本物のチップでは、レジスタの名前もビットの位置も違い、頼りになるのはデータシートです。
同じことをノートブックで
バーから JupyterLab を開き、start.ipynb をダブルクリックします。チップを作り、レジスタに書き込み、PWM の信号を作ります。セルごとに進みます。セルをクリックして Shift と Enter を押すと実行されます。最後のセルでは、比較値を変えて、デューティ比がいくつになったかを答えます。mcu.py は短くてコメントがついています。ノートブックの横に開いて読んでください。
最後の確認
ヒント
前を見ないで答えてください。1. ビットを立てる演算はどれで、反転させる演算はどれですか。2. 周期が 16 ティックで比較値が 8 のとき、デューティ比はいくつですか。3. 1回の押下が2回と数えられたのはなぜですか。4. このチップは本物ですか。答え: OR が立て、XOR が反転させる。50 パーセント。ボタンがバウンスして、立ち上がりのエッジごとに数えられたから。いいえ、架空のチップのシミュレーター。
先生へ
ヒント
このレッスンは、ラボで進めるレッスンの小さな例です。手順と実行ボタンがレッスンで、ラボは生徒ひとりひとりのコピーです。ラボはサービスがひとつで、架空の 8 ビットチップの短い Python シミュレーターの入った JupyterLab です。自動採点はありません。生徒は mcu.py を使った自分の短いスクリプトかノートブックを JupyterLab からダウンロードして、レジスタが何を示したかの一文といっしょに提出します。学習のポイント:1. レジスタは、ビットのひとつひとつに意味のある数。2. マスクは選んだビットを立てる、下ろす、反転させる。3. タイマーと比較値で PWM の信号を作る。4. ボタンはポーリングでも割り込みでも扱えるが、どちらでもバウンスする。よくある思い込み:割り込みでバウンスがなくなると考える。用意するもの:ラボとブラウザー。ヒント:実行を押す前に、表示される各行を予想させてください。シミュレーターであることをはっきり伝えてください。本物のチップは違い、本物ではデータシートを読みます。ご自分のモジュールにコピーして変更するか、レッスンビルダーのラボメニューから一から作ってください。