65.9K
CodeProject 正在变化。 阅读更多。
Home

使用新的 ESP32 LCD 面板 API 和 htcw_gfx 与 htcw_uix

starIconstarIconstarIconstarIconstarIcon

5.00/5 (15投票s)

2023年3月8日

MIT

12分钟阅读

viewsIcon

27565

downloadIcon

135

结合一些出色的技术,为 ESP32 上的物联网用户界面带来丰富、响应迅速的体验

Demo on the ESP_WROVER_KIT 4.1

引言

在本文中,我将介绍几种可以协同工作的技术,用于在 ESP32 MCU 系列上创建可维护且响应迅速的用户界面。

  • 新的 ESP LCD 面板 API 引入了一种与 LCD 设备通信的高效方式,但也带来了一些挑战。
  • htcw_gfx 提供了丰富的绘图操作集,支持任何指定的位图格式,但它本身没有直接与 LCD 通信的方式,需要某种驱动程序,并且它主要是无状态的,这与 ESP LCD 面板 API 的操作方式不太兼容——至少在没有帮助的情况下是这样。
  • htcw_uix 是一个初创的 UI/UX 库,我主要创建它是为了克服 LCD 面板 API 带来的挑战。然而,它还为创建物联网设备的屏幕提供了一种灵活且可维护的方式。

需要注意的是,htcw_gfx 和 htcw_uix eivät 与任何特定的物联网平台绑定,实际上,它们甚至可以在 PC 上运行。我们需要 ESP LCD 面板 API 的原因之一是为了完成将像素传输到显示器的最后一步,这部分是平台特定的。

有关 ESP LCD 面板 API 带来的挑战以及 htcw_uix 如何克服这些挑战的更多信息,请参阅本文。与其再次赘述,我将提供上一篇文章,如果有什么的话,它就是这篇文章的前奏。事实上,这里的代码是基于上一篇文章中的代码构建的。

我们在一个项目中一举多得,它运行在六种不同的设备上,利用了每种设备安装的任何输入外围设备。

必备组件

注意:项目在每台设备上的行为略有不同,因为每台设备都有不同的硬件。例如,ESP_WROVER_KIT 4.1 没有输入,TTGO T1 有两个物理按钮,ESP Display S3 有一个电容触摸屏。要体验完整的项目,您需要多台设备。

编程风格

与 STL 类似,htcw_gfx 大量使用泛型编程,以灵活的方式提供其功能。这需要理解和使用 `template` 和类型别名,如 `using` 和 `typedef`。

htcw_uix 更偏向 OOP 风格,具有虚拟类和二进制接口,但仍然从根本上使用模板来指示原生的像素格式和调色板(如果有的话)。

而 ESP 面板 API 则是纯 C 语言,并且级别相当低。

这对于刚接触 Arduino 框架的新手程序员来说可能不熟悉,Arduino 框架倾向于将开发者屏蔽在这些内容之外。我无法在这篇文章中帮助您学习所有这些,但或许可以参考 Andrew Koenig 和 Barbara Moo 的《Accelerated C++》一书。这本书篇幅很短,并且以“正确的方式”教授 C++,如果这种方式存在的话。

理解这段乱码

要有效地使用 ESP LCD 面板 API,您需要能够根据需要重绘屏幕的某些部分——每当它们发生变化时,并且您不能直接绘制到显示器。相反,您需要绘制到一个位图,然后将其发送到显示器。这意味着您基本上需要某种渲染框架,该框架可以跟踪脏矩形并在需要更新屏幕的一部分或全部时调用绘图代码。我在介绍中链接到的上一篇文章中涵盖了我们将要使用的方法,供进一步阅读。我们将使用 htcw_uix 来处理更高级别的窗口小部件/控件库以及它们的渲染,而 htcw_uix 本身则使用 htcw_gfx 来处理实际的绘图操作。

我们将涵盖的另一个主要内容是 ESP LCD 面板 API 本身,因为它具有相当陡峭的学习曲线,特别是当您必须为它不直接支持的显示控制器提供自己的驱动程序时。我已经提供了三个——一个用于 ESP_WROVER_KIT 中的 ILI9341,一个用于 ESP Display S3 中的 ILI9488,以及用于 M5Stack 设备中的 ILI9342。TTGO T1 Display 的 ST7789 已随 ESP-IDF 一起提供。ST7701 驱动程序随我的 _lcd_init.h_ 文件一起提供。

关于所用框架的注意事项:我为本项目选择了 Arduino 框架,但所有这三个 API 都可以从 ESP-IDF 中调用,并且在这些选项受限的框架中是创建用户界面的绝佳选择。

htcw_uix

该库提供了一个用于高级“窗口小部件”或控件的框架,以及一些基本控件,并且能够用相对较少的努力用您自己的控件进行扩展。它负责脏矩形跟踪、屏幕/控件渲染以及任何触摸显示输入。

我们来介绍主要元素

control<>

这个模板基类是库的核心之一,是基本构建块之一。每个窗口小部件/控件都公开继承自这个模板类。它提供了一些基本成员,用于在屏幕上定位控件、绘制自身、请求重绘控件的全部或一部分,以及响应触摸。

screen<>

屏幕负责跟踪所有控件、渲染它们并将触摸事件转发给适当的控件。为了高效渲染,它使用脏矩形方案。它还支持多个渲染缓冲区,以便在使用 DMA 时最大化吞吐量。

label<>

这是一个具体的控件,仅在屏幕上显示一些文本,并带有可选的背景色和边框颜色。

push_button<>

此控件提供一个响应触摸的按钮。否则,它的行为与 `label<>` 相同。

svg_box<>

此控件显示 SVG 图像文档 (`svg_doc`)。

注意:该项目还在 _/src/main.cpp_ 中提供了一个 `svg_box_touch<>` 控件,它演示了如何扩展控件,并为 SVG 图像添加了触摸功能。但是,它不是 UIX 本身的一部分。

htcw_gfx

该库提供了实际绘制控件所需的底层操作。简而言之,它是一个图形库。它位于 htcw_uix 和 ESP LCD 面板 API 之间,并提供关键的绘图图元、图像支持和文本,用于渲染显示。其中大部分核心功能由 `draw` 类提供。文档可以在此处找到。

ESP LCD 面板 API

这是 ESP-IDF 的一部分,尽管可以从 Arduino 调用它,它提供了与显示控制器和连接它的总线的底层接口。基本上,它配置显示器,然后知道如何与之通信,以便它可以接收位图并通过 DMA 异步将其传输到显示器。htcw_uix 向其提供 htcw_gfx 生成的位图,然后通过指定的总线(可能是 SPI、RGB(迄今为止仅在 _lcd_init.h_ 中用于 ESP32-S3)、i8080 或 I2C)将其发送到显示硬件。核心功能是用于初始化显示器的功能。其余功能由几个方法覆盖。我们将在稍后讨论代码时进行回顾。

编写这个混乱的程序

我将核心功能保留在 _/src/main.cpp_ 中,所以我们将首先介绍它。

main.cpp

#include <Arduino.h>
#include <Wire.h>
#include "config.h"
#include "lcd_config.h"
#define LCD_IMPLEMENTATION
#include "lcd_init.h"
#include <gfx.hpp>
using namespace gfx;
#include <uix.hpp>
using namespace uix;
// SVG converted to header using
// https://honeythecodewitch.com/gfx/converter
#include "bee_icon.hpp"
static const_buffer_stream& svg_stream = bee_icon;
// downloaded from fontsquirrel.com and header generated with
// https://honeythecodewitch.com/gfx/generator
//#include "fonts/Rubik_Black.hpp"
//#include "fonts/Telegrama.hpp"
#include "fonts/OpenSans_Regular.hpp"
static const open_font& text_font = OpenSans_Regular;

大部分是包含项,除了用于别名 SVG 文档流和字体的两行代码。第一部分是核心,包括 Arduino、所选设备的配置设置以及 `lcd_init.h`,其中包括 ESP LCD 面板 API 的初始化和接口代码。之后是更高级别的部分,例如 htcw_gfx、htcw_uix、SVG 图像和我们的字体作为嵌入式对象。

这些别名只是使得头文件中的字体和 SVG 文档易于更改。

#ifdef LCD_TOUCH
void svg_touch();
void svg_release();
#endif // LCD_TOUCH

// declare a custom control
template <typename PixelType, 
        typename PaletteType = gfx::palette<PixelType, PixelType>>
class svg_box_touch : public svg_box<PixelType, PaletteType> {
   // public and private type aliases
   // pixel_type and palette_type are 
   // required on any control
   public:
    using type = svg_box_touch;
    using pixel_type = PixelType;
    using palette_type = PaletteType;
   private:
    using base_type = svg_box<PixelType, PaletteType>;
    using control_type = control<PixelType, PaletteType>;
    using control_surface_type = typename control_type::control_surface_type;
     public:
    svg_box_touch(invalidation_tracker& parent, 
                const palette_type* palette = nullptr) 
                : base_type(parent, palette) {

    }
#ifdef LCD_TOUCH
    virtual bool on_touch(size_t locations_size,
                        const spoint16* locations) {
        svg_touch();     
        return true;
    }
    virtual void on_release() {
        svg_release();
    }
#endif // LCD_TOUCH
};

这是一个我们用来渲染 SVG 的自定义控件。htcw_uix 目前只有三个内置控件,所以了解如何创建自己的控件是很值得的。这并不难,尤其是在您完成一次之后。具体来说,您需要公开继承自 `control<>` 或派生控件,传递几个模板参数,并实现一个接受几个参数以转发给基类的构造函数。最后,您需要实现 `on_paint()` 和 `on_touch()`/`on_release()` 等方法来处理控件的绘制和触摸。

在 `on_paint()` 中,您可以使用 htcw_gfx 绘制到传递进来的目标。这个目标是控件的大小,从 (0,0) 开始。您还可以选择使用剪裁矩形来确定控件的哪个部分需要重绘。

在 `on_touch()` 中,您会收到相对于控件(从 (0,0) 开始)的位置数组,您的任务是命中测试您的控件以确定它是否被触摸,以响应触摸,并返回 true(如果发生命中测试)或 false(如果未发生)。命中测试是可选的。如果您始终返回 true,则命中测试区域有效地是控件边界的整个矩形,但命中测试允许您处理非矩形区域。您之所以会收到触摸位置数组,是因为某些触摸硬件允许您进行手势,因此可以返回多个点。

`on_release()` 仅在控件不再被触摸时通知控件。

注意:在上面的代码中,只有定义了 `LCD_TOUCH` 的设备才会处理触摸。

// declare the format of the screen
using screen_t = screen<LCD_WIDTH, LCD_HEIGHT, rgb_pixel<16>>;
// declare the control types to match the screen
#ifdef LCD_TOUCH
// since this supports touch, we use an interactive button
// instead of a static label
using label_t = push_button<typename screen_t::pixel_type>;
#else
using label_t = label<typename screen_t::pixel_type>;
#endif // LCD_TOUCH
using svg_box_t = svg_box_touch<typename screen_t::pixel_type>;
// for access to RGB565 colors which LCDs and the main screen use
using color16_t = color<rgb_pixel<16>>;
// for access to RGBA8888 colors which controls use
using color32_t = color<rgba_pixel<32>>;
#ifdef PIN_NUM_BUTTON_A
// declare the buttons if defined
using button_a_t = int_button<PIN_NUM_BUTTON_A, 10, true>;
#endif // PIN_NUM_BUTTON_A
#ifdef PIN_NUM_BUTTON_B
using button_b_t = int_button<PIN_NUM_BUTTON_B, 10, true>;
#endif
// if we have no inputs, declare
// a timer
#if !defined(PIN_NUM_BUTTON_A) && \
    !defined(PIN_NUM_BUTTON_B) && \
    !defined(LCD_TOUCH)
using cycle_timer_t = uix::timer;
#endif // !defined(PIN_NUM_BUTTON_A) ...
// declare touch if available
#ifdef LCD_TOUCH
using touch_t = LCD_TOUCH;
#endif // LCD_TOUCH

在这里,我们进行了一些类型别名,以简化一切。请注意,其中一些是基于您正在使用的硬件的条件,例如对于 ESP Display S3,标签实际上是一个按钮,以便它能够响应触摸。TTGO 使用物理按钮,而 ESP_WROVER_KIT 没有输入,因此它依赖计时器来使显示器动画化。

接下来是我们的全局变量。由于 Arduino 将您的应用程序分为 `setup()` 和 `loop()`,它基本上要求使用全局变量来在两个例程之间共享数据。

// UIX allows you to use two buffers for maximum DMA efficiency
// you don't have to, but performance is significantly better
// declare 64KB across two buffers for transfer
// RGB mode is the exception. We don't need two buffers
// because the display works differently.
#ifndef LCD_PIN_NUM_HSYNC
constexpr static const int lcd_buffer_size 
                            = 32 * 1024;
uint8_t lcd_buffer1[lcd_buffer_size];
uint8_t lcd_buffer2[lcd_buffer_size];
#else
constexpr static const int lcd_buffer_size 
                            = 64 * 1024;
uint8_t lcd_buffer1[lcd_buffer_size];
uint8_t* lcd_buffer2=nullptr;
#endif // !LCD_PIN_VSYNC
// our svg doc for svg_box
svg_doc doc;
// the main screen
screen_t main_screen(sizeof(lcd_buffer1), 
                    lcd_buffer1, 
                    lcd_buffer2);
// the controls
label_t test_label(main_screen);
svg_box_t test_svg(main_screen);
#ifdef PIN_NUM_BUTTON_A
button_a_t button_a;
#endif
#ifdef PIN_NUM_BUTTON_B
button_b_t button_b;
#endif
#ifdef EXTRA_DECLS
EXTRA_DECLS
#endif // EXTRA_DECLS

在这里,我们为 LCD 面板声明了一些全局变量。htcw_uix 可以在支持的平台上利用 DMA 传输。为了高效地做到这一点,它可以在写入另一个缓冲区时发送一个缓冲区。如果您使用两个缓冲区而不是一个,可以提高性能。这里我们使用两个 32KB 的缓冲区。请注意,缓冲区必须大小相同。例外情况是 RGB 模式,因为——尽管它使用 DMA——但它在不同的级别使用,并且不由用户级别控制。在这个级别,只需要一个缓冲区,所以我们使用我们传输内存(64KB)的全部大小来声明它。

// button callbacks
#ifdef PIN_NUM_BUTTON_A
void button_a_on_click(bool pressed, void* state) {
    if (pressed) {
        test_label.text_color(color32_t::red);
    } else {
        test_label.text_color(color32_t::blue);
    }
}
#endif // PIN_NUM_BUTTON_A
#ifdef PIN_NUM_BUTTON_B
void button_b_on_click(bool pressed, void* state) {
    if (pressed) {
        main_screen.background_color(color16_t::light_green);
    } else {
        main_screen.background_color(color16_t::white);
    }
}
#endif // PIN_NUM_BUTTON_B

// no inputs
#if !defined(PIN_NUM_BUTTON_A) && \
    !defined(PIN_NUM_BUTTON_B) && \
    !defined(LCD_TOUCH)
int cycle_state = 0;
cycle_timer_t cycle_timer(1000, [](void* state) {
    switch (cycle_state) {
        case 0:
            main_screen.background_color(color16_t::light_green);
            break;
        case 1:
            main_screen.background_color(color16_t::white);
            break;
        case 2:
            test_label.text_color(color32_t::red);
            break;
        case 3:
            test_label.text_color(color32_t::blue);
            break;
    }
    ++cycle_state;
    if (cycle_state > 3) {
        cycle_state = 0;
    }
});
#endif // !defined(PIN_NUM_BUTTON_A) ...

// touch inputs
#ifdef LCD_TOUCH
void svg_touch() {
    main_screen.background_color(color16_t::light_green);
}
void svg_release() {
    main_screen.background_color(color16_t::white);
}
#endif // LCD_TOUCH

#ifdef LCD_TOUCH
#ifdef LCD_TOUCH_WIRE
touch_t touch(LCD_TOUCH_WIRE);
#else
touch_t touch;
#endif // LCD_TOUCH_WIRE
static void uix_touch(point16* out_locations, 
                    size_t* in_out_locations_size, 
                    void* state) {
    if(in_out_locations_size<=0) {
        *in_out_locations_size=0;
        return;
    }
#ifdef LCD_TOUCH_IMPL
LCD_TOUCH_IMPL
#else
    in_out_locations_size = 0;
    return;
#endif // LCD_TOUCH_IMPL
}
#endif // LCD_TOUCH

这里我们提供了处理输入(例如:TTGO T1 Display/ESP Display S3)的代码,或设置计时器(例如:ESP_WROVER_KIT),在每种情况下都提供了一种使屏幕动画化的方法。TTGO 在您按下按钮时更改屏幕颜色。ESP Display 在触摸文本或 SVG 图像时更改屏幕颜色。ESP_WROVER_KIT 只需每秒循环一次屏幕颜色。每个设备都根据其可用的输入进行功能操作。

您可以看到实际的颜色更改非常容易,只需设置相应的成员值即可。屏幕将负责根据需要保持自身及其控件的更新。

ESP Display S3 的触摸处理例程可以稍作解释。UIX 可能支持手势,ESP Display S3 的触摸表面也支持手势。因此,可以使用多个手指来生成多个触摸点。当调用回调时,`in_out_locations_size` 以 `out_locations` 数组的可用插槽数量开始。我们根据触摸硬件报告的内容,使用可用值填充这些插槽。

#ifndef LCD_PIN_NUM_HSYNC
// not used in RGB mode
// tell UIX the DMA transfer is complete
static bool lcd_flush_ready(esp_lcd_panel_io_handle_t panel_io, 
                            esp_lcd_panel_io_event_data_t* edata, 
                            void* user_ctx) {
    main_screen.set_flush_complete();
    return true;
}
#endif // LCD_PIN_NUM_HSYNC

// tell the lcd panel api to transfer the display data
static void uix_flush(point16 location, 
                    typename screen_t::bitmap_type& bmp, 
                    void* state) {
    int x1 = location.x, 
        y1 = location.y, 
        x2 = location.x + bmp.dimensions().width-1, 
        y2 = location.y + bmp.dimensions().height-1;
    lcd_panel_draw_bitmap( x1, y1, x2, y2, bmp.begin());    

    // if we're in RGB mode:
#ifdef LCD_PIN_NUM_HSYNC
    // flushes are immediate in rgb mode
    main_screen.set_flush_complete();
#endif // LCD_PIN_NUM_HSYNC
}

这两个回调处理 htcw_uix 与 ESP 面板显示 API 的接口。第一个回调通知 htcw_uix 屏幕的刷新已完成。这让它知道它可以再次使用传输缓冲区。除 RGB 模式外,所有模式都需要此设置。

第二个回调是将位图数据从 htcw_uix 发送到显示器的控制器。位图是屏幕的一部分,最大尺寸为初始化屏幕时指定的尺寸——在这种情况下,非 RGB 接口为 32KB,否则为 64KB。`bmp.begin()` 为我们提供了位图中数据的起始指针。请注意,对于 RGB 模式,我们立即设置了屏幕的刷新完成,因为它不是后台传输。

// initialize the screen and controls
void screen_init() {
    test_label.bounds(srect16(spoint16(0, 10), ssize16(200, 60))
                .center_horizontal(main_screen.bounds()));
    test_label.text_color(color32_t::blue);
    test_label.text_open_font(&text_font);
    test_label.text_line_height(45);
    test_label.text_justify(uix_justify::center);
    test_label.round_ratio(NAN);
    test_label.padding({8, 8});
    test_label.text("Hello!");
    // make the backcolor transparent
    auto bg = color32_t::black;
    bg.channel<channel_name::A>(0);
    test_label.background_color(bg);
    // and the border
    test_label.border_color(bg);
#ifdef LCD_TOUCH
    test_label.pressed_text_color(color32_t::red);
    test_label.pressed_background_color(bg);
    test_label.pressed_border_color(bg);
#endif // LCD_TOUCH
    
    test_svg.bounds(srect16(spoint16(0, 70), ssize16(60, 60))
                .center_horizontal(main_screen.bounds()));
    gfx_result res = svg_doc::read(&svg_stream, &doc);
    if (gfx_result::success != res) {
        Serial.printf("Error reading SVG: %d", (int)res);
    }
    test_svg.doc(&doc);
    main_screen.background_color(color16_t::white);
    main_screen.register_control(test_label);
    main_screen.register_control(test_svg);
    main_screen.on_flush_callback(uix_flush);
#ifdef LCD_TOUCH
    main_screen.on_touch_callback(uix_touch);
#endif // LCD_TOUCH
}

这个例程非常直接,它的工作方式与您曾经使用过 .NET 中的 WinForms 的 `InitializeComponent()` 例程很相似。它只是初始化每个控件的“属性”,然后最后,将每个控件注册到屏幕,然后设置主屏幕的回调。

在这种情况下,它还加载了要与 `svg_box_touch<>` 一起使用的 SVG 文档。

请注意,我们将标签的背景和边框颜色设置为透明色,我们通过获取现有颜色(`black`,但无所谓)并设置 Alpha 通道(`channel_name::A`)为零来创建它。

// set up the hardware
void setup() {
    Serial.begin(115200);
#ifdef I2C_PIN_NUM_SDA
    Wire.begin(I2C_PIN_NUM_SDA,I2C_PIN_NUM_SCL);
#endif // I2C_PIN_NUM_SDA
#ifdef EXTRA_INIT
EXTRA_INIT
#endif //EXTRA_INIT

    // RGB mode uses a slightly different call:
#ifdef LCD_PIN_NUM_HSYNC
    lcd_panel_init();
#else
    lcd_panel_init(sizeof(lcd_buffer1),lcd_flush_ready);
#endif // LCD_PIN_NUM_HSYNC
    screen_init();
#ifdef PIN_NUM_BUTTON_A
    button_a.initialize();
    button_a.on_pressed_changed(button_a_on_click);
#endif // PIN_NUM_BUTTON_A
#ifdef PIN_NUM_BUTTON_B
    button_b.initialize();
    button_b.on_pressed_changed(button_b_on_click);
#endif // PIN_NUM_BUTTON_B
}

我们已经完成了大量的初始化工作,所以 `setup()` 几乎没有什么要做,只需调用我们上面创建的内容即可。在这里,我们初始化主串行端口和 I2C 总线,调用我们之前的初始化例程,并且根据我们的设备,我们初始化按钮,并且还允许在 `EXTRA_INIT` 中定义任何额外的初始化代码。

// keep our stuff up to date and responsive
void loop() {
#ifdef PIN_NUM_BUTTON_A
    button_a.update();
#endif // PIN_NUM_BUTTON_A
#ifdef PIN_NUM_BUTTON_B
    button_b.update();
#endif // PIN_NUM_BUTTON_B
#if !defined(PIN_NUM_BUTTON_A) && \
    !defined(PIN_NUM_BUTTON_B) && \
    !defined(LCD_TOUCH)
    cycle_timer.update();
#endif // !defined(PIN_NUM_BUTTON_A) ...
    main_screen.update();
}

在 `loop()` 中,我们只是给相关的对象一个处理的机会。对于按钮,这会在按钮被单击时触发按下事件。对于计时器,这使其有机会在间隔时触发回调,对于屏幕,这使其有机会处理触摸事件并重新渲染屏幕的任何脏区域。

/lib 文件夹

在此文件夹下,有几个用于 ILI9341 和 ILI9488 显示控制器的驱动程序,允许它们与 ESP LCD 面板 API 一起使用。编写驱动程序有点复杂,但如果您以前做过,您可能能够胜任。您可以从现有的代码库中复制代码初始化代码,然后根据需要进行调整。除此之外,探索这些驱动程序超出了本文的范围。

lcd_config.h 和 lcd_init.h

这些文件是我 _lcd_init_ 包的一部分,在此处。这些文件可以在其他项目中重用,以初始化 ESP LCD 面板 API 并为 RGB 模式提供支持代码。

config.h

此文件设置任何非 LCD 相关的配置。

#ifndef CONFIG_H
#define CONFIG_H
#ifdef TTGO_T1
#define PIN_NUM_BUTTON_A 35
#define PIN_NUM_BUTTON_B 0
#include <button.hpp> 
using namespace arduino;
#endif  // TTGO_T1

#ifdef ESP_WROVER_KIT
#include <esp_lcd_panel_ili9341.h>
#endif // ESP_WROVER_KIT

#ifdef ESP_DISPLAY_S3
#define I2C_PIN_NUM_SDA 38
#define I2C_PIN_NUM_SCL 39
#define LCD_TOUCH ft6236<LCD_HRES, LCD_VRES>
#define LCD_ROTATION 1
#define EXTRA_INIT      \
    touch.initialize(); \
    touch.rotation(LCD_ROTATION);
#define LCD_TOUCH_IMPL                                                     \
    if (touch.update()) {                                                  \
        if (touch.xy(&out_locations[0].x, &out_locations[0].y)) {          \
            if (*in_out_locations_size > 1) {                              \
                *in_out_locations_size = 1;                                \
                if (touch.xy2(&out_locations[1].x, &out_locations[1].y)) { \
                    *in_out_locations_size = 2;                            \
                }                                                          \
            } else {                                                       \
                *in_out_locations_size = 1;                                \
            }                                                              \
        } else {                                                           \
            *in_out_locations_size = 0;                                    \
        }                                                                  \
    }
#include <esp_lcd_panel_ili9488.h>
#include <ft6236.hpp>
using namespace arduino;
#endif  // ESP_DISPLAY_S3

#ifdef ESP_DISPLAY_4INCH
#define I2C_PIN_NUM_SDA 17
#define I2C_PIN_NUM_SCL 18
#define LCD_TOUCH_PIN_NUM_RST 38
#define LCD_TOUCH gt911<LCD_TOUCH_PIN_NUM_RST>
#define LCD_TOUCH_IMPL                          \
    touch.update();                             \
    size_t touches = touch.locations_size();    \
    if (touches) {                              \
        if (touches > *in_out_locations_size) { \
            touches = *in_out_locations_size;   \
        }                                       \
        decltype(touch)::point pt[5];           \
        touch.locations(pt, &touches);          \
        for (uint8_t i = 0; i < touches; i++) { \
            out_locations[i].x = pt[i].x;       \
            out_locations[i].y = pt[i].y;       \
        }                                       \
    }                                           \
    *in_out_locations_size = touches;
#define EXTRA_INIT touch.initialize();
#include <gt911.hpp> 
using namespace arduino;
#endif // ESP_DISPLAY_4INCH

#ifdef M5STACK_CORE2
#define LCD_TOUCH ft6336<LCD_HRES, LCD_VRES, -1>
#define LCD_TOUCH_WIRE Wire1
#define EXTRA_DECLS m5core2_power power;
#define EXTRA_INIT power.initialize();
#include <esp_lcd_panel_ili9342.h>
#include <m5core2_power.hpp>
#include <ft6336.hpp> 
using namespace arduino;
#endif // M5STACK_CORE2

#ifdef M5STACK_FIRE
#define PIN_NUM_BUTTON_A 39
#define PIN_NUM_BUTTON_B 38
#define PIN_NUM_BUTTON_C 37
#include <esp_lcd_panel_ili9342.h>
#include <button.hpp>
using namespace arduino;
#endif // M5STACK_FIRE

#ifdef T_DISPLAY_S3
#define PIN_NUM_BUTTON_A 0
#define PIN_NUM_BUTTON_B 14
#define PIN_NUM_POWER 15
#define EXTRA_INIT                  \
    pinMode(PIN_NUM_POWER, OUTPUT); \
    digitalWrite(PIN_NUM_POWER, HIGH);
#include <button.hpp> 
using namespace arduino;
#endif // T_DISPLAY_S3

#endif  // CONFIG_H

此文件设置每个设备,包括设备特定的文件,设置触摸处理程序以返回任何触摸位置,管理任何电源选项等。

历史

  • 2023年3月3日 - 初次提交
  • 2023年3月20日 - 重构,支持更多设备和 RGB 模式
© . All rights reserved.