MicroPython + OLED1 Xplained ProのOLEDディスプレイでHello, world!

MicroPythonとOLED1 XPlainedでHelloWorld Embedded
MicroPythonとOLED1 XPlainedでHelloWorld

概要

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 PinSAMD PinXpro Pin NameFunction
1IDIDID
2GNDGNDGND
3PA02ADC(+)BUTTON2
4PA03ADC(-)BUTTON3
5PB30GPIO1DATA_CMD_SEL
6PA15GPIO2LED3
7PA12PWM(+)LED1
8PA13PWM(-)LED2
9PA28IRQ/GPIOBUTTON1
10PA27SPI_SS_B/GPIODISPLAY_RESET
11PA08I2C_SDANC
12PA09I2C_SCLNC
13PB11UART_RXNC
14PB10UART_TXNC
15PB17SPI_SS_ADISPLAY_SS
16PB22SPI_MOSISPI MOSI
17PB16SPI_MISONC
18PB23SPI_SCKSPI SCK
19GNDGNDGND
20VCCVCCVCC

こうしてみると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 busclass 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の件はもしかしたら使い方が間違っているだけかもしれない。機会があったらもう少し踏み込んで見てみたい。

参考文献

コメント

タイトルとURLをコピーしました