LilyGO T-Display RP2040 MicroPython / CircuitPython で使う

RP2040にST7789Vを接続した開発ボードです。

- https://github.com/Xinyuan-LilyGO/LILYGO-T-display-RP2040

![pinout](https://raw.githubusercontent.com/Xinyuan-LilyGO/LILYGO-T-display-RP2040/main/img/T-display-RP2040.jpg)

# MicroPythonで使う

## セットアップ

ST7789V組み込みのファームが以下で公開されています。

- https://github.com/russhughes/st7789_mpy

`firmware/T-DISPLAY-RP2040/`にあるファイルをRP2040に書き込みます。
起動させ，`examples/configs/tdisplay_rp2040/`にある`tft_config.py`をフラッシュ領域のトップに保存します。(`main.py`と同じ位置）

```main.py
from machine import Pin, SPI
import st7789
import tft_config

tft = tft_config.config(0)

tft.init()
```

などと始めると，画面にいろいろなものもを描けるようになります。

## 文字・図形描画

上記のあと `tft.text()` 関数で描画できますが，描くだけなので，表示文字を更新するにはひと工夫必要です。
線分は`tft.line()`で，この２つが最低限あればいろいろ使えます。

ボタン2つを適当に押すと，それに応じた値を出して，波形のように流して表示するのを書いてみました。

```trend.py
from machine import Pin, SPI
import st7789
import tft_config

tft = tft_config.config(0)
tft.init()

#import vga1_8x8 as font
#import vga1_8x16 as font
#import vga1_16x16 as font
#import vga1_bold_16x16 as font
#import vga1_bold_16x32 as font

#import vga1_16x32 as font
import vga2_16x32 as font

tft.rotation(1)

'''
rotation(1):
(0,0)+-----------+ GPIO7 x<240
     |           |
     |           | USB
     |           |
     +-----------+ GPIO6
    y<128
'''

# boot message

tft.text(font, "TTGO RP2040", 16, 48, st7789.WHITE, st7789.BLACK)

import time
time.sleep(1)

tft.fill(st7789.BLACK)

# end of boot message

trend_X_top=20
trend_Y_top=0
trend_width=200 # dots
trend_height=62 # dots

trend_Y_max=150 # display y 0 to 150
trend_Y_min=0   # display y 0 to 150

trend_scale_base=[0,50,100,150] # display scale at y=0,50,100,150

trend_scale=[]
for i in range(len(trend_scale_base)):
    #print(i, trend_scale_base[i])
    trend_scale.append(
        int(trend_Y_top+trend_height-
            (trend_scale_base[i]-trend_Y_min)/(trend_Y_max-trend_Y_min)*trend_height))

# clear data

trend_data=[]
for i in range(trend_width):
    #trend_data.append(trend_Y_top+trend_height-(i % (trend_height+1))) # test data
    trend_data.append(
        int(trend_Y_top+trend_height-(
            0-trend_Y_min)/(trend_Y_max-trend_Y_min)*trend_height)
    )
    
def update_trend():
    for i in range(trend_width):
        # clear
        tft.line(
            trend_X_top+i, trend_Y_top,
            trend_X_top+i, trend_Y_top+trend_height,
            st7789.BLACK) 
        #wait(0.05)
        # scale
        for j in range(len(trend_scale)):
            tft.line(
                trend_X_top+i,
                trend_scale[j],
                trend_X_top+i,
                trend_scale[j],
                st7789.color565(127,127,127))
        #wait(0.05)
        if i==(trend_width-1):
            tft.line(
                trend_X_top+i,trend_data[i],
                trend_X_top+i,trend_data[i],st7789.YELLOW)
        else:
            tft.line(
                trend_X_top+i, trend_data[i],
                trend_X_top+i, trend_data[i+1],
                st7789.YELLOW)

def put_data_to_trend(d):
    del trend_data[0]
    trend_data.append(
            int(trend_Y_top+trend_height-(d-trend_Y_min)/(trend_Y_max-trend_Y_min)*trend_height)
            )
    update_trend()


class TextBox:
    def __init__(self,x,y,font,fc,bc):
        self.x=x
        self.y=y
        self.s=''
        self.font=font
        self.fc=fc # text color
        self.bc=bc # background color
        tft.text(self.font, self.s,
            self.x,self.y,
            self.fc,self.bc
         )
    def draw(self,s):
        tft.text(self.font, self.s,
            self.x,self.y,
            self.bc,self.bc
         )
        tft.text(self.font, s,
            self.x,self.y,
            self.fc,self.bc
                 )
        self.s=s

p7=Pin(7, Pin.IN, Pin.PULL_UP)
p6=Pin(6, Pin.IN, Pin.PULL_UP)

label0=TextBox(0,2*32,font,st7789.CYAN, st7789.BLACK)
label1=TextBox(0,3*32,font,st7789.WHITE, st7789.BLACK)

while True:
    put_data_to_trend(150-25-66*p7.value()-33*p6.value())
    label0.draw('p7 = '+str(p7.value())+' p6 = '+str(p6.value()))
    label1.draw('value '+str(150-25-66*p7.value()-33*p6.value()))
```

フォントは数種類ファームウエアに含まれていてありがたいのですが，縦16ドット以下のものは，私には小さくて読みにくく，32ドットは大きすぎて表示文字数を多くできないのが悩みですね... 外部フォントファイルも使えますが，遅いので計測的な目的では使いにくい。コンパイルして埋め込むほどの腕はない...

ただ，MicroPythonとして普通に使えるのでいいですね。

# CircuitPythonで使う

表示されている文字は，こちらのほうが見やすくていいですね。文字や線を出すまでに結構時間を取りました。

## セットアップ

- Raspberry Pi Pico としてCircuitPythonのファームを書き込みます。
- ST7789Vと文字・ピクセル表示のため，以下をRP2040の`/lib`フォルダーに置きます。
-- `adafruit_display_text/`
-- `adafruit_st7789.mpy`

このボードをCircuitPythonで使う例がずばりここに置いてありました。参照してST7789Vを使えるようにします。

- https://gist.github.com/todbot/a23c8cb28e5925dfa79e6dba5c9d0962

## 文字表示

こちらに複数文字を表示し，表示内容を更新する例がありました。

- http://helloraspberrypi.blogspot.com/2021/01/raspberry-pi-picocircuitpython-st7789.html

```main（抜粋）.py
splash = displayio.Group()
display.show(splash)

text1 = label.Label(terminalio.FONT, text='Demo', color=0xFFFFFF)
text_group = displayio.Group(scale=2, x=5, y=110)
text_group.append(text1)  # Subgroup for text
splash.append(text_group)

text2 = label.Label(terminalio.FONT, text='', color=0xFFFF00)
text_group = displayio.Group(scale=2, x=120, y=110)
text_group.append(text2)  # Subgroup for text
splash.append(text_group)
```
概念を理解するのに時間がかかりました...

本家に優しい解説があるのですが，
- https://learn.adafruit.com/circuitpython-display-support-using-displayio/text

これを使うと，複数文字列を同時に表示できません。上のように，`Group`の変数`splash`を用意し，そこに`label`を`append`することで文字列を追加していきます。

## 点を表示する

ここに解説がありました。

- https://learn.adafruit.com/circuitpython-display-support-using-displayio/draw-pixels

`TileGrid`のオブジェクトを作り，上の文字表示で用意された`splash`に`append`することで文字列と共存して表示します。

```main（抜粋）.py
bitmap = displayio.Bitmap(display.width, display.height, 4) # 4 color
```
としてしまうと，全画面領域がビットマップ用となってしまい，文字が表示できませんので，適宜領域を減らします。

```demo.py
import os
import board
import time
import terminalio
import displayio
import busio
from adafruit_display_text import label
import adafruit_st7789

displayio.release_displays()

tft_cs = board.GP5
tft_dc = board.GP1
tft_res = board.GP0
spi_mosi = board.GP3
spi_clk = board.GP2

import digitalio
displayio.release_displays()

tft_pwr = board.GP22
tft_clk = board.GP2
tft_mosi = board.GP3
tft_cs = board.GP5
tft_dc = board.GP1
tft_rst = board.GP0
tft_bl = board.GP4

pwr_pin = digitalio.DigitalInOut(tft_pwr)
pwr_pin.switch_to_output(value=True)

tft_spi = busio.SPI(clock=tft_clk, MOSI=tft_mosi)
display_bus = displayio.FourWire(tft_spi, command=tft_dc, chip_select=tft_cs, reset=tft_rst)
display = adafruit_st7789.ST7789(display_bus,
                                 width=135, height=240, 
                                 rowstart=40, colstart=53,
                                 backlight_pin=tft_bl)
display.rotation=270

'''
rotation=270

+------------------+
|                  |
|                  | USB
|                  |
+------------------+
'''

splash = displayio.Group()
display.show(splash)

text1 = label.Label(terminalio.FONT, text='Demo', color=0xFFFFFF)
text_group = displayio.Group(scale=2, x=5, y=110)
text_group.append(text1)  # Subgroup for text
splash.append(text_group)

text2 = label.Label(terminalio.FONT, text='', color=0xFFFF00)
text_group = displayio.Group(scale=2, x=120, y=110)
text_group.append(text2)  # Subgroup for text
splash.append(text_group)

#bitmap = displayio.Bitmap(display.width, display.height, 4) # 4 color
bitmap = displayio.Bitmap(display.width, 100, 4) # 4 color

palette = displayio.Palette(4)
palette[0] = 0x000000
palette[1] = 0xffff00 # yellow
palette[2] = 0x00ffff # cyan
palette[3] = 0x7f7f7f

tile_grid = displayio.TileGrid(bitmap, pixel_shader=palette)
splash.append(tile_grid)

for x in range(240):
    text2.text='x='+str(x)
    for c in [0,1,2,3]:
        bitmap[x, (c+1)*20] = c
```

## 補足

`adafruit_display_shapes` を使うと，線分や円も描けるようになるのですが，線分の端点を移動させる手段が見つかりませんでした... とりあえずは上の「点描画」をループで回してしのごうかと。

## ボードの補足

LiPoバッテリー対応なのは良いのですが，コネクターが底面にあるため，ブレッドボードにボードが素直におさまりません。最近樹脂のシェルも売られていますので，購入しました。ブレッドボードじゃなくてシェルにいれたまま，M5Stack/M5StickCPlusのように，「ちょっとした時」にささっと接続して MicroPython / CircuitPython で使うといいかなと。

ESP32はADCが少し難ありで，M5ではたいていADCユニットと一緒に使っていますが，RP2040ならADCは普通に使えますし。