📺

ILI9488をRaspberry Pi Zeroで動かしたら苦戦した話

に公開

事の発端

  • ILI9488買った
  • Raspberry Pi Zeroで使いたいな
  • なんかできない
  • !?!?!??

結果

いろいろいじったらできた
この手のものはラズパイ3以前と4以降でどうもSPIの動きが違うらしく、それのせいだと思う(ラズパイ4持ってないので実験はしてない)

やり方

ライブラリとラズパイの準備

RPi.GPIOとspidevが必要なのでこれを入れる

pip install RPi.GPIO spidev

SPIはraspi-configで有効にしておく

配線

LCD Rasp GPIO
VCC 3.3V
GND GND
CS 8
RST(RESET) 25
DC(D/C) 24
SDI(MOSI) 10
CLK,SCK(CLOCK) 11

コード

raspi_ili9488.py
import RPi.GPIO as GPIO
import spidev
import time

### Raspberry PiのGPIOピンのセッティング
DC  = 24
RST = 25
CS  = 8
LED = 18

### GPIO開始制御
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(DC, GPIO.OUT)
GPIO.setup(RST, GPIO.OUT)
GPIO.setup(CS, GPIO.OUT)
GPIO.setup(LED, GPIO.OUT)
GPIO.output(LED, GPIO.HIGH)  # バックライトON

### SPI開始制御
spi = spidev.SpiDev()
spi.open(0, 0)
spi.max_speed_hz = 10000000  # 10MHzで安定した 不安な場合は2MHz程度がいい
spi.mode = 0

### LCDの低レベル信号制御
def lcd_write_cmd(cmd):
    GPIO.output(CS, GPIO.LOW)
    GPIO.output(DC, GPIO.LOW)
    spi.writebytes([cmd])
    GPIO.output(CS, GPIO.HIGH)

def lcd_write_data(data):
    GPIO.output(CS, GPIO.LOW)
    GPIO.output(DC, GPIO.HIGH)
    if isinstance(data, int):
        spi.writebytes([data])
    else:
        # 大きなバッファは分割して送信
        # 大きくするほど早くなるかもしれないけど不安定になるので注意
        chunk_size = 4092
        for i in range(0, len(data), chunk_size):
            spi.writebytes(data[i:i+chunk_size])
    GPIO.output(CS, GPIO.HIGH)

def lcd_reset():
    GPIO.output(RST, GPIO.LOW)
    time.sleep(0.12)
    GPIO.output(RST, GPIO.HIGH)
    time.sleep(0.12)

### 初期化シーケンス
def lcd_init():
    lcd_reset()
    lcd_write_cmd(0xE0)
    lcd_write_data([0x00,0x03,0x09,0x08,0x16,0x0A,0x3F,0x78,0x4C,0x09,0x0A,0x08,0x16,0x1A,0x0F])
    lcd_write_cmd(0xE1)
    lcd_write_data([0x00,0x16,0x19,0x03,0x0F,0x05,0x32,0x45,0x46,0x04,0x0E,0x0D,0x31,0x37,0x0F])
    lcd_write_cmd(0xC0)
    lcd_write_data([0x17,0x15])
    lcd_write_cmd(0xC1)
    lcd_write_data([0x41])
    lcd_write_cmd(0xC5)
    lcd_write_data([0x00,0x12,0x80])
    lcd_write_cmd(0x36)
    lcd_write_data([0x48])# LCDのRGBと回転の命令 ※1
    lcd_write_cmd(0x3A)
    lcd_write_data([0x66])
    lcd_write_cmd(0xB0)
    lcd_write_data([0x00])
    lcd_write_cmd(0xB1)
    lcd_write_data([0xA0])
    lcd_write_cmd(0xB4)
    lcd_write_data([0x02])
    lcd_write_cmd(0xB6)
    lcd_write_data([0x02,0x02])
    lcd_write_cmd(0x11)
    time.sleep(0.12)
    lcd_write_cmd(0x29)

### 画面塗りつぶし
def fill_color(r, g, b):
    width, height = 320, 480
    # カラム設定
    lcd_write_cmd(0x2A)
    lcd_write_data([0,0,(width-1)>>8,(width-1)&0xFF])
    # ロウ設定
    lcd_write_cmd(0x2B)
    lcd_write_data([0,0,(height-1)>>8,(height-1)&0xFF])
    # メモリ書き込み
    lcd_write_cmd(0x2C)

    # 24bitカラーを18bitカラーに強制的にする
    # それぞれ8bitから2bitずつ落として6bitにする
    color_bytes = [r & 0xFC, g & 0xFC, b & 0xFC]
    block_size = 1024  # 一度に送るピクセル数
    total_pixels = width * height
    buf = color_bytes * block_size

    sent = 0
    while sent < total_pixels:
        to_send = min(block_size, total_pixels - sent)
        lcd_write_data(buf[:to_send*3])
        sent += to_send

### メイン実行
if __name__ == "__main__":
    try:
        lcd_init()
        fill_color(255,0,0)  # 赤
        time.sleep(2)
        fill_color(0,255,0)  # 緑
        time.sleep(2)
        fill_color(0,0,255)  # 青
        time.sleep(2)
    finally:
        GPIO.cleanup()

※1について

ILI9488の製造上の問題なのか使われているロジックチップが違うからなのか、このままだと正しく表示されないことがあります。
物によって設定が変わるので、自分で調整してみてください。

向き
0x00 標準向き
0x28 90°回転
0x48 90°回転 + RGB順
0x88 180°回転
0xE8 270°回転 + RGB順

こいつ!動くぞ!!

ILI9488が動作していることで喜びの舞

トラブルシューティング

Q1. 動かない、サンドストーム状態
A1. ILI9488じゃない可能性がある。同じ3.5inchのLCDでもILI9486の場合もあるから要注意

Q2. 画面が光らない
A2. 多分壊れているか、LEDに3.3Vが入っていない。raspi-configでSPIは有効になってる?SPIの配線は間違えていないかチェック。

Q3. 画面に半分しか表示されない
A3. ※1を参照して

Q4. RGBのカラーが反対になる
A4. ※1を参照して

文句

そもそもaliexpressで買うとなんで同じ見た目なのにこうも仕様が変わるのかわからないし、そもそもほかのILI系統を問題なくraspiで動かしてる人たちを見てやったのになぜかできなくて故障を疑った。
最終的に一番よく動いてくれたTFT_eSPIのライブラリをパクってやったらなんか動いてしまって、いろいろ調整してここまでもっていった。
ぼくの調べまくった時間を返してほしい。

Discussion