OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 37|回复: 0

《ESP32-S3使用指南—IDF版 V1.6》第三十六章照相机实验

[复制链接]

1168

主题

1180

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
5041
金钱
5041
注册时间
2019-5-8
在线时间
1264 小时
发表于 10 小时前 | 显示全部楼层 |阅读模式
本帖最后由 正点原子运营 于 2025-9-2 11:55 编辑

第三十六章照相机实验

1)实验平台:正点原子DNESP32S3开发板

2)章节摘自【正点原子】ESP32-S3使用指南—IDF版 V1.6


4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/esp32/ATK-DNESP32S3.html

5)正点原子官方B站:https://space.bilibili.com/394620890

6)正点原子DNESP32S3开发板技术交流群:132780729

155537c2odj87vz1z9vj6l.jpg

155537nfqovl2gg9faaol9.png

本章我们结合前面的摄像头实验,实现一个简单的照相机功能。本章分为如下几个小节:
36.1 OV5640和CAMERA模块简介
36.2 硬件设计
36.3 程序设计
36.4 下载验证

36.1 OV5640CAMERA模块简介
关于这部分的内容前一章节已经做出了详细介绍,请读者们回顾36.1小节的内容,笔者在此不再赘述。

36.2 硬件设计
36.2.1.例程功能
本章实验功能简介:程序下载完成,摄像头的图像数据在SPILCD显示屏上显示。
36.2.2.硬件资源
1. XL9555
IIC_SDA-IO41
IIC_SCL-IO42

2. SPILCD
CS-IO21
SCK-IO12
SDA-IO11
DC-IO40(在P5端口,使用跳线帽将IO_SET和LCD_DC相连)
PWR- IO1_3(XL9555)
RST- IO1_2(XL9555)

3. CAMERA
OV_SCL-IO38
OV_SDA- IO 39
VSYNC- IO 47
HREF- IO 48
PCLK- IO 45
D0- IO 4
D1- IO 5
D2- IO 6
D3- IO 7
D4- IO 15
D5- IO 16
D6- IO 17
D7- IO 18
RESET-IO0_5(XL9555)
PWDN-IO0_4(XL9555)

36.2.3.原理图
CAMERA接口与ESP32-S3的连接关系,如下图所示:                                 
image001.png
图36.2.3.1 CAMERA接口与ESP32-S3的连接电路图
36.3 程序设计
36.3.1 程序流程图
程序流程图能帮助我们更好的理解一个工程的功能和实现的过程,对学习和设计工程有很好的主导作用。下面看看本实验的程序流程图:
image004.png
图36.3.1.1 CAMERA_PHOTOGRAPH实验程序流程图
36.3.2 CAMERA_PHOTOGRAPH函数解析
本章实验要使用到乐鑫官方的esp32-camera驱动库,此驱动库承载ESP32系列Soc兼容的图像传感器驱动程序。此外,它还提供了一些工具,允许将捕获的帧数据转换为更常见的BMP和JPEG格式。要使用此功能,需要导入必要的头文件:
  1. #include"esp_camera.h"
复制代码
接下来,作者将介绍一些常用的ESP32-S3中的CAMERA函数,这些函数的描述及其作用如下:
1,初始化摄像头驱动
该函数用于检测并配置摄像头,其函数原型如下所示:
  1. esp_err_tesp_camera_init(const camera_config_t *config);
复制代码
该函数的形参描述,如下表所示:
QQ截图20250902114332.png
表36.3.2.1 函数esp_camera_init ()形参描述
该函数的返回值描述,如下表所示:
QQ截图20250902114347.png
表36.3.2.2 函数esp_camera_init ()
返回值描述该函数使用camera_config_t类型的结构体变量传入,该结构体的定义如下所示:
QQ截图20250902114436.png
表36.3.2.3 camera_config_t结构体参数值描述
完成上述结构体参数配置之后,可以将结构传递给 esp_camera_init () 函数,用以实例化CAMERA。
2,获取摄像头图像传感器
该函数用于获取指向图像传感器控制结构的指针,其函数原型如下所示:
  1. sensor_t *esp_camera_sensor_get(void);
复制代码
该函数的形参描述,如下表所示:
QQ截图20250902114634.png
表36.3.2.4 函数esp_camera_sensor_get ()形参描述
该函数的返回值描述,如下表所示:
QQ截图20250902114641.png
表36.3.2.5 函数esp_camera_sensor_get()返回值描述
36.3.3 CAMERA_PHOTOGRAPH驱动解析
在IDF版的25_2_camera_photograph例程中,作者在25_2_camera_photograph\components\decoder_ijg路径下新增了一个JPEG库用户文件。由于,25_1_ camera实验与25_2_camera_photograph用到的驱动一样,笔者在此也不再详细赘述,请读者们回顾第三十五章节的相关内容。
36.3.4 CMakeLists.txt文件
打开本实验BSP下的CMakeLists.txt文件,其内容如下所示:
  1. set(src_dirs
  2.             CAMERA
  3.             IIC
  4.             LCD
  5.             LED
  6.             SPI
  7.             XL9555)
  8. set(include_dirs
  9.             CAMERA
  10.             IIC
  11.             LCD
  12.             LED
  13.             SPI
  14.             XL9555)
  15. set(requires
  16.             driver
  17.             esp_lcd
  18.             esp32-camera)
  19. idf_component_register(SRC_DIRS${src_dirs}
  20. INCLUDE_DIRS ${include_dirs}REQUIRES ${requires})
  21. component_compile_options(-ffast-math -O3 -Wno-error=format=-Wno-format)
复制代码
上述的红色CAMERA驱动以及esp_ camera依赖库需要由开发者自行添加,以确保CAMERA驱动能够顺利集成到构建系统中。这一步骤是必不可少的,它确保了CAMERA驱动的正确性和可用性,为后续的开发工作提供了坚实的基础。
36.3.5 实验应用代码
打开main/main.c文件,该文件定义了工程入口函数,名为app_main。该函数代码如下。main.c函数我们在之前摄像头实验的基本上进行改动,首先我们要为图片分配一个与图片文件夹下名字不重复的文件名,我们复用FATFS的接口,设计如下:
  1. /**
  2. * @brief      得到path路径下,目标文件的总个数
  3. * @param      path : 路径
  4. * @retval     总有效文件数
  5. */
  6. uint16_tpic_get_tnum(char *path)
  7. {
  8.     uint8_t res;
  9.     uint16_t rval = 0;
  10.     FF_DIR tdir;                                         /* 临时目录 */
  11.     FILINFO *tfileinfo;                                 /* 临时文件信息 */
  12.     tfileinfo = (FILINFO*)malloc(sizeof(FILINFO));    /* 申请内存 */
  13.     res =f_opendir(&tdir, (const TCHAR*)path);           /* 打开目录 */
  14.     if (res == FR_OK&& tfileinfo)
  15.     {
  16.         while (1)                                         /* 查询总的有效文件数 */
  17.         {
  18.             res =f_readdir(&tdir, tfileinfo);        /* 读取目录下的一个文件 */
  19.          /* 错误了/到末尾了,退出 */
  20.             if (res != FR_OK|| tfileinfo->fname[0] == 0)break;
  21.             res =exfuns_file_type(tfileinfo->fname);
  22.             if ((res & 0X0F) != 0X00)               /* 取低四位,看看是不是图片文件 */
  23.             {
  24.                 rval++;                                   /* 有效文件数增加1 */
  25.             }
  26.         }
  27.     }
  28.     free(tfileinfo);                                     /* 释放内存 */
  29.     return rval;
  30. }
复制代码
通过以上程序,可以生成一个与当前文件夹下图片不重名的文件名字符串,并传给针对应的缓冲区。
  1. /**
  2. * @brief      task3
  3. * @param      pvParameters : 传入参数(未用到)
  4. * @retval     无
  5. */
  6. void task3(void *pvParameters)
  7. {
  8.     pvParameters =pvParameters;
  9.     char file_name[30];
  10.     uint32_t pictureNumber = 0;
  11.     uint8_t res = 0;
  12.     size_t writelen = 0;
  13.     FIL *fftemp;
  14.     res =exfuns_init();                            /* 为fatfs相关变量申请内存 */
  15.     pictureNumber =pic_get_tnum("0:/PICTURE");      /* 得到总有效文件数 */
  16.     pictureNumber =pictureNumber + 1;
  17.     while (1)
  18.     {
  19.         xSemaphoreTake(BinarySemaphore,portMAX_DELAY);/* 获取二值信号量 */
  20.         
  21.         /* SD卡挂载了,才能拍照 */
  22.         if (sd_check_en== 1)
  23.         {
  24.             sprintf(file_name, "0:/PICTURE/img%ld.jpg",pictureNumber);
  25. /* 分配内存 */
  26.             fftemp = (FIL *)malloc(sizeof(FIL));
  27.             res =f_open(fftemp,
  28. (const TCHAR *)file_name,
  29. FA_WRITE |
  30. FA_CREATE_NEW);   /* 尝试打开 */
  31.             if (res != FR_OK)
  32.             {
  33.                 ESP_LOGE(TAG, "imgopen err\r\n");
  34.             }
  35. /* 写入头数据 */
  36.             f_write(fftemp, (const void *)lcd_buf, sizeof(lcd_buf), &writelen);
  37.             if (writelen!= sizeof(lcd_buf))
  38.             {
  39.                 ESP_LOGE(TAG, "imgWrite err");
  40.             }
  41.             else
  42.             {
  43.                 ESP_LOGI(TAG, "writebuff len %d byte", writelen);
  44.                 pictureNumber++;
  45.             }
  46.             f_close(fftemp);
  47.             free(fftemp);
  48.         }
  49.     }
  50. }
复制代码
上述代码通过任务调度的方式,实现申请内存、分配内存以及读取指定目录下的的文件的操作。
main函数代码如下:
  1. /**
  2. * @brief      程序入口
  3. * @param      无
  4. * @retval     无
  5. */
  6. voidapp_main(void)
  7. {
  8.     unsigned long i = 0;
  9.     unsigned long j = 0;
  10.     uint8_t key = 0;
  11.     esp_err_t ret;
  12.    
  13.     ret =nvs_flash_init();               /* 初始化NVS */
  14. if (ret ==ESP_ERR_NVS_NO_FREE_PAGES ||
  15.        ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
  16.     {
  17.         ESP_ERROR_CHECK(nvs_flash_erase());
  18.         ret =nvs_flash_init();
  19.     }
  20.     led_init();                             /* 初始化LED*/
  21.     i2c0_master =iic_init(I2C_NUM_0);      /* 初始化IIC0 */
  22.     spi2_init();                            /* 初始化SPI2 */
  23.     xl9555_init(i2c0_master);            /* 初始化XL9555 */
  24.     lcd_init();                             /* 初始化LCD*/
  25.     while (sd_spi_init())                 /* 检测不到SD卡 */
  26.     {
  27.         lcd_show_string(30, 50, 200, 16, 16, "SDCard Failed!", RED);
  28.         vTaskDelay(200);
  29.         lcd_fill(30, 50, 200 + 30, 50 + 16, WHITE);
  30.         vTaskDelay(200);
  31.         sd_check_en = 0;
  32.     }
  33.     sd_check_en = 1;
  34.     lcd_show_string(30, 50, 200, 16, 16, "ESP32", RED);
  35.     lcd_show_string(30, 70, 200, 16, 16, "CAMERATEST", RED);
  36.     lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
  37.     /* 初始化摄像头 */
  38.     while (camera_init())
  39.     {
  40.         lcd_show_string(30, 110, 200, 16, 16, "CAMERAFail!", BLUE);
  41.         vTaskDelay(500);
  42.     }
  43.     rgb565 =malloc(240 * 320 * 2);
  44.     if (NULL ==rgb565)
  45.     {
  46.         ESP_LOGE(TAG, "can'talloc memory for rgb565 buffer");
  47.     }
  48.     lcd_clear(BLACK);
  49.     BinarySemaphore =xSemaphoreCreateBinary();
  50.     /* 创建任务3 */
  51.     xTaskCreatePinnedToCore((TaskFunction_t)task3,            /* 任务函数 */
  52.                             (const char*    )"task3",         /* 任务名称 */
  53.                             (uint16_t       )TASK3_STK_SIZE, /* 任务堆栈大小 */
  54.                             (void*          )NULL,        /* 传入给任务函数的参数 */
  55.                             (UBaseType_t    )TASK3_PRIO,     /* 任务优先级 */
  56.                             (TaskHandle_t*  )&Task3Task_Handler,/* 任务句柄 */
  57.                             (BaseType_t     ) 0);       /* 该任务哪个内核运行 */
  58.     while (1)
  59.     {
  60.         key =xl9555_key_scan(0);
  61.         camera_fb_t *pic =esp_camera_fb_get();
  62.         if (pic)
  63.         {
  64.             mjpegdraw(pic->buf, pic->len, (uint8_t *)rgb565, NULL);
  65.             lcd_set_window(0, 0, 0 + pic->width - 1, 0 + pic->height- 1);
  66.             if (key ==KEY0_PRES)
  67.             {
  68.                 /* lcd_buf存储摄像头整一帧RGB数据 */
  69.                 for (j = 0; j < pic->width * pic->height; j++)
  70.                 {
  71.                     lcd_buf[2 * j] = (pic->buf[2 * i]) ;
  72.                     lcd_buf[2 * j + 1] =  (pic->buf[2 * i + 1]);
  73.                     i ++;
  74.                 }
  75.                 xSemaphoreGive(BinarySemaphore);             /* 释放二值信号量 */
  76.             }
  77.             /* 处理SD卡释放挂载 */
  78.             if (sd_check_en== 1)
  79.             {
  80.                 if (sdmmc_get_status(card) !=ESP_OK)
  81.                 {
  82.                     sd_check_en = 0;
  83.                 }
  84.             }
  85.             else
  86.             {
  87.                 if (sd_spi_init() ==ESP_OK)
  88.                 {
  89.                     if (sdmmc_get_status(card) ==ESP_OK)
  90.                     {
  91.                         sd_check_en = 1;
  92.                     }
  93.                 }
  94.             }
  95.             
  96.             /* 例如:96*96*2/1536 = 12;分12次发送RGB数据 */
  97.             for(j = 0; j < (pic->width * pic->height* 2 / LCD_BUF_SIZE); j++)
  98.             {
  99.                 /*&lcd_buf[j * LCD_BUF_SIZE] 偏移地址发送数据 */
  100.                 lcd_write_data(&rgb565[j *LCD_BUF_SIZE] , LCD_BUF_SIZE);
  101.             }
  102.             esp_camera_fb_return(pic);
  103.         }
  104.         else
  105.         {
  106.             ESP_LOGE(TAG, "Getframe failed");
  107.         }
  108.         i = 0;
  109.         pic = NULL;
  110.         vTaskDelay(pdMS_TO_TICKS(1));
  111.     }
  112.     free(rgb565);
复制代码
该函数完成对各相关硬件的初始化,然后检测摄像头,初始化摄像头为RGB565模式,显示采集到的图像到LCD上面,实现对图像进行预览。进入主循环以后,按KEY0按键,可以实现拍照。
至此照相机实验代码编写完成。

36.4 下载验证
程序下载到开发板后,LCD显示屏不断更新摄像头输出的图像数据,如下图所示。
image006.png
图36.4.1 LCD显示效果图
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-9-2 22:01

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表