lcd and sd card file listing works
This commit is contained in:
+84
@@ -0,0 +1,84 @@
|
||||
# macOS
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Directory metadata
|
||||
.directory
|
||||
|
||||
# Temporary files
|
||||
*~
|
||||
*.swp
|
||||
*.swo
|
||||
*.bak
|
||||
*.tmp
|
||||
|
||||
# Log files
|
||||
*.log
|
||||
|
||||
# Build artifacts and directories
|
||||
**/build/
|
||||
build/
|
||||
*.o
|
||||
*.a
|
||||
*.out
|
||||
*.exe # For any host-side utilities compiled on Windows
|
||||
|
||||
# ESP-IDF specific build outputs
|
||||
*.bin
|
||||
*.elf
|
||||
*.map
|
||||
flasher_args.json # Generated in build directory
|
||||
sdkconfig.old
|
||||
sdkconfig.ci
|
||||
|
||||
# ESP-IDF dependencies
|
||||
# For older versions or manual component management
|
||||
/components/.idf/
|
||||
**/components/.idf/
|
||||
# For modern ESP-IDF component manager
|
||||
managed_components/
|
||||
# If ESP-IDF tools are installed/referenced locally to the project
|
||||
.espressif/
|
||||
|
||||
# CMake generated files
|
||||
CMakeCache.txt
|
||||
CMakeFiles/
|
||||
cmake_install.cmake
|
||||
install_manifest.txt
|
||||
CTestTestfile.cmake
|
||||
|
||||
# Python environment files
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.pyd
|
||||
__pycache__/
|
||||
*.egg-info/
|
||||
dist/
|
||||
|
||||
# Virtual environment folders
|
||||
venv/
|
||||
.venv/
|
||||
env/
|
||||
|
||||
# Language Servers
|
||||
.clangd/
|
||||
.ccls-cache/
|
||||
compile_commands.json
|
||||
|
||||
# Windows specific
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
Desktop.ini
|
||||
|
||||
# User-specific configuration files
|
||||
*.user
|
||||
*.workspace # General workspace files, can be from various tools
|
||||
*.suo # Visual Studio Solution User Options
|
||||
*.sln.docstates # Visual Studio
|
||||
|
||||
.devcontainer
|
||||
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
!.vscode/settings.example.json
|
||||
@@ -0,0 +1,8 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||
idf_build_set_property(MINIMAL_BUILD ON)
|
||||
project(display_image_viewer)
|
||||
@@ -0,0 +1,57 @@
|
||||
dependencies:
|
||||
espressif/esp_lcd_touch:
|
||||
component_hash: 3f85a7d95af876f1a6ecca8eb90a81614890d0f03a038390804e5a77e2caf862
|
||||
dependencies:
|
||||
- name: idf
|
||||
require: private
|
||||
version: '>=4.4.2'
|
||||
source:
|
||||
registry_url: https://components.espressif.com
|
||||
type: service
|
||||
version: 1.2.1
|
||||
espressif/esp_lcd_touch_gt911:
|
||||
component_hash: 6029febef66eefd0910d527d7f30292779ccbf22a8dedd2bd69466acf0068a7b
|
||||
dependencies:
|
||||
- name: espressif/esp_lcd_touch
|
||||
registry_url: https://components.espressif.com
|
||||
require: public
|
||||
version: ^1.2.0
|
||||
- name: idf
|
||||
require: private
|
||||
version: '>=5.2'
|
||||
source:
|
||||
registry_url: https://components.espressif.com/
|
||||
type: service
|
||||
version: 1.2.0~2
|
||||
espressif/esp_lvgl_port:
|
||||
component_hash: fb6c1fdfe70d4ca39a035d63ca394a35f17ec84ce16ff98ecb13a54155d75da4
|
||||
dependencies:
|
||||
- name: idf
|
||||
require: private
|
||||
version: '>=5.2'
|
||||
- name: lvgl/lvgl
|
||||
registry_url: https://components.espressif.com
|
||||
require: public
|
||||
version: '>=8,<10'
|
||||
source:
|
||||
registry_url: https://components.espressif.com/
|
||||
type: service
|
||||
version: 2.8.0~1
|
||||
idf:
|
||||
source:
|
||||
type: idf
|
||||
version: 6.0.2
|
||||
lvgl/lvgl:
|
||||
component_hash: 184e532558c1c45fefed631f3e235423d22582aafb4630f3e8885c35281a49ae
|
||||
dependencies: []
|
||||
source:
|
||||
registry_url: https://components.espressif.com
|
||||
type: service
|
||||
version: 9.5.0
|
||||
direct_dependencies:
|
||||
- espressif/esp_lcd_touch_gt911
|
||||
- espressif/esp_lvgl_port
|
||||
- idf
|
||||
manifest_hash: 469200faacb1f8184474aadd574818ca9f8ac903271356d7355ff8f9d4b2f785
|
||||
target: esp32s3
|
||||
version: 3.0.0
|
||||
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS main.c lcd/lcd.c sd/sd.c
|
||||
PRIV_REQUIRES spi_flash esp_psram esp_lcd esp_driver_gpio esp_driver_i2c esp_driver_sdspi esp_driver_spi esp_lvgl_port esp_lcd_touch_gt911 fatfs sdmmc
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,18 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
## Required IDF version
|
||||
idf:
|
||||
version: '>=5.2.0'
|
||||
# # Put list of dependencies here
|
||||
# # For components maintained by Espressif:
|
||||
# component: "~1.0.0"
|
||||
# # For 3rd party components:
|
||||
# username/component: ">=1.0.0,<2.0.0"
|
||||
# username2/component2:
|
||||
# version: "~1.0.0"
|
||||
# # For transient dependencies `public` flag can be set.
|
||||
# # `public` flag doesn't have an effect dependencies of the `main` component.
|
||||
# # All dependencies of `main` are public by default.
|
||||
# public: true
|
||||
espressif/esp_lcd_touch_gt911: ^1
|
||||
espressif/esp_lvgl_port: ^2.8.0~1
|
||||
+288
@@ -0,0 +1,288 @@
|
||||
#include "lcd/lcd.h"
|
||||
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/i2c_master.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_idf_version.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_lcd_panel_ops.h"
|
||||
#include "esp_lcd_panel_rgb.h"
|
||||
#include "esp_lcd_touch_gt911.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_lvgl_port.h"
|
||||
|
||||
#define LCD_DATA_WIDTH (16)
|
||||
#define LCD_RGB_BUFFER_NUMS (2)
|
||||
#define LCD_DRAW_BUFF_HEIGHT (100)
|
||||
#define LCD_LVGL_DIRECT_MODE (0)
|
||||
#define LCD_LVGL_AVOID_TEAR (0)
|
||||
#define LCD_RGB_BOUNCE_BUFFER_MODE (1)
|
||||
#define LCD_RGB_BOUNCE_BUFFER_HEIGHT (10)
|
||||
#define LCD_LVGL_ROTATION (LV_DISPLAY_ROTATION_90)
|
||||
|
||||
#define LCD_GPIO_VSYNC (GPIO_NUM_41)
|
||||
#define LCD_GPIO_HSYNC (GPIO_NUM_39)
|
||||
#define LCD_GPIO_DE (GPIO_NUM_40)
|
||||
#define LCD_GPIO_PCLK (GPIO_NUM_0)
|
||||
#define LCD_GPIO_DISP (GPIO_NUM_NC)
|
||||
#define LCD_GPIO_BACKLIGHT (GPIO_NUM_2)
|
||||
#define LCD_BACKLIGHT_ON_LEVEL (1)
|
||||
#define LCD_GPIO_DATA0 (GPIO_NUM_8)
|
||||
#define LCD_GPIO_DATA1 (GPIO_NUM_3)
|
||||
#define LCD_GPIO_DATA2 (GPIO_NUM_46)
|
||||
#define LCD_GPIO_DATA3 (GPIO_NUM_9)
|
||||
#define LCD_GPIO_DATA4 (GPIO_NUM_1)
|
||||
#define LCD_GPIO_DATA5 (GPIO_NUM_5)
|
||||
#define LCD_GPIO_DATA6 (GPIO_NUM_6)
|
||||
#define LCD_GPIO_DATA7 (GPIO_NUM_7)
|
||||
#define LCD_GPIO_DATA8 (GPIO_NUM_15)
|
||||
#define LCD_GPIO_DATA9 (GPIO_NUM_16)
|
||||
#define LCD_GPIO_DATA10 (GPIO_NUM_4)
|
||||
#define LCD_GPIO_DATA11 (GPIO_NUM_45)
|
||||
#define LCD_GPIO_DATA12 (GPIO_NUM_48)
|
||||
#define LCD_GPIO_DATA13 (GPIO_NUM_47)
|
||||
#define LCD_GPIO_DATA14 (GPIO_NUM_21)
|
||||
#define LCD_GPIO_DATA15 (GPIO_NUM_14)
|
||||
|
||||
#define TOUCH_I2C_NUM (1)
|
||||
#define TOUCH_I2C_SCL (GPIO_NUM_20)
|
||||
#define TOUCH_I2C_SDA (GPIO_NUM_19)
|
||||
#define TOUCH_RST (GPIO_NUM_NC)
|
||||
#define TOUCH_INT (GPIO_NUM_NC)
|
||||
|
||||
#define LCD_PANEL_RGB_TIMING() \
|
||||
{ \
|
||||
.pclk_hz = 12 * 1000 * 1000, \
|
||||
.h_res = LCD_H_RES, \
|
||||
.v_res = LCD_V_RES, \
|
||||
.hsync_pulse_width = 4, \
|
||||
.hsync_front_porch = 8, \
|
||||
.hsync_back_porch = 43, \
|
||||
.vsync_pulse_width = 4, \
|
||||
.vsync_front_porch = 8, \
|
||||
.vsync_back_porch = 12, \
|
||||
.flags.pclk_active_neg = true, \
|
||||
}
|
||||
|
||||
static const char *TAG = "lcd";
|
||||
|
||||
static esp_lcd_panel_handle_t lcd_panel;
|
||||
static esp_lcd_touch_handle_t touch_handle;
|
||||
static lv_display_t *lvgl_display;
|
||||
static lv_indev_t *lvgl_touch_indev;
|
||||
|
||||
static esp_err_t init_backlight(void)
|
||||
{
|
||||
const gpio_config_t backlight_config = {
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = 1ULL << LCD_GPIO_BACKLIGHT,
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(gpio_config(&backlight_config), TAG, "Backlight GPIO config failed");
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(LCD_GPIO_BACKLIGHT, LCD_BACKLIGHT_ON_LEVEL), TAG, "Backlight GPIO set failed");
|
||||
ESP_LOGI(TAG, "Backlight enabled on GPIO%d", LCD_GPIO_BACKLIGHT);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t probe_gt911_address(i2c_master_bus_handle_t i2c_handle, uint32_t *dev_addr)
|
||||
{
|
||||
const uint8_t addresses[] = {
|
||||
ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS,
|
||||
ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS_BACKUP,
|
||||
};
|
||||
|
||||
for (size_t address_index = 0; address_index < sizeof(addresses) / sizeof(addresses[0]); address_index++) {
|
||||
const uint8_t address = addresses[address_index];
|
||||
const esp_err_t probe_ret = i2c_master_probe(i2c_handle, address, 100);
|
||||
if (probe_ret == ESP_OK) {
|
||||
*dev_addr = address;
|
||||
ESP_LOGI(TAG, "GT911 found at I2C address 0x%02x", address);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
ESP_LOGW(TAG, "GT911 not found at I2C address 0x%02x: %s", address, esp_err_to_name(probe_ret));
|
||||
}
|
||||
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
static esp_err_t init_rgb_panel(void){
|
||||
ESP_LOGI(TAG, "Initialize RGB panel");
|
||||
|
||||
const esp_lcd_rgb_panel_config_t panel_config = {
|
||||
.clk_src = LCD_CLK_SRC_PLL160M,
|
||||
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0)
|
||||
.psram_trans_align = 64,
|
||||
#else
|
||||
.dma_burst_size = 64,
|
||||
#endif
|
||||
.data_width = LCD_DATA_WIDTH,
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0)
|
||||
.in_color_format = LCD_COLOR_FMT_RGB565,
|
||||
#else
|
||||
.bits_per_pixel = 16,
|
||||
#endif
|
||||
.de_gpio_num = LCD_GPIO_DE,
|
||||
.pclk_gpio_num = LCD_GPIO_PCLK,
|
||||
.vsync_gpio_num = LCD_GPIO_VSYNC,
|
||||
.hsync_gpio_num = LCD_GPIO_HSYNC,
|
||||
.disp_gpio_num = LCD_GPIO_DISP,
|
||||
.data_gpio_nums = {
|
||||
LCD_GPIO_DATA0,
|
||||
LCD_GPIO_DATA1,
|
||||
LCD_GPIO_DATA2,
|
||||
LCD_GPIO_DATA3,
|
||||
LCD_GPIO_DATA4,
|
||||
LCD_GPIO_DATA5,
|
||||
LCD_GPIO_DATA6,
|
||||
LCD_GPIO_DATA7,
|
||||
LCD_GPIO_DATA8,
|
||||
LCD_GPIO_DATA9,
|
||||
LCD_GPIO_DATA10,
|
||||
LCD_GPIO_DATA11,
|
||||
LCD_GPIO_DATA12,
|
||||
LCD_GPIO_DATA13,
|
||||
LCD_GPIO_DATA14,
|
||||
LCD_GPIO_DATA15,
|
||||
},
|
||||
.timings = LCD_PANEL_RGB_TIMING(),
|
||||
.flags.fb_in_psram = true,
|
||||
.num_fbs = LCD_RGB_BUFFER_NUMS,
|
||||
#if LCD_RGB_BOUNCE_BUFFER_MODE
|
||||
.bounce_buffer_size_px = LCD_H_RES * LCD_RGB_BOUNCE_BUFFER_HEIGHT,
|
||||
#endif
|
||||
};
|
||||
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_new_rgb_panel(&panel_config, &lcd_panel), TAG, "RGB panel create failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_init(lcd_panel), TAG, "LCD panel init failed");
|
||||
return init_backlight();
|
||||
}
|
||||
|
||||
static esp_err_t init_touch(void){
|
||||
ESP_LOGI(TAG, "Initialize GT911 touch");
|
||||
|
||||
i2c_master_bus_handle_t i2c_handle = NULL;
|
||||
const i2c_master_bus_config_t i2c_config = {
|
||||
.i2c_port = TOUCH_I2C_NUM,
|
||||
.sda_io_num = TOUCH_I2C_SDA,
|
||||
.scl_io_num = TOUCH_I2C_SCL,
|
||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||
.glitch_ignore_cnt = 7,
|
||||
.flags = {
|
||||
.enable_internal_pullup = true,
|
||||
},
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(i2c_new_master_bus(&i2c_config, &i2c_handle), TAG, "I2C bus init failed");
|
||||
|
||||
esp_lcd_panel_io_i2c_config_t touch_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG();
|
||||
ESP_RETURN_ON_ERROR(probe_gt911_address(i2c_handle, &touch_io_config.dev_addr), TAG, "GT911 I2C probe failed");
|
||||
|
||||
esp_lcd_touch_io_gt911_config_t gt911_config = {
|
||||
.dev_addr = touch_io_config.dev_addr,
|
||||
};
|
||||
|
||||
const esp_lcd_touch_config_t touch_config = {
|
||||
.x_max = LCD_H_RES,
|
||||
.y_max = LCD_V_RES,
|
||||
.rst_gpio_num = TOUCH_RST,
|
||||
.int_gpio_num = TOUCH_INT,
|
||||
.levels = {
|
||||
.reset = 0,
|
||||
.interrupt = 0,
|
||||
},
|
||||
.flags = {
|
||||
.swap_xy = false,
|
||||
.mirror_x = false,
|
||||
.mirror_y = false,
|
||||
},
|
||||
.driver_data = >911_config,
|
||||
};
|
||||
|
||||
esp_lcd_panel_io_handle_t touch_io_handle = NULL;
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_new_panel_io_i2c(i2c_handle, &touch_io_config, &touch_io_handle), TAG, "Touch IO init failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_touch_new_i2c_gt911(touch_io_handle, &touch_config, &touch_handle), TAG, "GT911 driver init failed");
|
||||
ESP_LOGI(TAG, "GT911 touch ready");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t init_lvgl(void){
|
||||
ESP_LOGI(TAG, "Initialize LVGL port");
|
||||
const lvgl_port_cfg_t lvgl_config = ESP_LVGL_PORT_INIT_CONFIG();
|
||||
ESP_RETURN_ON_ERROR(lvgl_port_init(&lvgl_config), TAG, "LVGL port init failed");
|
||||
|
||||
uint32_t buffer_size = LCD_H_RES * LCD_DRAW_BUFF_HEIGHT;
|
||||
#if LCD_LVGL_DIRECT_MODE
|
||||
buffer_size = LCD_H_RES * LCD_V_RES;
|
||||
#endif
|
||||
|
||||
const lvgl_port_display_cfg_t display_config = {
|
||||
.panel_handle = lcd_panel,
|
||||
.buffer_size = buffer_size,
|
||||
.double_buffer = false,
|
||||
.hres = LCD_H_RES,
|
||||
.vres = LCD_V_RES,
|
||||
.monochrome = false,
|
||||
#if LVGL_VERSION_MAJOR >= 9
|
||||
.color_format = LV_COLOR_FORMAT_RGB565,
|
||||
#endif
|
||||
.rotation = {
|
||||
.swap_xy = false,
|
||||
.mirror_x = false,
|
||||
.mirror_y = false,
|
||||
},
|
||||
.flags = {
|
||||
.buff_dma = false,
|
||||
.buff_spiram = false,
|
||||
.sw_rotate = true,
|
||||
#if LVGL_VERSION_MAJOR >= 9
|
||||
.swap_bytes = false,
|
||||
#endif
|
||||
#if LCD_LVGL_DIRECT_MODE
|
||||
.direct_mode = true,
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
const lvgl_port_display_rgb_cfg_t rgb_config = {
|
||||
.flags = {
|
||||
.bb_mode = true,
|
||||
#if LCD_LVGL_AVOID_TEAR
|
||||
.avoid_tearing = true,
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
ESP_LOGI(TAG, "Add LVGL RGB display");
|
||||
lvgl_display = lvgl_port_add_disp_rgb(&display_config, &rgb_config);
|
||||
ESP_RETURN_ON_FALSE(lvgl_display, ESP_FAIL, TAG, "LVGL display add failed");
|
||||
lv_display_set_rotation(lvgl_display, LCD_LVGL_ROTATION);
|
||||
ESP_LOGI(TAG, "LVGL display rotation set to %d", LCD_LVGL_ROTATION);
|
||||
|
||||
if (touch_handle) {
|
||||
const lvgl_port_touch_cfg_t touch_config = {
|
||||
.disp = lvgl_display,
|
||||
.handle = touch_handle,
|
||||
};
|
||||
lvgl_touch_indev = lvgl_port_add_touch(&touch_config);
|
||||
ESP_RETURN_ON_FALSE(lvgl_touch_indev, ESP_FAIL, TAG, "LVGL touch add failed");
|
||||
ESP_LOGI(TAG, "LVGL touch input ready");
|
||||
} else {
|
||||
ESP_LOGW(TAG, "LVGL touch input disabled");
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "LVGL ready");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_init(void){
|
||||
ESP_RETURN_ON_ERROR(init_rgb_panel(), TAG, "LCD panel init failed");
|
||||
const esp_err_t touch_ret = init_touch();
|
||||
if (touch_ret != ESP_OK) {
|
||||
ESP_LOGW(TAG, "Continuing without touch: %s", esp_err_to_name(touch_ret));
|
||||
}
|
||||
return init_lvgl();
|
||||
}
|
||||
|
||||
lv_display_t *lcd_get_display(void){
|
||||
return lvgl_display;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "lvgl.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define LCD_H_RES (800)
|
||||
#define LCD_V_RES (480)
|
||||
|
||||
esp_err_t lcd_init(void);
|
||||
lv_display_t *lcd_get_display(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
#include "esp_check.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_lvgl_port.h"
|
||||
#include "lcd/lcd.h"
|
||||
#include "sd/sd.h"
|
||||
#include "lvgl.h"
|
||||
|
||||
static const char *TAG = "app";
|
||||
static lv_obj_t *log_textarea = NULL;
|
||||
|
||||
static void ui_log_append(const char *text)
|
||||
{
|
||||
if(log_textarea == NULL || text == NULL) return;
|
||||
|
||||
lvgl_port_lock(0);
|
||||
lv_textarea_add_text(log_textarea, text);
|
||||
lv_textarea_add_text(log_textarea, "\n");
|
||||
lv_textarea_set_cursor_pos(log_textarea, LV_TEXTAREA_CURSOR_LAST);
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
|
||||
static void create_log_ui(void)
|
||||
{
|
||||
lv_obj_t *screen = lv_screen_active();
|
||||
lv_obj_set_style_bg_color(screen, lv_color_hex(0x101418), 0);
|
||||
|
||||
lv_obj_t *title = lv_label_create(screen);
|
||||
lv_label_set_text(title, "SD Image Viewer");
|
||||
lv_obj_set_style_text_color(title, lv_color_white(), 0);
|
||||
lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 14);
|
||||
|
||||
log_textarea = lv_textarea_create(screen);
|
||||
lv_obj_set_size(log_textarea, lv_pct(92), lv_pct(84));
|
||||
lv_obj_align(log_textarea, LV_ALIGN_BOTTOM_MID, 0, -14);
|
||||
lv_textarea_set_one_line(log_textarea, false);
|
||||
lv_textarea_set_cursor_click_pos(log_textarea, false);
|
||||
lv_textarea_set_text(log_textarea, "");
|
||||
lv_obj_set_style_bg_color(log_textarea, lv_color_hex(0x182028), 0);
|
||||
lv_obj_set_style_text_color(log_textarea, lv_color_hex(0xdde7ef), 0);
|
||||
lv_obj_set_style_border_color(log_textarea, lv_color_hex(0x4b6478), 0);
|
||||
lv_obj_set_style_radius(log_textarea, 4, 0);
|
||||
lv_obj_set_style_pad_all(log_textarea, 12, 0);
|
||||
|
||||
lv_textarea_add_text(log_textarea, "Boot UI ready\n");
|
||||
lv_textarea_set_cursor_pos(log_textarea, LV_TEXTAREA_CURSOR_LAST);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Start display application");
|
||||
ESP_ERROR_CHECK(lcd_init());
|
||||
|
||||
ESP_LOGI(TAG, "Creating log UI");
|
||||
lvgl_port_lock(0);
|
||||
create_log_ui();
|
||||
lvgl_port_unlock();
|
||||
|
||||
ESP_LOGI(TAG, "Initializing SD card");
|
||||
ui_log_append("Initializing SD card...");
|
||||
sd_init();
|
||||
print_sd_card_info();
|
||||
ui_log_append("SD initialization done");
|
||||
ESP_LOGI(TAG, "SD card initialization complete");
|
||||
|
||||
size_t file_count = sd_get_file_count();
|
||||
ESP_LOGI(TAG, "Found %d files in root directory", file_count);
|
||||
const char* filename = NULL;
|
||||
|
||||
while(file_count-- > 0 && (filename = sd_get_next_file()) != NULL){
|
||||
ESP_LOGI(TAG, "Found file: %s", filename);
|
||||
char line[96];
|
||||
snprintf(line, sizeof(line), "Found file: %s", filename);
|
||||
ui_log_append(line);
|
||||
}
|
||||
|
||||
/*
|
||||
FILE* file = sd_open_write("test.txt");
|
||||
if(file != NULL){
|
||||
fprintf(file, "Hello, SD card!\n");
|
||||
sd_close_file(file);
|
||||
ui_log_append("Wrote test.txt");
|
||||
} else {
|
||||
ui_log_append("Failed to open test.txt for writing");
|
||||
}
|
||||
*/
|
||||
|
||||
sd_destroy();
|
||||
ui_log_append("SD card unmounted");
|
||||
}
|
||||
+207
@@ -0,0 +1,207 @@
|
||||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "sd/sd.h"
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_vfs_fat.h"
|
||||
#include "sdmmc_cmd.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/sdspi_host.h"
|
||||
#include "driver/spi_common.h"
|
||||
|
||||
#define MAX_CHAR_SIZE 64
|
||||
#define MOUNT_POINT "/sdcard"
|
||||
|
||||
#define SD_PIN_MOSI (GPIO_NUM_11)
|
||||
#define SD_PIN_MISO (GPIO_NUM_13)
|
||||
#define SD_PIN_CLK (GPIO_NUM_12)
|
||||
#define SD_PIN_CS (GPIO_NUM_10)
|
||||
|
||||
static sdmmc_card_t* card = NULL;
|
||||
static const char *TAG = "sd";
|
||||
|
||||
static char** _file_list = NULL;
|
||||
static size_t _current_file = 0;
|
||||
static size_t _file_count = 0;
|
||||
|
||||
static esp_err_t get_top_level_files(void);
|
||||
|
||||
void sd_init(void){
|
||||
if(card != NULL){
|
||||
ESP_LOGW(TAG, "SD card already initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
sdmmc_host_t host = SDSPI_HOST_DEFAULT();
|
||||
host.unaligned_multi_block_rw_max_chunk_size = 8;
|
||||
|
||||
spi_bus_config_t bus_config = {
|
||||
.mosi_io_num = SD_PIN_MOSI,
|
||||
.miso_io_num = SD_PIN_MISO,
|
||||
.sclk_io_num = SD_PIN_CLK,
|
||||
.quadwp_io_num = GPIO_NUM_NC,
|
||||
.quadhd_io_num = GPIO_NUM_NC,
|
||||
.max_transfer_sz = 4000,
|
||||
};
|
||||
|
||||
esp_err_t ret = spi_bus_initialize(host.slot, &bus_config, SDSPI_DEFAULT_DMA);
|
||||
if(ret != ESP_OK){
|
||||
ESP_LOGE(TAG, "Failed to initialize SD SPI bus (%s)", esp_err_to_name(ret));
|
||||
return;
|
||||
}
|
||||
|
||||
sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
|
||||
slot_config.gpio_cs = SD_PIN_CS;
|
||||
slot_config.host_id = host.slot;
|
||||
|
||||
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
|
||||
.format_if_mount_failed = false,
|
||||
.max_files = MAX_SIMULTANEOUS_OPEN_FILES,
|
||||
.allocation_unit_size = 16 * 1024
|
||||
};
|
||||
|
||||
const char mount_point[] = MOUNT_POINT;
|
||||
|
||||
ret = esp_vfs_fat_sdspi_mount(mount_point, &host, &slot_config, &mount_config, &card);
|
||||
|
||||
if(ret != ESP_OK){
|
||||
if(ret == ESP_FAIL) ESP_LOGE(TAG, "Failed to mount filesystem.");
|
||||
else ESP_LOGE(TAG, "Failed to initialize the card (%s). ", esp_err_to_name(ret));
|
||||
spi_bus_free(host.slot);
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "SD card initialized");
|
||||
|
||||
if(get_top_level_files() == ESP_OK) ESP_LOGI(TAG, "Found %d files in root directory", _file_count);
|
||||
else ESP_LOGW(TAG, "No files found in root directory");
|
||||
}
|
||||
|
||||
void print_sd_card_info(void){
|
||||
if(card == NULL) return;
|
||||
sdmmc_card_print_info(stdout, card);
|
||||
}
|
||||
|
||||
static inline void make_file_path(char* buffer, size_t buffer_size, const char* filename){
|
||||
snprintf(buffer, buffer_size, "%s/%s", MOUNT_POINT, filename);
|
||||
}
|
||||
|
||||
FILE* sd_open_read(const char* filename){
|
||||
if(filename == NULL) return NULL;
|
||||
char full_path[strlen(MOUNT_POINT) + strlen(filename) + 2];
|
||||
make_file_path(full_path, sizeof(full_path), filename);
|
||||
return fopen(full_path, "r");
|
||||
}
|
||||
|
||||
FILE* sd_open_write(const char* filename){
|
||||
if(filename == NULL) return NULL;
|
||||
char full_path[strlen(MOUNT_POINT) + strlen(filename) + 2];
|
||||
make_file_path(full_path, sizeof(full_path), filename);
|
||||
return fopen(full_path, "w");
|
||||
}
|
||||
|
||||
void sd_close_file(FILE* file){
|
||||
if(file != NULL) fclose(file);
|
||||
}
|
||||
|
||||
static esp_err_t get_top_level_files(void){
|
||||
char** file_list = NULL;
|
||||
size_t count = 0;
|
||||
size_t capacity = 0;
|
||||
|
||||
DIR* dir = opendir(MOUNT_POINT);
|
||||
if(dir == NULL){
|
||||
ESP_LOGE(TAG, "Failed to open directory: %s", MOUNT_POINT);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
struct dirent* entry;
|
||||
while((entry = readdir(dir)) != NULL){
|
||||
if(entry->d_type == DT_REG){
|
||||
ESP_LOGI(TAG, "Found file: %s", entry->d_name);
|
||||
|
||||
size_t name_length = strlen(entry->d_name);
|
||||
if(name_length >= MAX_CHAR_SIZE){
|
||||
ESP_LOGW(TAG, "File name too long: %s", entry->d_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(count == capacity){
|
||||
size_t new_capacity = capacity == 0 ? 8 : capacity * 2;
|
||||
char** new_file_list = (char**)realloc(file_list, sizeof(char*) * new_capacity);
|
||||
if(new_file_list == NULL){
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for file list");
|
||||
for(size_t j = 0; j < count; j++) free(file_list[j]);
|
||||
free(file_list);
|
||||
closedir(dir);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
file_list = new_file_list;
|
||||
capacity = new_capacity;
|
||||
}
|
||||
|
||||
file_list[count] = (char*)malloc(sizeof(char) * (name_length + 1));
|
||||
if(file_list[count] == NULL){
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for file name");
|
||||
for(size_t j = 0; j < count; j++) free(file_list[j]);
|
||||
free(file_list);
|
||||
closedir(dir);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
memcpy(file_list[count], entry->d_name, name_length + 1);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
if(count == 0){
|
||||
ESP_LOGW(TAG, "No files found in directory: %s", MOUNT_POINT);
|
||||
free(file_list);
|
||||
_file_list = NULL;
|
||||
_current_file = 0;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
_file_list = file_list;
|
||||
_file_count = count;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
size_t sd_get_file_count(void){
|
||||
return _file_count;
|
||||
}
|
||||
|
||||
const char* sd_get_next_file(void){
|
||||
if(_current_file >= _file_count) _current_file = 0;
|
||||
return _file_list[_current_file++];
|
||||
}
|
||||
|
||||
const char* sd_get_previous_file(void){
|
||||
if(_current_file == 0) _current_file = _file_count;
|
||||
return _file_list[--_current_file];
|
||||
}
|
||||
|
||||
void sd_destroy(void){
|
||||
if(card != NULL){
|
||||
esp_vfs_fat_sdcard_unmount(MOUNT_POINT, card);
|
||||
card = NULL;
|
||||
ESP_LOGI(TAG, "SD card unmounted");
|
||||
}
|
||||
|
||||
if(_file_list != NULL){
|
||||
for(int i = 0; i < _file_count; i++) free((void*)_file_list[i]);
|
||||
free((void*)_file_list);
|
||||
_file_list = NULL;
|
||||
}
|
||||
|
||||
_file_count = 0;
|
||||
_current_file = 0;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define MAX_SIMULTANEOUS_OPEN_FILES 2
|
||||
|
||||
void sd_init(void);
|
||||
void sd_destroy(void);
|
||||
|
||||
void print_sd_card_info(void);
|
||||
|
||||
FILE* sd_open_read(const char* filename);
|
||||
FILE* sd_open_write(const char* filename);
|
||||
void sd_close_file(FILE* file);
|
||||
|
||||
size_t sd_get_file_count(void);
|
||||
const char* sd_get_next_file(void);
|
||||
const char* sd_get_previous_file(void);
|
||||
Reference in New Issue
Block a user