书接上回,上一篇blog是使用esp32s3通过ov2640摄像头拍摄到一帧照片,并把它保存到了SD卡中,这第二篇就通过LCD将拍摄到的图片显示到LCD上,本次分享硬件使用的 ESP32-S3-CAM 果云科技开发板,并且使用了配套的LCD扩展板,扩展板上可以插一个驱动为ili9341的SPI屏

一:现象!

第一件事,还是摆具体实现的现象:

摄像头拍到的图像实时传输到LCD屏幕上,唯一 缺点是使用的图像RGB565的格式,图像不那么清晰

二:配置!

这次esp32用到的外设有camera、lcd以及led,psram这些,没有其他的什么,

先来讲讲这些基础配置,camera和led的配置代码如上一篇blog一样,不需要做什么改变,在每次主函数和任务开始前进行一个初始化操作即可,然后是lcd,lcd的初始化主要是参考了idf的example:spi_lcd_touch,

因为我们lcd与mcu的通信一般是spi,所以需要初始化lcd和mcu之间的spi通信,代码如下所示:

spi_bus_config_t buscfg = {.sclk_io_num = EXAMPLE_PIN_NUM_SCLK,.mosi_io_num = EXAMPLE_PIN_NUM_MOSI,.miso_io_num = EXAMPLE_PIN_NUM_MISO,.quadwp_io_num = -1,.quadhd_io_num = -1,.max_transfer_sz = EXAMPLE_LCD_H_RES * 80 * sizeof(uint16_t),
};
spi_bus_initialize(LCD_HOST, &buscfg, SPI_DMA_CH_AUTO);

这里面我们需要定义spi的三个引脚,max_transfer_sz是一次最大传输字节数,这个配置影响的是DMA的传输性能

紧接着是要初始化esp_lcd_panel_io,也就是LCD面板IO句柄

esp_lcd_panel_io_spi_config_t io_config = {.dc_gpio_num = EXAMPLE_PIN_NUM_LCD_DC,.cs_gpio_num = EXAMPLE_PIN_NUM_LCD_CS,.pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,.lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS,.lcd_param_bits = EXAMPLE_LCD_PARAM_BITS,.spi_mode = 0,.trans_queue_depth = 10,
};esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_HOST, &io_config, &io_handle);

在此之前我们还需要定义一个面板驱动

esp_lcd_panel_handle_t panel_handle = NULL;

最后是初始化具体的lcd驱动,包括重启的io和lcd显示的rgb格式

    // esp_lcd_panel_handle_t panel_handle = NULL;esp_lcd_panel_dev_config_t panel_config = {.reset_gpio_num = EXAMPLE_PIN_NUM_LCD_RST,.rgb_ele_order = ESP_LCD_COLOR_SPACE_RGB,.bits_per_pixel = 16,};

最后是需要检查驱动ILI9341屏幕这一系列命令有没有报错

    ESP_ERROR_CHECK(esp_lcd_new_panel_ili9341(io_handle, &panel_config, &panel_handle));ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, true));ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, true));ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, true, false));// user can flush pre-defined pattern to the screen before we turn on the screen or backlightESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));ESP_LOGI(TAG, "Turn on LCD backlight");gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, EXAMPLE_LCD_BK_LIGHT_ON_LEVEL);

这里面有一句很关键的代码,就是    ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, true));
ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, true));

这两句代码翻转了LCD屏显示的方向,从原来的纵向改成了横向显示,以及将ILI9341默认的BGR565颜色编码格式改成了RGB565

最后就是根据原理图修改io引脚,

具体修改之后的.h文件如下:

#define LCD_HOST  SPI2_HOST////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// Please update the following configuration according to your LCD spec //////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define EXAMPLE_LCD_PIXEL_CLOCK_HZ     (40 * 1000 * 1000)
#define EXAMPLE_LCD_BK_LIGHT_ON_LEVEL  1
#define EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL !EXAMPLE_LCD_BK_LIGHT_ON_LEVEL
#define EXAMPLE_PIN_NUM_SCLK           3
#define EXAMPLE_PIN_NUM_MOSI           45
#define EXAMPLE_PIN_NUM_MISO           46
#define EXAMPLE_PIN_NUM_LCD_DC         47
#define EXAMPLE_PIN_NUM_LCD_RST        21
#define EXAMPLE_PIN_NUM_LCD_CS         14
#define EXAMPLE_PIN_NUM_BK_LIGHT       0
#define EXAMPLE_PIN_NUM_TOUCH_CS       -256// The pixel number in horizontal and vertical#define EXAMPLE_LCD_H_RES              240
#define EXAMPLE_LCD_V_RES              320
#define LCD_WIDTH 320
#define LCD_HEIGHT 240
// #elif CONFIG_EXAMPLE_LCD_CONTROLLER_GC9A01
// #define EXAMPLE_LCD_H_RES              240
// #define EXAMPLE_LCD_V_RES              240
// #endif
// Bit number used to represent command and parameter
#define EXAMPLE_LCD_CMD_BITS           8
#define EXAMPLE_LCD_PARAM_BITS         8

三:学习历程

为了实现我们最终想要实现的功能,我将他划分成了三个部分,首先是需要camera。camera可以捕获到照片,这是第一步,而第一步我们在第一篇blog已经完成了,然后是第二步,我们需要点亮lcd屏幕,这个部分我主要参考的是上文中提到的example,

代码我也贴在下面,

/** SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD** SPDX-License-Identifier: CC0-1.0*/#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_timer.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h"
#include "driver/gpio.h"
#include "driver/spi_master.h"
#include "esp_err.h"
#include "esp_log.h"
#include "lvgl.h"#if CONFIG_EXAMPLE_LCD_CONTROLLER_ILI9341
#include "esp_lcd_ili9341.h"
#elif CONFIG_EXAMPLE_LCD_CONTROLLER_GC9A01
#include "esp_lcd_gc9a01.h"
#endif#if CONFIG_EXAMPLE_LCD_TOUCH_CONTROLLER_STMPE610
#include "esp_lcd_touch_stmpe610.h"
#endifstatic const char *TAG = "example";// Using SPI2 in the example
#define LCD_HOST  SPI2_HOST////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// Please update the following configuration according to your LCD spec //////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define EXAMPLE_LCD_PIXEL_CLOCK_HZ     (40 * 1000 * 1000)
#define EXAMPLE_LCD_BK_LIGHT_ON_LEVEL  1
#define EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL !EXAMPLE_LCD_BK_LIGHT_ON_LEVEL
#define EXAMPLE_PIN_NUM_SCLK           3
#define EXAMPLE_PIN_NUM_MOSI           45
#define EXAMPLE_PIN_NUM_MISO           46
#define EXAMPLE_PIN_NUM_LCD_DC         47
#define EXAMPLE_PIN_NUM_LCD_RST        21
#define EXAMPLE_PIN_NUM_LCD_CS         14
#define EXAMPLE_PIN_NUM_BK_LIGHT       0
#define EXAMPLE_PIN_NUM_TOUCH_CS       1// The pixel number in horizontal and vertical
#if CONFIG_EXAMPLE_LCD_CONTROLLER_ILI9341
#define EXAMPLE_LCD_H_RES              240
#define EXAMPLE_LCD_V_RES              320
#elif CONFIG_EXAMPLE_LCD_CONTROLLER_GC9A01
#define EXAMPLE_LCD_H_RES              240
#define EXAMPLE_LCD_V_RES              240
#endif
// Bit number used to represent command and parameter
#define EXAMPLE_LCD_CMD_BITS           8
#define EXAMPLE_LCD_PARAM_BITS         8#define EXAMPLE_LVGL_TICK_PERIOD_MS    2
#define EXAMPLE_LVGL_TASK_MAX_DELAY_MS 500
#define EXAMPLE_LVGL_TASK_MIN_DELAY_MS 1
#define EXAMPLE_LVGL_TASK_STACK_SIZE   (4 * 1024)
#define EXAMPLE_LVGL_TASK_PRIORITY     2static SemaphoreHandle_t lvgl_mux = NULL;#if CONFIG_EXAMPLE_LCD_TOUCH_ENABLED
esp_lcd_touch_handle_t tp = NULL;
#endifextern void example_lvgl_demo_ui(lv_disp_t *disp);static bool example_notify_lvgl_flush_ready(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx)
{lv_disp_drv_t *disp_driver = (lv_disp_drv_t *)user_ctx;lv_disp_flush_ready(disp_driver);return false;
}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;// copy a buffer's content to a specific area of the displayesp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map);
}/* Rotate display and touch, when rotated screen in LVGL. Called when driver parameters are updated. */
static void example_lvgl_port_update_callback(lv_disp_drv_t *drv)
{esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data;switch (drv->rotated) {case LV_DISP_ROT_NONE:// Rotate LCD displayesp_lcd_panel_swap_xy(panel_handle, false);esp_lcd_panel_mirror(panel_handle, true, false);
#if CONFIG_EXAMPLE_LCD_TOUCH_ENABLED// Rotate LCD touchesp_lcd_touch_set_mirror_y(tp, false);esp_lcd_touch_set_mirror_x(tp, false);
#endifbreak;case LV_DISP_ROT_90:// Rotate LCD displayesp_lcd_panel_swap_xy(panel_handle, true);esp_lcd_panel_mirror(panel_handle, true, true);
#if CONFIG_EXAMPLE_LCD_TOUCH_ENABLED// Rotate LCD touchesp_lcd_touch_set_mirror_y(tp, false);esp_lcd_touch_set_mirror_x(tp, false);
#endifbreak;case LV_DISP_ROT_180:// Rotate LCD displayesp_lcd_panel_swap_xy(panel_handle, false);esp_lcd_panel_mirror(panel_handle, false, true);
#if CONFIG_EXAMPLE_LCD_TOUCH_ENABLED// Rotate LCD touchesp_lcd_touch_set_mirror_y(tp, false);esp_lcd_touch_set_mirror_x(tp, false);
#endifbreak;case LV_DISP_ROT_270:// Rotate LCD displayesp_lcd_panel_swap_xy(panel_handle, true);esp_lcd_panel_mirror(panel_handle, false, false);
#if CONFIG_EXAMPLE_LCD_TOUCH_ENABLED// Rotate LCD touchesp_lcd_touch_set_mirror_y(tp, false);esp_lcd_touch_set_mirror_x(tp, false);
#endifbreak;}
}#if CONFIG_EXAMPLE_LCD_TOUCH_ENABLED
static void example_lvgl_touch_cb(lv_indev_drv_t * drv, lv_indev_data_t * data)
{uint16_t touchpad_x[1] = {0};uint16_t touchpad_y[1] = {0};uint8_t touchpad_cnt = 0;/* Read touch controller data */esp_lcd_touch_read_data(drv->user_data);/* Get coordinates */bool touchpad_pressed = esp_lcd_touch_get_coordinates(drv->user_data, touchpad_x, touchpad_y, NULL, &touchpad_cnt, 1);if (touchpad_pressed && touchpad_cnt > 0) {data->point.x = touchpad_x[0];data->point.y = touchpad_y[0];data->state = LV_INDEV_STATE_PRESSED;} else {data->state = LV_INDEV_STATE_RELEASED;}
}
#endifstatic void example_increase_lvgl_tick(void *arg)
{/* Tell LVGL how many milliseconds has elapsed */lv_tick_inc(EXAMPLE_LVGL_TICK_PERIOD_MS);
}bool example_lvgl_lock(int timeout_ms)
{// Convert timeout in milliseconds to FreeRTOS ticks// If `timeout_ms` is set to -1, the program will block until the condition is metconst TickType_t timeout_ticks = (timeout_ms == -1) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);return xSemaphoreTakeRecursive(lvgl_mux, timeout_ticks) == pdTRUE;
}void example_lvgl_unlock(void)
{xSemaphoreGiveRecursive(lvgl_mux);
}static void example_lvgl_port_task(void *arg)
{ESP_LOGI(TAG, "Starting LVGL task");uint32_t task_delay_ms = EXAMPLE_LVGL_TASK_MAX_DELAY_MS;while (1) {// Lock the mutex due to the LVGL APIs are not thread-safeif (example_lvgl_lock(-1)) {task_delay_ms = lv_timer_handler();// Release the mutexexample_lvgl_unlock();}if (task_delay_ms > EXAMPLE_LVGL_TASK_MAX_DELAY_MS) {task_delay_ms = EXAMPLE_LVGL_TASK_MAX_DELAY_MS;} else if (task_delay_ms < EXAMPLE_LVGL_TASK_MIN_DELAY_MS) {task_delay_ms = EXAMPLE_LVGL_TASK_MIN_DELAY_MS;}vTaskDelay(pdMS_TO_TICKS(task_delay_ms));}
}void app_main(void)
{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 functionsESP_LOGI(TAG, "Turn off LCD backlight");gpio_config_t bk_gpio_config = {.mode = GPIO_MODE_OUTPUT,.pin_bit_mask = 1ULL << EXAMPLE_PIN_NUM_BK_LIGHT};ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));ESP_LOGI(TAG, "Initialize SPI bus");spi_bus_config_t buscfg = {.sclk_io_num = EXAMPLE_PIN_NUM_SCLK,.mosi_io_num = EXAMPLE_PIN_NUM_MOSI,.miso_io_num = EXAMPLE_PIN_NUM_MISO,.quadwp_io_num = -1,.quadhd_io_num = -1,.max_transfer_sz = EXAMPLE_LCD_H_RES * 80 * sizeof(uint16_t),};ESP_ERROR_CHECK(spi_bus_initialize(LCD_HOST, &buscfg, SPI_DMA_CH_AUTO));ESP_LOGI(TAG, "Install panel IO");esp_lcd_panel_io_handle_t io_handle = NULL;esp_lcd_panel_io_spi_config_t io_config = {.dc_gpio_num = EXAMPLE_PIN_NUM_LCD_DC,.cs_gpio_num = EXAMPLE_PIN_NUM_LCD_CS,.pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,.lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS,.lcd_param_bits = EXAMPLE_LCD_PARAM_BITS,.spi_mode = 0,.trans_queue_depth = 10,.on_color_trans_done = example_notify_lvgl_flush_ready,.user_ctx = &disp_drv,};// Attach the LCD to the SPI busESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_HOST, &io_config, &io_handle));esp_lcd_panel_handle_t panel_handle = NULL;esp_lcd_panel_dev_config_t panel_config = {.reset_gpio_num = EXAMPLE_PIN_NUM_LCD_RST,.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR,.bits_per_pixel = 16,};
#if CONFIG_EXAMPLE_LCD_CONTROLLER_ILI9341ESP_LOGI(TAG, "Install ILI9341 panel driver");ESP_ERROR_CHECK(esp_lcd_new_panel_ili9341(io_handle, &panel_config, &panel_handle));
#elif CONFIG_EXAMPLE_LCD_CONTROLLER_GC9A01ESP_LOGI(TAG, "Install GC9A01 panel driver");ESP_ERROR_CHECK(esp_lcd_new_panel_gc9a01(io_handle, &panel_config, &panel_handle));
#endifESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
#if CONFIG_EXAMPLE_LCD_CONTROLLER_GC9A01ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, true));
#endifESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, true, false));// user can flush pre-defined pattern to the screen before we turn on the screen or backlightESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));#if CONFIG_EXAMPLE_LCD_TOUCH_ENABLEDesp_lcd_panel_io_handle_t tp_io_handle = NULL;esp_lcd_panel_io_spi_config_t tp_io_config = ESP_LCD_TOUCH_IO_SPI_STMPE610_CONFIG(EXAMPLE_PIN_NUM_TOUCH_CS);// Attach the TOUCH to the SPI busESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_HOST, &tp_io_config, &tp_io_handle));esp_lcd_touch_config_t tp_cfg = {.x_max = EXAMPLE_LCD_H_RES,.y_max = EXAMPLE_LCD_V_RES,.rst_gpio_num = -1,.int_gpio_num = -1,.flags = {.swap_xy = 0,.mirror_x = 0,.mirror_y = 0,},};#if CONFIG_EXAMPLE_LCD_TOUCH_CONTROLLER_STMPE610ESP_LOGI(TAG, "Initialize touch controller STMPE610");ESP_ERROR_CHECK(esp_lcd_touch_new_spi_stmpe610(tp_io_handle, &tp_cfg, &tp));
#endif // CONFIG_EXAMPLE_LCD_TOUCH_CONTROLLER_STMPE610
#endif // CONFIG_EXAMPLE_LCD_TOUCH_ENABLEDESP_LOGI(TAG, "Turn on LCD backlight");gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, EXAMPLE_LCD_BK_LIGHT_ON_LEVEL);ESP_LOGI(TAG, "Initialize LVGL library");lv_init();// alloc draw buffers used by LVGL// it's recommended to choose the size of the draw buffer(s) to be at least 1/10 screen sizedlv_color_t *buf1 = heap_caps_malloc(EXAMPLE_LCD_H_RES * 20 * sizeof(lv_color_t), MALLOC_CAP_DMA);assert(buf1);lv_color_t *buf2 = heap_caps_malloc(EXAMPLE_LCD_H_RES * 20 * sizeof(lv_color_t), MALLOC_CAP_DMA);assert(buf2);// initialize LVGL draw bufferslv_disp_draw_buf_init(&disp_buf, buf1, buf2, EXAMPLE_LCD_H_RES * 20);ESP_LOGI(TAG, "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.drv_update_cb = example_lvgl_port_update_callback;disp_drv.draw_buf = &disp_buf;disp_drv.user_data = panel_handle;lv_disp_t *disp = lv_disp_drv_register(&disp_drv);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, EXAMPLE_LVGL_TICK_PERIOD_MS * 1000));#if CONFIG_EXAMPLE_LCD_TOUCH_ENABLEDstatic lv_indev_drv_t indev_drv;    // Input device driver (Touch)lv_indev_drv_init(&indev_drv);indev_drv.type = LV_INDEV_TYPE_POINTER;indev_drv.disp = disp;indev_drv.read_cb = example_lvgl_touch_cb;indev_drv.user_data = tp;lv_indev_drv_register(&indev_drv);
#endiflvgl_mux = xSemaphoreCreateRecursiveMutex();assert(lvgl_mux);ESP_LOGI(TAG, "Create LVGL task");xTaskCreate(example_lvgl_port_task, "LVGL", EXAMPLE_LVGL_TASK_STACK_SIZE, NULL, EXAMPLE_LVGL_TASK_PRIORITY, NULL);ESP_LOGI(TAG, "Display LVGL Meter Widget");// Lock the mutex due to the LVGL APIs are not thread-safeif (example_lvgl_lock(-1)) {example_lvgl_demo_ui(disp);// Release the mutexexample_lvgl_unlock();}
}

主要用的是lvgl,在根据原理图我修改了IO口配置后,也成功将lcd点亮,

效果图如下:

这一步通过lvgl也完成了,然后就需要将lcd代码加入到我们已有的工程中:

首先是将lvgl、ili9341显示屏驱动加入到工程中

使用命令:

idf.py add-dependency "lvgl/lvgl@v8.3.3"
idf.py add-dependency "someuser/esp_lcd_ili9341"

即可将需要的依赖加入其中,然后是先编译一下,将这些依赖编译加入到我们的项目中,然后移植一下,就可以实现example中的效果

最后,就是将拍摄到的图像显示到lcd屏幕上了

我们在这里创建一个lcd的task,通过camera的 esp_camera_fb_get这个函数,将捕获到的一帧数据存到一个指针中,然后通过判断拍摄到的分辨率和lcd是否一致,可以将这帧数据直接发送给lcd并且显示出来了,代码如下:

            // 如果分辨率和LCD一致if (fb->width == LCD_WIDTH && fb->height == LCD_HEIGHT && fb->format == PIXFORMAT_RGB565){// 直接送LCDesp_lcd_panel_draw_bitmap(panel_handle, 0, 0, LCD_WIDTH, LCD_HEIGHT, fb->buf);}else{ESP_LOGE("LCD", "Frame size/format mismatch: %dx%d fmt:%d", fb->width, fb->height, fb->format);}// 释放摄像头缓冲esp_camera_fb_return(fb);}else{ESP_LOGE("LCD", "Failed to get camera frame");}

因为之前的配置,我已经把所有的坑帮你们趟过去了,所以应该会很顺利的显示出来图像

,最后如果需要显示视频的话,加一个while循环就可以了。

void lcd_task(void *param)
{while (1){camera_fb_t *fb = esp_camera_fb_get();if (fb){// 如果分辨率和LCD一致if (fb->width == LCD_WIDTH && fb->height == LCD_HEIGHT && fb->format == PIXFORMAT_RGB565){// 直接送LCDesp_lcd_panel_draw_bitmap(panel_handle, 0, 0, LCD_WIDTH, LCD_HEIGHT, fb->buf);}else{ESP_LOGE("LCD", "Frame size/format mismatch: %dx%d fmt:%d", fb->width, fb->height, fb->format);}// 释放摄像头缓冲esp_camera_fb_return(fb);}else{ESP_LOGE("LCD", "Failed to get camera frame");}// 根据需要调整刷新率vTaskDelay(pdMS_TO_TICKS(33)); // ~100fps上限}
}

最后是main函数

void app_main(void)
{esp_err_t ret;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 functionsled_init();// 检测 PSRAM 存在性和大小if(esp_psram_get_size() == 0) {ESP_LOGE("BOOT", "PSRAM NOT DETECTED! Check hardware connection.");vTaskDelay(pdMS_TO_TICKS(1000));esp_restart(); // 自动重启}ESP_LOGI("BOOT", "PSRAM Size: %d KB", esp_psram_get_size() / 1024);// // 初始化 PSRAM 缓存esp_psram_init();// 启用 PSRAM 分配// heap_caps_malloc_extmem_enable(64); // 最小分配单元64字节bsp_camera_init();lcd_init();xTaskCreatePinnedToCore(lcd_task,"lcd",4096,NULL,3,NULL,1);xTaskCreatePinnedToCore(led_blink_task,"led",4096,NULL,3,NULL,1);
//  x

最后源码链接:

通过网盘分享的文件:esp32_video
链接: https://pan.baidu.com/s/1WMGu-un_VoYQoCyNi2LvMQ?pwd=vide 提取码: vide 
--来自百度网盘超级会员v4的分享

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/93091.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/93091.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/93091.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

攻防世界-ics-05(远程文件执行)

一.审题大致浏览一下网页&#xff0c;发现就这边会有东西。看一下源码会不会有东西或者稍微点击一下这个页面的内容看会不会出现东西。点击了一下这个云平台设备维护中心发现url变了&#xff0c;是get的方法传page参数二.尝试漏洞类型自己这边试了sql注入发现不是&#xff0c;试…

Dell PowerEdge: Servers by generation (按代系划分的服务器)

Dell PowerEdge: Servers by generation {按代系划分的服务器}1. Table of 17th, 16th, 15th, and 14th Generation PowerEdge servers2. List of all PowerEdge server models including Type, CPU vendor, Generation, and Remote ManagementReferencesPowerEdge: Servers by…

Rust学习笔记(二)|变量、函数与控制流

本篇文章包含的内容1 变量与常量2 类型2.1 标量类型2.2 复合类型3 函数4 控制流4.1 分支4.2 循环1 变量与常量 在Rust中&#xff0c;使用let关键字声明一个变量&#xff0c;变量默认是不可变的。如果要声明可变变量&#xff0c;需要使用mut关键字将其声明为可变变量。 let x …

【渲染流水线】[几何阶段]-[图元装配]以UnityURP为例

【从UnityURP开始探索游戏渲染】专栏-直达 前情提要 【渲染流水线】主线索引-从数据到图像以UnityURP为例-CSDN博客 图元装配负责将离散顶点组装成完整几何图元&#xff08;如点、线、三角形、三角形条带&#xff09; &#xff08;对渲染的探索是个持续不断完善的过程&#x…

jvm有哪些垃圾回收器,实际中如何选择?

7.G1收集器一款面向服务端应用的垃圾收集器。 特点如下&#xff1a; 并行与并发&#xff1a;G1能充分利用多CPU、多核环境下的硬 件优势&#xff0c;使用多个CPU来缩短Stop-The-World停顿时间。部分收集器原本需要停顿Java线程来执行GC动作&#xff0c;G1收 集器仍然可以通过并…

多语言与隐形攻击:LLM安全防线为何被频频突破?

你是否曾以为&#xff0c;只要加装了“防火墙”&#xff0c;大型语言模型&#xff08;LLM&#xff09;就能高枕无忧&#xff1f;Trendoyl 的实际测试却让我大吃一惊&#xff1a;即便部署了 Meta 的 Llama Guard&#xff0c;攻击者还是能轻松用多语种、字符混淆&#xff0c;甚至…

分布式光伏气象站:为光伏电站的 “气象感知眼”

分布式光伏气象站&#xff1a;为光伏电站的 “气象感知眼”柏峰 【BF-GFQX】在全球能源转型的浪潮中&#xff0c;分布式光伏发电凭借其就近消纳、清洁高效的优势&#xff0c;成为能源结构优化的重要力量。而分布式光伏气象站&#xff0c;作为光伏电站的 “智慧感知眼”&#xf…

TCP与UDP:如何选择最佳传输协议

应用场景选择如果需要可靠传输&#xff0c;首选 TCP如果需要传输的数据包很大&#xff0c;也首选 TCP绝大部分的场景&#xff0c;都可以优先考虑 TCPUDP 相比于 TCP&#xff0c;最大的优点在于传输效率有些情况&#xff0c;既需要可靠性又需要性能&#xff0c;这个时候时候就需…

《Leetcode》-面试题-hot100-栈

题目列表 20. 有效的括号 简单难度 leetcode链接 155. 最小栈 中等难度 leetcode链接 394. 字符串解码 中等难度 leetcode链接 739. 每日温度 中等难度 leetcode链接 84. 柱状图中最大的矩形 困难难度 leetcode链接 题目 &#xff08;1&#xff09;有效的括号 题目 给…

GPT-5、Claude-4 同台亮相!OneEval发布全新“大模型+知识库”评测白皮书!

OneEval官网地址&#xff1a;http://OneEval.OpenKG.cnOneEval文章链接&#xff1a;https://arxiv.org/abs/2506.12577要点导读 今年4月&#xff0c;OpenKG发布“大模型知识库”融合能力评估榜单OneEval v1.0。近期&#xff0c;OpenKG在此基础上&#xff0c;组织撰写了OneEv…

【最新版】沃德云商协系统全开源+uniapp小程序

一.介绍沃德云商协是一款基于FastAdmin&#xff08;thinkphp&#xff09;Uniapp开发的“多组织”的云服务平台&#xff0c;打造总商会、总协会、总校友会、工商联等多组织无障碍沟通合作平台&#xff0c;让各大分会、各大分校友会、分组织实现轻松管理&#xff0c;线上宣传展示…

Wireshark专家模式定位网络故障:14种TCP异常深度解剖

TCP连接如同精密运转的传送带&#xff0c;每一个异常数据包都是故障的早期信号。作为网络工程师的“外科手术刀”&#xff0c;Wireshark在TCP故障诊断领域的价值无可替代。本文将通过14个真实故障场景&#xff0c;揭示如何利用Wireshark专家系统&#xff08;Expert System&…

Python Day28 HTML 与 CSS 核心知识点 及例题分析

一、HTML 布局标签&#xff08;含 H5 语义化标签&#xff09;传统布局多使用div标签&#xff0c;H5 新增语义化标签增强可读性&#xff1a;核心知识点header&#xff1a;替代div#header&#xff0c;用于页面头部&#xff08;如标题、导航&#xff09;。footer&#xff1a;替代d…

MySQL 数据库表操作与查询实战案例

MySQL 数据库表操作与查询实战案例 在数据库学习过程中&#xff0c;熟练掌握表的创建、数据插入及各类查询操作是基础且重要的技能。本文将通过实际案例&#xff0c;详细介绍 MySQL 中数据库表的设计、数据插入以及常用的查询操作&#xff0c;帮助初学者快速上手。 项目一&…

THCV215一种高速视频数据收发器,采用低电压差分信号(LVDS)技术支持高速串行数据传输,支持1080p/60Hz高分辨率传输

THCV215 是一款符合 V-by-One HS 标准的 高速视频数据收发器。THCV215和THCV216被设计为支持主机和显示器之间的视频数据传输。该芯片组可以在20MHz至100MHz的LVDS时钟频率下&#xff0c;仅通过一根差分电缆传输39bit视频数据和3bit同步数据。该芯片组有两个高速数据通道&#…

Linux 系统下 VS Code 降级至 1.85 版本教程:通过历史版本网站解决兼容性问题

一、问题背景 当前使用的 VS Code 版本为 1.102.3&#xff0c;这一版本可能是未来版本、内部测试版或 Insiders 版本&#xff0c;而目前最新的稳定版属于 1.8x 系列。由于版本过新&#xff0c;可能导致与部分插件&#xff08;如旧版 Remote-SSH&#xff09;或系统环境不兼容。…

一个基于 PyTorch 的完整模型训练流程

一个基于 PyTorch 的完整模型训练流程 flyfish训练步骤具体操作目的1. 训练前准备设置随机种子、配置超参数&#xff08;batch size、学习率等&#xff09;、选择计算设备&#xff08;CPU/GPU&#xff09;确保实验可复现&#xff1b;统一控制训练关键参数&#xff1b;利用硬件加…

ffmpeg,ffplay, vlc,rtsp-simple-server,推拉流命令使用方法,及测试(二)

一、常用命令 ffmpeg 推流命令 : ffmpeg -re -i input.mp4 -c copy -f flv rtmp://39.105.129.233/myapp/ffmpeg -re -i input.mp4 -c copy -f flv rtsp://39.105.129.233/myapp/-re 读取流 -i 输入文件 -f # 指定推流formatffplay 拉流命令 : ffplay rtmp://39.105.129.233/m…

使用行为树控制机器人(三) ——通用端口

文章目录一、通用端口功能实现1. 功能实现1.1 头文件定义1.2 源文件实现1.3 main文件实现1.4 tree.xml 实现2. 执行结果使用行为树控制机器人(一) —— 节点使用行为树控制机器人(二) —— 黑板使用行为树控制机器人(三) —— 通用端口有了上述前两节我们已经可以实现节点间的通…

DataDome反爬虫验证技术深度解析:无感、滑块与设备验证全攻略

DataDome反爬虫验证技术深度解析&#xff1a;无感、滑块与设备验证全攻略 随着网络安全威胁的不断演进&#xff0c;企业对数据保护的需求日益增强。DataDome作为业界领先的反爬虫解决方案&#xff0c;以其三层防护机制在众多知名网站中得到广泛应用。本文将深入解析DataDome的…