05.M5Tab5にてLVGLのsliderで輝度調整


05.M5Tab5にてLVGLのsliderで輝度調整

M5Stack Tab5でLVGL 9.3.0のスライダーを使用して画面の輝度調整をします。

前回の修正

前回、M5.Display.setRotation(3); で画面を回転していましたが、これではタッチ位置が合わなくなる事がわかったのでこの行は削除します。
今回一部の拡大は出来ましたが、LVGLでは文字単体の拡大・回転は出来ないようです。LVGLで文字単体の拡大は、大きなサイズのfontを使用(自作)し、文字の回転は画面全体の回転をするようです。
今回の画面の向きは、電池が上に来る向きです。

説明

今回、デフォルトfontは、cjkの16pxにします。
lv_conf.hの 632行目は
#define LV_FONT_DEFAULT &lv_font_source_han_sans_sc_16_cjk
に変更します。しかし、これは漢字の文字数が少ないので、自作フォントを考えないとダメなようです。

明るさには、0-255の値が使えますが、0は真っ暗でタッチ位置がわからなくなってしまうので、1-255の範囲で可変します。また、起動時の明るさは50としました。
lv_sliderの説明
https://docs.lvgl.io/master/details/widgets/slider.html

LVGLの動作は、setup() でlvを書いても1回だけ動作するのではなく、loop() 内の lv_timer_handler(); を高速ループさせることによりsetup()に書いたlvの動作を更新しています。

スケッチ


// 向きは電池上、スライダーは正常動作
// lv_conf.hの 632 #define LV_FONT_DEFAULT &lv_font_source_han_sans_sc_16_cjk
// Arduino-IDE 2.3.6
// LVGL 9.3.0
// M5Unified 0.2.7
// M5GFX 0.2.9
#include <lvgl.h>         // LVGL 9.3.0を使用
#include <M5Unified.h>    // M5統合ライブラリ
#define TFT_HOR_RES 720   // 画面幅 Tab5
#define TFT_VER_RES 1280  // 画面高さ
#define DRAW_BUF_SIZE (TFT_HOR_RES * TFT_VER_RES / 10 * (LV_COLOR_DEPTH / 8))
//LVGLはこのバッファに描画を行う、画面サイズの1/10バイト
uint32_t draw_buf[DRAW_BUF_SIZE / 4];  //
static lv_obj_t *label;                //

void my_print(lv_log_level_t level, const char *buf) {
  //
  LV_UNUSED(level);     // 未使用の変数?
  Serial.println(buf);  // bufをシリアルモニタに表示
  Serial.flush();       // 送信完まで待つ
}

void my_disp_flush(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map) {
  //レンダリングされた画像をdisplayにcopyする自作関数
  uint32_t width = (area->x2 - area->x1 + 1);   //
  uint32_t height = (area->y2 - area->y1 + 1);  //
  lv_draw_sw_rgb565_swap(px_map, width * height);
  // RGB565バッファの上位バイトと下位バイトを入れ替えます(バッファへのポインタ,ピクセル数)
  M5.Display.pushImageDMA<uint16_t>(area->x1, area->y1, width, height, (uint16_t *)px_map);
  // DMAを使用して画像を効率的に表示(x0,y0,w,h,画像データへのポインタ、通常は16bit RGB565)
  lv_display_flush_ready(disp);  // 書込完でdisplayドライバから呼出す
}

void my_touchpad_read(lv_indev_t *indev, lv_indev_data_t *data) {
  // タッチを取得する自作関数
  lgfx::touch_point_t tp;                   //
  M5.update();                              // touch状態更新
  if (M5.Display.getTouch(&tp)) {           // タッチパネルの情報を取得
    data->point.x = tp.x;                   // タッチx値
    data->point.y = tp.y;                   // タッチy値
    data->state = LV_INDEV_STATE_PRESSED;   // 入力デバイスが押された状態
    Serial.print(data->point.x);            // タッチx値をシリアルモニタへ表示
    Serial.print(",");                      // シリアルモニタへ表示
    Serial.println(data->point.y);          // タッチy値をシリアルモニタへ表示
  } else {                                  // タッチしていなければ
    data->state = LV_INDEV_STATE_RELEASED;  // 入力デバイスが解放の状態
  }
}

static uint32_t my_tick() {  // 経過時間の自作関数
  return millis();           // Arduinoのmillis()を使用
}

static void slider_event_cb(lv_event_t *e) {  //
  lv_obj_t *slider = lv_event_get_target_obj(e);
  // イベントが最初にターゲットにしたオブジェクトを取得
  lv_label_set_text_fmt(label, "画面の明るさ %" LV_PRId32, lv_slider_get_value(slider));
  //ラベルに新しい書式設定されたテキストを設定 PRId32マクロ=int32_tフォーマット
  M5.Display.setBrightness(lv_slider_get_value(slider));  // スライダーの値で画面明るさ設定
  lv_obj_align_to(label, slider, LV_ALIGN_OUT_TOP_LEFT, -30, -100);
  // スライダー上部を揃える オブジェクトを別のオブジェクトに整列(整列するオブジェクトへのポインタ,
  // 別のオブジェクトへのポインタ,配置タイプ(上の外側の左),整列後のxオフセット,yオフセット)
  // https://docs.lvgl.io/master/details/common-widget-features/coordinates.html
}

void lv_slider_test() {
  // 画面中央にスライダーを作り、その上に値を表示
  lv_obj_t *slider = lv_slider_create(lv_screen_active());  // スライダーを作成
  lv_obj_set_size(slider, 400, 50);                         // 幅と高さを設定 太さ100は止まる
  lv_slider_set_range(slider, 1, 255);                      // minとmax 0は真っ黒
  lv_obj_center(slider);                                    // 親画面の中心に揃える
  lv_obj_add_event_cb(slider, slider_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
  // イベントハンドラ関数を追加 (オブジェクトへのポインタ,イベントを呼出すイベントコード,
  // 新しいイベント関数,カスタムデータはevent_cb)
  lv_slider_set_value(slider, 50, LV_ANIM_ON);
  // バーの開始値 (ポインタ,開始値,バー表示する)
  M5.Display.setBrightness(lv_slider_get_value(slider));  // スライダーの値で画面明るさ設定

  label = lv_label_create(lv_screen_active());
  // ラベルを作成 (オブジェクトへのポインタ、新しいラベルの親になります)
  lv_label_set_text(label, "画面の明るさ 50");
  // ラベルの新テキスト (ラベルオブジェクトへのポインタ,'\0'で終わる文字列 NULLは現在のテキストで更新)
  lv_obj_align_to(label, slider, LV_ALIGN_OUT_TOP_LEFT, -30, -100);  // 上と同じ
  lv_obj_set_style_transform_zoom(label, 1024, 0);                   // ズーム4(=1024/256)倍
}

void setup() {
  M5.begin();            // M5初期化
  Serial.begin(115200);  // シリアルモニタの通信速度
  //M5.Display.setRotation(3);// タッチが合わなくなる
  lv_init();                // LVGLライブラリを初期化
  lv_tick_set_cb(my_tick);  // 経過時間(mS)を取得するためのコールバック関数

#if LV_USE_LOG != 0  // logを表示するなら
  // C:\Users\.....\Documents\Arduino\libraries\lv_conf.h の278行目の #defineで0
  lv_log_register_print_cb(my_print);  //デバッグ用レジスタプリント
#endif

  lv_display_t *disp;                                  //
  disp = lv_display_create(TFT_HOR_RES, TFT_VER_RES);  // 指定解像度で新しいディスプレイを作成
  lv_display_set_flush_cb(disp, my_disp_flush);
  // レンダリングされたイメージをdisplayにcopyするために呼出されるコールバックを設定
  lv_display_set_buffers(disp, draw_buf, nullptr, sizeof(draw_buf), LV_DISPLAY_RENDER_MODE_PARTIAL);
  // displayのバッファを設定(displayへのポインタ,バッファ1,バッファ2,buf_size,レンダリングモード)

  lv_indev_t *indev = lv_indev_create();            // 入力デバイスindevの作成
  lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);  //タッチはPOINTERタイプ
  lv_indev_set_read_cb(indev, my_touchpad_read);
  // 入力デバイスのdataをindevに読込むためのコールバック関数を設定 タッチしてるか タッチx,y値

  lv_slider_test();            // スライダで画面明るさ設定
  Serial.println("Setup 完");  // シリアルモニタに表示
}

void loop() {
  lv_timer_handler();  // 数mSごとに定期的にLVGLを呼出す
  delay(5);            // 5mS待つ(応答性を維持するmax値)
}
* flash memory(6.5Mbyte)のうち、スケッチが13%使用。RAM(327kbyte)のうち、global変数が84%使用、local変数で52kbyte使用可能。(1000byte=1kbyteで計算)