ESP32S3CAM_LCD
·
前言:
本章主要讲述 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:测试结果 如果对你又帮助,麻烦点个赞,加个关注
角度不一样,清晰度有差异

更多推荐
所有评论(0)