新しいものづくりがわかるメディア

RSS


Raspberry Pi Picoを始めよう! 週末工作でステップアップ

ラズパイPicoで始める自作キーボード入門 | PIOで4×4キーパッドを制御

キーボードの自作が一時期注目を集めました。現在でもオリジナルキーボードを作ってみたいという人は多いでしょう。Raspberry Pi Pico(以下、Pico)シリーズはGPIOが豊富で、USBホスト機能が利用でき、なおかつ安価なので、キーボードに組み込むマイコンにも最適です。

10キーパッドから100以上のキーがあるフルサイズキーボードまで、ほとんどすべてのキーボードは、キーマトリクスを使って多数のキーのオン/オフを検出しています。キーマトリクスの仕組みや扱い方はキーボードの大きさにかかわらず、たいして変わりません。

そこで本記事では、もっともシンプルな4×4キーパッドを例にキーマトリクスの利用を紹介します。単にキーマトリクスを扱うだけではつまらないので、Picoシリーズの特徴でもあるProgrammable IO(PIO)を使って4×4キーパッドを制御してみることにしましょう。

市販のArduino用4×4キーパッドを使う

今回、扱う4×4キーパッドは、名前の通り16個のキースイッチからなるキーパッドです。たった16個とはいえ、自分でスイッチをはんだ付けしてキーパッドを作るのは結構、面倒なものですが、幸いなことに現在はArduinoマイコン向けの完成品キーパッドが手頃な価格で購入できます。

筆者がAmazonから購入したキーパッドは図1のようなもので、5個セットで699円でした。タクトスイッチをバラで買って自作するよりずっと安価です。


https://www.amazon.co.jp/dp/B094JFQGZ8

図1:Arduino向けの4×4キーパッド 図1:Arduino向けの4×4キーパッド

写真のように16個のタクトスイッチが4行4列に取り付けられている小さな基板で、C1~C4とR1~R4の合計8ピンの端子が基板サイドにあります。CはColumn(桁)、RはRaw(行)の略で、基板上では図2のように格子状(キーマトリクス)にスイッチが配線されています。

図2:4×4キーパッドの配線図 図2:4×4キーパッドの配線図

一般的には、R1~R4とC1~C4の8本の端子を、すべてマイコンのGPIOに接続します。例えば、R1のGPIOをオンにしてC1~C4の4本のGPIOを読み取るとしましょう。このとき、スイッチS1が押されていれば、C1~C4のGPIOからは0b0001が読み取れるでしょう。

このようにして、R1~R4を順にオンにしていき、C1~C4を読み取れば16個のキーの状態がすべてわかるわけです。これをキースキャンといいます。実に単純ですね。

キーマトリクスでスイッチを配線しキースキャンを利用することで、多数のスイッチが扱えます。例えば、パソコン用フルサイズキーボードのキーの数は標準だと109キー程度です。11×10のキーマトリクスで対応できるので、21本のGPIOがあるマイコンならキーボードコントローラーとして利用できることになります。

実際にPicoにマトリクスを接続する際には少々考えるべきこともあります。今回は4×4キーパッドを図3のように接続してみました。

図3:Picoシリーズと4×4キーパッドの接続 図3:Picoシリーズと4×4キーパッドの接続

回路図にすると複雑そうですが、基本はR1~R4をGP6~GP9に、C1~C4をGP2~GP5に接続するだけです。使用しているGPIOは特段の理由があって決めているわけではなく、連続したGPIOであれば、どこを使っても構いません。ただし、冒頭で述べたようにPIOを使用するのでR1~R4、C1~C4それぞれに連続したGPIOを接続します。

R1~R4に入れているダイオードD1~D4は逆流防止用です。逆流防止用のダイオードを入れないと、出力ポートに意図しない電圧がかかることがあります。例えば、GP6をオンにしたときに、S1とS5の両方が同時に押されていると、出力ポートのGP7に電圧がかかってしまいます。出力ポートはローインピーダンスですから、電流が流れることになります。

PicoシリーズのGPIOポートのデフォルトは、出力電流として最大4mAが設定されていて、同じく最大4mAの電流の吸い込みが可能です。したがって、逆流防止用のダイオードがなくてもPicoが壊れることはありません。しかし、無駄に電気が流れるのは好ましくないので、逆流防止用のダイオードを入れておいたほうがいいでしょう。

使用するダイオードは、小信号用シリコンダイオードなら何でも構いません。こうした用途で現在よく使われるのは1N4148なので、小信号用シリコンダイオードの手持ちがない人は購入しておくといいでしょう。

ちなみに、3個以上のキースイッチが押されていると、逆流によって押されていないキーがオンになる現象が起きます。これを「ゴースト」などといいます。

ゴーストを避けるなら、各スイッチに個別の逆流防止用ダイオードやトランジスタを入れる方法が考えられます。パソコン用の高級キーボードはゴーストが生じないことを意味する「アンチゴースト」を売りにしている製品が多いのですが、こうした製品はスイッチごとに逆流防止措置が行われているわけですね。その分だけ部品点数が増えますから、価格が高くなります。

今回、利用している4×4キーパッドは出来合いの基板ですからゴーストの発生は避けられません。しかし、タクトスイッチは固いので、3つのキーを同時に押すことはほぼないと想定して構わないでしょう。

C1~C4に入っている集合抵抗RAは、プルダウン抵抗です。Picoの内部プルダウン抵抗も利用できますが、例えばMicroPythonだとデフォルトのI2Cポートに割り当てられているGP4のプルアップ状態が無効にならないといった面倒があります。この例のように内部プルダウン抵抗を使う前提だと妙なトラブルに悩まされることがあるので、外部プルダウンを使ったほうが確実でしょう。

筆者は手持ちの10kΩ×4の集合抵抗を使いましたが、集合抵抗がない場合は、10kΩの抵抗4本を使ってください。図のようにC1~C4をプルダウンしておけばOKです。


※ どこでも買えますが、例えば秋月電子ならば販売コード100941など

配線例 配線例

PIOでキースキャン

後は先述のようにキースキャンを行えば、16個のキー状態を取り出せます。キースキャンの頻度はケースバイケースですが、一例としてPC用のキーボードだと1秒間に1000回が標準的です。

利用している4×4キーパッドのタクトスイッチは固いので、素早く押すことが物理的に難しく、人間の操作速度はせいぜい数百ミリ秒といったとことでしょう。したがって、1秒間に数百回程度スキャンすれば十分に実用になるはずです。スキャン頻度を上げるとチャタリングの対策が必要になる一方、スキャン頻度を下げる、つまりサンプリング周波数を下げればチャタリングの影響を必然的に排除できるという利点もあるので、むやみにスキャン頻度を上げる必要はなさそうです。

いずれにしても、キースキャンに代表されるGPIOで同じ動作を繰り返す処理にCPUを使うのは少々もったいないものがあります。Picoシリーズが搭載するマイコン「RP2040」「RP2350」にはGPIOで同じ動作を繰り返すのに便利なPIOという機構が組み込まれており、キースキャンのような単純動作はPIOに最適な仕事といえます。

PIOは機能が限られている上になんだか面倒そうと敬遠している人もいるかもしれませんが、使いようによっては便利なものなので、キースキャンのために必要なPIOのコードを少し説明しておくことにしましょう。

R1~R4が接続されているGPIOをRawポート、C1~C4が接続されているGPIOをColumnポートと呼ぶことにします。キースキャンは、Rawポートを1から順にHighに切り替えていき、その都度Columnポートを読み込む動作の繰り返しが基本になります。

PIOにおいて、決まったGPIOをオン/オフさせる動作は、set命令かsidesetを使うのが一般的です。set命令のポートをRawポート、Columnポートをin命令のポートに設定するとして、MicroPythonでPIOコードを書くと次のようになります。

@rp2.asm_pio(set_init=(rp2.PIO.OUT_LOW,rp2.PIO.OUT_LOW,rp2.PIO.OUT_LOW,rp2.PIO.OUT_LOW),in_shiftdir=rp2.PIO.SHIFT_LEFT)
def keypad_scanner():
    # ループ先頭
    set(pins,0b1000).delay(1)   # R4をHigh
    in_(pins, 4)                # Columnポートから4ビット読む
    set(pins,0b0100).delay(1)   # R3をHigh
    in_(pins, 4)                # Columnポートから4ビット読む
    set(pins,0b0010).delay(1)   # R2をHigh
    in_(pins, 4)                # Columnポートから4ビット読む
    set(pins,0b0001).delay(1)   # R1をHigh
    in_(pins, 4)                # Columnポートから4ビット読む
    # ループ終わり

set命令のベースGPIOをRawポートの先頭GPIOにする想定で、例えば命令set(pins,0b1000).delay(1)により、R4が接続されているGPIOにHighが出力されます。命令の後ろに付けている.delay(1)は命令実行後に1クロックのディレイ(遅延)を入れるという修飾です。

極めて高速にスキャンを行う場合は1クロックのディレイを入れないとバスが安定しない可能性があるということでディレイを入れていますが、先述の通り4×4キーパッドは高速にスキャンする必要がないので、必ずしもディレイは必要はありません。念のために入れているというだけです。

Rawポートを変化させた後、in命令でColumnポート4ビットを読み取ります。in_shiftdirにrp2.PIO.SHIFT_LEFTを設定しているので、Columnポートから4ビット分の値を読み取るたびに入力レジスタisrは4ビット左シフトします。したがって、1ループ終了すれば入力レジスタisrに16キー分のColumnポートのデータ……これをキースキャンデータと呼ぶことにします……が読み取られることになります。

なお、このコードではR4から順にRawポートをHighにしていっているので、isrのキースキャンデータは第15ビットがスイッチS16、最下位ビットがスイッチS1です。キースキャンデータのビット並びがキーの順番になるので、メインプログラムからは扱いやすいでしょう。

ループの末尾にpush()命令を入れれば、isrのキースキャンデータがFIFOバッファにプッシュされ(同時にisrがクリアされ)、メインプログラムがキースキャンデータを読み出せるようになります。以上でキースキャンとして最低限機能するPIOコードになります。

ただ、これだけだとメインプログラムはFIFOバッファを読み続ける必要があり、PIOにキースキャンを任せたメリットが今一つありません。そこでもう一捻りしましょう。

メインプログラムがFIFOバッファを読み続けなくても済むようにするために、キーの状態に変化があったときだけPIOからキースキャンデータをメインプログラムに送る仕様にします。また、キーの状態に変化があったことをメインプログラムに通知すれば、メインプログラムは通知を受けたときだけFIFOバッファを読めば済み、処理の負荷が大幅に軽減できます。

キーの状態に変化があったことを判断するためには、1つ前のキースキャンデータと最新のキースキャンデータを比較しなければなりません。PIOには比較命令はありませんが「xレジスタとyレジスタが等しくない」というjmp命令の条件式が利用できそうです。

そこで、yレジスタに1つ前のキースキャンデータを保存しておき、スキャンが終わったら入力レジスタisrの値をxレジスタにコピーしてxレジスタとyレジスタが同一でなければ、状態が変化したわけですからキースキャンデータをFIFOバッファに送り、さらに割り込みを発生させます。これにより前述の目的が達成できるでしょう。完成したPIOコードは次の通りです。

@rp2.asm_pio(set_init=(rp2.PIO.OUT_LOW,rp2.PIO.OUT_LOW,rp2.PIO.OUT_LOW,rp2.PIO.OUT_LOW),in_shiftdir=rp2.PIO.SHIFT_LEFT)
def keypad_scanner():
    mov(y, null)                # y = 現在のスキャンデータ
    set(pins, 0)                # RAWをクリア
# ループ先頭
    wrap_target()
    label("loop_start")
    mov(isr, null)              # ISRを0クリア
    set(pins, 0b1000).delay(1)  # RAW4をHIGH
    in_(pins, 4)                # COL読み込み
    set(pins, 0b0100).delay(1)  # RAW3をHIGH
    in_(pins, 4)                # COL読み込み
    set(pins, 0b0010).delay(1)  # RAW2をHIGH
    in_(pins, 4)                # COL読み込み
    set(pins, 0b0001).delay(1)  # RAW1をHIGH
    in_(pins, 4)                # COL読み込み
    mov(x, isr)
    jmp(x_not_y, "state_changes" )
    jmp("loop_start")           # ループ合計16クロック
# ループ終わり
    label("state_changes")      # スキャンデータが変化した
    mov(y, x)                   # yを更新
    push()                      # 最新のスキャンデータをpush
    irq(rel(0))                 # 割り込み生成
    wrap()

label("state_changes")以下がスキャンデータに変化があったときのジャンプ先です。yレジスタを更新するとともに値をFIFOバッファにpush()して、irq命令で割り込みを発生させます。irq()は0~7の番号を渡すことができ、0~3はCPU側のメインプログラムへの割り込みです。CPU側のコードはPIOからの割り込みがあったときだけFIFOバッファを読めばよく、キースキャン以外の処理に専念できます。

1.3 VSCodeのPico拡張でMicroPython

4×4キーパッドの動作を検証するために、今回はMicroPythonを利用します。筆者はRaspberry Pi Pico 2(以下、Pico 2)を利用しましたが、Pico 2に固有の機能は使っていないので、Raspberry Pi Pico(以下、Pico 1)でも問題なく動作します。なので、どちらのPicoを使って試しても問題ありません。

さらに、Visual Studio Code(以下、VSCode)向けの公式エクステンションに含まれているMicroPythonプロジェクトを使って開発を行ってみることにしましょう。

MicroPythonの使い方はご存じの方が多いでしょうが簡単に手順を説明しておくと、MicroPython公式サイトのダウンロードページから、利用するPicoを選んでクリックします。Releasesに列挙されている拡張子.uf2のファイルのうち最新の日付のファイルをダウンロードしてください。執筆時は2024年11月29日付の「v1.24.1」が最新でした(※Pico 2向けはRPI_PICO2-20241129-v1.24.1.uf2、Pico 1向けはRPI_PICO-20241129-v1.24.1.uf2)。

PicoのBOOTSELボタンを押しながら、USBケーブルでPCにPicoを接続します。USBストレージが開くので、先にダウンロードした拡張子.uf2のファイルをドラッグアンドドロップしてください。Pico再起動し、REPLのシリアルポートがPCから認識されます。WindowsならCOMポートがあることを確認しておきます。

VSCodeのRaspberry Pi公式拡張機能については前回の記事で取り上げています。VSCodeや拡張機能を導入していないという方は、記事を参照してインストールしておいてください。

PicoをPCに接続した状態で、左アクティビティバーのPicoアイコンをクリックし、左ペインに表示されるクイックメニューから、New Python Projectを選択します(図4)。

図4:MicroPythonプロジェクトを新規作成 図4:MicroPythonプロジェクトを新規作成

すると図5のようなプロジェクト設定画面が開きます。

図5:プロジェクトの設定 図5:プロジェクトの設定

Name欄にプロジェクト名を設定します。プロジェクトディレクトリはLocation欄に設定したパスの下のプロジェクト名ディレクトリです。今回はプロジェクト名KeyPad_Exampleとしておきます。

注意が必要なのはPython version欄です。PCにPythonをインストールしているのなら、そのインタープリタの実行ファイルを設定できますが、トラブルが起きる可能性があります。Use system versionに設定しておくのが無難でしょう。Pico拡張が利用している言語サーバーpylanceとPython拡張および文法チェッカーPylintの拡張がぶつかることがあるので後で触れることにします。

以上を設定したら、Createボタンを押してください。KeyPad_Exampleのプロジェクトディレクトリが作成され、サンプルとしてLEDを点滅させるblink.pyが作成されます。また、右下ペインにMicroPythonのREPLが開くはずです(図6)。

PCにPicoが接続されていないとシリアルポートが開けない旨のエラーが出続けるので注意しましょう。

なお、blink.pyはただのサンプルで特に意味があるわけではありません。試しに実行してみるなり削除するなり自由にして構いません。

図6:MicroPythonプロジェクト作成直後の画面 図6:MicroPythonプロジェクト作成直後の画面

1.3.1 Python拡張やPylintがインストールされている場合

VSCodeにPython拡張やPylintをインストールしているユーザーは多いでしょう。Python関連のコードをVSCodeで触るとインストールを促されるからです。しかし、Pico拡張ではより新しいPython向けの言語サーバーPylanceを使用しており、Pylint向けの設定は行われていないので、Pylintによるエラーの指摘が山のように表示され、非常に煩わしい画面になります。

なので、Pico拡張を使用するときにはPylintを無効化してしまったほうがいいでしょう。アクティビティバーで拡張機能アイコンをクリックし、検索欄にPylintと入力してPylint拡張を開き、「有効にする」をプルダウンし「無効」を選択しておきます(図7)。

図7:煩わしいPylintは無効に 図7:煩わしいPylintは無効に

また、Python拡張の「Default Interpreter Path」が正しく設定されていないと、IntelliSenseが正しく構成できずエラーが表示されます。その結果、MicroPythonコード作成時にIntelliSenseによるコード補完が機能しません。

IntelliSenseのエラーが表示されるようなら、設定から「Python: Default Interpreter Path」を探してシステムにインストールしているPythonインタープリタの実行ファイルを設定してください(図8)。WindowsユーザーでPythonインタープリタをWindowsにインストールしていない場合、Pico SDK(C:\Users\ユーザー名\.pico-sdk\python\バージョン\)以下にインストールされているPythonインタープリタ(python.exe)が利用できます。

図8:Python拡張のPythonインタープリタを正しく設定する 図8:Python拡張のPythonインタープリタを正しく設定する

KeyPadクラスの作成

では、先のPIOコードをもとに4×4キーパッドを制御するクラスを作成していきましょう。KeyPad_Exampleプロジェクトを開いた状態で、VSCodeのエクスプローラー欄ワークスペース上で右クリックし、「新しいファイル」を選択して「keyPad.py」を新規作成してください。keyPad.pyには4×4キーパッドを扱うためのkeyPadクラスを記述します。内容は次の通りです。

from machine import Pin
from utime import sleep
import rp2
from micropython import schedule

@rp2.asm_pio(set_init=(rp2.PIO.OUT_LOW,rp2.PIO.OUT_LOW,rp2.PIO.OUT_LOW,rp2.PIO.OUT_LOW),in_shiftdir=rp2.PIO.SHIFT_LEFT)
def keypad_scanner():
    mov(y, null)                # y = 現在のスキャンデータ
    set(pins, 0)                # RAWをクリア
# ループ先頭
    wrap_target()
    label("loop_start")
    mov(isr, null)              # ISRを0クリア
    set(pins, 0b1000).delay(1)  # RAW4をHIGH
    in_(pins, 4)                # COL読み込み
    set(pins, 0b0100).delay(1)  # RAW3をHIGH
    in_(pins, 4)                # COL読み込み
    set(pins, 0b0010).delay(1)  # RAW2をHIGH
    in_(pins, 4)                # COL読み込み
    set(pins, 0b0001).delay(1)  # RAW1をHIGH
    in_(pins, 4)                # COL読み込み
    mov(x, isr)
    jmp(x_not_y, "state_changes" )
    jmp("loop_start")           # ループ合計16クロック
# ループ終わり
    label("state_changes")      # スキャンデータが変化した
    mov(y, x)                   # yを更新
    push()                      # 最新のスキャンデータをpush
    irq(rel(0))                 # 割り込み生成
    wrap()

class keyPad():
    
    _prev_code = 0      # 1つ前のスキャンデータ
    _callbackfuncs = [] # コールバック関数

    def __init__(self, callback=None, rawBasePort=6, columnBasePort=2):
        # PIOのステートマシン
        self.sm = rp2.StateMachine(0, keypad_scanner, freq=16000,
         set_base=Pin(rawBasePort),in_base=Pin(columnBasePort),
         in_shiftdir=rp2.PIO.SHIFT_LEFT)
        # 割り込みハンドラの登録
        self.sm.irq(lambda p: schedule(self.keyInterrupt, p))
        # コールバック関数
        self.addCallback(callback)
        # PIOステートマシン起動
        self.sm.active(1)

    def addCallback(self, callback):
        if callback is not None:
            self._callbackfuncs.append(callback)
    
    def keyInterrupt(self, p):
        code = self.sm.get()
        change_bit = code ^ self._prev_code
        mask = 1
        for key in range(16):
            if (change_bit & mask) != 0:
                state = (code & mask) >> key
                # コールバック関数の呼び出し
                for callback in self._callbackfuncs:
                    if callback is not None:
                        callback( (key+1, state) )
            mask = mask << 1
        self._prev_code = code

keyPadクラスのコンストラクタで、先述のPIOコードを使用したステートマシンを作成しています。

         self.sm = rp2.StateMachine(0, keypad_scanner, freq=16000,
         set_base=Pin(rawBasePort),in_base=Pin(columnBasePort),
         in_shiftdir=rp2.PIO.SHIFT_LEFT)

set命令のベースGPIO(set_base)にはRawポートを接続している6を、またColumnポートを接続している2をin命令のベースGPIO(in_base)に設定します。

ステートマシンの動作クロックは16kHz(16000)としておきました。その理由は、PIOコードのループの総クロック数が16だからです。PIOは正確に1クロック1命令を実行するので、16kHzとしておけばループ1回あたり1ミリ秒となり、標準的なキースキャン速度である1秒間1000回のスキャンが行えます。先述の通り、4×4キーパッドは1秒間1000回もスキャンする必要はないかもしれませんが、チャタリングはほとんど見られなかったので、標準的なスキャン速度にしておきました。もしチャタリングが気になるようなら動作クロック(freqの右辺)を落とすとよいでしょう。

コンストラクタの次の行で、PIOの割り込みハンドラを登録します。

self.sm.irq(lambda p: schedule(self.keyInterrupt, p))

このように割り込みハンドラではmicropythonモジュールのschedule()を使って割り込み処理関数keyInterrupt()を登録しています。schedule()は、MicroPythonが持つ関数の遅延呼び出しです。schedule()に渡された関数はMicroPythonインタープリタのスケジュールキューに登録され、MicroPythonインタープリタが都合の良いときにキューに積まれた関数を呼び出す仕組みです。

micropythonモジュールのschedule()を利用する理由は、割り込みハンドラの処理時間を短縮するためです。一般論として、割り込みハンドラはできるだけ短く、短時間で終わるように記述すべきです。なので、割り込みハンドラではschedule()を使って割り込み処理関数keyInterrupt()をスケジュールキューに積むだけにしておき、実際の割り込み処理は適宜MicroPythonインタープリタから呼び出されるようにしておきます。

もっとも、MicroPythonにおけるPIO割り込みはデフォルトではソフトウェア割り込みです。つまりインタープリタがPIOのハードウェア割り込みを受け付け、MicroPythonインタープリタが都合の良いときにユーザーの割り込みハンドラを呼び出す形になっています。したがって、割り込みハンドラの処理時間を神経質に切り詰める必要はないかもしれません。

ただ、keyInterrupt()では、キーが押されたときに何かをするためのコールバック関数を呼び出す実装にしたいということがあります。コールバック関数の実行時間は4×4キーパッドを活用するアプリケーションによって異なってきます。つまり実行時間が読めないので、割り込みハンドラから直接的に呼び出すのはあまり適切とはいえません。そこでmicropythonクラスのschedule()を利用したわけです。

keyInterrupt()では、まずStateMachineクラスのget()を使ってPIOからのキースキャンデータを読み出し、1つ前のキースキャンデータを保存しているクラス変数_prev_codeとXORを取って変化したビットを取り出します。先述のように、キースキャンデータはビット順がキー番号に対応しているので、下位ビットから変化したビットを調べ、(キー番号,キーステート)のリストをコールバック関数に渡します。キーステートは1ならキーオン、0ならキーオフです。

keyPad.pyをPicoの仮想ストレージに転送する

keyPad.pyはMicroPythonコードからインポートして利用できるよう、Pico側MicroPythonの仮想ストレージに格納しておく必要があります。VSCodeのPico拡張には、MicroPythonによる開発作業を行うために必要な、いくつかの機能が備わっています。

MicroPython向けの主な機能はエディタ最下段のツールバーに集約されています(図9)。左から順に次のような機能が割り当てられています。

  • Picoとの接続状態を示すインジケーター
  • 現在開いているPythonコードをMicroPythonで実行/停止するRun/Stopボタン
  • ソフトウェアリセットボタン
  • MicroPythonコマンドメニュー
  • 仮想ストレージファイル一覧
図9:ツールバーのMicroPython関連アイコン 図9:ツールバーのMicroPython関連アイコン

keyPad.pyを仮想ストレージに転送するには、まずエクスプローラー上で作成したkeyPad.pyを右クリックしてください。「Upload file to Pico」という項目を選択すると、keyPad.pyがMicroPythonの仮想ストレージに転送されます。

転送を終えたら、ツールバーの「Toggle Mpy FS」をクリックしてください。エクスプローラーに「Mpy Remote Workspace」という項目が追加されます。その下の一覧がMicroPython側の仮想ファイルシステムにあるファイル群で、現時点では先に転送したkeyPad.pyが表示されているはずです(図10)。

図10:仮想ファイルシステムを表示させた様子 図10:仮想ファイルシステムを表示させた様子

Mpy Remote Workspace以下に表示されている仮想ファイルシステム上のファイルを、VSCodeで開いて編集することもできます。ローカルファイルシステムのファイルと、仮想ファイルシステム上のファイルが今一つ区別しづらいのが難点で、ローカルファイルと仮想ファイルの内容が異なっていると混乱するかもしれません。仮想ファイルシステム上のファイルをエディタで更新するときは気を付けたほうがいいでしょう。

仮想ファイルシステムのファイルを削除したいときには、エクスプローラーのファイル名の上で右クリックし、「完全に削除」を選択します。

4×4キーパッドをテストするPythonコード

では、最後に4×4キーパッドの動作をテストしてみましょう。次のようなkeytest.pyをローカルファイルシステム側に作成します。

from keyPad import keyPad
from machine import idle

def mycallback(code):
    if code[1] != 0:
        print(str(code[0]) + " is On")
    else:
        print(str(code[0]) + " is Off")

kp = keyPad(mycallback)

while True:
    idle()

入力したら、ステータスバーのRun/Stopボタンを押してください。keytest.pyが実行されるので、4×4キーパッドを適当に押してみます。ターミナル上に図11のように、ボタンを押すと番号 is On、ボタンを離すと番号 is Offと表示されます。コールバック関数はaddCallback()を使っていくらでも登録できるので、4×4キーパッドをさまざまなアプリケーションに活用できるはずです。

図11:keytest.py実行中のターミナルの表示 図11:keytest.py実行中のターミナルの表示

参考までに、図12は、ロジックアナライザーでRawポートを測定した様子です。3からR4、0がR1の順になっていて、1回のキースキャンがおよそ1ミリ秒になっていることが確認できます。R1のオン時間が長い理由はおわかりと思いますが、他に比べて4命令多いからです。

図12:Rawポートの信号 図12:Rawポートの信号

このように、PIOを用いるとキースキャンのような単純な繰り返し動作をCPUを使わずに実行でき、また正確にGPIOを制御できるのです。Picoではぜひとも活用したい機能です。

次回は、4×4キーパッドをUSB接続キーパッドに仕立ててみます。MicroPythonでは難しいので、C SDKを使用していくことになります。

おすすめ記事

 

コメント

ニュース

編集部のおすすめ

連載・シリーズ

注目のキーワード

もっと見る