2026年2月17日 星期二

[OTTO GO] 圖片顯示,利用LVGL技術



1.圖檔轉換工具:先把圖檔轉換成LVGL格式

https://lvgl.io/tools/imageconverter

2.選擇RGB565產生一個.c的圖檔,例子是animation2.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#ifdef __has_include
    #if __has_include("lvgl.h")
        #ifndef LV_LVGL_H_INCLUDE_SIMPLE
            #define LV_LVGL_H_INCLUDE_SIMPLE
        #endif
    #endif
#endif

#if defined(LV_LVGL_H_INCLUDE_SIMPLE)
    #include "lvgl.h"
#else
    #include "lvgl/lvgl.h"
#endif


#ifndef LV_ATTRIBUTE_MEM_ALIGN
#define LV_ATTRIBUTE_MEM_ALIGN
#endif

#ifndef LV_ATTRIBUTE_IMAGE_TILE000
#define LV_ATTRIBUTE_IMAGE_TILE000
#endif

const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMAGE_TILE000 uint8_t tile000_map[] = {
/*圖檔資料太, 省略*/
};

const lv_image_dsc_t tile000 = {
  .header.cf = LV_COLOR_FORMAT_RGB565,
  .header.magic = LV_IMAGE_HEADER_MAGIC,
  .header.w = 320,
  .header.h = 240,
  .data_size = 76800 * 2,
  .data = tile000_map,
};

3.把檔案切割成animation2.h和animation2.c
animation2.h:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#pragma once
#include <Arduino.h>

#ifdef __cplusplus
extern "C" {
#endif
extern const uint8_t animation2_map[];
#ifdef __cplusplus
}
#endif

static const uint16_t ANIM2_W = 320;
static const uint16_t ANIM2_H = 240;
static const uint32_t ANIM2_SIZE = (uint32_t)ANIM2_W * (uint32_t)ANIM2_H * 2u;

antimation2.c:

1
2
3
4
#include <stdint.h>
const uint8_t animation2_map[] = {
/*圖檔資料太, 省略*/
}

4. arduino主檔程式


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
/*
  ESP32 + Adafruit_ST7789(240x320) 顯示 animation2.c 的 RGB565 圖 (320x240)

  TFT pins (你前面驗證可用那組):
    MOSI=19, SCLK=18, CS=5, DC=16, RST=23

  重要:
  - tft.setRotation(1) 讓螢幕座標變成 320x240,剛好完整顯示
  - animation2.c 需移除底部 lv_image_dsc_t descriptor(只留 animation2_map 陣列)
*/

#include <Arduino.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>

#include "animation2.h"   // extern animation2_map + W/H

// ===== TFT pins =====
#define TFT_MOSI 19
#define TFT_SCLK 18
#define TFT_CS    5
#define TFT_DC   16
#define TFT_RST  23

Adafruit_ST7789 tft(TFT_CS, TFT_DC, TFT_RST);

// 320 像素一行的暫存(RAM 640 bytes)
static uint16_t lineBuf[ANIM2_W];

// 你的 animation2.c 是 RGB565 的 bytes
// 多數 LVGL 匯出 RGB565 會是 little-endian:低位元組在前
// 若你顏色不對(例如整體偏紫/偏綠),把這個改成 0 試試
static const bool SOURCE_IS_LITTLE_ENDIAN = true;

static inline uint16_t read565(const uint8_t *p) {
  if (SOURCE_IS_LITTLE_ENDIAN) {
    return (uint16_t)p[0] | ((uint16_t)p[1] << 8);
  } else {
    return ((uint16_t)p[0] << 8) | (uint16_t)p[1];
  }
}

void drawRGB565_320x240(const uint8_t *dataBytes) {
  // dataBytes 長度必須 = 320*240*2 = 153600
  tft.startWrite();
  tft.setAddrWindow(0, 0, ANIM2_W, ANIM2_H);

  const uint32_t rowBytes = (uint32_t)ANIM2_W * 2u;

  for (uint16_t y = 0; y < ANIM2_H; y++) {
    const uint8_t *row = dataBytes + (uint32_t)y * rowBytes;

    for (uint16_t x = 0; x < ANIM2_W; x++) {
      lineBuf[x] = read565(row + (uint32_t)x * 2u);
    }

    // 一次推一行
    // Adafruit_ST7789(Adafruit_SPITFT) 提供 writePixels
    tft.writePixels(lineBuf, ANIM2_W, true);
  }

  tft.endWrite();
}

void setup() {
  Serial.begin(115200);
  delay(200);

  // 用你穩定的 SPI begin(Adafruit_ST7789 會使用預設 SPI instance)
  SPI.begin(TFT_SCLK, -1, TFT_MOSI, TFT_CS);

  tft.init(240, 320);
  tft.setRotation(1);          // 變成 320x240
  tft.fillScreen(ST77XX_BLACK);

  Serial.printf("TFT size: %d x %d\n", tft.width(), tft.height());
  Serial.printf("Expect:   %u bytes\n", (unsigned)ANIM2_SIZE);

  // 顯示一次
  drawRGB565_320x240(animation2_map);

  // 顯示文字提示
  tft.setTextColor(ST77XX_YELLOW);
  tft.setTextSize(2);
  tft.setCursor(8, 8);
  tft.print("animation2.c RGB565");
}

void loop() {
  // 你這份 animation2.c 只有單張圖,所以 loop 不會動
  delay(1000);
}

沒有留言:

張貼留言