😸

pico-jxblib と TFT LCD の話 (続き)

に公開

pico-jxglib は、ワンボードマイコン Raspberry Pi Pico の Pico SDK プログラミングをサポートするライブラリです。

https://zenn.dev/ypsitau/articles/2025-01-24-jxglib-intro

今回は TFT LCD の初期化と描画方法についての詳細、そして複数の LCD を接続する方法について説明します。

プログラムの解説

前回のサンプルではデモのためにテスト用関数を使いましたが、今回は素の API を使ってそれぞれの動作を見ていきたいと思います。ソースファイル lcdtest.cpp を以下のように書き換えます。

lcdtest.cpp
#include <stdio.h>
#include "pico/stdlib.h"
#include "jxglib/ST7789.h"
#include "jxglib/sample/cat-240x320.h"
#include "jxglib/Font/shinonome16-japanese-level1.h"

using namespace jxglib;

int main()
{
    // Initialize devices
    ::stdio_init_all();
    ::spi_init(spi1, 125 * 1000 * 1000);
    GPIO14.set_function_SPI1_SCK();
    GPIO15.set_function_SPI1_TX();
    ST7789 display(spi1, 240, 320, {RST: GPIO10, DC: GPIO11, CS: GPIO12, BL: GPIO13});
    display.Initialize(Display::Dir::Rotate0);
    // Display items
    display.DrawImage(20, 20, image_cat_240x320, {20, 20, 200, 200});
    display.SetFont(Font::shinonome16);
    const char* str = "吾輩は猫である";
    Size sizeStr = display.CalcStringSize(str);
    int x = (display.GetWidth() - sizeStr.width) / 2, y = 230;
    display.DrawString(x, y, str);
    display.DrawRect(x - 8, y - 4, sizeStr.width + 8 * 2, sizeStr.height + 4 * 2, Color::white);
    display.DrawRectFill(0, 260, 55, 60, Color::red);
    display.DrawRectFill(60, 260, 55, 60, Color::green);
    display.DrawRectFill(120, 260, 55, 60, Color::blue);
    display.DrawRectFill(180, 260, 55, 60, Color::aqua);
}

lcdtest-apis.jpg

ソースファイルの前半でデバイスの初期化を行っています。

::spi_init(spi1, 125 * 1000 * 1000);`

Pico SDK の関数です。SPI1 を動作クロック 125MHz で初期化します

GPIO14.set_function_SPI1_SCK();
GPIO15.set_function_SPI1_TX();

GPIO14 と GPIO15 をそれぞれ SPI1 の SCK、TX (MOSI) に設定します

ST7789 display(spi1, 240, 320, {RST: GPIO10, DC: GPIO11, CS: GPIO12, BL: GPIO13});

ST7789 の操作をするインスタンスを生成します。接続する SPI、ディプレイサイズ、接続する GPIO (RST: Reset、DC: Data/Command、CS: Chip Select、BL: Backlight) を指定します

display.Initialize(Display::Dir::Rotate0);

LCD を初期化して描画可能な状態にします。引数には以下に示す LCD の描画方向を指定します

  • Display::Dir::Rotate0 または Display::Dir::Normal ... 通常方向で描画をします
  • Display::Dir::Rotate90 ... 90 度回転します
  • Display::Dir::Rotate180 ... 180 度回転します
  • Display::Dir::Rotate270 ... 270 度回転します
  • Display::Dir::MirrorHorz ... 水平方向に鏡像反転します
  • Display::Dir::MirrorVert ... 垂直方向に鏡像反転します

これ以降は display インスタンスに対して描画処理をすることができます。

display.DrawImage(20, 20, image_cat_240x320, {20, 20, 200, 200});

指定した座標にイメージを描画します。四番目の引数はイメージ内のクリッピング領域を指定します

display.SetFont(Font::shinonome16);

インクルードファイル jxglib/Font/shinonome16-japanese-level1.h で定義されているフォントデータ Font::shinonome16 を指定します

Size sizeStr = display.CalcStringSize(str);

指定したフォントで文字列を描画したときのサイズを計算します

display.DrawString(x, y, str);

指定した座標に文字列を描画します

display.DrawRect(x - 8, y - 4, sizeStr.width + 8 * 2, sizeStr.height + 4 * 2, Color::white);

座標・大きさ・色を指定して矩形を描画します

display.DrawRectFill(0, 260, 55, 60, Color::red);
display.DrawRectFill(60, 260, 55, 60, Color::green);
display.DrawRectFill(120, 260, 55, 60, Color::blue);
display.DrawRectFill(180, 260, 55, 60, Color::aqua);

座標・大きさ・色を指定して塗りつぶし矩形を描画します


pico-jxglib が提供する描画機能はここに挙げたものでほぼ全てです。円や直線描画は?と思われるかもしれません。でも、ユーザがグラフィック描画に求める仕様は非常に高く、それらを自前で苦労して作っても結局は中途半端なものになってしまい、実用にはならないのです。ですから、そういう高度な描画処理は専用のライブラリにまかせるというポリシーをとります。例えばボタンやリストボックスといった Windows のような GUI が必要ならば、LVGL という優れたライブラリがあります。pico-jxglib はそのようなライブラリへの橋渡しをするアダプタを提供します。

複数の LCD 接続

LCD デバイスに CS (Chip Select) 端子がついていれば、同じ SPI インターフェースに並列で複数接続することができます。

ここでは、ST7789 を 2 個接続してみます。ブレッドボードの配線イメージは以下の通りです。

circuit-st7789-multi.png

ソースファイル lcdtest.cpp を以下のように書き換えます。

lcdtest.cpp
#include <stdio.h>
#include "pico/stdlib.h"
#include "jxglib/ST7789.h"
#include "jxglib/Font/shinonome16-japanese-level1.h"
#include "jxglib/sample/cat-240x320.h"
#include "jxglib/sample/Text_Botchan.h"

using namespace jxglib;

int main()
{
    ::stdio_init_all();
    ::spi_init(spi1, 125 * 1000 * 1000);
    GPIO14.set_function_SPI1_SCK();
    GPIO15.set_function_SPI1_TX();
    ST7789 display1(spi1, 240, 320, {RST: GPIO10, DC: GPIO11, CS: GPIO12, BL: GPIO13});
    ST7789 display2(spi1, 240, 320, {RST: GPIO18, DC: GPIO19, CS: GPIO20, BL: GPIO21});
    display1.Initialize(Display::Dir::Rotate0);
    display2.Initialize(Display::Dir::Rotate90);
    display1.DrawImage(0, 0, image_cat_240x320);
    display2.SetFont(Font::shinonome16);
    display2.DrawStringWrap(0, 0, Text_Botchan);
}

lcdtest-multi.jpg

GPIO さえ確保すれば、同じ SPI インターフェースにいくつでも LCD をぶらさげられる ... と言いたいところですが、あまりつなげると信号波形が劣化するようです。4 個の LCD を同一の SPI に接続できる[1]ことを確認しましたが、そのうちのひとつを ILI9341 にすると表示がされませんでした。

脚注
  1. このときは、さすがに GPIO が足りなくなくなったので BL (バックライト) 端子を VCC (3.3V) に接続しました。 ↩︎

GitHubで編集を提案

Discussion