1. はじめに
この記事では、飛び込みで Open Source Conference 2025 Tokyo/Spring に出展したサンプルを紹介しつつ、
LVGL による描画ライブラリに触れていきたいと思います。
(本当は、Zephyrが得意としているIoTを全面に押し出した内容と考えていたのですが間に合わず…そのネタもいずれは)
2. 実機とリポジトリ
実機は STM32H747I Discovery を用いました。
リポジトリはいつもの Playbook で、sample/osc2025_tokyo としてブランチを切っています。
3. 使い方
まずは動かすまでの手順を示します(以下、コピペでOK)。
mkdir -p ~/osc2025_tokyo && cd ~/osc2025_tokyo
python3 -m venv .venv
source .venv/bin/activate
pip install west
git clone https://github.com/Corgeek/ZephyrOpsPlaybook.git -b sample/osc2025_tokyo playbook
west init -l playbook
west update
pip install -r zephyr/scripts/requirements.txt
west sdk install
次に、playbook で用意したスクリプトを使い、ターゲットの指定や環境依存パスを解消する設定群である scripts/west_env.bat
を生成します。
cd playbook
./scripts/setup.sh
今回は LCD を用いるのですが、このボードでは shield 指定する必要があります。
通常、west build
の際に、--shield hogehoge
を追加指定する必要があるのですが、
playbook のスクリプトは --shield
オプションをまだサポートしていないため scripts/west_env.bat
内の BOARD_TYPE
を下記のように手作業で編集してください。
BOARD_TYPE="stm32h747i_disco/stm32h747xx/m7 --shield st_b_lcd40_dsi1_mb1166_a09"
(※ ボードの生産が古いと、--shield st_b_lcd40_dsi1_mb1166
の可能性があります)
以上で、ビルド環境は整いました。
以下でビルド&書き込みを行います。
./scripts/build.bat /r && ./scripts/build.bat /f
4. 解説
続いて、実際の処理や構成について解説していきます。
大まかな流れとしては以下の通りです。
- sample/osc2025_tokyo ブランチから派生
- bitmap ファイルを配列データに変換
- 配列データを描画ライブラリの LVGL 用に手直し
- CMakeLists.txt にファイルを登録
- main 関数で描画処理を実装
また、表示しているのは1枚の大きめなBMP画像のみ(手抜き)です。
画像データも BMP を C言語の配列形式に変換し、それを ROM の const 領域に格納する(横着な)運用を取っています。
ちなみに、本機はSDカードスロットも標準装備しており、Zephyr 側も SDカードのドライバと FAT 形式のファイルフォーマットも標準サポートしているため、
これらを利用することである程度容量を気にせず運用することも可能です。
4.1. sample/osc2025_tokyo ブランチから派生
ベースは dev/minimal ブランチから派生しており、prj.conf で CONFIG_LVGL を有効化などの初設定を加えています。
sample/osc2025_tokyo をそのまま利用して差分を確認するか、これをベースに新しいブランチを作成するなどしてください。
git checkout origin/sample/osc2025_tokyo -b sample/lvgl
4.2. bitmap ファイルを配列データに変換
以下のコマンドで bitmap データを変換できます。実際に使用した画像は rsc/osc_tokyo_565.bmp に置いてあり、容量を減らすため RGB565 の 16bit color の BMP ファイルにしています。
xxd -i rsc/osc_tokyo_565.bmp > src/osc_tokyo_bmp.c
4.3. 配列データを描画ライブラリの LVGL 用に手直し
xxd で出力されるデータは、bmp のヘッダー部分も含んだ data 領域用の配列形式として出力されるため、ヘッダー部分を削り、さらに const 領域に収めるように以下のように手直ししています。
unsigned char rsc_osc_tokyo_565_bmp[] = {
0x42, 0x4d, 0x42, 0xc4, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00,
0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x20, 0x03, 0x00, 0x00, 0x90, 0x01,
0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0xc4,
0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0xe0, 0x07,
0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
static const uint8_t s_osc_tokyo_bmp[] = {
/* 0x42, 0x4d, 0x42, 0xc4, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00,
0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x20, 0x03, 0x00, 0x00, 0x90, 0x01,
0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0xc4,
0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0xe0, 0x07,
0x00, 0x00, 0x1f, 0x00, 0x00, 0x00,*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
また、今回利用する描画ライブラリがこのBMPのデータにアクセスできるようにするため、以下のように追加しています。
#include <zephyr/kernel.h>
#include <lvgl_zephyr.h>
#include <lvgl.h>
const lv_image_dsc_t g_img_osc_tokyo = {
.header.magic = LV_IMAGE_HEADER_MAGIC,
.header.cf = LV_COLOR_FORMAT_RGB565,
.header.flags = 0,
.header.w = 800,
.header.h = 400,
.header.stride = 0,
.data_size = sizeof(s_osc_tokyo_bmp),
.data = s_osc_tokyo_bmp,
};
4.4. CMakeLists.txt にファイルを登録
手直しが済んだあと、このファイルを CMakeLists.txt にコンパイル対象として登録
target_sources(app
PRIVATE
${CMAKE_CURRENT_LIST_DIR}/main.c
${CMAKE_CURRENT_LIST_DIR}/osc_tokyo_bmp.c
)
4.5. main 関数で描画処理を実装
あとは追加した情報を lv_image_set_src() を利用して img にセットして、その描画箇所を指定するだけです。
#include <zephyr/kernel.h>
#include <zephyr/drivers/display.h>
#include <lvgl.h>
#include "boards/unique.h"
extern const lv_image_dsc_t g_img_osc_tokyo;
int main(void)
{
lv_obj_t *img;
img = lv_image_create(lv_screen_active());
lv_image_set_src(img, &g_img_osc_tokyo);
lv_obj_center(img);
while (true) {
k_msleep(MIN(lv_timer_handler(), INT32_MAX));
}
return 0;
}
5. あとがき
今回は、秘密結社オープンフォースの他の活動や、プライベートな環境変化などが重なり、
労力に対してのコスパ優先を選び、描画ライブラリを使った展示としてみました。
LVGL 自体は今回初めての利用だったのですが、サンプルや各種機能を見る限り、画像のアニメーション描画や、フォント指定ができる文字の描画だけでなく、よくある button や spinbox, slideer なども標準装備しており、結構いろんな事ができそうです。
参考として、Zephyr で動作している LVGL の sample を動かす方法は以下の通り。
cd ~/osc2025_tokyo
source .venv/bin/activate
west build -b stm32h747i_disco/stm32h747xx/m7 --shield st_b_lcd40_dsi1_mb1166_a09 zephyr/samples/modules/lvgl/demos/
west flash
また、LVGL の機能の構成や利用の仕方、用語など、Qtに近しいものを感じました。
(マルチプラットフォームを想定すると同じ設計思想に落ち着くのかな?)
user@host:~/osc2025_tokyo$ ls modules/lib/gui/lvgl/src/layouts/
flex grid lv_layout.c lv_layout.h lv_layout_private.h
user@host:~/osc2025_tokyo$ $ ls modules/lib/gui/lvgl/src/widgets/
animimage calendar image line objx_templ span tabview
arc canvas imagebutton list property spinbox textarea
bar chart keyboard lottie roller spinner tileview
button checkbox label menu scale switch win
buttonmatrix dropdown led msgbox slider table
2024年末に発表された下記件も個人的にかなり期待している所でして、まだまだ Zephyr の世界が便利に広がりそうです…!
stm32h747i_disco 自体は 15,000〜20,000 円程度と少し高めですが、ST-LinkV3 も内蔵していて、ディスプレイや ethernet を利用した https client/server 機能、SD カードスロットのドライバや FAT ファイルシステム、Arduino 互換 pin なども Zephyr で動作することを確認しており、一式が揃っているという点では非常に便利に活用できそうです。
また、stm32h747i_disco/stm32h747xx/m4 もビルド&書き込み確認できており、SMPは非対応なもののできることはかなり広そうです。
(※ ただし CortexM4 側は openOCD 非対応。 STM32Cube で m4 を使った単純動作のセンサー制御を開発して、m7側は Zephyr を使ってマルチメディアやGUIなどの分担が良さそう?)
まだまだプライベートなイベントが目白押しですが、少しずつでも継続してアウトプットを出せるようにしていきたいと思っています。