概要
OLED1 Xplained Pro(以下OLED1)に搭載されているOLEDディスプレイをSAMD21 Xplained Pro(以下Xpro)にインストールしたMicroPythonから動かした。MicroPythonにおけるSPIの使い方とOLED UG-2832HSWEG04 およびそのドライバーICであるSSD1306の構成と制御方法について調べコードに実装した。framebufに対応してなさそうだったので、framebufを使わないSSD1306制御クラスを作成し、フォントを作り、Hello,worldを表示させた。
OLED

User Guideおよび回路図より、使用しているOLEDディスプレイはUG-2832HSWEG04と書いてあった。128 × 32 Pixelsのモノクロディスプレイである。このデータシートをググると、Driver ICとしてSSD1306が使われていることがわかった。ディスプレイモジュールのデータシートを見てもどうコントロールするのかわからず、Driver ICのデータシートを見ないといけないというのはOLEDに限らずLCDもディスプレイモノのお決まりなのかもしれない。
SSD1306のデータシートを開いてみると、I/Fは次の5種類が使えるようだ。
- 8-bit 8080
- 8-bit 6800
- 3-wire SPI
- 4-wire SPI
- I2C
ただそれを選べるのはこのOLEDモジュールを作る人だけで、我々はそのモジュールの実装から与えられたI/Fを使うしかない。OLED1に搭載されているのは4-wire SPIなので、それを使用する。
UG-2832でググってもよくわからなかったが、SSD1306でググると多くのOLEDディスプレイモジュールに搭載され使用されていることがわかった。中にはSSD1306 OLED ディスプレイという文字列も出てくる。Driver ICがなぜかディスプレイ名になっているのである。ここまで有名だと、たいてい先人がライブラリを作っているものである。ていうかあった。
SSD1306モジュールをインストール
電子工作界隈で有名なデバイスはほとんど既に先人がライブラリを作っている。早速使わせてもらおう。下記のコマンドでssd1306モジュールをXproにインストールできる。
mpremote connect COM17 mip install ssd1306
これでssd1306モジュールをインストールでき―――なかった。
Install ssd1306
Installing ssd1306 (latest) from https://micropython.org/pi/v2 to /lib
Installing: /lib/ssd1306.mpy
Traceback (most recent call last):
(中略)
mpremote.transport.TransportExecError: Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: 2
しばらく悩んだ結果やっとわかった。 そこメッセージに/lib/ssd1306.mpy とあるようにMicroPythonのモジュールはlibディレクトリに入るらしい。まだ始めたばかりのボードではそれがない。つまりlibディレクトリを作っていないから出るエラー、ということがわかった。
mpremote connect COM17 mkdir :lib
このあとmip installを実行すると無事インストールできた。
OLEDの接続を確認
前回作ったポート表を改めて確認してOLEDの制御に必要なポートを洗い出す。
| OLED1 Pin | SAMD Pin | Xpro Pin Name | Function |
|---|---|---|---|
| 1 | ID | ID | ID |
| 2 | GND | GND | GND |
| 3 | PA02 | ADC(+) | BUTTON2 |
| 4 | PA03 | ADC(-) | BUTTON3 |
| 5 | PB30 | GPIO1 | DATA_CMD_SEL |
| 6 | PA15 | GPIO2 | LED3 |
| 7 | PA12 | PWM(+) | LED1 |
| 8 | PA13 | PWM(-) | LED2 |
| 9 | PA28 | IRQ/GPIO | BUTTON1 |
| 10 | PA27 | SPI_SS_B/GPIO | DISPLAY_RESET |
| 11 | PA08 | I2C_SDA | NC |
| 12 | PA09 | I2C_SCL | NC |
| 13 | PB11 | UART_RX | NC |
| 14 | PB10 | UART_TX | NC |
| 15 | PB17 | SPI_SS_A | DISPLAY_SS |
| 16 | PB22 | SPI_MOSI | SPI MOSI |
| 17 | PB16 | SPI_MISO | NC |
| 18 | PB23 | SPI_SCK | SPI SCK |
| 19 | GND | GND | GND |
| 20 | VCC | VCC | VCC |
こうしてみると4-wire SPIと言いながらMISOがつながっていないので読み出しはせず書きっぱなしのようだ。レジスタに正しくかけたか確認したいとしてもリードバックでデバッグすることはできない。evaluation platformというならつながっていてほしいところだが、OLEDはevaluation対象ということではないようだ。
サンプルコードはUsing a SSD1306 OLED displayに書いてあった。
from machine import Pin, SPI
import ssd1306
hspi = SPI(1) # sck=14 (scl), mosi=13 (sda), miso=12 (unused)
dc = Pin(4) # data/command
rst = Pin(5) # reset
cs = Pin(15) # chip select, some modules do not have a pin for this
display = ssd1306.SSD1306_SPI(128, 64, hspi, dc, rst, cs)
display.text('Hello, World!', 0, 0, 1)
display.show()
これはESP8266ボード用のコードなので、これをXproに合わせて作り変えていく。
まず、SPIのインスタンス。SAMDマイコン用の記述は Quick reference for the SAMD21/SAMD51 family の Hardware SPI bus で見つけた。
from machine import SPI
spi = SPI(1, sck=Pin("SCK"), mosi=Pin("MOSI"), miso=Pin("MISO"), baudrate=10000000)
spi.write('Hello World')
これらをXproに合わせて修正すると、こうなった。
from machine import Pin, SPI
import ssd1306
sck = Pin("PB23")
miso = None
mosi = Pin("PB22")
dc = Pin("PB30") # data/command
rst = Pin("PA27") # reset
cs = Pin("PB17") # chip select
spi = SPI(3, sck=sck, mosi=mosi, miso=miso, baudrate=10000000)
display = ssd1306.SSD1306_SPI(128, 32, spi, dc, rst, cs)
これをrunすると。。。
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "ssd1306.py", line 1, in <module>
ImportError: module not found
今度は何だ?
ssd1306.pyの1行目は何か。別途拾ってきたソースコードを確認してみる。
# MicroPython SSD1306 OLED driver, I2C and SPI interfaces
from micropython import const
import framebuf
# register definitions
SET_CONTRAST = const(0x81)
SET_ENTIRE_ON = const(0xA4)
(略)
よくわからないのでREPLに1行ずつ貼ってみる。
MicroPython v1.24.1 on 2024-11-29; SAMD21-XPLAINED-PRO with SAMD21J18A
Type "help()" for more information.
>>>
>>> from micropython import const
>>> import framebuf
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: module not found
>>>
framebufというモジュールがないようだ。
class SSD1306(framebuf.FrameBuffer):
def __init__(self, width, height, external_vcc):
framebuf.FrameBufferを継承しているのでssd1306.mpyは当然framebufがなければならない。framebufはまだSAMDのMicroPythonには来ていないのかもしれない。まだこの辺はよくわからないので、framebufを使わないSSD1306クラスを作っていこう。
SPIを動かす
とりあえずSPIを動かすところから。 Quick reference for the SAMD21/SAMD51 familyのHardware SPI bus と class SPI – a Serial Peripheral Interface bus protocol (controller side) を見てMicroPythonにおけるSPIの動かし方を調べた。Pinout for the SAMD machine modulesのSAMD21 Xplained PRO pin assignment table でOLEDに接続されているSPIのポートを見ると、Serialの番号は5/*と記載されている。したがってコンストラクタのidは5となる。
次のようなコードを書いてみた。
from machine import Pin, SPI
import time
sck = Pin("PB23")
miso = None
mosi = Pin("PB22")
dc = Pin("PB30") # data/command
rst = Pin("PA27") # reset
cs = Pin("PB17", Pin.OUT, value=1) # chip select
spi = SPI(5, sck=sck, mosi=mosi, miso=miso, baudrate=10000000)
while (True):
cs.value(0)
spi.write('Hello World!')
cs.value(1)
time.sleep(1)

このような波形が取れた。とりあえず動いているようだがCS→SCKの間隔を見る限りだいぶオーバーヘッドが大きいようだ。
SSD1306のframebufなしバージョンを作る
ssd1306.pyのソースコードから単純にframebuf関連を削除して、ssd1306_wo_framebuf.pyとして保存した。
# MicroPython SSD1306 OLED driver, I2C and SPI interfaces
from micropython import const
# register definitions
SET_CONTRAST = const(0x81)
SET_ENTIRE_ON = const(0xA4)
SET_NORM_INV = const(0xA6)
SET_DISP = const(0xAE)
SET_MEM_ADDR = const(0x20)
SET_COL_ADDR = const(0x21)
SET_PAGE_ADDR = const(0x22)
SET_DISP_START_LINE = const(0x40)
SET_SEG_REMAP = const(0xA0)
SET_MUX_RATIO = const(0xA8)
SET_COM_OUT_DIR = const(0xC0)
SET_DISP_OFFSET = const(0xD3)
SET_COM_PIN_CFG = const(0xDA)
SET_DISP_CLK_DIV = const(0xD5)
SET_PRECHARGE = const(0xD9)
SET_VCOM_DESEL = const(0xDB)
SET_CHARGE_PUMP = const(0x8D)
class SSD1306_WO_FRAMEBUF:
def __init__(self, width, height, external_vcc):
self.width = width
self.height = height
self.external_vcc = external_vcc
self.pages = self.height // 8
self.buffer = bytearray(self.pages * self.width)
self.init_display()
def init_display(self):
for cmd in (
SET_DISP | 0x00, # off
# address setting
SET_MEM_ADDR,
0x00, # horizontal
# resolution and layout
SET_DISP_START_LINE | 0x00,
SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0
SET_MUX_RATIO,
self.height - 1,
SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0
SET_DISP_OFFSET,
0x00,
SET_COM_PIN_CFG,
0x02 if self.width > 2 * self.height else 0x12,
# timing and driving scheme
SET_DISP_CLK_DIV,
0x80,
SET_PRECHARGE,
0x22 if self.external_vcc else 0xF1,
SET_VCOM_DESEL,
0x30, # 0.83*Vcc
# display
SET_CONTRAST,
0xFF, # maximum
SET_ENTIRE_ON, # output follows RAM contents
SET_NORM_INV, # not inverted
# charge pump
SET_CHARGE_PUMP,
0x10 if self.external_vcc else 0x14,
SET_DISP | 0x01,
): # on
self.write_cmd(cmd)
# self.fill(0)
self.show()
def poweroff(self):
self.write_cmd(SET_DISP | 0x00)
def poweron(self):
self.write_cmd(SET_DISP | 0x01)
def contrast(self, contrast):
self.write_cmd(SET_CONTRAST)
self.write_cmd(contrast)
def invert(self, invert):
self.write_cmd(SET_NORM_INV | (invert & 1))
def show(self):
x0 = 0
x1 = self.width - 1
if self.width == 64:
# displays with width of 64 pixels are shifted by 32
x0 += 32
x1 += 32
self.write_cmd(SET_COL_ADDR)
self.write_cmd(x0)
self.write_cmd(x1)
self.write_cmd(SET_PAGE_ADDR)
self.write_cmd(0)
self.write_cmd(self.pages - 1)
self.write_data(self.buffer)
class SSD1306_I2C(SSD1306_WO_FRAMEBUF):
def __init__(self, width, height, i2c, addr=0x3C, external_vcc=False):
self.i2c = i2c
self.addr = addr
self.temp = bytearray(2)
self.write_list = [b"\x40", None] # Co=0, D/C#=1
super().__init__(width, height, external_vcc)
def write_cmd(self, cmd):
self.temp[0] = 0x80 # Co=1, D/C#=0
self.temp[1] = cmd
self.i2c.writeto(self.addr, self.temp)
def write_data(self, buf):
self.write_list[1] = buf
self.i2c.writevto(self.addr, self.write_list)
class SSD1306_SPI(SSD1306_WO_FRAMEBUF):
def __init__(self, width, height, spi, dc, res, cs, external_vcc=False):
self.rate = 10 * 1024 * 1024
dc.init(dc.OUT, value=0)
res.init(res.OUT, value=0)
cs.init(cs.OUT, value=1)
self.spi = spi
self.dc = dc
self.res = res
self.cs = cs
import time
self.res(1)
time.sleep_ms(1)
self.res(0)
time.sleep_ms(10)
self.res(1)
super().__init__(width, height, external_vcc)
def write_cmd(self, cmd):
self.spi.init(baudrate=self.rate, polarity=0, phase=0)
self.cs(1)
self.dc(0)
self.cs(0)
self.spi.write(bytearray([cmd]))
self.cs(1)
def write_data(self, buf):
self.spi.init(baudrate=self.rate, polarity=0, phase=0)
self.cs(1)
self.dc(1)
self.cs(0)
self.spi.write(buf)
self.cs(1)
ssd1306_wo_framebuf.pyを転送する
ここでできあがったssd1306_wo_framebuf.pyを転送しようとしたのだが、どうやってもできなかった。
mpremote connect COM17 cp ssd1306_wo_framebuf.py
mpremote: Error with transport:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: 2
いろいろ試した結果、同じ中身でもmain.pyとすれば転送できた。print("hello world")だけ書いたファイルもファイル名がmain.pyでなければ転送できない。つまり何か構文にエラーがあったわけでもなく、単にファイル名だけが問題のようだ。もしかして、と思ってampyで転送してみると難なくできた。
ampy --port=COM17 put .\ssd1306_wo_framebuf.py
mpremote connect COM17 ls
ls :
24 main.py
4478 ssd1306_wo_framebuf.py
これでかなり時間を費やしてしまった。今度からampyを使おう。
追記
――しかしうっかりmpremoteでcpしたら今度は成功した。もしや?一度rmするとやはりcpはエラー。ampyで転送後にmpremoteでcpすると成功。どうやら同名のファイルが既にあれば、つまり上書きなら成功するようだ。ホントか?このときのバージョンをメモしておこう。
mpremote 1.24.1
OLEDに何か表示させる
framebufを取っ払ったことで、各種描画関数が使えなくなった。なので、自前で描画関数を作っていく。
SSD1306のデータシートを見ていくと、描画に使用するGDDRAMのデータ構造がわかった。

1バイトのデータが横1×縦8のピクセルを司る。つまりこんな感じだ。

それを踏まえてmain.pyを次のようにしてみた。
from machine import Pin, SPI
import ssd1306_wo_framebuf
sck = Pin("PB23")
miso = None
mosi = Pin("PB22")
dc = Pin("PB30", Pin.OUT) # data/command
rst = Pin("PA27", Pin.OUT) # reset
cs = Pin("PB17", Pin.OUT, value=1) # chip select
spi = SPI(5, sck=sck, mosi=mosi, miso=miso, baudrate=10000000)
display = ssd1306_wo_framebuf.SSD1306_SPI(128, 32, spi, dc, rst, cs)
display.show()
display.write_data(bytearray(b'\x7e\x11\x11\x11\x7e\x00\x7f\x49\x49\x49\x36'))

うまくいったようだ。
よく使うキャラクタLCDなどと違って文字を指定しての表示はできずドット毎の描画が必要、また位置を指定しての描画はおそらくできないので、やはり画面描画用のバッファを一画面分用意してまるっと書き込むスタイルのほうがいいようだ。
フォントの準備
画面サイズが128×32なので、5×7フォントを利用して1ドット/1ラインずつ間を入れることを考えれば、横は128÷6=21文字(2ドット余り)、縦は32÷8=4行取ることができる。ST7032のフォントをお借りしてMicroPythonで使いやすい形に変換しfont5x7.pyとして保存した。メモリ容量の関係でこういうデータは不可変になるようにしておくとFlashROMに保存されるためRAMを食わないらしいということなので、ひとつながりの文字列として生成した。これでいいのかな?
fonts =\
b'\x00\x00\x7e\x01\x02'\
b'\x20\x40\x3f\x00\x00'\
b'\x30\x48\x30\x48\x30'\
b'\x3f\x11\x09\x05\x03'\
b'\x10\x38\x54\x10\x1f'\
b'\x04\x02\x7f\x02\x04'\
b'\x10\x20\x7f\x20\x10'\
b'\x08\x08\x2a\x1c\x08'\
b'\x08\x1c\x2a\x08\x08'\
b'\x7f\x01\x01\x01\x01'\
b'\x01\x01\x01\x01\x7f'\
b'\x7f\x40\x40\x40\x40'\
b'\x40\x40\x40\x40\x7f'\
b'\x00\x18\x18\x00\x00'\
b'\x3e\x41\x75\x4b\x3e'\
b'\x3e\x63\x5d\x6b\x3e'\
b'\x79\x11\x27\x11\x79'\
b'\x04\x04\x7f\x04\x04'\
b'\x0a\x55\x55\x55\x28'\
b'\x06\x0f\x7f\x01\x7f'\
b'\x7f\x01\x01\x01\x07'\
b'\x60\x50\x48\x44\x7e'\
b'\x3e\x49\x49\x49\x3e'\
b'\x70\x0c\x03\x0c\x70'\
b'\x63\x49\x49\x49\x63'\
b'\x01\x7f\x01\x7f\x01'\
b'\x41\x63\x55\x49\x41'\
b'\x06\x01\x7e\x01\x06'\
b'\x18\xa5\xff\xa5\x18'\
b'\x0f\x10\x7f\x10\x0f'\
b'\x4e\x71\x01\x71\x4e'\
b'\x38\x44\x44\x38\x44'\
b'\x00\x00\x00\x00\x00'\
b'\x00\x00\x4f\x00\x00'\
b'\x00\x07\x00\x07\x00'\
b'\x14\x7f\x14\x7f\x14'\
b'\x24\x2a\x7f\x2a\x12'\
b'\x23\x13\x08\x64\x62'\
b'\x36\x49\x55\x22\x50'\
b'\x00\x05\x03\x00\x00'\
b'\x00\x1c\x22\x41\x00'\
b'\x00\x41\x22\x1c\x00'\
b'\x14\x08\x3e\x08\x14'\
b'\x08\x08\x3e\x08\x08'\
b'\x00\x50\x30\x00\x00'\
b'\x08\x08\x08\x08\x08'\
b'\x00\x60\x60\x00\x00'\
b'\x20\x10\x08\x04\x02'\
b'\x3e\x51\x49\x45\x3e'\
b'\x00\x42\x7f\x40\x00'\
b'\x42\x61\x51\x49\x46'\
b'\x21\x41\x45\x4b\x31'\
b'\x18\x14\x12\x7f\x10'\
b'\x27\x45\x45\x45\x39'\
b'\x3c\x4a\x49\x49\x30'\
b'\x01\x71\x09\x05\x03'\
b'\x36\x49\x49\x49\x36'\
b'\x06\x49\x49\x29\x1e'\
b'\x00\x36\x36\x00\x00'\
b'\x00\x56\x36\x00\x00'\
b'\x08\x14\x22\x41\x00'\
b'\x14\x14\x14\x14\x14'\
b'\x00\x41\x22\x14\x08'\
b'\x02\x01\x51\x09\x06'\
b'\x32\x49\x79\x41\x3e'\
b'\x7e\x11\x11\x11\x7e'\
b'\x7f\x49\x49\x49\x36'\
b'\x3e\x41\x41\x41\x22'\
b'\x7f\x41\x41\x22\x1c'\
b'\x7f\x49\x49\x49\x41'\
b'\x7f\x09\x09\x09\x01'\
b'\x3e\x41\x49\x49\x7a'\
b'\x7f\x08\x08\x08\x7f'\
b'\x00\x41\x7f\x41\x00'\
b'\x20\x40\x41\x3f\x01'\
b'\x7f\x08\x14\x22\x41'\
b'\x7f\x40\x40\x40\x40'\
b'\x7f\x02\x0c\x02\x7f'\
b'\x7f\x04\x08\x10\x7f'\
b'\x3e\x41\x41\x41\x3e'\
b'\x7f\x09\x09\x09\x06'\
b'\x3e\x41\x51\x21\x5e'\
b'\x7f\x09\x19\x29\x46'\
b'\x46\x49\x49\x49\x31'\
b'\x01\x01\x7f\x01\x01'\
b'\x3f\x40\x40\x40\x3f'\
b'\x1f\x20\x40\x20\x1f'\
b'\x3f\x40\x38\x40\x3f'\
b'\x63\x14\x08\x14\x63'\
b'\x07\x08\x70\x08\x07'\
b'\x61\x51\x49\x45\x43'\
b'\x00\x7f\x41\x41\x00'\
b'\x15\x16\x7c\x16\x15'\
b'\x00\x41\x41\x7f\x00'\
b'\x04\x02\x01\x02\x04'\
b'\x40\x40\x40\x40\x40'\
b'\x00\x01\x02\x04\x00'\
b'\x20\x54\x54\x54\x78'\
b'\x7f\x48\x44\x44\x38'\
b'\x38\x44\x44\x44\x20'\
b'\x38\x44\x44\x48\x7f'\
b'\x38\x54\x54\x54\x18'\
b'\x08\x7e\x09\x01\x02'\
b'\x0c\x52\x52\x52\x3e'\
b'\x7f\x08\x04\x04\x78'\
b'\x00\x44\x7d\x40\x00'\
b'\x20\x40\x44\x3d\x00'\
b'\x7f\x10\x28\x44\x00'\
b'\x00\x41\x7f\x40\x00'\
b'\x7c\x04\x78\x04\x78'\
b'\x7c\x08\x04\x04\x78'\
b'\x38\x44\x44\x44\x38'\
b'\x7c\x14\x14\x14\x08'\
b'\x08\x14\x14\x18\x7c'\
b'\x7c\x08\x04\x04\x08'\
b'\x48\x54\x54\x54\x20'\
b'\x04\x3f\x44\x40\x20'\
b'\x1c\x20\x40\x20\x1c'\
b'\x0c\x10\x20\x10\x0c'\
b'\x3c\x40\x30\x40\x3c'\
b'\x44\x28\x10\x28\x44'\
b'\x0c\x50\x50\x50\x3c'\
b'\x44\x64\x54\x4c\x44'\
b'\x00\x08\x36\x41\x00'\
b'\x00\x00\x7f\x00\x00'\
b'\x00\x41\x36\x08\x00'\
b'\x08\x08\x2a\x1c\x08'\
b'\x08\x1c\x2a\x08\x08'\
b'\x0e\x51\x51\x71\x11'\
b'\x38\x42\x40\x22\x78'\
b'\x38\x54\x56\x55\x18'\
b'\x20\x56\x55\x56\x78'\
b'\x20\x55\x54\x55\x78'\
b'\x20\x55\x56\x54\x78'\
b'\x20\x54\x55\x54\x78'\
b'\x0c\x52\x52\x72\x12'\
b'\x38\x56\x55\x56\x18'\
b'\x38\x55\x54\x55\x18'\
b'\x38\x55\x56\x54\x18'\
b'\x00\x45\x7c\x41\x00'\
b'\x00\x4a\x79\x42\x00'\
b'\x00\x49\x7a\x40\x00'\
b'\x70\x29\x24\x29\x70'\
b'\x70\x2a\x25\x2a\x70'\
b'\x7c\x54\x56\x55\x44'\
b'\x24\x54\x78\x54\x58'\
b'\x7e\x09\x7f\x49\x49'\
b'\x30\x4a\x49\x4a\x30'\
b'\x30\x4a\x48\x4a\x30'\
b'\x30\x49\x4a\x48\x30'\
b'\x38\x42\x41\x22\x78'\
b'\x38\x41\x42\x20\x78'\
b'\x0c\x51\x50\x51\x3c'\
b'\x3d\x42\x42\x42\x3d'\
b'\x3c\x41\x40\x41\x3c'\
b'\x7a\x11\x09\x0a\x71'\
b'\x7a\x09\x11\x22\x79'\
b'\x48\x55\x55\x55\x5e'\
b'\x4e\x51\x51\x51\x4e'\
b'\x30\x48\x45\x40\x20'\
b'\x00\x00\x00\x00\x00'\
b'\x70\x50\x70\x00\x00'\
b'\x00\x00\x0f\x01\x01'\
b'\x40\x40\x78\x00\x00'\
b'\x10\x20\x40\x00\x00'\
b'\x00\x18\x18\x00\x00'\
b'\x0a\x0a\x4a\x2a\x1e'\
b'\x04\x44\x34\x14\x0c'\
b'\x20\x10\x78\x04\x00'\
b'\x18\x08\x4c\x48\x38'\
b'\x48\x48\x78\x48\x48'\
b'\x48\x28\x18\x7c\x08'\
b'\x08\x7c\x08\x28\x18'\
b'\x40\x48\x48\x78\x40'\
b'\x54\x54\x54\x7c\x00'\
b'\x18\x00\x58\x40\x38'\
b'\x08\x08\x08\x08\x08'\
b'\x01\x41\x3d\x09\x07'\
b'\x10\x08\x7c\x02\x01'\
b'\x0e\x02\x43\x22\x1e'\
b'\x42\x42\x7e\x42\x42'\
b'\x22\x12\x0a\x7f\x02'\
b'\x42\x3f\x02\x42\x3e'\
b'\x0a\x0a\x7f\x0a\x0a'\
b'\x08\x46\x42\x22\x1e'\
b'\x04\x03\x42\x3e\x02'\
b'\x42\x42\x42\x42\x7e'\
b'\x02\x47\x22\x1f\x02'\
b'\x4a\x4a\x40\x20\x1c'\
b'\x42\x22\x12\x2a\x46'\
b'\x02\x3f\x42\x4a\x46'\
b'\x06\x48\x40\x20\x1e'\
b'\x08\x46\x4a\x32\x1e'\
b'\x0a\x4a\x3e\x09\x08'\
b'\x0e\x00\x4e\x20\x1e'\
b'\x04\x45\x3d\x05\x04'\
b'\x00\x7f\x08\x10\x00'\
b'\x44\x24\x1f\x04\x04'\
b'\x40\x42\x42\x42\x40'\
b'\x42\x2a\x12\x2a\x06'\
b'\x22\x12\x7b\x16\x22'\
b'\x00\x40\x20\x1f\x00'\
b'\x78\x00\x02\x04\x78'\
b'\x3f\x44\x44\x44\x44'\
b'\x02\x42\x42\x22\x1e'\
b'\x04\x02\x04\x08\x30'\
b'\x32\x02\x7f\x02\x32'\
b'\x02\x12\x22\x52\x0e'\
b'\x00\x2a\x2a\x2a\x40'\
b'\x38\x24\x22\x20\x70'\
b'\x40\x28\x10\x28\x06'\
b'\x0a\x3e\x4a\x4a\x4a'\
b'\x04\x7f\x04\x14\x0c'\
b'\x40\x42\x42\x7e\x40'\
b'\x4a\x4a\x4a\x4a\x7e'\
b'\x04\x05\x45\x25\x1c'\
b'\x0f\x40\x20\x1f\x00'\
b'\x7c\x00\x7e\x40\x30'\
b'\x7e\x40\x20\x10\x08'\
b'\x7e\x42\x42\x42\x7e'\
b'\x0e\x02\x42\x22\x1e'\
b'\x42\x42\x40\x20\x18'\
b'\x02\x04\x01\x02\x00'\
b'\x07\x05\x07\x00\x00'\
b'\x20\x54\x56\x55\x78'\
b'\x00\x48\x7a\x41\x00'\
b'\x30\x48\x4a\x49\x30'\
b'\x38\x40\x42\x21\x78'\
b'\x1c\x22\x7f\x22\x22'\
b'\x48\x7e\x49\x49\x42'\
b'\x15\x16\x7c\x16\x15'\
b'\x7f\x05\x15\x7a\x50'\
b'\x20\x48\x3e\x09\x02'\
b'\x00\x00\x79\x00\x00'\
b'\x72\x29\x29\x2a\x71'\
b'\x22\x51\x55\x56\x79'\
b'\x3a\x45\x45\x46\x39'\
b'\x32\x49\x49\x4a\x31'\
b'\x5c\x32\x2a\x26\x1d'\
b'\x18\x64\x3c\x26\x18'\
b'\x00\x00\x01\x00\x00'\
b'\x00\x01\x00\x01\x00'\
b'\x00\x02\x05\x02\x00'\
b'\x00\x01\x02\x00\x00'\
b'\x00\x00\x02\x01\x00'\
b'\x0f\x00\x48\x64\x58'\
b'\x0f\x00\x3c\x20\x78'\
b'\x22\x14\x08\x14\x22'\
b'\x08\x08\x2a\x08\x08'\
b'\x40\x44\x4a\x51\x40'\
b'\x40\x51\x4a\x44\x40'\
b'\x08\x14\x2a\x14\x22'\
b'\x22\x14\x2a\x14\x08'\
b'\x14\x74\x1c\x17\x14'\
b'\x10\x20\x7f\x01\x01'\
b'\x01\x01\x01\x01\x01'
ASCIIコード順に5バイトずつ並べているので、使うときはスライスして使う。
これをOLEDに転送するmain.pyがこちら。
from machine import Pin, SPI
import ssd1306_wo_framebuf
import font5x7
sck = Pin("PB23")
miso = None
mosi = Pin("PB22")
dc = Pin("PB30", Pin.OUT) # data/command
rst = Pin("PA27", Pin.OUT) # reset
cs = Pin("PB17", Pin.OUT, value=1) # chip select
spi = SPI(5, sck=sck, mosi=mosi, miso=miso, baudrate=10000000)
display = ssd1306_wo_framebuf.SSD1306_SPI(128, 32, spi, dc, rst, cs)
text = "Hello, world!"
for t in text:
displayfont = font5x7.fonts[ord(t)*5:ord(t)*5+5]
display.write_data(bytearray(b'\x00'+displayfont))
無事Hello, worldすることができた。改行するほどの長さの文字列にするとずれてしまうので調整が必要だし(横8ピクセルのフォントにすれば簡単かもしれない)当然SSD1306クラスにwrite_textなどと実装して描画と文字列処理はそちらに任せるのがいいのだが、今後framebufさえ使えるようになれば全く不要なので今そこまでするかと言われると?

まとめ
というわけで、SAMD21 Xplained ProにインストールしたMicroPythonでOLED1 Xplained ProのOLEDディスプレイにHello, world!を表示することができた。framebufでうまくいかないときに諦めかけたがとりあえず文字列を表示させるところまで漕ぎ着けたのでよかった。
framebufの件はもしかしたら使い方が間違っているだけかもしれない。機会があったらもう少し踏み込んで見てみたい。
参考文献
- OLED1 Xplained Pro User Guide https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/UserGuides/40002176A.pdf
- OLED1 Xplained Pro Design Documentation (ZIP) https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/BoardDesignFiles/OLED1-Xplained-Pro_Design-Documentation.zip
- UG-2832HSWEG04 Doc No.: SAS1-B020-B
https://cdn-shop.adafruit.com/datasheets/UG-2832HSWEG04.pdf - SSD1306 Advance Information https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf
- Using a SSD1306 OLED display — MicroPython latest documentation https://docs.micropython.org/en/latest/esp8266/tutorial/ssd1306.html
- class SPI – a Serial Peripheral Interface bus protocol (controller side) — MicroPython latest documentation https://docs.micropython.org/en/latest/library/machine.SPI.html#machine-spi
- Pinout for the SAMD machine modules — MicroPython latest documentation https://docs.micropython.org/en/latest/samd/pinout.html#samd21-xplained-pro-pin-assignment-table
- Sitronix ST7032 Dot Matrix LCD Controller/Driver
https://strawberry-linux.com/pub/ST7032i.pdf


コメント