GOOUUU ESP32-S3-CAM 果云科技開發板開發指南(二)(超詳細!)Vscode+espidf 攝像頭拍攝視頻實時傳輸到LCD,文末附源碼

書接上回,上一篇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的…