以下是推荐的引脚连接方案(可根据实际调整):
ESP32-S3 引脚 | SDNAND 引脚 | 功能说明 |
---|---|---|
GPIO12 | CS | 片选信号 |
GPIO14 | CLK | 时钟信号 |
GPIO11 | MOSI | 主输出从输入 |
GPIO13 | MISO | 主输入从输出 |
3.3V | VCC | 电源 |
GND | GND | 地 |
LVGL 需要一个显示设备来渲染界面,以下是 SPI 接口 TFT 的典型连接:
ESP32-S3 引脚 | TFT 引脚 | 功能说明 |
---|---|---|
GPIO15 | CS | 片选信号 |
GPIO14 | CLK | 时钟信号 |
GPIO11 | MOSI | 数据输出 |
GPIO4 | DC/RS | 数据 / 命令选择 |
GPIO5 | RST | 复位信号 |
3.3V | VCC | 电源 |
GND | GND | 地 |
打开终端,执行以下命令:
# 创建新项目idf.py create-project esp32s3_sdnand_lvglcd esp32s3_sdnand_lvgl# 设置目标芯片idf.py set-target esp32s3
在项目根目录下创建components
文件夹,并添加以下组件:
创建spi_master.c
文件,初始化 SPI 总线:
include "driver/spi_master.h"#include "esp_log.h"#define SDNAND_HOST SPI2_HOST#define PIN_NUM_MISO 13#define PIN_NUM_MOSI 11#define PIN_NUM_CLK 14#define PIN_NUM_CS 12spi_device_handle_t sdnand_handle;esp_err_t sdnand_spi_init(void){ esp_err_t ret; spi_bus_config_t buscfg = { .miso_io_num = PIN_NUM_MISO, .mosi_io_num = PIN_NUM_MOSI, .sclk_io_num = PIN_NUM_CLK, .quadwp_io_num = -1, .quadhd_io_num = -1, .max_transfer_sz = 4096, }; // 初始化SPI总线 ret = spi_bus_initialize(SDNAND_HOST, &buscfg, SPI_DMA_CH_AUTO); if (ret != ESP_OK) { ESP_LOGE("SDNAND", "SPI总线初始化失败: %d", ret); return ret; } // 配置SDNAND设备 spi_device_interface_config_t devcfg = { .clock_speed_hz = 20 * 1000 * 1000, // 20MHz .mode = 0, // SPI模式0 .spics_io_num = PIN_NUM_CS, .queue_size = 7, .cs_ena_posttrans = 3, // 传输后保持CS低电平的时间 }; // 注册SDNAND设备 ret = spi_bus_add_device(SDNAND_HOST, &devcfg, &sdnand_handle); if (ret != ESP_OK) { ESP_LOGE("SDNAND", "添加SPI设备失败: %d", ret); return ret; } return ESP_OK;}
创建sdnand.c
文件,实现基本的 SDNAND 操作
#include "sdnand.h"#include "driver/spi_master.h"#include "esp_log.h"#include "esp_err.h"#include "freertos/FreeRTOS.h"#include "freertos/task.h"// SDNAND命令定义#define CMD_RESET 0xFF // 复位命令#define CMD_READ_ID 0x9F // 读取ID命令#define CMD_READ_STATUS 0x05 // 读取状态寄存器#define CMD_WRITE_ENABLE 0x06 // 写使能#define CMD_PAGE_READ 0x13 // 页读取命令esp_err_t sdnand_init(void){ esp_err_t ret; // 初始化SPI总线 ret = sdnand_spi_init(); if (ret != ESP_OK) { return ret; } // 复位SDNAND ret = sdnand_reset(); if (ret != ESP_OK) { ESP_LOGE("SDNAND", "复位失败"); return ret; } // 读取设备ID进行验证 uint8_t id[5] = {0}; ret = sdnand_read_id(id); if (ret != ESP_OK) { ESP_LOGE("SDNAND", "读取ID失败"); return ret; } ESP_LOGI("SDNAND", "设备ID: %02X %02X %02X %02X %02X", id[0], id[1], id[2], id[3], id[4]); return ESP_OK;}// 实现其他SDNAND操作函数...
在CMakeLists.txt
中添加 LVGL 依赖:
idf_component_register(SRCS "main.c" "spi_master.c" "sdnand.c" "lvgl_driver.c" INCLUDE_DIRS "." REQUIRES lvgl esp_lcd)
创建lvgl_driver.c
文件,实现 LVGL 的显示接口:
#include "lvgl.h"#include "driver/spi_master.h"#include "esp_lcd_panel_io.h"#include "esp_lcd_panel_vendor.h"#include "esp_lcd_panel_ops.h"#include "esp_err.h"#include "esp_log.h"#define LCD_HOST SPI3_HOST#define PIN_NUM_CS 15#define PIN_NUM_DC 4#define PIN_NUM_RST 5#define PIN_NUM_CLK 14#define PIN_NUM_MOSI 11#define LCD_H_RES 240#define LCD_V_RES 320static lv_disp_draw_buf_t draw_buf;static lv_color_t buf[LCD_H_RES * 10]; // 部分帧缓冲区// 显示刷新回调函数static void disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p){ // 实现显示刷新逻辑 // ... // 标记刷新完成 lv_disp_flush_ready(disp);}esp_err_t lvgl_driver_init(void){ esp_err_t ret; // 初始化SPI总线 spi_bus_config_t buscfg = { .miso_io_num = -1, .mosi_io_num = PIN_NUM_MOSI, .sclk_io_num = PIN_NUM_CLK, .quadwp_io_num = -1, .quadhd_io_num = -1, .max_transfer_sz = LCD_H_RES * 40, }; ret = spi_bus_initialize(LCD_HOST, &buscfg, SPI_DMA_CH_AUTO); if (ret != ESP_OK) { ESP_LOGE("LVGL", "SPI总线初始化失败: %d", ret); return ret; } // 配置LCD接口 esp_lcd_panel_io_spi_config_t io_config = { .cs_gpio_num = PIN_NUM_CS, .dc_gpio_num = PIN_NUM_DC, .spi_mode = 0, .pclk_hz = 40 * 1000 * 1000, .trans_queue_depth = 10, }; esp_lcd_panel_io_handle_t io_handle = NULL; ret = esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_HOST, &io_config, &io_handle); if (ret != ESP_OK) { ESP_LOGE("LVGL", "创建SPI面板IO失败: %d", ret); return ret; } // 配置LCD面板 esp_lcd_panel_dev_config_t panel_config = { .reset_gpio_num = PIN_NUM_RST, .color_space = ESP_LCD_COLOR_SPACE_RGB, .bits_per_pixel = 16, }; esp_lcd_panel_handle_t panel_handle = NULL; ret = esp_lcd_new_panel_st7789(io_handle, &panel_config, &panel_handle); if (ret != ESP_OK) { ESP_LOGE("LVGL", "创建ST7789面板失败: %d", ret); return ret; } // 初始化LCD esp_lcd_panel_reset(panel_handle); esp_lcd_panel_init(panel_handle); esp_lcd_panel_invert_color(panel_handle, true); esp_lcd_panel_mirror(panel_handle, false, true); // 根据实际显示方向调整 // 初始化LVGL lv_init(); // 初始化LVGL显示缓冲区 lv_disp_draw_buf_init(&draw_buf, buf, NULL, LCD_H_RES * 10); // 注册LVGL显示驱动 lv_disp_drv_t disp_drv; lv_disp_drv_init(&disp_drv); disp_drv.hor_res = LCD_H_RES; disp_drv.ver_res = LCD_V_RES; disp_drv.flush_cb = disp_flush; disp_drv.draw_buf = &draw_buf; lv_disp_drv_register(&disp_drv); return ESP_OK;}
创建main.c
文件,整合 SDNAND 和 LVGL 功能:
#include "esp_system.h"#include "esp_log.h"#include "nvs_flash.h"#include "sdnand.h"#include "lvgl_driver.h"#include "lvgl.h"static const char *TAG = "MAIN";// 创建一个简单的LVGL界面static void create_sample_gui(void){ // 创建一个屏幕对象 lv_obj_t *scr = lv_scr_act(); lv_obj_set_style_bg_color(scr, lv_color_hex(0x000000), LV_PART_MAIN); // 创建一个标签 lv_obj_t *label = lv_label_create(scr); lv_label_set_text(label, "ESP32-S3 + SDNAND + LVGL"); lv_obj_set_style_text_color(label, lv_color_hex(0xFFFFFF), LV_PART_MAIN); lv_obj_set_style_text_font(label, &lv_font_montserrat_20, LV_PART_MAIN); lv_obj_center(label); // 创建一个按钮 lv_obj_t *btn = lv_btn_create(scr); lv_obj_set_size(btn, 120, 50); lv_obj_align(btn, LV_ALIGN_CENTER, 0, 50); // 按钮上的标签 lv_obj_t *btn_label = lv_label_create(btn); lv_label_set_text(btn_label, "测试SDNAND"); lv_obj_center(btn_label); // 设置按钮点击事件 static lv_event_cb_t btn_event_cb = [](lv_event_t *e) { // 读取SDNAND测试 uint8_t data[1024] = {0}; esp_err_t ret = sdnand_read_page(0, data, 1024); if (ret == ESP_OK) { ESP_LOGI(TAG, "SDNAND读取成功"); } else { ESP_LOGI(TAG, "SDNAND读取失败"); } }; lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_CLICKED, NULL);}void app_main(void){ esp_err_t ret; // 初始化NVS ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } ESP_ERROR_CHECK(ret); ESP_LOGI(TAG, "ESP32-S3 启动中..."); // 初始化SDNAND ret = sdnand_init(); if (ret != ESP_OK) { ESP_LOGE(TAG, "SDNAND初始化失败"); return; } // 初始化LVGL显示系统 ret = lvgl_driver_init(); if (ret != ESP_OK) { ESP_LOGE(TAG, "LVGL初始化失败"); return; } // 创建GUI create_sample_gui(); // LVGL定时器处理任务 static lv_timer_t *timer = NULL; timer = lv_timer_create([](lv_timer_t *timer) { lv_task_handler(); }, 5, NULL); ESP_LOGI(TAG, "系统初始化完成");}
idf.py menuconfig
编译项目:
idf.py build
烧录固件:
idf.py -p /dev/ttyUSB0 flash # 根据实际串口设备修改
idf.py -p /dev/ttyUSB0 monitor
验证 SDNAND 是否正常工作:
验证 LVGL 显示是否正常:
通过以上步骤,您应该能够成功在 ESP32-S3 上使用 SPI 模式驱动 SDNAND 并集成 LVGL 显示系统。如果遇到问题,请检查硬件连接和日志输出,逐步排查。