diff --git a/ESP32_LVGL/LVGL8/ZX2D10GE01R-V4848_Arduino/ZX2D10GE01R-V4848_Arduino.ino b/ESP32_LVGL/LVGL8/ZX2D10GE01R-V4848_Arduino/ZX2D10GE01R-V4848_Arduino.ino new file mode 100644 index 0000000..486ad68 --- /dev/null +++ b/ESP32_LVGL/LVGL8/ZX2D10GE01R-V4848_Arduino/ZX2D10GE01R-V4848_Arduino.ino @@ -0,0 +1,195 @@ +/* + * Project ZX2D10GE01R-V4848 for Arduino + * Description: The base project that can be used in the Arduino environment using the ZX2D10GE01R-V4848 device + * Author: Eric Nam + * Date: 03-18-2023 + */ + +// ** Prerequisites ** +// ESP32 Arduino 2.0.7 based on ESP-IDF 4.4.4 +// https://github.com/espressif/arduino-esp32 + +// LVGL version 8.3.5 +// https://github.com/lvgl/lvgl + +// GFX Library for Arduino 1.3.2 +// https://github.com/moononournation/Arduino_GFX + +// ZX2D10GE01R-V4848 for ESP-IDF +// https://github.com/wireless-tag-com/ZX2D10GE01R-V4848 + +#include +#include +#include "button.hpp" +#include "mt8901.hpp" + +#define ECO_O(y) (y > 0) ? -1 : 1 +#define ECO_STEP(x) x ? ECO_O(x) : 0 +#define GFX_BL 38 + +Arduino_DataBus *bus = new Arduino_SWSPI( + GFX_NOT_DEFINED /* DC */, 21 /* CS */, + 47 /* SCK */, 41 /* MOSI */, GFX_NOT_DEFINED /* MISO */); +Arduino_ESP32RGBPanel *rgbpanel = new Arduino_ESP32RGBPanel( + 39 /* DE */, 48 /* VSYNC */, 40 /* HSYNC */, 45 /* PCLK */, + 10 /* R0 */, 16 /* R1 */, 9 /* R2 */, 15 /* R3 */, 46 /* R4 */, + 8 /* G0 */, 13 /* G1 */, 18 /* G2 */, 12 /* G3 */, 11 /* G4 */, 17 /* G5 */, + 47 /* B0 */, 41 /* B1 */, 0 /* B2 */, 42 /* B3 */, 14 /* B4 */, + 1 /* hsync_polarity */, 10 /* hsync_front_porch */, 10 /* hsync_pulse_width */, 10 /* hsync_back_porch */, + 1 /* vsync_polarity */, 14 /* vsync_front_porch */, 2 /* vsync_pulse_width */, 12 /* vsync_back_porch */); +Arduino_RGB_Display *gfx = new Arduino_RGB_Display( + 480 /* width */, 480 /* height */, rgbpanel, 0 /* rotation */, true /* auto_flush */, + bus, GFX_NOT_DEFINED /* RST */, st7701_type7_init_operations, sizeof(st7701_type7_init_operations)); + +static button_t *g_btn; +static uint32_t screenWidth; +static uint32_t screenHeight; +static lv_disp_draw_buf_t draw_buf; +static lv_color_t *disp_draw_buf; +static lv_disp_drv_t disp_drv; +static lv_group_t *lv_group; +static lv_obj_t *lv_btn_1; +static lv_obj_t *lv_btn_2; + +/* Display flushing */ +void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) { + uint32_t w = (area->x2 - area->x1 + 1); + uint32_t h = (area->y2 - area->y1 + 1); + +#if (LV_COLOR_16_SWAP != 0) + gfx->draw16bitBeRGBBitmap(area->x1, area->y1, (uint16_t *)&color_p->full, w, h); +#else + gfx->draw16bitRGBBitmap(area->x1, area->y1, (uint16_t *)&color_p->full, w, h); +#endif + + lv_disp_flush_ready(disp); +} + +void encoder_read(lv_indev_drv_t *drv, lv_indev_data_t *data) { + static int16_t cont_last = 0; + int16_t cont_now = mt8901_get_count(); + data->enc_diff = ECO_STEP(cont_now - cont_last); + cont_last = cont_now; + if (button_isPressed(g_btn)) { + data->state = LV_INDEV_STATE_PR; + } else { + data->state = LV_INDEV_STATE_REL; + } +} + +void setup() { + Serial.begin(115200); + + gfx->begin(); + gfx->fillScreen(BLACK); + + pinMode(GFX_BL, OUTPUT); + digitalWrite(GFX_BL, HIGH); + + lv_init(); + + // Hardware Button + g_btn = button_attch(3, 0, 10); + + // Magnetic Encoder + mt8901_init(5, 6); + + screenWidth = gfx->width(); + screenHeight = gfx->height(); + + // Must to use PSRAM + disp_draw_buf = (lv_color_t *)heap_caps_malloc(sizeof(lv_color_t) * screenWidth * 32, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + + if (!disp_draw_buf) { + Serial.println("LVGL disp_draw_buf allocate failed!"); + } else { + lv_disp_draw_buf_init(&draw_buf, disp_draw_buf, NULL, screenWidth * 32); + + /* Initialize the display */ + lv_disp_drv_init(&disp_drv); + /* Change the following line to your display resolution */ + disp_drv.hor_res = screenWidth; + disp_drv.ver_res = screenHeight; + disp_drv.flush_cb = my_disp_flush; + disp_drv.draw_buf = &draw_buf; + lv_disp_drv_register(&disp_drv); + + /* Initialize the input device driver */ + static lv_indev_drv_t indev_drv; + lv_indev_drv_init(&indev_drv); + indev_drv.read_cb = encoder_read; + indev_drv.type = LV_INDEV_TYPE_ENCODER; + lv_indev_drv_register(&indev_drv); + + init_lv_group(); + lv_example_get_started_1(); + } +} + +void loop() { + lv_timer_handler(); /* let the GUI do its work */ + delay(5); +} + +void init_lv_group() { + lv_group = lv_group_create(); + lv_group_set_default(lv_group); + + lv_indev_t *cur_drv = NULL; + for (;;) { + cur_drv = lv_indev_get_next(cur_drv); + if (!cur_drv) { + break; + } + + if (cur_drv->driver->type == LV_INDEV_TYPE_ENCODER) { + lv_indev_set_group(cur_drv, lv_group); + } + } +} + +static void btn_event_cb(lv_event_t *e) { + lv_event_code_t code = lv_event_get_code(e); + lv_obj_t *btn = lv_event_get_target(e); + if (code == LV_EVENT_CLICKED) { + + uint8_t cnt = 0; + if (btn == lv_btn_1) { + static uint8_t cnt_1 = 0; + cnt_1++; + cnt = cnt_1; + } else { + static uint8_t cnt_2 = 0; + cnt_2++; + cnt = cnt_2; + } + /*Get the first child of the button which is the label and change its text*/ + lv_obj_t *label = lv_obj_get_child(btn, 0); + lv_label_set_text_fmt(label, "Button: %d", cnt); + } +} + +/** + * Create a button with a label and react on click event. + */ +void lv_example_get_started_1(void) { + lv_btn_1 = lv_btn_create(lv_scr_act()); /*Add a button the current screen*/ + lv_obj_set_size(lv_btn_1, 120, 50); /*Set its size*/ + lv_obj_align(lv_btn_1, LV_ALIGN_CENTER, 0, -40); /*Set its position*/ + lv_obj_add_event_cb(lv_btn_1, btn_event_cb, LV_EVENT_ALL, NULL); /*Assign a callback to the button*/ + + lv_obj_t *label = lv_label_create(lv_btn_1); /*Add a label to the button*/ + lv_label_set_text(label, "Button"); /*Set the labels text*/ + lv_obj_center(label); + + lv_btn_2 = lv_btn_create(lv_scr_act()); /*Add a button the current screen*/ + lv_obj_set_style_bg_color(lv_btn_2, lv_palette_main(LV_PALETTE_RED), LV_PART_MAIN); /*Set its main color*/ + + lv_obj_set_size(lv_btn_2, 120, 50); /*Set its size*/ + lv_obj_align(lv_btn_2, LV_ALIGN_CENTER, 0, 40); /*Set its position*/ + lv_obj_add_event_cb(lv_btn_2, btn_event_cb, LV_EVENT_ALL, NULL); /*Assign a callback to the button*/ + + lv_obj_t *label2 = lv_label_create(lv_btn_2); /*Add a label to the button*/ + lv_label_set_text(label2, "Button"); /*Set the labels text*/ + lv_obj_center(label2); +} diff --git a/ESP32_LVGL/LVGL8/ZX2D10GE01R-V4848_Arduino/button.cpp b/ESP32_LVGL/LVGL8/ZX2D10GE01R-V4848_Arduino/button.cpp new file mode 100755 index 0000000..9b02760 --- /dev/null +++ b/ESP32_LVGL/LVGL8/ZX2D10GE01R-V4848_Arduino/button.cpp @@ -0,0 +1,92 @@ +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" +#ifndef CONFIG_IDF_TARGET_ESP32C3 +#include "freertos/xtensa_api.h" +#endif +#include "freertos/portmacro.h" +#include "rom/gpio.h" +#include "driver/gpio.h" +#include "button.hpp" +#include "esp_log.h" + +static QueueHandle_t btn_queue = NULL; + +static void IRAM_ATTR gpio_isr_handler(void* arg) { + xQueueSendFromISR(btn_queue, &arg, NULL); +} + +static void button_update_task(void* arg) { + button_t* btn = NULL; + for (;;) { + xQueueReceive(btn_queue, &btn, portMAX_DELAY); + uint32_t time = xTaskGetTickCount(); + if (time - btn->last_press_time < btn->filter_time) { + continue; + } + if (time - btn->last_release_time < btn->filter_time) { + continue; + } + vTaskDelay(pdMS_TO_TICKS(btn->filter_time)); + if (gpio_get_level(gpio_num_t(btn->pin_io)) == btn->pressed_value) { + btn->pressed = 1; + btn->last_press_time = time; + } else { + btn->released = 1; + btn->last_release_time = time; + } + } +} + +uint8_t button_isPressed(button_t* button) { + return gpio_get_level(gpio_num_t(button->pin_io)) == button->pressed_value; +} + +uint8_t button_isRelease(button_t* button) { + return !(gpio_get_level(gpio_num_t(button->pin_io)) == button->pressed_value); +} + +uint8_t button_wasPressed(button_t* button) { + uint32_t time = xTaskGetTickCount(); + uint8_t pressed = button->pressed; + button->pressed = 0; + return pressed && ((time - button->last_press_time) < 300); +} + +uint8_t button_wasPressFor(button_t* button, uint32_t press_time) { + uint32_t time_now = xTaskGetTickCount(); + if (button->pressed && ((time_now - button->last_press_time) > press_time) && button->last_press_time > button->last_release_time) { + button->pressed = 0; + return 1; + } + return 0; +} + +uint8_t button_wasRelease(button_t* button) { + uint32_t time = xTaskGetTickCount(); + uint8_t released = button->released; + button->released = 0; + return released && ((time - button->last_release_time) < 300); +} + +button_t* button_attch(uint16_t gpio_pin, uint8_t press_value, uint16_t filter_time) { + if (btn_queue == NULL) { + btn_queue = xQueueCreate(10, sizeof(button_t*)); + xTaskCreatePinnedToCore(button_update_task, "btn", 2 * 1024, NULL, 4, NULL, 1); + gpio_install_isr_service(0); + } + + button_t* button = (button_t*)calloc(1, sizeof(button_t)); + button->pin_io = gpio_pin; + button->pressed_value = press_value; + button->filter_time = filter_time; + + gpio_pad_select_gpio(gpio_num_t(button->pin_io)); + gpio_set_direction(gpio_num_t(button->pin_io), GPIO_MODE_INPUT); + + gpio_set_intr_type(gpio_num_t(button->pin_io), GPIO_INTR_ANYEDGE); + gpio_isr_handler_add(gpio_num_t(button->pin_io), gpio_isr_handler, button); + + return button; +} diff --git a/ESP32_LVGL/LVGL8/ZX2D10GE01R-V4848_Arduino/button.hpp b/ESP32_LVGL/LVGL8/ZX2D10GE01R-V4848_Arduino/button.hpp new file mode 100755 index 0000000..502df87 --- /dev/null +++ b/ESP32_LVGL/LVGL8/ZX2D10GE01R-V4848_Arduino/button.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include "stdint.h" + +typedef struct { + uint32_t pin_io; + uint8_t pressed_value; + uint8_t pressed; + uint8_t released; + uint32_t last_press_time; + uint32_t last_release_time; + uint16_t filter_time; +} button_t; + +uint8_t button_isPressed(button_t* button); +uint8_t button_isRelease(button_t* button); +uint8_t button_wasPressed(button_t* button); +uint8_t button_wasRelease(button_t* button); +uint8_t button_wasPressFor(button_t* button, uint32_t press_time); +button_t* button_attch(uint16_t gpio_pin, uint8_t press_value, uint16_t filter_time); diff --git a/ESP32_LVGL/LVGL8/ZX2D10GE01R-V4848_Arduino/mt8901.cpp b/ESP32_LVGL/LVGL8/ZX2D10GE01R-V4848_Arduino/mt8901.cpp new file mode 100755 index 0000000..62d9eaf --- /dev/null +++ b/ESP32_LVGL/LVGL8/ZX2D10GE01R-V4848_Arduino/mt8901.cpp @@ -0,0 +1,44 @@ +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "driver/pcnt.h" + +#define PCNT_H_LIM_VAL INT16_MAX +#define PCNT_L_LIM_VAL INT16_MIN + +static pcnt_unit_t unit = PCNT_UNIT_0; + +void mt8901_init(int16_t sig_pin, int16_t dir_pin) { + /* Prepare configuration for the PCNT unit */ + pcnt_config_t pcnt_config; + pcnt_config.pulse_gpio_num = sig_pin; + pcnt_config.ctrl_gpio_num = dir_pin; + pcnt_config.channel = PCNT_CHANNEL_0; + pcnt_config.unit = unit; + pcnt_config.pos_mode = PCNT_COUNT_DEC; + pcnt_config.neg_mode = PCNT_COUNT_INC; + pcnt_config.lctrl_mode = PCNT_MODE_REVERSE; + pcnt_config.hctrl_mode = PCNT_MODE_KEEP; + pcnt_config.counter_h_lim = PCNT_H_LIM_VAL; + pcnt_config.counter_l_lim = PCNT_L_LIM_VAL; + + /* Initialize PCNT unit */ + pcnt_unit_config(&pcnt_config); + + /* Configure and enable the input filter */ + pcnt_set_filter_value(unit, 1000); + pcnt_filter_enable(unit); + + /* Initialize PCNT's counter */ + pcnt_counter_pause(unit); + pcnt_counter_clear(unit); + + /* Everything is set up, now go to counting */ + pcnt_counter_resume(unit); +} + +int16_t mt8901_get_count() { + int16_t count = 0; + pcnt_get_counter_value(unit, &count); + return count; +} diff --git a/ESP32_LVGL/LVGL8/ZX2D10GE01R-V4848_Arduino/mt8901.hpp b/ESP32_LVGL/LVGL8/ZX2D10GE01R-V4848_Arduino/mt8901.hpp new file mode 100755 index 0000000..c37f90c --- /dev/null +++ b/ESP32_LVGL/LVGL8/ZX2D10GE01R-V4848_Arduino/mt8901.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include "stdint.h" + +void mt8901_init(int16_t sig_pin, int16_t dir_pin); + +int16_t mt8901_get_count();