Skip to main content

Seeed Studio XIAO nRF54L15 用 MicroPython

MicroPython の理解

このチュートリアルは、XIAO nRF54L15 を基に Thonny で MicroPython を使用する方法を紹介することを目的としています。

MicroPython は、部分的なネイティブコードコンパイル機能を持つ Python インタープリターです。組み込みプロセッサーと制約のあるシステム向けに実装された Python 3.5 機能のサブセットを提供します。CPython とは異なり、違いについてはこちらで詳しく読むことができます。より興味深いコレクションが必要な場合は、こちらをチェックできます。

ハードウェアの準備

Seeed Studio XIAO nRF54L15 Seeed Studio XIAO nRF54L15 SenseSeeed Studio XIAO Debug Mate

Thonny IDE のインストール

Thonny IDE

インストールに適したバージョンを選択してください。ここでは、Windows システムにインストールするため、Windows バージョンを選択しました。

希望する Python バージョンの指示に従ってください。

その後、設定のデフォルト手順に従うだけです。

リポジトリのダウンロード

ローカルマシンにクローンし、この XIAO nRF54L15 の MicroPython が保存されているパスを覚えておいてください。このパスは後で使用されます。

git clone https://github.com/Seeed-Studio/micropython-seeed-boards.git

ボードファイルのアップロード

ステップ 0. USB ケーブルを使用して XIAO NRF54L15 をコンピューターに接続します

ステップ 1. XIAO nRF54L15 用の MicroPython ファームウェアをフラッシュします

  • ファームウェアパッケージをダウンロードして適切な場所に展開します。その後、flash.bat をクリックすると、自動的にファームウェアがフラッシュされます。

    [ファームウェア] XIAO nRF54L15 MicroPython ファームウェア

結果は以下の通りです

tip

このスクリプトには事前設定されたフラッシュツールチェーンコマンドがあります。初回使用時は少し時間がかかる場合があります。ダウンロード後に自動的にクラッシュします。XIAO NRF54L15 が接続されていない場合、フラッシュ中に '200' エラーが発生します。

ステップ 2. Thonny IDE を開き、インターフェースの右下角をクリックしてインタープリターオプションを設定します。MicroPython (generic) とポートを選択します

ステップ 3. ボードファイルをアップロードします

  • "view" を開き、"File" を選択すると、ファイルマネージャーパスが左サイドバーに表示されます。
  • クローンまたはダウンロードしたファイルのパスを開き、micropython-seeed-boards\examples を開きます
  • "boards" フォルダーを右クリックしてフラッシュにアップロードします。その後、MicroPython デバイス/フラッシュにアップロードされたファイルが表示されます。

正常な場合、位置 '3' にアイコンが表示されます

ステップ 4. LED を点灯させます

新しいファイル(XX.py)を開くか、スタートページに移動し、コードをコピーして F5 を押して実行します。

import time
from boards.xiao import XiaoPin

led = "led"

try:
# Initialize LED
led = XiaoPin(led, XiaoPin.OUT)
while True:
# LED 0.5 seconds on, 0.5 seconds off
led.value(1)
time.sleep(0.5)
led.value(0)
time.sleep(0.5)
except KeyboardInterrupt:
print("\nProgram interrupted by user")
except Exception as e:
print("\nError occurred: %s" % {e})
finally:
led.value(1)

結果は以下の通りです:

デジタル

ハードウェア

Seeed Studio XIAO nRF54L15 SenseSeeed Studio Expansion Base for XIAO with Grove OLEDGrove - Relay

ソフトウェア

from machine import Pin
from boards.xiao_nrf54l15 import xiao_nrf54l15 as xiao

# Define a function to get the GPIO information corresponding to pin A0
def get_a0_pin():
# Get the information of pin A0 through the pin method of the xiao module
# According to the definition in xiao_nrf54l15.py, A0 corresponds to digital pin 0
pin_info = xiao.pin(0) # Get the information of digital pin 0, the return value is a tuple, such as ("gpio1", 4)
return pin_info

# Define a function to set pin A0 to high level
def set_a0_high():
# Get the GPIO information of pin A0
gpio_port, gpio_pin = get_a0_pin() # Get the port and pin number
# Create a Pin object, specify the pin as output mode, and set it to high level
pin = Pin((gpio_port, gpio_pin), Pin.OUT) # Initialize the pin as output mode
pin.value(1) # Set the pin to high level

# Main program
if __name__ == "__main__":
set_a0_high() # Call the function to set pin A0 to high level
print("Pin A0 has been set to high level") # Output prompt information

コードの説明: このコードには4つの部分があります:モジュールのインポート、ピンA0のGPIO情報を取得する関数、ピンA0を高レベルに設定する関数、そしてメイン関数です。メインプログラムでは、ピンA0を高レベルに設定する操作を呼び出します。

結果

アナログ

ハードウェア

Seeed Studio XIAO nRF54L15 SenseGrove-Variable Color LEDGrove-Rotary Angle Sensor Seeed Studio Grove Base for XIAO

ソフトウェア

import time
from boards.xiao import XiaoPin, XiaoADC, XiaoPWM

adc = 0 #D0
pwm = 1 #D1

try:
# Initialize ADC for potentiometer
adc = XiaoADC(adc)
# Initialize PWM for LED control
pwm = XiaoPWM(pwm)
FREQ = 1000
PERIOD_NS = 1000000
pwm.init(freq=FREQ, duty_ns=0)
# Potentiometer parameters
MIN_VOLTAGE = 0.0
MAX_VOLTAGE = 3.3
DEAD_ZONE = 0.05
last_duty = -1
while True:
# Read ADC voltage value
voltage = adc.read_uv() / 1000000

# Ensure voltage is within valid range
if voltage < MIN_VOLTAGE:
voltage = MIN_VOLTAGE
elif voltage > MAX_VOLTAGE:
voltage = MAX_VOLTAGE

duty_percent = (voltage - MIN_VOLTAGE) / (MAX_VOLTAGE - MIN_VOLTAGE)

# Apply dead zone to prevent tiny fluctuations
if abs(duty_percent - last_duty) < DEAD_ZONE / 100:
time.sleep(0.05)
continue

# Calculate duty cycle time (nanoseconds)
duty_ns = int(duty_percent * PERIOD_NS)

# Set PWM duty cycle
pwm.duty_ns(duty_ns)

# Print current status
print("Voltage: {:.2f}V, Duty Cycle: {:.1f}%".format(voltage, duty_percent * 100))

# Update last duty cycle value
last_duty = duty_percent

# Short delay
time.sleep(0.05)
except KeyboardInterrupt:
print("\nProgram interrupted by user")
except Exception as e:
print("\nError occurred: %s" % {e})
finally:
pwm.deinit()

コードの説明: このコードは4つの部分に分けることができます:

  • モジュールのインポート:遅延操作のためのtimeモジュール、およびXiaoADCとXiaoPWMモジュールを含む
  • ハードウェアの初期化:ADCとPWMピンを定義し、ポテンショメータの電圧を読み取るためのADCを初期化し、LED輝度を制御するためのPWMを初期化する
  • メインプログラムロジック:無限ループ内で、ポテンショメータの電圧を読み取り、それをPWMデューティサイクルに変換し、電圧に応じてLED輝度を調整する
  • 例外処理とクリーンアップ:ユーザー割り込み(Ctrl+Cの押下など)やその他の例外をキャッチして、プログラムが安全に終了することを保証する

結果

I2C

ハードウェア

Seeed Studio XIAO nRF54L15 SenseSeeed Studio Expansion Board Base for XIAO

ソフトウェア

import time
from boards.xiao import XiaoI2C

sda = 4 #D4
scl = 5 #D5
i2c = "i2c0"
frq = 400000
i2c = XiaoI2C(i2c, sda, scl, frq)

# --- SSD1306 I2C address and command definitions ---
SSD1306_I2C_ADDR = 0x3C
SSD1306_SET_CONTRAST = 0x81
SSD1306_DISPLAY_ALL_ON_RESUME = 0xA4
SSD1306_DISPLAY_ALL_ON = 0xA5
SSD1306_NORMAL_DISPLAY = 0xA6
SSD1306_INVERT_DISPLAY = 0xA7
SSD1306_DISPLAY_OFF = 0xAE
SSD1306_DISPLAY_ON = 0xAF
SSD1306_SET_DISPLAY_OFFSET = 0xD3
SSD1306_SET_COM_PINS = 0xDA
SSD1306_SET_VCOM_DETECT = 0xDB
SSD1306_SET_DISPLAY_CLOCK_DIV = 0xD5
SSD1306_SET_PRECHARGE = 0xD9
SSD1306_SET_MULTIPLEX = 0xA8
SSD1306_SET_LOW_COLUMN = 0x00
SSD1306_SET_HIGH_COLUMN = 0x10
SSD1306_SET_START_LINE = 0x40
SSD1306_MEMORY_MODE = 0x20
SSD1306_COLUMN_ADDR = 0x21
SSD1306_PAGE_ADDR = 0x22
SSD1306_COM_SCAN_INC = 0xC0
SSD1306_COM_SCAN_DEC = 0xC8
SSD1306_SEG_REMAP = 0xA0
SSD1306_CHARGE_PUMP = 0x8D

# Display dimensions
SSD1306_WIDTH = 128
SSD1306_HEIGHT = 64
SSD1306_PAGES = 8

# Basic 8x8 font
font_data = {
' ': [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00],
'A': [0x18,0x24,0x42,0x7E,0x42,0x42,0x42,0x00],
'B': [0x7C,0x42,0x42,0x7C,0x42,0x42,0x7C,0x00],
'C': [0x3C,0x42,0x40,0x40,0x40,0x42,0x3C,0x00],
'D': [0x78,0x44,0x42,0x42,0x42,0x44,0x78,0x00],
'E': [0x7C,0x40,0x40,0x78,0x40,0x40,0x7C,0x00],
'F': [0x7C,0x40,0x40,0x78,0x40,0x40,0x40,0x00],
'G': [0x3C,0x42,0x40,0x4E,0x42,0x42,0x3C,0x00],
'H': [0x44,0x44,0x44,0x7C,0x44,0x44,0x44,0x00],
'I': [0x38,0x10,0x10,0x10,0x10,0x10,0x38,0x00],
'J': [0x1C,0x08,0x08,0x08,0x08,0x48,0x30,0x00],
'K': [0x44,0x48,0x50,0x60,0x50,0x48,0x44,0x00],
'L': [0x40,0x40,0x40,0x40,0x40,0x40,0x7C,0x00],
'M': [0x42,0x66,0x5A,0x42,0x42,0x42,0x42,0x00],
'N': [0x42,0x62,0x52,0x4A,0x46,0x42,0x42,0x00],
'O': [0x3C,0x42,0x42,0x42,0x42,0x42,0x3C,0x00],
'P': [0x7C,0x42,0x42,0x7C,0x40,0x40,0x40,0x00],
'Q': [0x3C,0x42,0x42,0x42,0x4A,0x44,0x3A,0x00],
'R': [0x7C,0x42,0x42,0x7C,0x48,0x44,0x42,0x00],
'S': [0x3C,0x42,0x40,0x3C,0x02,0x42,0x3C,0x00],
'T': [0x7C,0x10,0x10,0x10,0x10,0x10,0x10,0x00],
'U': [0x42,0x42,0x42,0x42,0x42,0x42,0x3C,0x00],
'V': [0x42,0x42,0x42,0x42,0x42,0x24,0x18,0x00],
'W': [0x42,0x42,0x42,0x42,0x5A,0x66,0x42,0x00],
'X': [0x42,0x24,0x18,0x18,0x18,0x24,0x42,0x00],
'Y': [0x44,0x44,0x28,0x10,0x10,0x10,0x10,0x00],
'Z': [0x7E,0x04,0x08,0x10,0x20,0x40,0x7E,0x00],
'0': [0x3C,0x42,0x46,0x4A,0x52,0x62,0x3C,0x00],
'1': [0x10,0x30,0x10,0x10,0x10,0x10,0x38,0x00],
'2': [0x3C,0x42,0x02,0x0C,0x30,0x40,0x7E,0x00],
'3': [0x3C,0x42,0x02,0x1C,0x02,0x42,0x3C,0x00],
'4': [0x08,0x18,0x28,0x48,0x7E,0x08,0x08,0x00],
'5': [0x7E,0x40,0x7C,0x02,0x02,0x42,0x3C,0x00],
'6': [0x1C,0x20,0x40,0x7C,0x42,0x42,0x3C,0x00],
'7': [0x7E,0x42,0x04,0x08,0x10,0x10,0x10,0x00],
'8': [0x3C,0x42,0x42,0x3C,0x42,0x42,0x3C,0x00],
'9': [0x3C,0x42,0x42,0x3E,0x02,0x04,0x38,0x00],
'!': [0x10,0x10,0x10,0x10,0x10,0x00,0x10,0x00],
'?': [0x3C,0x42,0x02,0x0C,0x10,0x00,0x10,0x00],
'.': [0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00],
',': [0x00,0x00,0x00,0x00,0x00,0x10,0x10,0x20],
':': [0x00,0x10,0x00,0x00,0x00,0x10,0x00,0x00],
';': [0x00,0x10,0x00,0x00,0x00,0x10,0x10,0x20],
'-': [0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x00],
'_': [0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00],
'+': [0x00,0x10,0x10,0x7C,0x10,0x10,0x00,0x00],
'*': [0x00,0x24,0x18,0x7E,0x18,0x24,0x00,0x00],
'/': [0x02,0x04,0x08,0x10,0x20,0x40,0x00,0x00],
'\\': [0x40,0x20,0x10,0x08,0x04,0x02,0x00,0x00],
'=': [0x00,0x00,0x7E,0x00,0x7E,0x00,0x00,0x00],
'\'': [0x10,0x10,0x20,0x00,0x00,0x00,0x00,0x00],
'"': [0x24,0x24,0x00,0x00,0x00,0x00,0x00,0x00],
'(': [0x08,0x10,0x20,0x20,0x20,0x10,0x08,0x00],
')': [0x20,0x10,0x08,0x08,0x08,0x10,0x20,0x00],
'[': [0x1C,0x10,0x10,0x10,0x10,0x10,0x1C,0x00],
']': [0x38,0x08,0x08,0x08,0x08,0x08,0x38,0x00],
'{': [0x0C,0x10,0x10,0x60,0x10,0x10,0x0C,0x00],
'}': [0x30,0x08,0x08,0x06,0x08,0x08,0x30,0x00],
'<': [0x08,0x10,0x20,0x40,0x20,0x10,0x08,0x00],
'>': [0x20,0x10,0x08,0x04,0x08,0x10,0x20,0x00],
'|': [0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00],
'@': [0x3C,0x42,0x5A,0x5A,0x5C,0x40,0x3C,0x00],
'#': [0x24,0x24,0x7E,0x24,0x7E,0x24,0x24,0x00],
'$': [0x10,0x3C,0x50,0x3C,0x12,0x3C,0x10,0x00],
'%': [0x62,0x64,0x08,0x10,0x26,0x46,0x00,0x00],
'^': [0x10,0x28,0x44,0x00,0x00,0x00,0x00,0x00],
'&': [0x30,0x48,0x50,0x20,0x54,0x48,0x34,0x00],
'~': [0x00,0x00,0x34,0x4C,0x00,0x00,0x00,0x00]
}

# --- Helper functions ---

# Write a single command byte to SSD1306 via I2C
def ssd1306_write_command(cmd):
i2c.writeto(SSD1306_I2C_ADDR, bytes([0x00, cmd]))

# Write multiple command bytes to SSD1306 via I2C
def ssd1306_write_commands(cmds):
data = bytearray([0x00] + list(cmds))
i2c.writeto(SSD1306_I2C_ADDR, data)

# Write display data bytes to SSD1306 via I2C
def ssd1306_write_data(data):
buffer = bytearray(len(data) + 1)
buffer[0] = 0x40
buffer[1:] = data
i2c.writeto(SSD1306_I2C_ADDR, buffer)

# Clear the entire SSD1306 display
def ssd1306_clear():
ssd1306_write_commands(bytearray([SSD1306_COLUMN_ADDR, 0, SSD1306_WIDTH - 1]))
ssd1306_write_commands(bytearray([SSD1306_PAGE_ADDR, 0, SSD1306_PAGES - 1]))

empty_data = bytearray(SSD1306_WIDTH)
for _ in range(SSD1306_PAGES):
ssd1306_write_data(empty_data)
ssd1306_write_commands([SSD1306_COLUMN_ADDR, 0, SSD1306_WIDTH - 1])

# Initialize SSD1306 display with recommended settings
def ssd1306_init():
commands = [
bytearray([SSD1306_DISPLAY_OFF]),
bytearray([SSD1306_SET_DISPLAY_CLOCK_DIV, 0x80]),
bytearray([SSD1306_SET_MULTIPLEX, SSD1306_HEIGHT - 1]),
bytearray([SSD1306_SET_DISPLAY_OFFSET, 0x00]),
bytearray([SSD1306_SET_START_LINE | 0x00]),
bytearray([SSD1306_CHARGE_PUMP, 0x14]),
bytearray([SSD1306_MEMORY_MODE, 0x00]),
bytearray([SSD1306_SEG_REMAP | 0x01]),
bytearray([SSD1306_COM_SCAN_DEC]),
bytearray([SSD1306_SET_COM_PINS, 0x12]),
bytearray([SSD1306_SET_CONTRAST, 0xCF]),
bytearray([SSD1306_SET_PRECHARGE, 0xF1]),
bytearray([SSD1306_SET_VCOM_DETECT, 0x40]),
bytearray([SSD1306_DISPLAY_ALL_ON_RESUME]),
bytearray([SSD1306_NORMAL_DISPLAY]),
bytearray([SSD1306_DISPLAY_ON])
]

for cmd in commands:
ssd1306_write_commands(cmd)

ssd1306_clear()
print("SSD1306 initialized successfully.")
ssd1306_write_commands([SSD1306_COLUMN_ADDR, 0, SSD1306_WIDTH - 1])

# Draw a string of text at specified column and page (row) on SSD1306
def ssd1306_draw_text(text, x, y):
ssd1306_write_commands(bytearray([SSD1306_COLUMN_ADDR, x, x + len(text) * 8 - 1]))
ssd1306_write_commands(bytearray([SSD1306_PAGE_ADDR, y, y + 0]))

display_data = bytearray()
for char in text:
font_bytes = font_data.get(char.upper(), font_data[' '])
for col in range(7, -1, -1):
val = 0
for row in range(8):
if font_bytes[row] & (1 << col):
val |= (1 << row)
display_data.append(val)

ssd1306_write_data(display_data)

i2c_addr = i2c.scan()
if SSD1306_I2C_ADDR not in i2c_addr:
raise Exception("SSD1306 not found on I2C bus")
else:
print("SSD1306 found on I2C bus: 0x{:02X}".format(SSD1306_I2C_ADDR))

# Initialize display
ssd1306_init()
ssd1306_draw_text("NRF54L15", 30, 2)
ssd1306_draw_text("HELLO WORLD", 20, 4)

コード説明:

このコードは、I2C通信を介してSSD1306 OLEDディスプレイを初期化および制御し、ディスプレイのコマンドとパラメータを定義し、画面のクリア、初期化、テキスト表示の機能を実装します。

  • モジュールのインポートとI2C通信の初期化: 遅延操作のためにtimeモジュールをインポートし、I2C通信を初期化するためにXiaoI2Cモジュールをインポートしました。I2CのSDAとSCLピンを定義し、I2C周波数を設定しました。その後、OLEDディスプレイなどのI2Cデバイスとの通信用にXiaoI2Cオブジェクトを作成しました。

  • SSD1306ディスプレイの命令とパラメータの定義: SSD1306ディスプレイのI2Cアドレスと一連の制御コマンド(コントラスト設定、ディスプレイのオン/オフなど)を定義します。また、ディスプレイのサイズパラメータ(幅、高さ、ページ数)と、画面に文字を表示するためのシンプルな8x8ドットマトリックスフォントライブラリも定義します。

  • ヘルパー関数の定義: SSD1306にコマンドとデータを送信するための一連のヘルパー関数を定義します。

  • メインプログラムロジック: まず、I2Cスキャンを実行してSSD1306ディスプレイがI2Cバスに接続されているかを確認します。ディスプレイが見つかった場合、ssd1306_init関数を呼び出してディスプレイを初期化します。その後、ssd1306_draw_text関数を呼び出して、ディスプレイに「NRF54L15」と「HELLO WORLD」の2行のテキストを表示します。

結果

SPI

ハードウェア

Seeed Studio XIAO nRF54L15 SenseePaper Driver Board for Seeed Studio XIAO

ソフトウェア

import time
from boards.xiao import XiaoPin, XiaoSPI

# -------- Pins & SPI --------
RST = 0; CS = 1; DC = 3; BUSY = 5
sck = 9; mosi = 10; miso = 8; spi_id = "spi0"

RST = XiaoPin(RST, XiaoPin.OUT)
CS = XiaoPin(CS, XiaoPin.OUT)
DC = XiaoPin(DC, XiaoPin.OUT)
BUSY = XiaoPin(BUSY, XiaoPin.IN, XiaoPin.PULL_UP)
spi = XiaoSPI(spi_id, 20_000_000, sck, mosi, miso)

# -------- ePaper basics --------
def reset():
RST.value(0); time.sleep_ms(10)
RST.value(1); time.sleep_ms(10)

def send_command(cmd):
DC.value(0); CS.value(0)
spi.write(bytearray([cmd & 0xFF]))
CS.value(1)

def send_data(data):
DC.value(1); CS.value(0)
if isinstance(data, int):
spi.write(bytearray([data & 0xFF]))
else:
spi.write(data)
CS.value(1)

def wait_until_idle():
# If BUSY = 0, it indicates that the device is busy. You can then switch back to polling.
# while BUSY.value() == 0: time.sleep_ms(1)
time.sleep_ms(1)

def init_display():
reset()
send_command(0x00); send_data(0x1F)
send_command(0x04); time.sleep_ms(100); wait_until_idle()
send_command(0x50); send_data(0x21); send_data(0x07)

def clear_screen():
CS.value(0)
DC.value(0); spi.write(b'\x10'); DC.value(1)
for _ in range(48000): spi.write(b'\xFF')
DC.value(0); spi.write(b'\x13'); DC.value(1)
for _ in range(48000): spi.write(b'\xFF')
DC.value(0); spi.write(b'\x12'); CS.value(1)
wait_until_idle()

# -------- Geometry --------
WIDTH, HEIGHT = 800, 480
BYTES_PER_ROW = WIDTH // 8
linebuf = bytearray(BYTES_PER_ROW)

# -------- Minimal 5x7 glyphs (columns, LSB=top) --------
FONT_W, FONT_H = 5, 7
G = {
' ':[0x00,0x00,0x00,0x00,0x00],
# Digits
'0':[0x3E,0x51,0x49,0x45,0x3E],
'1':[0x00,0x42,0x7F,0x40,0x00],
'2':[0x42,0x61,0x51,0x49,0x46],
'3':[0x21,0x41,0x45,0x4B,0x31],
'4':[0x18,0x14,0x12,0x7F,0x10],
'5':[0x27,0x45,0x45,0x45,0x39],
'6':[0x3C,0x4A,0x49,0x49,0x30],
'7':[0x01,0x71,0x09,0x05,0x03],
'8':[0x36,0x49,0x49,0x49,0x36],
'9':[0x06,0x49,0x49,0x29,0x1E],
# Uppercase
'A':[0x7E,0x11,0x11,0x11,0x7E],
'F':[0x7F,0x09,0x09,0x09,0x01],
'H':[0x7F,0x08,0x08,0x08,0x7F],
'I':[0x00,0x41,0x7F,0x41,0x00],
'L':[0x7F,0x40,0x40,0x40,0x40],
'M':[0x7F,0x02,0x0C,0x02,0x7F],
'O':[0x3E,0x41,0x41,0x41,0x3E],
'P':[0x7F,0x09,0x09,0x09,0x06],
'R':[0x7F,0x09,0x19,0x29,0x46],
'T':[0x01,0x01,0x7F,0x01,0x01],
'X':[0x63,0x14,0x08,0x14,0x63],
'Y':[0x07,0x08,0x70,0x08,0x07],
# Lowercase
'a':[0x20,0x54,0x54,0x54,0x78],
'c':[0x38,0x44,0x44,0x44,0x20],
'e':[0x38,0x54,0x54,0x54,0x18],
'h':[0x7F,0x08,0x04,0x04,0x78],
'i':[0x00,0x44,0x7D,0x40,0x00],
'l':[0x00,0x41,0x7F,0x40,0x00],
'n':[0x7C,0x08,0x04,0x04,0x78],
'o':[0x38,0x44,0x44,0x44,0x38],
'p':[0x7C,0x14,0x14,0x14,0x08],
'r':[0x7C,0x08,0x04,0x04,0x08],
't':[0x04,0x3F,0x44,0x40,0x20],
'y':[0x0C,0x50,0x50,0x50,0x3C],
}

def glyph(ch):
return G.get(ch, G[' '])

# -------- Text helpers --------
def text_size(text, scale=1, spacing=1):
w = 0
for _ in text:
w += (FONT_W * scale + spacing)
if w: w -= spacing
return w, FONT_H * scale

def text_pixel(x, y, text, sx, sy, scale=1, spacing=1):
# Return 0 = Black, 1 = White
if y < sy or y >= sy + FONT_H * scale:
return 1
lx = x - sx
if lx < 0:
return 1
cursor = 0
for ch in text:
cw = FONT_W * scale
if cursor <= lx < cursor + cw:
cx_scaled = lx - cursor
cy_scaled = y - sy
cx = cx_scaled // scale
cy = cy_scaled // scale
col = glyph(ch)[cx]
bit = (col >> cy) & 1
return 0 if bit else 1
cursor += cw + spacing
return 1

# -------- Stream update --------
def epaper_update_lines(lines):
CS.value(0)

# The old picture is completely white.
DC.value(0); spi.write(b'\x10'); DC.value(1)
for _ in range(HEIGHT * BYTES_PER_ROW):
spi.write(b'\xFF')

# New image: Generated row by row
DC.value(0); spi.write(b'\x13'); DC.value(1)
for y in range(HEIGHT):
bi = 0; bitpos = 7; linebuf[:] = b'\x00' * BYTES_PER_ROW
for x in range(WIDTH):
val = 1 # Default white
for (txt, tx, ty, scale) in lines:
if text_pixel(x, y, txt, tx, ty, scale) == 0:
val = 0
break
if val:
linebuf[bi] |= (1 << bitpos) # 1 = white
bitpos -= 1
if bitpos < 0:
bitpos = 7; bi += 1
spi.write(linebuf)

# Redresh
DC.value(0); spi.write(b'\x12'); CS.value(1)
wait_until_idle()

# -------- Main --------
LINE1 = "XIAO nRF541L15"
LINE2 = "Hello MicroPython"
SCALE1 = 3
SCALE2 = 3

def main():
init_display()
clear_screen()

# Centered layout
w1, h1 = text_size(LINE1, SCALE1)
w2, h2 = text_size(LINE2, SCALE2)
total_h = h1 + 12 + h2 # Line spacing: 12 px
y0 = (HEIGHT - total_h) // 2
x1 = (WIDTH - w1) // 2
x2 = (WIDTH - w2) // 2
y1 = y0
y2 = y0 + h1 + 12

lines = [
(LINE1, x1, y1, SCALE1),
(LINE2, x2, y2, SCALE2),
]
epaper_update_lines(lines)

while True:
time.sleep(1_000_000)

if __name__ == "__main__":
main()

コード説明:

  • モジュールのインポート

    • time:遅延などの時間関連機能を有効にします。
    • XiaoPin and XiaoSPIboards.xiaoからインポート;XiaoPinはGPIOピンの制御に使用され、XiaoSPIはSPI通信を処理します。
  • ピンとSPIの設定

    • 特定のピンを定義:リセット(RST)、チップセレクト(CS)、データ/コマンド(DC)、ビジー(BUSY)。
    • SPI関連ピン(SCK、MOSI、MISO)とSPIコントローラを設定。
    • すべてのGPIOピンの動作モード(入力/出力)を初期化。
    • 20MHzの周波数でSPIインスタンスを作成。
  • ePaper基本機能

    • reset():ディスプレイのハードウェアリセット操作を実行。
    • send_command(cmd):単一バイトコマンドを送信。
    • send_data(data):データを送信、単一バイトまたは複数バイト可能。
    • wait_until_idle():ディスプレイがアイドル状態になるまで待機(現在はシンプルな遅延で実装)。
    • init_display():ディスプレイの初期化手順を実行。
    • clear_screen():画面をクリアし、完全な白色状態に設定。
  • ディスプレイパラメータ

    • WIDTH, HEIGHT = 800, 480:ディスプレイの解像度を指定。
    • BYTES_PER_ROW:各ピクセル行に必要なバイト数を示す。
    • linebuf:単一行のピクセルデータを一時的に格納するラインバッファ。
  • フォントシステム

    • シンプルな5x7ピクセルフォントを定義、G辞書に格納。
    • glyph(ch):指定された文字に対応するピクセルデータを取得。
    • text_size():指定されたスケーリング比率でテキストが表示される際の寸法を計算。
    • text_pixel(): 特定の位置にピクセルを描画するかどうかを決定します(テキストレンダリングで使用)。
  • ディスプレイ更新

    • epaper_update_lines(lines): ディスプレイを更新するためのコア関数。
    • まず、全白背景を設定するためのデータを送信します。
    • 次に、新しい画像データを行ごとに計算して送信します。
    • 最後に、新しいコンテンツを表示するためにディスプレイリフレッシュをトリガーします。
    • 複数行テキスト表示をサポートし、各行は異なる位置とスケーリング比率を持つことができます。
  • main() 関数

    • ディスプレイを初期化します。
    • テキストの中央位置を計算します。
    • テキスト行の設定リストを作成します。
    • epaper_update_lines() を呼び出してディスプレイコンテンツを更新します。
    • 無限スリープループに入ります。

結果

プログラムの自動実行

プログラムを自動実行できるようにしたい場合は、以下の手順に従ってください:

ステップ 1. 新しいプログラムファイルを作成し、Ctrl + S を使用してMicroPythonデバイスのフラッシュメモリに保存し、main.py という名前を付けます。

ここではblinkプログラムを例に取ります

その後、MicroPythonデバイス/フラッシュセクションの下に表示されます。

ステップ 2. オンボードのResetボタンを押すことで、自動実行効果を実現できます。

効果:

FAQ

ブートローダーの更新

Thonnyを使用してMicroPythonプログラムをアップロードできない状況に遭遇した場合、工場出荷時に使用されたブートローダーが古いバージョンだったためです。

ステップ 1. 配線

OpenOCD / JTAG / SWDXIAO nRF54L15
5V5V
GNDGND
SWDIOSWDIO2
SWDCLKSWDCLK2
RSTRST
tip

書き込みプロセスの失敗を防ぐため、ピン接続が正しいことを確認してください。

ステップ 2. ファームウェア書き込みプログラムをダウンロード

xiao_samd11_flash

ステップ 3. スクリプトを実行

Windowsシステムを例に取ります。ダウンロードしたファイルを解凍し、フォルダ内で右クリックしてターミナルを開きます。.\xiao_samd11_openocd_flash.bat を実行します。配線が正しければ、結果は以下の画像のようになります。

tip

Mac/Linuxシステムでは、.bat.sh に変更する必要があります

技術サポート & 製品ディスカッション

弊社製品をお選びいただき、ありがとうございます!弊社製品での体験が可能な限りスムーズになるよう、さまざまなサポートを提供しています。異なる好みやニーズに対応するため、複数のコミュニケーションチャンネルを提供しています。

Loading Comments...