LoginSignup
0
0

LILYGO T-RGB Rotary touch interface

Posted at

0. はじめに

前回、「LILYGO T-RGB」を紹介しました。
このデバイスはタッチセンサ付きの円形デバイスのため、当然、クルクル廻したくなります。そこで、今回は、ロータリーエンコーダのような出力が得られる「Rotary touch interface」を作った記事です。

1. 応用例

↓このような操作ができるようになります。

ダブルタップで正位に戻すようにしています。

2. ソースコード

まずはArduino IDEで使うため、今回はC言語で。

以下のコードをファイル名RotaryIF.hで保存し、スケッチ(*.ino)でこのファイルをincludeする。

RotaryIF.h
#pragma once

#define CANVAS_WIDTH    480
#define CANVAS_HEIGHT   480
const float pi = 3.141592653589793;
const float countPerAround = 60; //quickly:60, normal:24, slowly:6
int count = 0;
enum Direction {
    counterclockwise = -1,
    clockwise = 1,
    idle = 0,
} direction = idle;
float _locationOffset;
float _currentLocation;
float _previousLocation;
float _lap;

float _degrees(touch_point_t point) {
    float r = atan2(point.y - CANVAS_HEIGHT / 2, point.x - CANVAS_WIDTH / 2);
    r += pi / 2;
    if (r < 0) r += 2 * pi;
    r /= (2 * pi);
    return r;
}
void reset_location() {
  count = 0;
  direction = idle;
  _currentLocation = 0;
  _previousLocation = 0;
  _lap = 0;
  _locationOffset = 0;
}
void check_location(touch_point_t location, bool begin=false) {
    if (!location.x && !location.y) {
        direction = idle;
        _currentLocation -= _locationOffset;
        _locationOffset = 0;
        return;
    }

    float newLocation = _degrees(location);

    float diff = newLocation - _currentLocation;
    if (begin) _locationOffset = diff;

    float diff2 = _currentLocation - _previousLocation;
    if (abs(diff) < 0.001) return;
    
    _previousLocation = _currentLocation;
    _currentLocation = newLocation;
    float needleLocation = _currentLocation - _locationOffset;

    int lastCount = count;
    
    if (abs(diff) > 0.5) {
        if (diff2 > 0 && direction == clockwise) _lap += 1;
        if (diff2 < 0 && direction == counterclockwise) _lap -= 1;
        float d = floor(countPerAround * needleLocation);
        count = d + (countPerAround * _lap);
    } else {
        if (diff > 0) direction = clockwise;
        if (diff < 0) direction = counterclockwise;
        float d = floor(countPerAround * needleLocation);
        count = d + (countPerAround * _lap);
    }

    if (lastCount != count) { /* send event */ }
}

アンダーライン_で始まる変数と関数はprivate。
publicな変数と関数は以下の通り。(次回はクラス化します)

# 変数名/関数名 I/O 内容
1 countPerAround I 一周分のカウント数(大きいとクイック、小さいとゆっくりな操作感となる)
(ロータリーエンコーダでいうところのクリック数)
2 count O 回転操作から得られるカウント数
時計回りだとカウントアップ、反時計回りだとカウントダウンされる。反時計回りを繰り返すと、マイナス数値となる
3 direction O 回転している方向。
 時計回り:clockwise(1)、
 反時計回り:counterclockwise(-1)。
 指を離す:idle(0)
4 check_location() - タッチイベント検知したときに、この関数を呼び出す必要がある
5 reset_location() - 保持しているタッチ情報のリセット。強制的に移動したときは、この関数を呼び出す必要がある

reset_location()は引数無し。
check_location()の引数の意味は以下の通り。

# 引数 I/O 内容
1 location I touch_point_t タッチイベントを検知したときのタッチ座標。
指が離れたときは{0, 0}で呼び出すこと。
2 begin I bool 最初のタッチイベントの場合はtrue。
指が離れずタッチ座標が変化している状態のときは、falseで呼び出す。
 touchDown : true、
 touchMoveとtouchUp : false

touch_point_t型は、examples/factory/factory_ui.hに定義されている(include必須)。

処理コード及び使い方の説明は以上。実際の使用例のコードは、次に示すように簡単です。

3. 使用例

#include "factory_ui.h"
#include "RotaryIF.h"

void touch_read(touch_point_t p) {

//タッチ操作で動作する処理の最後に、以下のコードを追加する
//タッチされた座標が、touch_point_t型の変数pにセットされているとする

    static bool began = true;
    static touch_point_t last_point = {0xffff, 0xffff};
    if (last_point.x == p.x && last_point.y == p.y) return;

    last_point = p;
    check_location(p, began); //タッチイベントを通知
    began = (!p.x && !p.y);

    Serial.printf("touch_point: (%d, %d), dir: %d, count: %d\r\n", p.x, p.y, direction, count);

    // イメージを回転
    int wkcount = count;
    while (wkcount < 0) wkcount += countPerAround;
    float angle = 3600.0 * (float)wkcount / countPerAround;
    lv_img_set_angle(img, (int16_t)angle);
}

4. 完全なプログラム

公式サンプルプロジェクトexamples/factoryをコピーし、factory.inoの中身を以下のコードにそっくり置き換えると、冒頭のロゴイメージを回転させるプログラムが完成します。
なお、LOGOファイルはexamples/esp-idf-example/T-RGB/main/logo.cからコピーする必要があるで忘れずに。

下記の257行〜285行目に、上記使用例に示したコード(および、ダブルタップで正位に戻すコード)が埋め込まれている。

factory.ino全体(600行弱)
factory.ino
#include "Wire.h"
#include "XL9535_driver.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_ops.h"
#include "esp_lcd_panel_rgb.h"
#include "esp_lcd_panel_vendor.h"
#include "factory_ui.h"
#include "img.h"
#include "lvgl.h"
#include "pin_config.h"
#include <Arduino.h>
#include "RotaryIF.h"

#define USING_2_1_INC_CST820     1           //  Full circle 2.1 inches using CST820 touch screen
// #define USING_2_8_INC_GT911      1           //  Full circle 2.8 inches using GT911 touch screen
// #define USING_2_1_INC_FT3267     1           //  Half circle 2.1 inches use FT3267 touch screen


#if defined(USING_2_1_INC_FT3267)
#include "ft3267.h"
#endif
#if defined(USING_2_1_INC_CST820)
#define TOUCH_MODULES_CST_SELF
#include "TouchLib.h"
TouchLib touch(Wire, IIC_SDA_PIN, IIC_SCL_PIN, CTS820_SLAVE_ADDRESS);
#elif defined(USING_2_8_INC_GT911)
#define TOUCH_MODULES_GT911
#include "TouchLib.h"
TouchLib touch(Wire, IIC_SDA_PIN, IIC_SCL_PIN, GT911_SLAVE_ADDRESS1);
#endif



#if !defined(USING_2_1_INC_CST820) && !defined(USING_2_8_INC_GT911) && !defined(USING_2_1_INC_FT3267)
#error "Please define the size of the screen and open the macro definition at the top of the sketch"
#endif

typedef struct {
    uint8_t cmd;
    uint8_t data[16];
    uint8_t databytes; // No of data in data; bit 7 = delay after set; 0xFF = end of cmds.
} lcd_init_cmd_t;

#if defined(USING_2_1_INC_CST820) || defined(USING_2_1_INC_FT3267)
DRAM_ATTR static const lcd_init_cmd_t st_init_cmds[] = {
    {0xFF, {0x77, 0x01, 0x00, 0x00, 0x10}, 0x05},
    {0xC0, {0x3b, 0x00}, 0x02},
    {0xC1, {0x0b, 0x02}, 0x02},
    {0xC2, {0x07, 0x02}, 0x02},
    {0xCC, {0x10}, 0x01},
    {0xCD, {0x08}, 0x01}, // 用565时屏蔽    666打开
    {0xb0, {0x00, 0x11, 0x16, 0x0e, 0x11, 0x06, 0x05, 0x09, 0x08, 0x21, 0x06, 0x13, 0x10, 0x29, 0x31, 0x18}, 0x10},
    {0xb1, {0x00, 0x11, 0x16, 0x0e, 0x11, 0x07, 0x05, 0x09, 0x09, 0x21, 0x05, 0x13, 0x11, 0x2a, 0x31, 0x18}, 0x10},
    {0xFF, {0x77, 0x01, 0x00, 0x00, 0x11}, 0x05},
    {0xb0, {0x6d}, 0x01},
    {0xb1, {0x37}, 0x01},
    {0xb2, {0x81}, 0x01},
    {0xb3, {0x80}, 0x01},
    {0xb5, {0x43}, 0x01},
    {0xb7, {0x85}, 0x01},
    {0xb8, {0x20}, 0x01},
    {0xc1, {0x78}, 0x01},
    {0xc2, {0x78}, 0x01},
    {0xc3, {0x8c}, 0x01},
    {0xd0, {0x88}, 0x01},
    {0xe0, {0x00, 0x00, 0x02}, 0x03},
    {0xe1, {0x03, 0xa0, 0x00, 0x00, 0x04, 0xa0, 0x00, 0x00, 0x00, 0x20, 0x20}, 0x0b},
    {0xe2, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 0x0d},
    {0xe3, {0x00, 0x00, 0x11, 0x00}, 0x04},
    {0xe4, {0x22, 0x00}, 0x02},
    {0xe5, {0x05, 0xec, 0xa0, 0xa0, 0x07, 0xee, 0xa0, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 0x10},
    {0xe6, {0x00, 0x00, 0x11, 0x00}, 0x04},
    {0xe7, {0x22, 0x00}, 0x02},
    {0xe8, {0x06, 0xed, 0xa0, 0xa0, 0x08, 0xef, 0xa0, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 0x10},
    {0xeb, {0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00}, 0x07},
    {0xed, {0xff, 0xff, 0xff, 0xba, 0x0a, 0xbf, 0x45, 0xff, 0xff, 0x54, 0xfb, 0xa0, 0xab, 0xff, 0xff, 0xff}, 0x10},
    {0xef, {0x10, 0x0d, 0x04, 0x08, 0x3f, 0x1f}, 0x06},
    {0xFF, {0x77, 0x01, 0x00, 0x00, 0x13}, 0x05},
    {0xef, {0x08}, 0x01},
    {0xFF, {0x77, 0x01, 0x00, 0x00, 0x00}, 0x05},
    {0x36, {0x08}, 0x01},
    {0x3a, {0x66}, 0x01},
    {0x11, {0x00}, 0x80},
    // {0xFF, {0x77, 0x01, 0x00, 0x00, 0x12}, 0x05},
    // {0xd1, {0x81}, 0x01},
    // {0xd2, {0x06}, 0x01},
    {0x29, {0x00}, 0x80},
    {0, {0}, 0xff}
};
#elif defined(USING_2_8_INC_GT911)
DRAM_ATTR static const lcd_init_cmd_t st_init_cmds[] = {
    {0xFF, {0x77, 0x01, 0x00, 0x00, 0x13}, 0x05},
    {0xEF, {0x08}, 0x01},
    {0xFF, {0x77, 0x01, 0x00, 0x00, 0x10}, 0x05},
    {0xC0, {0x3B, 0X00}, 0x02},
    {0xC1, {0x10, 0x0C}, 0x02},
    {0xC2, {0x07, 0x0A}, 0x02},
    {0xC7, {0x00}, 0x01},
    {0xCC, {0x10}, 0x01},
    {0xCD, {0x08}, 0x01}, // 用565时屏蔽    666打开
    {0xb0, {0x05, 0x12, 0x98, 0x0e, 0x0F, 0x07, 0x07, 0x09, 0x09, 0x23, 0x05, 0x52, 0x0F, 0x67, 0x2C, 0x11}, 0x10},
    {0xb1, {0x0B, 0x11, 0x97, 0x0C, 0x12, 0x06, 0x06, 0x08, 0x08, 0x22, 0x03, 0x51, 0x11, 0x66, 0x2B, 0x0F}, 0x10},
    {0xFF, {0x77, 0x01, 0x00, 0x00, 0x11}, 0x05},
    {0xb0, {0x5d}, 0x01},
    {0xb1, {0x2D}, 0x01},
    {0xb2, {0x81}, 0x01},
    {0xb3, {0x80}, 0x01},
    {0xb5, {0x4E}, 0x01},
    {0xb7, {0x85}, 0x01},
    {0xb8, {0x20}, 0x01},
    {0xc1, {0x78}, 0x01},
    {0xc2, {0x78}, 0x01},
    // {0xc3, {0x8c}, 0x01},
    {0xd0, {0x88}, 0x01},
    {0xe0, {0x00, 0x00, 0x02}, 0x03},
    {0xe1, {0x06, 0x30, 0x08, 0x30, 0x05, 0x30, 0x07, 0x30, 0x00, 0x33, 0x33}, 0x0b},
    {0xe2, {0x11, 0x11, 0x33, 0x33, 0xf4, 0x00, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00}, 0x0c},
    {0xe3, {0x00, 0x00, 0x11, 0x11}, 0x04},
    {0xe4, {0x44, 0x44}, 0x02},
    {0xe5, {0x0d, 0xf5, 0x30, 0xf0, 0x0f, 0xf7, 0x30, 0xf0, 0x09, 0xf1, 0x30, 0xf0, 0x0b, 0xf3, 0x30, 0xf0}, 0x10},
    {0xe6, {0x00, 0x00, 0x11, 0x11}, 0x04},
    {0xe7, {0x44, 0x44}, 0x02},
    {0xe8, {0x0c, 0xf4, 0x30, 0xf0, 0x0e, 0xf6, 0x30, 0xf0, 0x08, 0xf0, 0x30, 0xf0, 0x0a, 0xf2, 0x30, 0xf0}, 0x10},
    {0xe9, {0x36}, 0x01},
    {0xeb, {0x00, 0x01, 0xe4, 0xe4, 0x44, 0x88, 0x40}, 0x07},
    {0xed, {0xff, 0x10, 0xaf, 0x76, 0x54, 0x2b, 0xcf, 0xff, 0xff, 0xfc, 0xb2, 0x45, 0x67, 0xfa, 0x01, 0xff}, 0x10},
    {0xef, {0x08, 0x08, 0x08, 0x45, 0x3f, 0x54}, 0x06},
    {0xFF, {0x77, 0x01, 0x00, 0x00, 0x00}, 0x05},

    {0x11, {0x00}, 0x80},
    {0x3a, {0x66}, 0x01},
    {0x36, {0x08}, 0x01},
    {0x35, {0x00}, 0x01},
    {0x29, {0x00}, 0x80},
    {0, {0}, 0xff}
};
#endif


XL9535 xl;

const int backlightPin = EXAMPLE_PIN_NUM_BK_LIGHT;
bool click = false;
TaskHandle_t pvCreatedTask;


void tft_init(void);
void lcd_cmd(const uint8_t cmd);
void lcd_data(const uint8_t *data, int len);
bool touchDevicesOnline = false;
uint8_t touchAddress = 0;

lv_obj_t *img;

const char *getTouchAddr()
{
    if (touchAddress == FT5x06_ADDR) {
        return "FT3267";
    } else if (touchAddress == CST820_ADDR) {
        return "CST820";
    } else if (touchAddress == GT911_ADDR) {
        return "GT911";
    }
#ifdef USING_2_1_INC_CST820
    return "CST820";
#else
    return "UNKONW";
#endif
}

void scanDevices(void)
{
    byte error, address;
    int nDevices = 0;
    Serial.println("Scanning for I2C devices ...");
    for (address = 0x01; address < 0x7f; address++) {
        Wire.beginTransmission(address);
        error = Wire.endTransmission();
        if (error == 0) {
            Serial.printf("I2C device found at address 0x%02X\r\n", address);
            if (address == FT5x06_ADDR) {
                Serial.println("Find FT5X06 touch device!"); touchDevicesOnline = true; touchAddress = FT5x06_ADDR;
            } else if (address == CST820_ADDR) {
                Serial.println("Find CST820 touch device!"); touchDevicesOnline = true; touchAddress = CST820_ADDR;
            } else if (address == GT911_ADDR) {
                Serial.println("Find GT911 touch device!"); touchDevicesOnline = true; touchAddress = GT911_ADDR;
            }
            nDevices++;
        } else if (error != 2) {
            Serial.printf("Error %d at address 0x%02X\r\n", error, address);
        }
    }
    if (nDevices == 0) {
        Serial.println("No I2C devices found");
    }
}

void print_chip_info(void)
{
    Serial.print("Chip: ");
    Serial.println(ESP.getChipModel());
    Serial.print("ChipRevision: ");
    Serial.println(ESP.getChipRevision());
    Serial.print("Psram size: ");
    Serial.print(ESP.getPsramSize() / 1024);
    Serial.println("KB");
    Serial.print("Flash size: ");
    Serial.print(ESP.getFlashChipSize() / 1024);
    Serial.println("KB");
    Serial.print("CPU frequency: ");
    Serial.print(ESP.getCpuFreqMHz());
    Serial.println("MHz");
}

static void example_lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
{
    esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)drv->user_data;
    int offsetx1 = area->x1;
    int offsetx2 = area->x2;
    int offsety1 = area->y1;
    int offsety2 = area->y2;
    esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map);
    lv_disp_flush_ready(drv);
}

static void lv_touchpad_read(lv_indev_drv_t *indev_driver, lv_indev_data_t *data)
{

    static uint16_t lastX, lastY;
    touch_point_t p = {0};
#if defined(USING_2_1_INC_FT3267)

    uint8_t touch_points_num;
    ft3267_read_pos(&touch_points_num, &p.x, &p.y);
    data->point.x = p.x;
    data->point.y = p.y;
    if (p.x != lastX  || p.y != lastY) {
        lastX = p.x;
        lastY = p.y;
        data->state = LV_INDEV_STATE_PR;
    } else {
        data->state = LV_INDEV_STATE_REL;
    }

#elif defined(USING_2_1_INC_CST820) || defined(USING_2_8_INC_GT911)
    if (touch.read()) {
        TP_Point t = touch.getPoint(0);
        data->point.x = p.x = t.x;
        data->point.y = p.y = t.y;
        data->state = LV_INDEV_STATE_PR;
    } else {
        data->state = LV_INDEV_STATE_REL;
    }
#endif

    //================================================vvv
    static uint32_t last_touch_end_millis = 0;
    static bool began = true;
    static touch_point_t last_point = {0xffff, 0xffff};
    if (last_point.x == p.x && last_point.y == p.y) return;

    last_point = p;
    check_location(p, began);
    began = (!p.x && !p.y);

    Serial.printf("touch_point: (%d, %d), dir: %d. count: %d\r\n", p.x, p.y, direction, count);

    if (!p.x && !p.y) {
        uint32_t last = last_touch_end_millis;
        uint32_t now_millis = millis();
        last_touch_end_millis = now_millis;
        //for double taps less than 300 ms
        if (now_millis - last <= 300) {
            lv_img_set_angle(img, 0); //reset angle 
            reset_location(); //reset
            return;
        }
    }

    // rotate image
    int wkcount = count;
    while (wkcount < 0) wkcount += countPerAround;
    float angle = 3600.0 * (float)wkcount / countPerAround;
    lv_img_set_angle(img, (int16_t)angle);
    //================================================^^^
}

void waitInterruptReady()
{
    Serial.println("waitInterruptReady ...");

    uint32_t timeout = millis() + 500;

// The touch interrupt of CST820 is a high and low pulse, it is not fixed, there will be a low level every ~10 milliseconds interval
#ifdef USING_2_1_INC_CST820
    while (timeout > millis()) {
        while (!digitalRead(TP_INT_PIN)) {
            delay(20);
            timeout = millis() + 500;
        }
    }
#else
    //Wait for the GT911 interrupt signal to be ready
    while (!digitalRead(TP_INT_PIN)) {
#if defined(USING_2_8_INC_GT911)
        if (timeout <  millis()) {
            Serial.println("timeout !");
            esp_restart();
        }
        touch.read();
#endif
        delay(10);
    }

#endif

}

// LilyGo  T-RGB  control backlight chip has 16 levels of adjustment range
// The adjustable range is 0~15, 0 is the minimum brightness, 15 is the maximum brightness
void setBrightness(uint8_t value)
{
    static uint8_t level = 0;
    static uint8_t steps = 16;
    if (value == 0) {
        digitalWrite(backlightPin, 0);
        delay(3);
        level = 0;
        return;
    }
    if (level == 0) {
        digitalWrite(backlightPin, 1);
        level = steps;
        delayMicroseconds(30);
    }
    int from = steps - level;
    int to = steps - value;
    int num = (steps + to - from) % steps;
    for (int i = 0; i < num; i++) {
        digitalWrite(backlightPin, 0);
        digitalWrite(backlightPin, 1);
    }
    level = value;
}

void setup()
{
    static lv_disp_draw_buf_t disp_buf; // contains internal graphic buffer(s) called draw buffer(s)
    static lv_disp_drv_t disp_drv;      // contains callback functions
    static lv_indev_drv_t indev_drv;

    Serial.begin(115200);

    // put your setup code here, to run once:
    pinMode(BAT_VOLT_PIN, ANALOG);

    Wire.begin(IIC_SDA_PIN, IIC_SCL_PIN);

    xl.begin();

    uint8_t pin = (1 << PWR_EN_PIN) | (1 << LCD_CS_PIN) | (1 << TP_RES_PIN) | (1 << LCD_SDA_PIN) | (1 << LCD_CLK_PIN) |
                  (1 << LCD_RST_PIN) | (1 << SD_CS_PIN);

    xl.pinMode8(0, pin, OUTPUT);
    xl.digitalWrite(PWR_EN_PIN, HIGH);

    print_chip_info();

#ifdef USING_2_8_INC_GT911
    //Reset GT911
    xl.pinMode(TP_RES_PIN, OUTPUT);
    pinMode(TP_INT_PIN, OUTPUT);
    digitalWrite(TP_INT_PIN, LOW);
    xl.digitalWrite(TP_RES_PIN, LOW);
    delayMicroseconds(120);
    xl.digitalWrite(TP_RES_PIN, HIGH);
    delay(8);
    pinMode(TP_INT_PIN, INPUT);

    waitInterruptReady();
#else
    delay(100);
    xl.digitalWrite(TP_RES_PIN, LOW);
    delay(300);
    xl.digitalWrite(TP_RES_PIN, HIGH);
    delay(300);
    pinMode(TP_INT_PIN, INPUT);
#endif

    // Scanning I2C cannot get the device address of CST820, it is a non-standard I2C device
    scanDevices();

#if defined(USING_2_1_INC_FT3267)
    ft3267_init(Wire);
#elif defined(USING_2_8_INC_GT911) || defined(USING_2_1_INC_CST820)
    touch.init();
#endif

    tft_init();
    esp_lcd_panel_handle_t panel_handle = NULL;
    esp_lcd_rgb_panel_config_t panel_config = {
        .clk_src = LCD_CLK_SRC_PLL160M,
        .timings =
        {
            .pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
            .h_res = EXAMPLE_LCD_H_RES,
            .v_res = EXAMPLE_LCD_V_RES,
            // The following parameters should refer to LCD spec
            .hsync_pulse_width = 1,
            .hsync_back_porch = 30,
            .hsync_front_porch = 50,
            .vsync_pulse_width = 1,
            .vsync_back_porch = 30,
            .vsync_front_porch = 20,
            .flags =
            {
                .pclk_active_neg = 1,
            },
        },
        .data_width = 16, // RGB565 in parallel mode, thus 16bit in width
        .psram_trans_align = 64,
        .hsync_gpio_num = EXAMPLE_PIN_NUM_HSYNC,
        .vsync_gpio_num = EXAMPLE_PIN_NUM_VSYNC,
        .de_gpio_num = EXAMPLE_PIN_NUM_DE,
        .pclk_gpio_num = EXAMPLE_PIN_NUM_PCLK,
        .data_gpio_nums =
        {
            // EXAMPLE_PIN_NUM_DATA0,
            EXAMPLE_PIN_NUM_DATA13,
            EXAMPLE_PIN_NUM_DATA14,
            EXAMPLE_PIN_NUM_DATA15,
            EXAMPLE_PIN_NUM_DATA16,
            EXAMPLE_PIN_NUM_DATA17,

            EXAMPLE_PIN_NUM_DATA6,
            EXAMPLE_PIN_NUM_DATA7,
            EXAMPLE_PIN_NUM_DATA8,
            EXAMPLE_PIN_NUM_DATA9,
            EXAMPLE_PIN_NUM_DATA10,
            EXAMPLE_PIN_NUM_DATA11,
            // EXAMPLE_PIN_NUM_DATA12,

            EXAMPLE_PIN_NUM_DATA1,
            EXAMPLE_PIN_NUM_DATA2,
            EXAMPLE_PIN_NUM_DATA3,
            EXAMPLE_PIN_NUM_DATA4,
            EXAMPLE_PIN_NUM_DATA5,
        },
        .disp_gpio_num = EXAMPLE_PIN_NUM_DISP_EN,
        .on_frame_trans_done = NULL,
        .user_ctx = NULL,
        .flags =
        {
            .fb_in_psram = 1, // allocate frame buffer in PSRAM
        },
    };
    ESP_ERROR_CHECK(esp_lcd_new_rgb_panel(&panel_config, &panel_handle));
    ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
    ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));


    lv_init();
    // alloc draw buffers used by LVGL from PSRAM
    lv_color_t *buf1 = (lv_color_t *)ps_malloc(EXAMPLE_LCD_H_RES * EXAMPLE_LCD_V_RES * sizeof(lv_color_t));
    assert(buf1);
    lv_color_t *buf2 = (lv_color_t *)ps_malloc(EXAMPLE_LCD_H_RES * EXAMPLE_LCD_V_RES * sizeof(lv_color_t));
    assert(buf2);
    lv_disp_draw_buf_init(&disp_buf, buf1, buf2, EXAMPLE_LCD_H_RES * EXAMPLE_LCD_V_RES);

    Serial.println("Register display driver to LVGL");
    lv_disp_drv_init(&disp_drv);
    disp_drv.hor_res = EXAMPLE_LCD_H_RES;
    disp_drv.ver_res = EXAMPLE_LCD_V_RES;
    disp_drv.flush_cb = example_lvgl_flush_cb;
    disp_drv.draw_buf = &disp_buf;
    disp_drv.user_data = panel_handle;
    lv_disp_t *disp = lv_disp_drv_register(&disp_drv);

    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_POINTER;
    indev_drv.read_cb = lv_touchpad_read;
    lv_indev_drv_register(&indev_drv);



    //Test screen color
    LV_IMG_DECLARE(logo);

    img = lv_img_create(lv_scr_act());
    lv_img_set_src(img, &logo);
    lv_img_set_zoom(img, 204); //80%
    lv_obj_set_size(img, CANVAS_WIDTH, CANVAS_HEIGHT);
    lv_obj_center(img);

    //Wait for the GT911 interrupt signal to be ready
    waitInterruptReady();

    lv_task_handler();


    pinMode(backlightPin, OUTPUT);
    //LilyGo T-RGB control backlight chip has 16 levels of adjustment range
    for (int i = 0; i < 16; ++i) {
        setBrightness(i);
        delay(30);
    }

}

bool lastStatus = false;

void loop()
{
    // put your main code here, to run repeatedly:
    delay(2);
    lv_timer_handler();

#ifndef USING_2_1_INC_CST820
    bool touched = digitalRead(TP_INT_PIN) == LOW;
    if (touched) {
        lastStatus = touched;
        lv_msg_send(MSG_TOUCH_INT_UPDATE, &touched);
    } else if (!touched && lastStatus) {
        lastStatus = false;
        lv_msg_send(MSG_TOUCH_INT_UPDATE, &touched);
    }
#endif
}

void lcd_send_data(uint8_t data)
{
    uint8_t n;
    for (n = 0; n < 8; n++) {
        if (data & 0x80)
            xl.digitalWrite(LCD_SDA_PIN, 1);
        else
            xl.digitalWrite(LCD_SDA_PIN, 0);

        data <<= 1;
        xl.digitalWrite(LCD_CLK_PIN, 0);
        xl.digitalWrite(LCD_CLK_PIN, 1);
    }
}

void lcd_cmd(const uint8_t cmd)
{
    xl.digitalWrite(LCD_CS_PIN, 0);
    xl.digitalWrite(LCD_SDA_PIN, 0);
    xl.digitalWrite(LCD_CLK_PIN, 0);
    xl.digitalWrite(LCD_CLK_PIN, 1);
    lcd_send_data(cmd);
    xl.digitalWrite(LCD_CS_PIN, 1);
}

void lcd_data(const uint8_t *data, int len)
{
    uint32_t i = 0;
    if (len == 0)
        return; // no need to send anything
    do {
        xl.digitalWrite(LCD_CS_PIN, 0);
        xl.digitalWrite(LCD_SDA_PIN, 1);
        xl.digitalWrite(LCD_CLK_PIN, 0);
        xl.digitalWrite(LCD_CLK_PIN, 1);
        lcd_send_data(*(data + i));
        xl.digitalWrite(LCD_CS_PIN, 1);
        i++;
    } while (len--);
}

void tft_init(void)
{
    xl.digitalWrite(LCD_CS_PIN, 1);
    xl.digitalWrite(LCD_SDA_PIN, 1);
    xl.digitalWrite(LCD_CLK_PIN, 1);

    // Reset the display
    xl.digitalWrite(LCD_RST_PIN, 1);
    vTaskDelay(200 / portTICK_PERIOD_MS);
    xl.digitalWrite(LCD_RST_PIN, 0);
    vTaskDelay(200 / portTICK_PERIOD_MS);
    xl.digitalWrite(LCD_RST_PIN, 1);
    vTaskDelay(200 / portTICK_PERIOD_MS);
    int cmd = 0;
    while (st_init_cmds[cmd].databytes != 0xff) {
        lcd_cmd(st_init_cmds[cmd].cmd);
        lcd_data(st_init_cmds[cmd].data, st_init_cmds[cmd].databytes & 0x1F);
        if (st_init_cmds[cmd].databytes & 0x80) {
            vTaskDelay(100 / portTICK_PERIOD_MS);
        }
        cmd++;
    }
    Serial.println("Register setup complete");
}

5. おわりに

いかがでしょうか。
タッチパネル付きのLCDは、いろいろ応用ができそうです。
今回は、C言語(Arduino)でしたが、Micropython向けのコードを作成したら、いずれ追加します。

以上


予告で使用した以下の動画は、Rotary touch interface の情報をLCD上で確認できます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0