前言:
本章主要讲述 ESP32S3(N16R8) CAMERA 显示到LCD屏(驱动ST7789,SPI 8pin) 上

1:硬件及环境
ESP32-IDF5.4
ESP32S3-CAM(N16R8 44pin),不是 S3-EYE版本的
LCD 2.4 320*240 (st7789 SPI 8PIN引脚,就是有背板的)
面包板 杜邦线 若干

2:上代码
代码基于 helloworld 模板 开发

idf_component.yml
可以用命令增加 依赖
idf.py add-dependency “espressif/esp32-camera”

## IDF Component Manager Manifest File
dependencies:
  ## Required IDF version
  idf:
    version: '>=4.1.0'
  espressif/esp32-camera: '*'

espressif/esp32-camera 项目自带依赖 esp_jpeg 所以不需要额外增加到当前项目里
在这里插入图片描述

main/CMakeLists.txt

idf_component_register(SRCS "hello_world_main.c"
                    PRIV_REQUIRES spi_flash 
                    REQUIRES driver nvs_flash  esp_lcd esp_timer
                    INCLUDE_DIRS "")

hello_world_main.c

/*
 * SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: CC0-1.0
 */

#include <esp_log.h>
#include <esp_err.h>
#include <nvs.h>
#include <nvs_flash.h>
#include <driver/gpio.h>
#include "esp_camera.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_timer.h"
#include "math.h"

///lcd driver  spi 8pin//////////////////
#include "driver/spi_master.h"
#include "esp_lcd_panel_ops.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_heap_caps.h"
#include "esp_lcd_panel_io.h"

#define TAG "main"
/////////////////////////////////////////////////////////////////////////////////
// 摄像头引脚配置
#define PWDN_GPIO_NUM     -1
#define RESET_GPIO_NUM    -1
#define XCLK_GPIO_NUM     15
#define SIOD_GPIO_NUM     4
#define SIOC_GPIO_NUM     5
#define Y9_GPIO_NUM       16
#define Y8_GPIO_NUM       17
#define Y7_GPIO_NUM       18
#define Y6_GPIO_NUM       12
#define Y5_GPIO_NUM       10
#define Y4_GPIO_NUM       8
#define Y3_GPIO_NUM       9
#define Y2_GPIO_NUM       11
#define VSYNC_GPIO_NUM    6
#define HREF_GPIO_NUM     7
#define PCLK_GPIO_NUM     13
/////////////////////////////////////////////////////////////////////////////////////

//st7789 lcd driver///////////////////////////////////////////////////////////
#define LCD_HOST       SPI2_HOST
#define CONFIG_EXAMPLE_LCD_FLUSH_PARALLEL_LINES 16
#define PARALLEL_LINES CONFIG_EXAMPLE_LCD_FLUSH_PARALLEL_LINES

#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_LCD_H_RES              320
#define EXAMPLE_LCD_V_RES              240
#define EXAMPLE_LCD_CMD_BITS           8
#define EXAMPLE_LCD_PARAM_BITS         8

#define EXAMPLE_PIN_NUM_DATA0          GPIO_NUM_2
#define EXAMPLE_PIN_NUM_PCLK           GPIO_NUM_1
#define EXAMPLE_PIN_NUM_CS             GPIO_NUM_40
#define EXAMPLE_PIN_NUM_DC             GPIO_NUM_41
#define EXAMPLE_PIN_NUM_RST            GPIO_NUM_42
#define EXAMPLE_PIN_NUM_BK_LIGHT       GPIO_NUM_39

// 图像缓冲区
#define CAMERA_WIDTH   640
#define CAMERA_HEIGHT  480
#define LCD_WIDTH      320
#define LCD_HEIGHT     240

static uint16_t *lcd_frame_buffer = NULL;
static esp_lcd_panel_handle_t panel_handle = NULL;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////

// 摄像头配置 - 尝试不同的格式和分辨率
static camera_config_t camera_example_config = {
    .pin_pwdn       = PWDN_GPIO_NUM,
    .pin_reset      = RESET_GPIO_NUM,
    .pin_xclk       = XCLK_GPIO_NUM,
    .pin_sccb_sda   = SIOD_GPIO_NUM,
    .pin_sccb_scl   = SIOC_GPIO_NUM,

    .pin_d7         = Y9_GPIO_NUM,
    .pin_d6         = Y8_GPIO_NUM,
    .pin_d5         = Y7_GPIO_NUM,
    .pin_d4         = Y6_GPIO_NUM,
    .pin_d3         = Y5_GPIO_NUM,
    .pin_d2         = Y4_GPIO_NUM,
    .pin_d1         = Y3_GPIO_NUM,
    .pin_d0         = Y2_GPIO_NUM,

    .pin_vsync      = VSYNC_GPIO_NUM,
    .pin_href       = HREF_GPIO_NUM,
    .pin_pclk       = PCLK_GPIO_NUM,

    .xclk_freq_hz   = 20000000,
    .ledc_timer     = LEDC_TIMER_0,
    .ledc_channel   = LEDC_CHANNEL_0,
    .pixel_format   = PIXFORMAT_RGB565,  // 使用RGB565
    .frame_size     = FRAMESIZE_QVGA,     // 640x480
    .jpeg_quality   = 12,
    .fb_count       = 3,
    .fb_location    = CAMERA_FB_IN_PSRAM,
    .grab_mode      = CAMERA_GRAB_WHEN_EMPTY
};

esp_err_t camera_example_init(){
    esp_err_t err = esp_camera_init(&camera_example_config);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Camera Init Failed: 0x%x", err);
    } else {
        ESP_LOGI(TAG, "Camera Init Success");
        
        // 获取传感器并调整参数
        sensor_t *s = esp_camera_sensor_get();
        if (s != NULL) {
            ESP_LOGI(TAG, "Camera sensor PID: 0x%02x", s->id.PID);
            
            // 设置传感器参数
            s->set_brightness(s, 0);     // 亮度
            s->set_contrast(s, 0);       // 对比度
            s->set_saturation(s, 0);     // 饱和度
            s->set_special_effect(s, 0); // 特效 (0: 无)
            s->set_whitebal(s, 1);       // 白平衡 (1: 自动)
            s->set_awb_gain(s, 1);       // 自动白平衡增益
            s->set_wb_mode(s, 0);        // 白平衡模式
            s->set_exposure_ctrl(s, 1);  // 自动曝光
            s->set_aec2(s, 0);           // AEC2
            s->set_ae_level(s, 0);       // AE级别
            s->set_aec_value(s, 300);    // 曝光值
            s->set_gain_ctrl(s, 1);      // 自动增益
            s->set_agc_gain(s, 0);       // AGC增益
            s->set_gainceiling(s, (gainceiling_t)0);  // 增益上限
            s->set_bpc(s, 0);            // 黑像素校正
            s->set_wpc(s, 1);            // 白像素校正
            s->set_raw_gma(s, 1);        // RAW GMA
            s->set_lenc(s, 1);           // 镜头校正
            s->set_hmirror(s, 0);        // 水平镜像
            s->set_vflip(s, 0);          // 垂直翻转
            s->set_dcw(s, 1);            // DCW
            s->set_colorbar(s, 0);       // 颜色条测试模式
            
            ESP_LOGI(TAG, "Camera sensor configured");
        }
    }
    return err;
}

// 生成彩色测试图案
void draw_color_test_pattern(uint16_t *buffer) {
    // 创建彩虹渐变
    for (int y = 0; y < LCD_HEIGHT; y++) {
        for (int x = 0; x < LCD_WIDTH; x++) {
            // 计算HSV颜色
            float h = (float)x / LCD_WIDTH * 360.0f;  // 色相
            float s = 1.0f;                          // 饱和度
            float v = (float)y / LCD_HEIGHT;         // 亮度
            
            // HSV转RGB
            float c = v * s;
            float x_val = c * (1 - fabs(fmod(h / 60.0f, 2) - 1));
            float m = v - c;
            
            float r, g, b;
            if (h < 60) {
                r = c; g = x_val; b = 0;
            } else if (h < 120) {
                r = x_val; g = c; b = 0;
            } else if (h < 180) {
                r = 0; g = c; b = x_val;
            } else if (h < 240) {
                r = 0; g = x_val; b = c;
            } else if (h < 300) {
                r = x_val; g = 0; b = c;
            } else {
                r = c; g = 0; b = x_val;
            }
            
            // 转换为RGB565
            uint8_t red = (uint8_t)((r + m) * 255);
            uint8_t green = (uint8_t)((g + m) * 255);
            uint8_t blue = (uint8_t)((b + m) * 255);
            
            uint16_t color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
            buffer[y * LCD_WIDTH + x] = color;
        }
    }
}

// 简单的颜色条测试
void draw_color_bars(uint16_t *buffer) {
    // 定义标准颜色条
    uint16_t colors[] = {
        0xF800, // 红
        0xF8C0, // 橙
        0xFFE0, // 黄  
        0x87E0, // 绿
        0x07FF, // 青
        0x001F, // 蓝
        0x781F, // 紫
        0xFFFF, // 白
        0x7BEF, // 灰
        0x0000  // 黑
    };
    
    int bar_width = LCD_WIDTH / 10;
    
    for (int y = 0; y < LCD_HEIGHT; y++) {
        for (int x = 0; x < LCD_WIDTH; x++) {
            int color_index = x / bar_width;
            if (color_index >= 10) color_index = 9;
            buffer[y * LCD_WIDTH + x] = colors[color_index];
        }
    }
}

// 检查摄像头数据格式
void analyze_camera_data(camera_fb_t *fb) {
    static int analysis_count = 0;
    if (analysis_count++ % 60 != 0) return; // 每60帧分析一次
    
    ESP_LOGI(TAG, "Camera data analysis:");
    ESP_LOGI(TAG, "  Format: %d, Size: %dx%d, Length: %zu", 
             fb->format, fb->width, fb->height, fb->len);
    
    // 检查前几个像素值
    if (fb->format == PIXFORMAT_RGB565 && fb->len >= 10) {
        uint16_t *pixels = (uint16_t*)fb->buf;
        ESP_LOGI(TAG, "  First 5 pixels: 0x%04X, 0x%04X, 0x%04X, 0x%04X, 0x%04X",
                 pixels[0], pixels[1], pixels[2], pixels[3], pixels[4]);
        
        // 分析颜色分布
        int red_count = 0, green_count = 0, blue_count = 0, gray_count = 0;
        for (int i = 0; i < 100; i++) { // 采样前100个像素
            uint16_t pixel = pixels[i];
            uint8_t r = (pixel >> 11) & 0x1F;
            uint8_t g = (pixel >> 5) & 0x3F;
            uint8_t b = pixel & 0x1F;
            
            if (r > g && r > b) red_count++;
            else if (g > r && g > b) green_count++;
            else if (b > r && b > g) blue_count++;
            else gray_count++;
        }
        
        ESP_LOGI(TAG, "  Color distribution - R:%d, G:%d, B:%d, Gray:%d",
                 red_count, green_count, blue_count, gray_count);
    }
}

// 高质量缩放函数 - 双线性插值
void scale_image_high_quality(const uint8_t *src, uint16_t *dst,
                             int src_width, int src_height,
                             int dst_width, int dst_height) {
    float x_ratio = (float)(src_width - 1) / dst_width;
    float y_ratio = (float)(src_height - 1) / dst_height;
    
    for (int y = 0; y < dst_height; y++) {
        for (int x = 0; x < dst_width; x++) {
            float x_src = x * x_ratio;
            float y_src = y * y_ratio;
            
            int x1 = (int)x_src;
            int y1 = (int)y_src;
            int x2 = x1 + 1;
            int y2 = y1 + 1;
            
            // 边界检查
            if (x2 >= src_width) x2 = src_width - 1;
            if (y2 >= src_height) y2 = src_height - 1;
            
            // 获取四个相邻像素
            int idx1 = (y1 * src_width + x1) * 2;
            int idx2 = (y1 * src_width + x2) * 2;
            int idx3 = (y2 * src_width + x1) * 2;
            int idx4 = (y2 * src_width + x2) * 2;
            
            uint16_t p1 = (src[idx1] << 8) | src[idx1 + 1];
            uint16_t p2 = (src[idx2] << 8) | src[idx2 + 1];
            uint16_t p3 = (src[idx3] << 8) | src[idx3 + 1];
            uint16_t p4 = (src[idx4] << 8) | src[idx4 + 1];
            
            // 双线性插值
            float dx = x_src - x1;
            float dy = y_src - y1;
            
            // 提取RGB分量
            int r1 = (p1 >> 11) & 0x1F;
            int g1 = (p1 >> 5) & 0x3F;
            int b1 = p1 & 0x1F;
            
            int r2 = (p2 >> 11) & 0x1F;
            int g2 = (p2 >> 5) & 0x3F;
            int b2 = p2 & 0x1F;
            
            int r3 = (p3 >> 11) & 0x1F;
            int g3 = (p3 >> 5) & 0x3F;
            int b3 = p3 & 0x1F;
            
            int r4 = (p4 >> 11) & 0x1F;
            int g4 = (p4 >> 5) & 0x3F;
            int b4 = p4 & 0x1F;
            
            // 插值计算
            int r = (int)(r1 * (1-dx) * (1-dy) + r2 * dx * (1-dy) + r3 * (1-dx) * dy + r4 * dx * dy);
            int g = (int)(g1 * (1-dx) * (1-dy) + g2 * dx * (1-dy) + g3 * (1-dx) * dy + g4 * dx * dy);
            int b = (int)(b1 * (1-dx) * (1-dy) + b2 * dx * (1-dy) + b3 * (1-dx) * dy + b4 * dx * dy);
            
            // 限制范围
            r = (r < 0) ? 0 : (r > 31) ? 31 : r;
            g = (g < 0) ? 0 : (g > 63) ? 63 : g;
            b = (b < 0) ? 0 : (b > 31) ? 31 : b;
            
            // 重新组合为RGB565
            dst[y * dst_width + x] = (r << 11) | (g << 5) | b;
        }
    }
}

// 显示图像到LCD
void display_image_on_lcd(uint16_t *image_data) {
    if (panel_handle && image_data) {
        esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, LCD_WIDTH, LCD_HEIGHT, image_data);
    }
}

void app_main(void)
{
    ESP_LOGI(TAG, "Starting application...");
    
    // Initialize NVS
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_LOGW(TAG, "Erasing NVS flash to fix corruption");
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);

    // 分配LCD帧缓冲区
    lcd_frame_buffer = heap_caps_malloc(LCD_WIDTH * LCD_HEIGHT * sizeof(uint16_t), MALLOC_CAP_SPIRAM);
    if (lcd_frame_buffer == NULL) {
        ESP_LOGE(TAG, "Failed to allocate frame buffer in SPIRAM");
        lcd_frame_buffer = malloc(LCD_WIDTH * LCD_HEIGHT * sizeof(uint16_t));
        if (lcd_frame_buffer == NULL) {
            ESP_LOGE(TAG, "Failed to allocate frame buffer");
            return;
        }
        ESP_LOGW(TAG, "Using internal RAM for frame buffer");
    } else {
        ESP_LOGI(TAG, "Frame buffer allocated in SPIRAM");
    }

    // 初始化LCD
    ESP_LOGI(TAG, "Initializing LCD...");
    
    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));

    spi_bus_config_t buscfg = {
        .sclk_io_num = EXAMPLE_PIN_NUM_PCLK,
        .mosi_io_num = EXAMPLE_PIN_NUM_DATA0,
        .miso_io_num = -1,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
        .max_transfer_sz = LCD_WIDTH * LCD_HEIGHT * 2 + 8,
        .flags = SPICOMMON_BUSFLAG_MASTER
    };
    
    ESP_ERROR_CHECK(spi_bus_initialize(LCD_HOST, &buscfg, SPI_DMA_CH_AUTO));

    esp_lcd_panel_io_handle_t io_handle = NULL;
    esp_lcd_panel_io_spi_config_t io_config = {
        .dc_gpio_num = EXAMPLE_PIN_NUM_DC,
        .cs_gpio_num = EXAMPLE_PIN_NUM_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_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_HOST, &io_config, &io_handle));

    esp_lcd_panel_dev_config_t panel_config = {
        .reset_gpio_num = EXAMPLE_PIN_NUM_RST,
        .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,  // 尝试RGB顺序
        .bits_per_pixel = 16,
        .flags.reset_active_high =0,
        //.reset_active_high = 0,
    };
    
    ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(io_handle, &panel_config, &panel_handle));

    // LCD配置
    ESP_ERROR_CHECK(gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL));
    
    // 重置LCD
    ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
    vTaskDelay(100 / portTICK_PERIOD_MS);
    
    // 初始化LCD面板
    ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
    
    // 配置LCD方向 - 尝试不同的配置
    ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, true));
    ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, true, true));
    
    // 关闭颜色反转
    ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, false));
    
    // 打开显示
    ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));
    
    // 打开背光
    ESP_ERROR_CHECK(gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, EXAMPLE_LCD_BK_LIGHT_ON_LEVEL));

    ESP_LOGI(TAG, "LCD initialization complete");

    // 第一步:测试LCD彩色显示
    ESP_LOGI(TAG, "Testing LCD with rainbow pattern...");
    draw_color_test_pattern(lcd_frame_buffer);
    display_image_on_lcd(lcd_frame_buffer);
    vTaskDelay(3000 / portTICK_PERIOD_MS);
    
    ESP_LOGI(TAG, "Testing LCD with color bars...");
    draw_color_bars(lcd_frame_buffer);
    display_image_on_lcd(lcd_frame_buffer);
    vTaskDelay(3000 / portTICK_PERIOD_MS);

    // 第二步:初始化摄像头
    ESP_LOGI(TAG, "Initializing camera...");
    if(ESP_OK != camera_example_init()) {
        ESP_LOGE(TAG, "Camera init failed");
        
        // 摄像头失败时显示错误图案
        for (int i = 0; i < LCD_WIDTH * LCD_HEIGHT; i++) {
            lcd_frame_buffer[i] = 0xF800; // 红色表示错误
        }
        display_image_on_lcd(lcd_frame_buffer);
        
        free(lcd_frame_buffer);
        return;
    }

    ESP_LOGI(TAG, "Starting camera video stream...");

    // 主循环
    int frame_count = 0;
    while (1) {
        // 获取摄像头帧
        camera_fb_t *pic = esp_camera_fb_get();
        if (!pic) {
            ESP_LOGE(TAG, "Camera capture failed");
            vTaskDelay(10 / portTICK_PERIOD_MS);
            continue;
        }

        // 分析摄像头数据
        if (frame_count % 60 == 0) {
            analyze_camera_data(pic);
        }

        // // 处理图像数据
        // if (pic->format == PIXFORMAT_RGB565) {
        //     // 使用高质量缩放
        //     scale_image_high_quality(pic->buf, lcd_frame_buffer, 
        //                            pic->width, pic->height,
        //                            LCD_WIDTH, LCD_HEIGHT);
        // } else {
        //     ESP_LOGW(TAG, "Unsupported image format: %d", pic->format);
            
        //     // 显示格式错误图案
        //     for (int i = 0; i < LCD_WIDTH * LCD_HEIGHT; i++) {
        //         lcd_frame_buffer[i] = 0xFFE0; // 黄色警告
        //     }
        // }

        // 显示到LCD
       // display_image_on_lcd(lcd_frame_buffer);
        esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, pic->width, pic->height, pic->buf);

        // 返回帧缓冲区
        esp_camera_fb_return(pic);

        frame_count++;
        vTaskDelay(100 / portTICK_PERIOD_MS); // 约30fps
    }

    // 清理资源
    free(lcd_frame_buffer);
}

esp32s3 cam
在这里插入图片描述
LCD
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

3:测试结果 如果对你又帮助,麻烦点个赞,加个关注
角度不一样,清晰度有差异
在这里插入图片描述

更多推荐