ESP32-S3模组上跑通esp32-camera(19)

接前一篇文章:ESP32-S3模组上跑通esp32-camera(18)

 

本文内容参考:

esp32-camera入门(基于ESP-IDF)_esp32 camera-CSDN博客

OV5640手册解读-CSDN博客

ESP32_CAM CameraWebServer例程源码解析笔记(一)_void startcameraserver();-CSDN博客

esp32-cam驱动程序阅读 - 哔哩哔哩

特此致谢!

 

一、OV5640初始化

2. 相机初始化及图像传感器配置

在解析完cam_init函数之后,回到esp_camera_init函数中,接下来该进行camera的探测(调用camera_probe函数)了。为了便于理解和回顾,再次贴出esp_camera_init函数的源码,在components/esp32-camera/driver/esp_camera.c中,如下:

esp_err_t esp_camera_init(const camera_config_t *config)
{
    esp_err_t err;
    err = cam_init(config);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Camera init failed with error 0x%x", err);
        return err;
    }

    camera_model_t camera_model = CAMERA_NONE;
    err = camera_probe(config, &camera_model);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Camera probe failed with error 0x%x(%s)", err, esp_err_to_name(err));
        goto fail;
    }

    framesize_t frame_size = (framesize_t) config->frame_size;
    pixformat_t pix_format = (pixformat_t) config->pixel_format;

    if (PIXFORMAT_JPEG == pix_format && (!camera_sensor[camera_model].support_jpeg)) {
        ESP_LOGE(TAG, "JPEG format is not supported on this sensor");
        err = ESP_ERR_NOT_SUPPORTED;
        goto fail;
    }

    if (frame_size > camera_sensor[camera_model].max_size) {
        ESP_LOGW(TAG, "The frame size exceeds the maximum for this sensor, it will be forced to the maximum possible value");
        frame_size = camera_sensor[camera_model].max_size;
    }

    err = cam_config(config, frame_size, s_state->sensor.id.PID);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Camera config failed with error 0x%x", err);
        goto fail;
    }

    s_state->sensor.status.framesize = frame_size;
    s_state->sensor.pixformat = pix_format;

    ESP_LOGD(TAG, "Setting frame size to %dx%d", resolution[frame_size].width, resolution[frame_size].height);
    if (s_state->sensor.set_framesize(&s_state->sensor, frame_size) != 0) {
        ESP_LOGE(TAG, "Failed to set frame size");
        err = ESP_ERR_CAMERA_FAILED_TO_SET_FRAME_SIZE;
        goto fail;
    }
    s_state->sensor.set_pixformat(&s_state->sensor, pix_format);
#if CONFIG_CAMERA_CONVERTER_ENABLED
    if(config->conv_mode) {
        s_state->sensor.pixformat = get_output_data_format(config->conv_mode); // If conversion enabled, change the out data format by conversion mode
    }
#endif

    if (s_state->sensor.id.PID == OV2640_PID) {
        s_state->sensor.set_gainceiling(&s_state->sensor, GAINCEILING_2X);
        s_state->sensor.set_bpc(&s_state->sensor, false);
        s_state->sensor.set_wpc(&s_state->sensor, true);
        s_state->sensor.set_lenc(&s_state->sensor, true);
    }

    if (pix_format == PIXFORMAT_JPEG) {
        s_state->sensor.set_quality(&s_state->sensor, config->jpeg_quality);
    }
    s_state->sensor.init_status(&s_state->sensor);

    cam_start();

    return ESP_OK;

fail:
    esp_camera_deinit();
    return err;
}

当前来到以下代码片段:

    camera_model_t camera_model = CAMERA_NONE;
    err = camera_probe(config, &camera_model);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Camera probe failed with error 0x%x(%s)", err, esp_err_to_name(err));
        goto fail;
    }

camera_probe函数在components/esp32-camera/driver/esp_camera.c中,代码如下:

static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out_camera_model)
{
    esp_err_t ret = ESP_OK;
    *out_camera_model = CAMERA_NONE;
    if (s_state != NULL) {
        return ESP_ERR_INVALID_STATE;
    }

    s_state = (camera_state_t *) calloc(sizeof(camera_state_t), 1);
    if (!s_state) {
        return ESP_ERR_NO_MEM;
    }

    if (config->pin_xclk >= 0) {
        ESP_LOGD(TAG, "Enabling XCLK output");
        CAMERA_ENABLE_OUT_CLOCK(config);
    }

    if (config->pin_sccb_sda != -1) {
        ESP_LOGD(TAG, "Initializing SCCB");
        ret = SCCB_Init(config->pin_sccb_sda, config->pin_sccb_scl);
    } else {
        ESP_LOGD(TAG, "Using existing I2C port");
        ret = SCCB_Use_Port(config->sccb_i2c_port);
    }

    if(ret != ESP_OK) {
        ESP_LOGE(TAG, "sccb init err");
        goto err;
    }

    if (config->pin_pwdn >= 0) {
        ESP_LOGD(TAG, "Resetting camera by power down line");
        gpio_config_t conf = { 0 };
        conf.pin_bit_mask = 1LL << config->pin_pwdn;
        conf.mode = GPIO_MODE_OUTPUT;
        gpio_config(&conf);

        // carefull, logic is inverted compared to reset pin        
        gpio_set_level(config->pin_pwdn, 1);
        vTaskDelay(10 / portTICK_PERIOD_MS);
        gpio_set_level(config->pin_pwdn, 0);
        vTaskDelay(10 / portTICK_PERIOD_MS);
        
    }

    if (config->pin_reset >= 0) {
        ESP_LOGD(TAG, "Resetting camera");
        gpio_config_t conf = { 0 };
        conf.pin_bit_mask = 1LL << config->pin_reset;
        conf.mode = GPIO_MODE_OUTPUT;
        gpio_config(&conf);        
        gpio_set_level(config->pin_reset, 0);
        vTaskDelay(10 / portTICK_PERIOD_MS);
        gpio_set_level(config->pin_reset, 1);
        vTaskDelay(10 / portTICK_PERIOD_MS);        
    }

    ESP_LOGD(TAG, "Searching for camera address");
    //vTaskDelay(10 / portTICK_PERIOD_MS);

    uint8_t slv_addr = SCCB_Probe();

    if (slv_addr == 0) {
        ret = ESP_ERR_NOT_FOUND;
        goto err;
    }

    ESP_LOGI(TAG, "Detected camera at address=0x%02x", slv_addr);
    s_state->sensor.slv_addr = slv_addr;
    s_state->sensor.xclk_freq_hz = config->xclk_freq_hz;

    /**
     * Read sensor ID and then initialize sensor
     * Attention: Some sensors have the same SCCB address. Therefore, several attempts may be made in the detection process
     */
    sensor_id_t *id = &s_state->sensor.id;
    for (size_t i = 0; i < sizeof(g_sensors) / sizeof(sensor_func_t); i++) {
        if (g_sensors[i].detect(slv_addr, id)) {
            camera_sensor_info_t *info = esp_camera_sensor_get_info(id);
            if (NULL != info) {
                *out_camera_model = info->model;
                ESP_LOGI(TAG, "Detected %s camera", info->name);
                g_sensors[i].init(&s_state->sensor);
                break;
            }
        }
    }

    if (CAMERA_NONE == *out_camera_model) { //If no supported sensors are detected
        ESP_LOGE(TAG, "Detected camera not supported.");
        ret = ESP_ERR_NOT_SUPPORTED;
        goto err;
    }

    ESP_LOGI(TAG, "Camera PID=0x%02x VER=0x%02x MIDL=0x%02x MIDH=0x%02x",
             id->PID, id->VER, id->MIDH, id->MIDL);

    ESP_LOGD(TAG, "Doing SW reset of sensor");
    //vTaskDelay(10 / portTICK_PERIOD_MS);

    return s_state->sensor.reset(&s_state->sensor);
err :
    CAMERA_DISABLE_OUT_CLOCK();
    return ret;
}

camera_probe函数代码也比较长,仍然一段一段来看。第1段代码如下:

    if (s_state != NULL) {
        return ESP_ERR_INVALID_STATE;
    }

    s_state = (camera_state_t *) calloc(sizeof(camera_state_t), 1);
    if (!s_state) {
        return ESP_ERR_NO_MEM;
    }

s_state是同文件(components/esp32-camera/driver/esp_camera.c)中的静态全局变量,定义及初始化代码如下:

static camera_state_t *s_state = NULL;

came_state_t也在同文件中定义(就在上边),如下:

typedef struct {
    sensor_t sensor;
    camera_fb_t fb;
} camera_state_t;

第一次走到cam_probe函数的时候,s_state肯定是NULL,因此为走到为其动态分配内存的地方;如果不是第一次走到此处,那么s_state很可能已经分配了,就直接返回ESP_ERR_INVALID_STATE。

接下来是第2段代码:

    if (config->pin_xclk >= 0) {
        ESP_LOGD(TAG, "Enabling XCLK output");
        CAMERA_ENABLE_OUT_CLOCK(config);
    }

config->pin_xclk前文书已经讲过了,参见:ESP32-S3模组上跑通esp32-camera(1)_esp32 s3 性能 比esp32 cam-CSDN博客

72dbd37682274d628a89bcd840fda2ba.png

86b31c0c83194980b57c908446a019e6.png

0e59b5debda146b7a3572c8d460f5b2a.png

1945f2bc2a9c4b42a02a9a92d6825f07.png

这里就是“后文书”了,如果CAM_PIN_XCLK并不是定义为-1,也就是说分配到了实际的引脚,则会进入判断体,否则就不会进入。

    if (config->pin_xclk >= 0) {
        ESP_LOGD(TAG, "Enabling XCLK output");
        CAMERA_ENABLE_OUT_CLOCK(config);
    }

前文书在讲ll_cam_set_pin函数的时候,已经讲过了对于config->pin_xclk的配置,参见:https://phmatthaus.blog.csdn.net/article/details/143652297

d8079cbbe560430ead016f5dbe716dd0.png

在ll_cam_set_pin函数中只是设置了pin_xclk引脚为输出(ESP32-S3的输出、OV5640的输入),这里就要使能时钟了。

    if (config->pin_xclk >= 0) {
        ESP_LOGD(TAG, "Enabling XCLK output");
        CAMERA_ENABLE_OUT_CLOCK(config);
    }

CAMERA_ENABLE_OUT_CLOCK宏也在components/esp32-camera/driver/esp_camera.c中定义,代码如下:

#if CONFIG_IDF_TARGET_ESP32S3 // LCD_CAM module of ESP32-S3 will generate xclk
#define CAMERA_ENABLE_OUT_CLOCK(v)
#define CAMERA_DISABLE_OUT_CLOCK()
#else
#define CAMERA_ENABLE_OUT_CLOCK(v) camera_enable_out_clock((v))
#define CAMERA_DISABLE_OUT_CLOCK() camera_disable_out_clock()
#endif

camera_enable_out_clock和camera_disable_out_clock函数都是在components/esp32-camera/target/xclk.c中,代码如下:

esp_err_t camera_enable_out_clock(const camera_config_t* config)
{
    esp_err_t err = xclk_timer_conf(config->ledc_timer, config->xclk_freq_hz);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "ledc_timer_config failed, rc=%x", err);
        return err;
    }

    g_ledc_channel = config->ledc_channel;
    ledc_channel_config_t ch_conf;
    ch_conf.gpio_num = config->pin_xclk;
    ch_conf.speed_mode = LEDC_LOW_SPEED_MODE;
    ch_conf.channel = config->ledc_channel;
    ch_conf.intr_type = LEDC_INTR_DISABLE;
    ch_conf.timer_sel = config->ledc_timer;
    ch_conf.duty = 1;
    ch_conf.hpoint = 0;
    err = ledc_channel_config(&ch_conf);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "ledc_channel_config failed, rc=%x", err);
        return err;
    }
    return ESP_OK;
}

void camera_disable_out_clock()
{
    if (g_ledc_channel != NO_CAMERA_LEDC_CHANNEL) {
        ledc_stop(LEDC_LOW_SPEED_MODE, g_ledc_channel, 0);
        g_ledc_channel = NO_CAMERA_LEDC_CHANNEL;
    }
}

不过这里应该用不到,因为在bottom/build/bootloader/config/sdkconfig.h中,将CONFIG_IDF_TARGET_ESP32S3宏定义成了1。

bc3e5d02862d4987b46df5d28031a50d.png

这样就如上边注释所说,ESP32-S3的LCD_CAM模块将生成xclk,也就是OV5640的XCLK由LCD_CAM模块提供了。因此对于ESP32-S3来说,实际上定义为空。

fb400ce91802471ba04401de2f4ada0e.png

camera_probe函数的第2段代码就解析完了。实际上也是比较粗线条地过了一遍,后续也需要“二次深造”。

下一回继续讲解camera_probe函数的后续代码。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/917997.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

vmWare虚拟环境centos7安装Hadoop 伪分布式实践

背景&#xff1a;近期在研发大数据中台&#xff0c;需要研究Hadoop hive 的各种特性&#xff0c;需要搭建一个Hadoop的虚拟环境&#xff0c;本来想着使用dock &#xff0c;但突然发现docker 公共仓库的镜像 被XX 了&#xff0c;无奈重新使用vm 搭建虚拟机。 大概经历了6个小时完…

ARM(安谋) China处理器

0 Preface/Foreword 0.1 参考博客 Cortex-M23/M33与STAR-MC1星辰处理器 ARM China&#xff0c;2018年4月established&#xff0c;独立运行。 1 处理器类型 1.1 周易AIPU 1.2 STAR-MC1&#xff08;星辰处理器&#xff09; STAT-MC1&#xff0c;主要为满足AIOT应用性能、功…

c++--------《set 和 map》

c--------《set 和 map》 1 set系列的使⽤1.1 set类的介绍1.2 set的构造和迭代器1.3 set重要接口 2 实现样例2.1: insert和迭代器遍历使⽤样例&#xff1a;2.2: find和erase使⽤样例&#xff1a; 练习3.map系列的使用3.1 map类的介绍3.1.1 pair类型介绍 3.2 map的数据修改3.3mu…

MySQL面试之底层架构与库表设计

华子目录 mysql的底层架构客户端连接服务端连接的本质&#xff0c;连接用完会立马丢弃吗解析器和优化器的作用sql执行前会发生什么客户端的连接池和服务端的连接池数据库的三范式 mysql的底层架构 客户端连接服务端 连接的本质&#xff0c;连接用完会立马丢弃吗 解析器和优化器…

vscode vite+vue3项目启动调试

1、经常我们在普通的项目中&#xff0c;如果算法并不复杂&#xff0c;那么基本上console.log就可以搞定&#xff0c;当然也可以直接alert&#xff0c;打包的时候如果不去掉&#xff0c;还会在发版中上接弹出&#xff0c;给你个惊喜。 2、碰到了有些算法过程比较复杂的情况下&a…

详解八大排序(一)------(插入排序,选择排序,冒泡排序,希尔排序)

文章目录 前言1.插入排序&#xff08;InsertSort&#xff09;1.1 核心思路1.2 实现代码 2.选择排序&#xff08;SelectSort&#xff09;2.1 核心思路2.2 实现代码 3.冒泡排序&#xff08;BubbleSort&#xff09;3.1 核心思路3.2 实现代码 4.希尔排序&#xff08;ShellSort&…

IPv6 NDP 记录

NDP&#xff08;Neighbor Discovery Protocol&#xff0c;邻居发现协议&#xff09; 是 IPv6 的一个关键协议&#xff0c;它组合了 IPv4 中的 ARP、ICMP 路由器发现和 ICMP 重定向等协议&#xff0c;并对它们作出了改进。该协议使用 ICMPv6 协议实现&#xff0c;作为 IPv6 的基…

【包教包会】CocosCreator3.x框架——带翻页特效的场景切换

一、效果演示 二、如何获取 1、https://gitee.com/szrpf/TurnPage 2、解压&#xff0c;导入cocos creator&#xff08;版本3.8.2&#xff09;&#xff0c;可以直接运行Demo演示 三、算法思路 1、单场景 页面预制体 通过loadScene来切换页面&#xff0c;无法实现页面特效。…

拉取docker镜像应急方法

发现许多docker hub镜像网址速度也慢得发指啦&#xff0c;如果想速度快点&#xff0c;可以考虑买个按量计费的公有云服务器&#xff0c;用他们的内网镜像&#xff0c;然后再导出&#xff0c;然后传到本地。 开通服务器 可以考虑个开通最低配的&#xff0c;这里我用的是腾讯的…

Cyberchef配合Wireshark提取并解析HTTP/TLS流量数据包中的文件

本文将介绍一种手动的轻量级的方式&#xff0c;还原HTTP/TLS协议中传输的文件&#xff0c;为流量数据包中的文件分析提供帮助。 如果捕获的数据包中存在非文本类文件&#xff0c;例如png,jpg等图片文件&#xff0c;或者word&#xff0c;Excel等office文件异或是其他类型的二进…

Stable diffusion详细讲解

&#x1f33a;系列文章推荐&#x1f33a; 扩散模型系列文章正在持续的更新&#xff0c;更新节奏如下&#xff0c;先更新SD模型讲解&#xff0c;再更新相关的微调方法文章&#xff0c;敬请期待&#xff01;&#xff01;&#xff01;&#xff08;本文及其之前的文章均已更新&…

机器学习-37-对ML的思考之机器学习发展的三个阶段和驱动AI发展三驾马车的由来

文章目录 1 引言2 机器学习发展的三个阶段2.1 萌芽期(20世纪50年代)2.1.1 达特茅斯会议(人工智能诞生)2.1.2 机器学习名称的由来2.2 知识期(20世纪80年代)2.2.1 知识瓶颈问题2.2.2 机器学习顶级会议ICML2.2.3 Machine Learning创刊2.2.4 神经网络规则抽取2.3 算法期(20世纪90年…

SpringCloud篇(服务网关 - GateWay)

目录 一、简介 二、为什么需要网关 二、gateway快速入门 1. 创建gateway服务&#xff0c;引入依赖 2. 编写启动类 3. 编写基础配置和路由规则 4. 重启测试 5. 网关路由的流程图 6. 总结 三、断言工厂 四、过滤器工厂 1. 路由过滤器的种类 2. 请求头过滤器 3. 默认…

代码段数据段的划分

DPL DPL存储在段描述符中&#xff0c;规定访问该段的权限级别(Descriptor Privilege Level) CPL CPL是当前进程的权限级别(Current Privilege Level)&#xff0c;是当前正在指向的代码段所在段的成绩&#xff0c;也就是CS段的DPL RPL RPL说明的是进程对段访问的请求权限(Re…

HTML5+CSS前端开发【保姆级教学】+前端介绍和软件安装

学习了基础编程刚刚开始学习计算机的程序员&#xff0c;你是否会这样的想法:前端和后端是什么呢&#xff1f;如果你是刚上大学的大一大二基础小白&#xff0c;但是身边的卷王同学已经超前知道之后要从事前后端开发了&#xff0c;并且在学习各种框架的课程&#xff0c;Aahhahah,…

【Rabbitmq篇】RabbitMQ⾼级特性----消息确认

目录 前言&#xff1a; 一.消息确认机制 • ⾃动确认 • ⼿动确认 手动确认方法又分为三种&#xff1a; 二. 代码实现&#xff08;spring环境&#xff09; 配置相关信息&#xff1a; 1&#xff09;. AcknowledgeMode.NONE 2 &#xff09;AcknowledgeMode.AUTO 3&…

QT入门之下载、工程创建、学习方法

1.QT下载链接 因为我的是下载在LINUX上面&#xff0c;所以这里提供LINUX平台下的下载方式&#xff1a; wget http://download.qt.io/archive/qt/5.12/5.12.9/qt-opensource-linux-x64-5.12.9.run 赋予可执行权限&#xff0c;加上 sudo 权限进入安装&#xff0c;这样会安装在…

初识Linux—— 基本指令(上)

前言 Linux简述 ​ Linux是一种开源、自由、类UNIX的操作系统&#xff0c;由著名的芬兰程序员林纳斯托瓦兹&#xff08;Linus Torvalds&#xff09;于1991年首次发布。Linux的内核在GNU通用公共许可证&#xff08;GPL&#xff09;下发布&#xff0c;这意味着任何人都可以自由…

劳动力市场

1.劳动力市场概述 &#xff08;1&#xff09;劳动力&#xff1a;所有有工作能力且愿意工作的人的总称&#xff0c;由那些正在工作&#xff08;就业&#xff09;和正在寻找工作&#xff08;失业&#xff09;的人组成&#xff0c;表示为&#xff1a;L&#xff08;劳动力&#xf…

PHP代码审计 --MVC模型开发框架rce示例

MVC模型开发框架 控制器Controller&#xff1a;负责响应用户请求、准备数据&#xff0c;及决定如何展示数据 模块Model&#xff1a;管理业务逻辑和数据库逻辑&#xff0c;提供链接和操作数据库的抽象层 视图View&#xff1a;负责前端模板渲染数据&#xff0c;通过html呈现给用户…