2024-11-30 21:08:22 +00:00
|
|
|
#include <inttypes.h>
|
2024-12-03 22:55:46 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/lock.h>
|
2024-11-30 21:08:22 +00:00
|
|
|
|
|
|
|
#include "sdkconfig.h"
|
|
|
|
#include "freertos/FreeRTOS.h"
|
|
|
|
#include "freertos/task.h"
|
2024-12-03 22:55:46 +00:00
|
|
|
|
|
|
|
#include "esp_err.h"
|
|
|
|
#include "esp_log.h"
|
|
|
|
#include "esp_timer.h"
|
|
|
|
#include "esp_lcd_panel_ops.h"
|
|
|
|
#include "esp_lcd_panel_rgb.h"
|
|
|
|
#include "driver/i2c_master.h"
|
|
|
|
|
|
|
|
#include "lvgl.h"
|
|
|
|
|
|
|
|
#define LCD_HRES 800
|
|
|
|
#define LCD_VRES 480
|
|
|
|
#define LCD_PIXEL_SIZE 2
|
|
|
|
|
|
|
|
// in ms
|
|
|
|
#define LVGL_TICK_PERIOD 2
|
|
|
|
#define LVGL_TASK_PRIORITY 2
|
|
|
|
#define LVGL_TASK_STACK_SIZE (4 * 1024)
|
|
|
|
|
|
|
|
static const char *TAG = "valconomy";
|
|
|
|
|
|
|
|
i2c_master_bus_handle_t i2c_bus_handle;
|
|
|
|
|
|
|
|
// LVGL library is not thread-safe, this example will call LVGL APIs from different tasks, so use a mutex to protect it
|
|
|
|
static _lock_t lvgl_api_lock;
|
|
|
|
|
|
|
|
extern void example_lvgl_demo_ui(lv_display_t *disp);
|
|
|
|
|
|
|
|
static bool example_on_vsync(esp_lcd_panel_handle_t panel, const esp_lcd_rgb_panel_event_data_t *event_data, void *user_ctx) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void example_lvgl_flush_cb(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map) {
|
|
|
|
esp_lcd_panel_handle_t panel_handle = lv_display_get_user_data(disp);
|
|
|
|
// pass the draw buffer to the driver
|
|
|
|
esp_lcd_panel_draw_bitmap(panel_handle, area->x1, area->y1, area->x2 + 1, area->y2 + 1, px_map);
|
|
|
|
lv_disp_flush_ready(disp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void example_increase_lvgl_tick(void *arg) {
|
|
|
|
/* Tell LVGL how many milliseconds has elapsed */
|
|
|
|
lv_tick_inc(LVGL_TICK_PERIOD);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void example_lvgl_port_task(void *arg) {
|
|
|
|
ESP_LOGI(TAG, "Starting LVGL task");
|
|
|
|
uint32_t time_till_next_ms = 0;
|
|
|
|
while (1) {
|
|
|
|
_lock_acquire(&lvgl_api_lock);
|
|
|
|
time_till_next_ms = lv_timer_handler();
|
|
|
|
_lock_release(&lvgl_api_lock);
|
|
|
|
|
|
|
|
// in case of task watch dog timeout, set the minimal delay to 10ms
|
|
|
|
if (time_till_next_ms < 10) {
|
|
|
|
time_till_next_ms = 10;
|
|
|
|
}
|
|
|
|
|
|
|
|
usleep(1000 * time_till_next_ms);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static esp_err_t i2c_master_init(void) {
|
|
|
|
i2c_master_bus_config_t i2c_mst_config = {
|
|
|
|
.clk_source = I2C_CLK_SRC_DEFAULT,
|
|
|
|
.i2c_port = I2C_NUM_0,
|
|
|
|
.scl_io_num = 9,
|
|
|
|
.sda_io_num = 8,
|
|
|
|
.glitch_ignore_cnt = 7,
|
|
|
|
.flags.enable_internal_pullup = true,
|
|
|
|
};
|
|
|
|
|
|
|
|
return i2c_new_master_bus(&i2c_mst_config, &i2c_bus_handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void setup_lcd() {
|
|
|
|
ESP_LOGI(TAG, "Init I2C");
|
|
|
|
ESP_ERROR_CHECK(i2c_master_init());
|
|
|
|
|
|
|
|
i2c_device_config_t exio_dev_cfg = {
|
|
|
|
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
|
|
|
|
.device_address = 0x24, // CH422G config register
|
|
|
|
.scl_speed_hz = 100000,
|
|
|
|
};
|
|
|
|
|
|
|
|
i2c_master_dev_handle_t exio_cfg_handle, exio_o_handle;
|
|
|
|
ESP_ERROR_CHECK(i2c_master_bus_add_device(i2c_bus_handle, &exio_dev_cfg, &exio_cfg_handle));
|
|
|
|
exio_dev_cfg.device_address = 0x38;
|
|
|
|
ESP_ERROR_CHECK(i2c_master_bus_add_device(i2c_bus_handle, &exio_dev_cfg, &exio_o_handle));
|
|
|
|
|
|
|
|
uint8_t i2c_b;
|
|
|
|
i2c_b = 0x01; // Output enable
|
|
|
|
ESP_ERROR_CHECK(i2c_master_transmit(exio_cfg_handle, &i2c_b, 1, -1));
|
|
|
|
|
|
|
|
i2c_b = 0x00; // Reset everything
|
|
|
|
ESP_ERROR_CHECK(i2c_master_transmit(exio_o_handle, &i2c_b, 1, -1));
|
|
|
|
usleep(10 * 1000);
|
|
|
|
i2c_b = 0x08; // Pull LCD out of reset
|
|
|
|
ESP_ERROR_CHECK(i2c_master_transmit(exio_o_handle, &i2c_b, 1, -1));
|
|
|
|
usleep(100 * 1000);
|
|
|
|
|
|
|
|
ESP_LOGI(TAG, "Install RGB LCD panel driver");
|
|
|
|
esp_lcd_panel_handle_t panel_handle = NULL;
|
|
|
|
esp_lcd_rgb_panel_config_t panel_config = {
|
|
|
|
.data_width = 16, // RGB565
|
|
|
|
.psram_trans_align = 64,
|
|
|
|
.num_fbs = 2,
|
|
|
|
|
|
|
|
.clk_src = LCD_CLK_SRC_DEFAULT,
|
|
|
|
.disp_gpio_num = -1, // Only accessible via GPIO expander
|
|
|
|
.pclk_gpio_num = 7,
|
|
|
|
.vsync_gpio_num = 3,
|
|
|
|
.hsync_gpio_num = 46,
|
|
|
|
.de_gpio_num = 5,
|
|
|
|
.data_gpio_nums = {
|
|
|
|
14, // B3
|
|
|
|
38, // B4
|
|
|
|
18, // B5
|
|
|
|
17, // B6
|
|
|
|
10, // B7
|
|
|
|
39, // G2
|
|
|
|
0, // G3
|
|
|
|
45, // G4
|
|
|
|
48, // G5
|
|
|
|
47, // G6
|
|
|
|
21, // G7
|
|
|
|
1, // R3
|
|
|
|
2, // R4
|
|
|
|
42, // R5
|
|
|
|
41, // R6
|
|
|
|
40, // R7
|
|
|
|
},
|
|
|
|
.timings = {
|
|
|
|
.pclk_hz = 18 * 1000 * 1000, // 18000000 / (hs+hbp+hfp+hres) / (vs+vbp+hfp+vres) = ~42Hz
|
|
|
|
.h_res = LCD_HRES,
|
|
|
|
.v_res = LCD_VRES,
|
|
|
|
.hsync_back_porch = 8,
|
|
|
|
.hsync_front_porch = 8,
|
|
|
|
.hsync_pulse_width = 4,
|
|
|
|
.vsync_back_porch = 16,
|
|
|
|
.vsync_front_porch = 16,
|
|
|
|
.vsync_pulse_width = 4,
|
|
|
|
.flags = {
|
|
|
|
.pclk_active_neg = true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
.flags.fb_in_psram = true, // allocate frame buffer in PSRAM
|
|
|
|
};
|
|
|
|
ESP_ERROR_CHECK(esp_lcd_new_rgb_panel(&panel_config, &panel_handle));
|
|
|
|
|
|
|
|
ESP_LOGI(TAG, "Initialize RGB LCD panel");
|
|
|
|
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
|
|
|
|
ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
|
|
|
|
|
|
|
|
ESP_LOGI(TAG, "Turn on LCD backlight");
|
|
|
|
i2c_b = 0x0e; // LCD out of reset, backlight on
|
|
|
|
ESP_ERROR_CHECK(i2c_master_transmit(exio_o_handle, &i2c_b, 1, -1));
|
|
|
|
|
|
|
|
ESP_LOGI(TAG, "Initialize LVGL library");
|
|
|
|
lv_init();
|
|
|
|
// create a lvgl display
|
|
|
|
lv_display_t *display = lv_display_create(LCD_HRES, LCD_VRES);
|
|
|
|
// associate the rgb panel handle to the display
|
|
|
|
lv_display_set_user_data(display, panel_handle);
|
|
|
|
// set color depth
|
|
|
|
lv_display_set_color_format(display, LV_COLOR_FORMAT_RGB565);
|
|
|
|
|
|
|
|
// create draw buffers
|
|
|
|
void *buf1 = NULL;
|
|
|
|
void *buf2 = NULL;
|
|
|
|
ESP_LOGI(TAG, "Use frame buffers as LVGL draw buffers");
|
|
|
|
ESP_ERROR_CHECK(esp_lcd_rgb_panel_get_frame_buffer(panel_handle, 2, &buf1, &buf2));
|
|
|
|
// set LVGL draw buffers and direct mode
|
|
|
|
lv_display_set_buffers(display, buf1, buf2, LCD_HRES * LCD_VRES * LCD_PIXEL_SIZE, LV_DISPLAY_RENDER_MODE_DIRECT);
|
|
|
|
|
|
|
|
// set the callback which can copy the rendered image to an area of the display
|
|
|
|
lv_display_set_flush_cb(display, example_lvgl_flush_cb);
|
|
|
|
|
|
|
|
ESP_LOGI(TAG, "Register event callbacks");
|
|
|
|
esp_lcd_rgb_panel_event_callbacks_t cbs = {
|
|
|
|
.on_vsync = example_on_vsync,
|
|
|
|
};
|
|
|
|
ESP_ERROR_CHECK(esp_lcd_rgb_panel_register_event_callbacks(panel_handle, &cbs, display));
|
|
|
|
|
|
|
|
ESP_LOGI(TAG, "Install LVGL tick timer");
|
|
|
|
// Tick interface for LVGL (using esp_timer to generate 2ms periodic event)
|
|
|
|
const esp_timer_create_args_t lvgl_tick_timer_args = {
|
|
|
|
.callback = &example_increase_lvgl_tick,
|
|
|
|
.name = "lvgl_tick"
|
|
|
|
};
|
|
|
|
esp_timer_handle_t lvgl_tick_timer = NULL;
|
|
|
|
ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
|
|
|
|
ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, LVGL_TICK_PERIOD * 1000));
|
|
|
|
|
|
|
|
ESP_LOGI(TAG, "Create LVGL task");
|
|
|
|
xTaskCreate(example_lvgl_port_task, "LVGL", LVGL_TASK_STACK_SIZE, NULL, LVGL_TASK_PRIORITY, NULL);
|
|
|
|
|
|
|
|
ESP_LOGI(TAG, "Display LVGL UI");
|
|
|
|
// Lock the mutex due to the LVGL APIs are not thread-safe
|
|
|
|
_lock_acquire(&lvgl_api_lock);
|
|
|
|
example_lvgl_demo_ui(display);
|
|
|
|
_lock_release(&lvgl_api_lock);
|
|
|
|
}
|
2024-11-30 21:08:22 +00:00
|
|
|
|
|
|
|
void app_main(void) {
|
2024-12-03 22:55:46 +00:00
|
|
|
setup_lcd();
|
2024-11-30 21:08:22 +00:00
|
|
|
}
|