みかんのゆるふわ技術ブログ

Raspberry PiやIoT関係のことを書き残していきます

Raspberry Piに2.3ドルの1.3インチIPS液晶ディスプレイをつなぐ

中国のお店が中国国外に売る通販モールAliExpressで、激安の小型IPS液晶ディスプレイを買いました。

www.aliexpress.com

サイズは1.3インチ、解像度は240x240px、65万色対応だそうです。液晶コントローラとしてST7789VWというLSIが搭載されており、SPIで制御できます。

小型でちょっとした情報を表示するのに便利そうですね🥰

おそらく、Amazon.co.jpで売られているこちらのものと類似品だと思います。基板のシルク印刷が微妙に違いますが、ピン配置等は同じように見えます。

AliExpressでの私が購入時の値段は、なんと$2.30でした!300円しないのでお買い得ですね。

このディスプレイもPythonから簡単にアクセスできるライブラリを公開されている方がいて、ドライバを書かずとも簡単に好きなものが表示できます。 とても便利ですね🥰早速Raspberry Piにつないでみましょう。

Raspberry Piに接続

購入した小型液晶ディスプレイ

f:id:kimura_khs:20210130170712j:plain

こんな感じのものが届きました。画面についていた保護シールははがしています。 ピンヘッダもついていて、はんだ付けなしでジャンパワイヤーなどを使って簡単にRaspberry Piに接続できます。

Raspberry Piにつないでみよう

f:id:kimura_khs:20200922113215p:plain
画像出典: raspberrypi.org

Raspberry Piのピンヘッダは上図のように機能が割り当てられています。 このモジュールはSPIで通信しますが、そのほかにも制御線をつなぐ必要があります。 SPI以外はどのGPIOでも良い(Pythonプログラムで指定可)のですが、以下のようにしてみました。

RasPiピン番号 RasPi 機能 液晶ディスプレイ 説明
17 3V3 power VIN 電源(3.3V)
19 MOSI SDA SPIデータ(Raspi→液晶)
23 SCLK SCK SPIクロック
25 GND GND グランド(基準電圧0V)
29 GPIO5 RES 液晶リセット信号
31 GPIO6 DC 液晶データ・コマンド選択
33 GPIO13 BLK バックライトON/OFF

バックライトON/OFFは、制御しなければ何も接続しなくて大丈夫でした。 内部でプルアップされており、接続しなければ常時ONになるみたいです。 私は接続無しで使ってみました。

これらをジャンプワイヤーで接続すると、次のようになりました。

f:id:kimura_khs:20210130165426j:plain

SPI通信について

今回はRaspberry PiがSPIコントローラ、液晶ディスプレイがSPIペリフェラルになります(ペリフェラルというのは、周辺機器というような意味です)。 SPIコントローラがクロックを出して通信をリードします。

SPI通信はSCLK(クロック)、SDO(データ出力)、SDI(データ入力)、CS(チップ選択)の4つの信号を使って通信します。 I2Cと違って、入力と出力用にそれぞれ信号線が用意されています。複数のデバイスで信号線を共有できるようにCS(チップ選択)線があり、 コントローラが通信したい相手を選んでCSをLow(0)にして、デバイスと通信します。

この液晶ディスプレイはコントローラからの信号を受けるだけで自分は何も出力しないので、信号線を片方向(MOSI)だけ使うようになっています。 なので、データ線が1本しか出ていません。

さらに、CSはピンが出ておらず、内部的にLowに落とされているようで、常時選択状態になっています。 つまり、このSPI信号線を独占してしまっています。 この液晶と同時に他のSPIペリフェラルを使うことはできません。

なお、以前はコントローラのことをマスター(ご主人様)、ペリフェラルをスレーブ(奴隷)といい、 信号線もMOSI(マスター出力スレーブ入力)、MISO(マスター入力スレーブ出力)と言っていましたが、 ご主人様が奴隷を使うイメージで例えるのは良くないということで、2020年ごろから名称が変わってきています。

Raspberry Piの準備

SPIの有効化

Raspberry Pi OSのインストール直後はSPIが無効になっていて、そのままでは使えません🙅‍♀️

有効化するには、raspi-configのメニューから設定できます。

$ sudo raspi-config

で設定画面を起動し、"5 Interfacing Options" → "P4 SPI" → "Yes" を選択してください。

スポンサーリンク

Pythonで表示させてみよう

Pythonライブラリーのインストール

イギリスで教育用ハードウェアなどを開発しているPimoroni社がPythonライブラリーを開発しています。

github.com

これを使うと簡単に表示できます。

$ sudo apt-get install python3-rpi.gpio python3-spidev python3-pip python3-pil python3-numpy
$ pip3 install st7789

基本的な使い方

基本的な使い方から見ていきましょう。まず、

$ python3

でPythonのシェルを起動します。起動したら、必要なライブラリを読み込みます。

>>> import ST7789
>>> from PIL import Image

1つのST7789が先ほど紹介したこの液晶ディスプレイ用のライブラリです。 2つめはPillow (PIL)という有名なPython画像処理ライブラリです。 実はこのST7789ライブラリはPillowで生成した画像を表示するライブラリとなっているので、Pillowが必要です。

続いて、ST7789のインスタンスを作成します。

>>> disp = ST7789.ST7789(port=0, cs=0, rst=5, dc=6, backlight=None, spi_speed_hz=80 * 1000 * 1000)

引数ですが、portでSPIのポートを指定します。Raspberry Piのピンヘッダが出ているのは0番なので、0を指定します。

続いてcsのピン番号ですが、この液晶ディスプレイにはCSピンが無いので使いません。とりあえず0にしておきます。

rstはリセット端子をGPIO5につないでいるので、5にします。同様にdcにデータ・コントロール選択端子を指定します。 GPIO6につないでいるので6です。backlightはバックライト制御端子を接続した場合は同様に指定します。 接続していない場合はNoneでOKです。

最後にspi_speed_hzですが、SPIクロックの速度を指定します。ここではライブラリのサンプルにあった通り80MHzにしています。 ジャンプワイヤーでつないでいるのにこんな高速で大丈夫かな?🤔とも思いましたが、動いたのでとりあえずそのままにしました。

このライブラリでは、インスタンス作成時にディスプレイのリセットを行い、初期化コマンドを送信して液晶ディスプレイをONにするようです。

次に、表示する画像を用意しましょう。まずは単純に、全面を色で塗りつぶしてみます。

>>> image = Image.new("RGB", (disp.width, disp.height), (102, 187, 106))

これでPillowの機能を使って画面サイズ分の緑色の画像を作成しました。色は最後の引数のタプルで指定し、ここではR=102, G=187, B=106です。 いよいよ表示してみましょう。

>>> disp.display(image)

ST7789インスタンスのdisplay()メソッドの引数に、Pillowの画像を渡すとそれを表示してくれます。

f:id:kimura_khs:20210130165534j:plain

このように、緑色1色で塗りつぶされました…となるはずが、私の場合はうまくいかず、画面が真っ黒のままでした😢

SPI Mode変更

色々調べたところ、私の買った液晶ディスプレイはSPI Mode 0(ライブラリのデフォルト)だとタイミングの問題でデータが取れないのか使えず、SPI Mode 3にすると描画できました。

Google検索するとそういう人もいたので、同じようなST7789搭載液晶ディスプレイでもいろんなバリエーションがあるのかもしれません。

ちなみにSPIのMODEとは、クロックとデータ取り込みのタイミングを規定するもので、Mode 0~3まで4種類あります。

このライブラリはSPI Modeを変更するインターフェースが無いので、以下のように中のプライベート変数を直接いじって動かしました。

import ST7789
from PIL import Image

# Create a display instance
disp = ST7789.ST7789(port=0, cs=0, rst=5, dc=6, backlight=None, spi_speed_hz=80 * 1000 * 1000)

# Added: Change to SPI MODE 3
disp._spi.mode = 3
disp.reset()
disp._init()

# Create a green image
image = Image.new("RGB", (disp.width, disp.height), (67, 160, 71))

# Show it on display
disp.display(image)

私の場合はこれで問題なく表示できました。 もし似たような液晶ディスプレイを買って動かなかった方は試してみてください。

スポンサーリンク

いろいろ表示させてみよう

せっかくなので、いろんなものを表示させてみましょう。 Pillowを使って描画できれば、何でも表示できます。

画像の表示

やっぱり液晶があると、美しい画像を表示させたいですね。ということで、ネットにある美しい画像を拾ってきましょう。

$ wget https://cdn-ak.f.st-hatena.com/images/fotolife/k/kimura_khs/20210110/20210110170457.jpg

次のようにして簡単に表示させられます。

import ST7789
from PIL import Image

# Create a display instance
disp = ST7789.ST7789(port=0, cs=0, rst=5, dc=6, backlight=None, spi_speed_hz=80 * 1000 * 1000)

# Added: Change to SPI MODE 3
disp._spi.mode = 3
disp.reset()
disp._init()

# Open image file
image = Image.open("20210110170457.jpg")

# Resize to screen size
image = image.resize((disp.width, disp.height), resample=Image.LANCZOS)

# Show it on display
disp.display(image)

これをファイルに保存し、実行すると次のようになりました。

f:id:kimura_khs:20210130165548j:plain

奈良にある東大寺 二月堂の写真です。元の写真が縦横比1:1でないため横が縮小されてしまっていて申し訳ないのですが、表示できていますね。

ちなみに、この写真は私が撮影した写真です😆

文字の表示

Pillowがあれば、OpenType/TrueTypeフォントで文字も書けます。 フォントをダウンロードしてみましょう。

$ sudo apt install fonts-noto-cjk fonts-roboto

プログラムは以下のようになります。

import ST7789
from PIL import Image, ImageDraw, ImageFont

# Create a display instance
disp = ST7789.ST7789(port=0, cs=0, rst=5, dc=6, backlight=None, spi_speed_hz=80 * 1000 * 1000)

# Added: Change to SPI MODE 3
disp._spi.mode = 3
disp.reset()
disp._init()

# Define fonts
FONT_ROBOTO = ImageFont.truetype("Roboto-Medium.ttf", 24)
FONT_NOTO = ImageFont.truetype("NotoSansCJK-Regular.ttc", 48)

# Define colors
COLOR_ORANGE = (255, 167, 38)
COLOR_GREEN = (129, 199, 132)

# Create an image with black background
image = Image.new("RGB", (disp.width, disp.height), (0, 0, 0))

# Draw some text
draw = ImageDraw.Draw(image)
draw.text((0, 0), "みかんの", font=FONT_NOTO, fill=COLOR_ORANGE)
draw.text((48, 48), "ゆるふわ", font=FONT_NOTO, fill=COLOR_ORANGE)
draw.text((0, 96), "技術ブログ", font=FONT_NOTO, fill=COLOR_ORANGE)
draw.text((0, 160), "www.mikan-tech.net", font=FONT_ROBOTO, fill=COLOR_GREEN)

# Show it on display
disp.display(image)

f:id:kimura_khs:20210130165608j:plain

これで、文字も表示できるようになりました。

やはりカラー表示できるのは便利ですね。 文字や画像を組み合わせて、ちょっとした情報をカッコよく表示できるので、皆様もぜひDIYなどにお使いください!