はじめに
本当はRaspberry Pi 4 Model Bあたりが欲しかったのですが、昨今の半導体不足等のせいで手に入らず、仕方なくPicoを購入しました。
しかし、できることは限られるので、この際だからMicroPythonを使ってみることにしました。
目的
とりあえずは、Raspberry Pi PicoをMicroPythonで動かすこと。
便利そうなら、他のマイコンもMiroPythonで動かすことを考えたいです。
今回やること
どうやら「machine」モジュールが肝となっていそうなので、その理解を進めます。
ぶっちゃけ、公式ドキュメントのまとめになります。
https://micropython-docs-ja.readthedocs.io/ja/latest/library/machine.html
「machine」モジュール
- 特定のボード上のハードウェアに関連する固有の関数を含んでいる
- いくつかの「関数」と「定数」と「クラス」を持っている
以降、良く使うそうな機能だけをざっくりと説明していきます。
関数
- リセット関連
- リセット:reset()
- ソフトリセット:soft_reset()
- リセットの原因の取得:reset_cause()
- ブートローダーの起動:bootloader()
- 割り込み関連
- 無効化/有効化:disable_irq()/enable_irq()
- 電力関連
- CPU周波数の取得:freq()
- アイドリング:idle()
- スリープ(長期/短期):lightsleep()/deepsleep()
- 起床の原因の取得:wake_reason()
- その他
- ボード/SoCのIDの取得:unique_id()
- パルスの持続時間の取得:time_pulse_us()
- ビット送信:bitstream()
- 乱数の取得:rng()
定数
※省略
クラス
用意されているのは、以下の14クラス。
- Pin:I/Oピンの制御
- Signal:外部I/Oデバイスの制御
- ADC:アナログ-デジタル変換
- ADCBlock:ADCペリフェラルの制御
- PWM:パルス幅変調
- UART:二重シリアル通信バス
- SPI:シリアルペリフェラルインターフェースバスプロトコル(コントローラ側)
- I2C:2線式シリアルプロトコル
- I2S:IC間(Inter-IC Sound)サウンドバスプロトコル
- RTC:リアルタイムクロック
- Timer:ハードウェアタイマーの制御
- WDT:ウォッチドッグタイマー
- SD:セキュアデジタルメモリカード
- SDCard:SDメモリカード
このうち、重要そうなもの(赤文字)だけ説明します。
Pinクラス
GPIOピンへのデジタル制御(ON/OFF)で使用。
(例)LEDの点灯
- ピン番号(+入出力)を指定して、インスタンス(オブジェクト)を生成
- ON/OFFを入出力
from machine import Pin
# ピン #0 を出力ピンとして作成
p0 = Pin(0, Pin.OUT)
# 値をロー、ハイと順に設定
p0.value(0)
p0.value(1)
# ピン #2 をプルアップ抵抗付きの入力ピンとして作成
p2 = Pin(2, Pin.IN, Pin.PULL_UP)
# ピンの値を読み込んで表示
print(p2.value())
# ピン #0 を入力モード&プルダウン抵抗指定で再設定
p0.init(p0.IN, p0.PULL_DOWN)
# irq コールバックを設定
p0.irq(lambda p:print(p))
ADCクラス
アナログ値(電圧の変動)をデジタル値に変換して取得。
(例)ボリューム値の取得
- チャンネル(ピン)を指定してインスタンス(オブジェクト)を生成
- 各チャンネルがどのGPIOピンに割り当てられているのかは、ハードウェアによる
- 現在値の読み込み
- uint(0~65,535でスケーリング)、または、マイクロボルト値
from machine import ADC
adc = ADC(pin) # 指定のピンで動作する ADC オブジェクトを作成
val = adc.read_u16() # 生のアナログ値を 0-65535 の範囲で読込み
val = adc.read_uv() # アナログ値をマイクロボルトで読込み
PWMクラス
パルス幅を変調した信号を出力。
(例)LEDの明るさの制御
- ピンを指定して、インスタンス(オブジェクト)を生成
- 周波数、パルス幅を指定
from machine import PWM
pwm = PWM(pin) # 指定のピンの PWM オブジェクトを作成
pwm.duty_u16(32768) # パルス幅を 50% に設定
# 200us の周期、デューティ比を 5us で再初期化
pwm.init(freq=5000, duty_ns=5000)
pwm.duty_ns(3000) # 3us のパルス幅を設定
pwm.deinit()
UARTクラス
二重(送受信)シリアル通信を実装。
バイトデータ(文字など)をやり取りする感じ。
(例)CO2センサー(MH-Z19Cなど)との通信
- ピンを指定して、インスタンス(オブジェクト)を生成
- 通信速度、ビット数などを設定
- 複数バイトを送受信
from machine import UART
uart = UART(1, 9600) # 与えたボーレートで初期化
uart.init(9600, bits=8, parity=None, stop=1) # 与えたパラメータで初期化
uart.read(10) # 10文字を読み込んで、bytes 型オブジェクトを返す
uart.read() # 可能な限り文字を読み込む
uart.readline() # 1行を読み込む
uart.readinto(buf) # 読み込んで、与えたバッファに格納
uart.write('abc') # 3文字を書き込む
SPIクラス(SoftSPIクラス)
同期通信を実装。
バイナリデータをやり取りする感じ。
ソフトウェアSPIも可。
(例)OLEDディスプレイ(SSD1306など)との通信
- IDを指定して、インスタンス(オブジェクト)を生成
- 各IDがどのGPIOピンに割り当てられているのかは、ハードウェアによる
- 通信速度、ビット数などを設定
- 複数バイトを送受信
from machine import SPI, Pin
spi = SPI(0, baudrate=400000) # 周波数 400kHz で SPI ペリフェラル 0 を作成
# ユースケースによっては、追加のパラメータが必要な場合があります。使用するバスの
# 特性やピンを選択するための追加のパラメータが必要になる場合があります。
cs = Pin(4, mode=Pin.OUT, value=1) # ピン 4 でチップセレクトを作成。
try:
cs(0) # ペリフェラルを選択。
spi.write(b"12345678") # 8 バイトを書き出し、受信データについては無視。
finally:
cs(1) # ペリフェラルを選択解除。
try:
cs(0) # ペリフェラルを選択。
rxdata = spi.read(8, 0x42) # 0x42 をバイトごとに書き出しながら、合計 8 バイトを読み込む。
finally:
cs(1) # ペリフェラルを選択解除。
rxdata = bytearray(8)
try:
cs(0) # ペリフェラルを選択。
spi.readinto(rxdata, 0x42) # 0x42 をバイトごとに書き出しながら、合計 8 バイトを指定場所に読み込む。
finally:
cs(1) # ペリフェラルを選択解除。
txdata = b"12345678"
rxdata = bytearray(len(txdata))
try:
cs(0) # ペリフェラルを選択。
spi.write_readinto(txdata, rxdata) # バイト列の書出しと読込みを同時に行う。
finally:
cs(1) # ペリフェラルを選択解除。
I2Cクラス
2線式プロトコルでの通信を実装。
使い方的にはSPIと似た感じ。
(例)加速度センサー(ADXL345など)との通信
- インスタンス(オブジェクト)を生成
- 生成時には特にID等は指定しない
- 通信速度、ビット数などを設定
- 複数バイトを送受信
- 送受信時に、相手(ID)を指定
from machine import I2C
i2c = I2C(freq=400000) # ポートに依存して 400kHz の周波数でI2Cペリフェラルを
# 作成します。使用するペリフェラルやピンを選択するために
# 追加のパラメータが必要になる場合があります
i2c.scan() # ペリフェラルをスキャンし、7ビットアドレスのリストを返します
i2c.writeto(42, b'123') # 7ビットアドレス 42 のペリフェラルに3バイトを書き込みます
i2c.readfrom(42, 4) # 7ビットアドレス 42 のペリフェラルから4バイトを読み込みます
i2c.readfrom_mem(42, 8, 3) # スレーブ 42 のメモリから、ペリフェラルのメモリアドレス 8 で
# 始まる3バイトを読み込みます
i2c.writeto_mem(42, 2, b'\x10') # スレーブ 42 のメモリの、ペリフェラルのメモリアドレス 2 で
# 始まるところに2バイトを書き込みます
最後に
machineモジュール以外にも、ファイルアクセスやネットワーク関連のモジュールのことも知っておいた方がいいけど、ここではここまでにしておきます。
おまけ
C/C++党だけど、何かと(環境構築とか)楽なので、Python派に流れそう...
しかも、ESP32もMicroPythonが動くのね。知らなかった。