当前位置: 首页 > article >正文

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

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

 

本文内容参考:

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

OV5640手册解读-CSDN博客

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

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

特此致谢!

 

一、OV5640初始化

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

上一回解析了camera_probe函数的第3段代码,本回继续往下解析该函数后续内容。为了便于理解和回顾,再次贴出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;
}

接下来来到以下代码片段:

    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);        
    }

按说这是两段代码,但是放在一起说才更好理解。

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

72dbd37682274d628a89bcd840fda2ba.png

86b31c0c83194980b57c908446a019e6.png

0e59b5debda146b7a3572c8d460f5b2a.png

9f24f1bec03a4d9eafa72ee6445d744e.png

回到以上代码片段中:

    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);        
    }

其实代码很好理解,分别对与OV5640的PWDN和RESETB引脚相连接的ESP32-S3的相应GPIO引脚进行初始化,均设置为输出。之后使PWDN先置高10毫秒再置低10毫秒;而RESETB正好反过来,先置低10毫秒后置高10毫秒。

这样操作是为了符合OV5640的上电时序,参见OV5640数据手册的以下内容:

5f79cf68d9354617946227dc07f4ace8.png

PWDN先置高,10毫秒后由高到低发生跳变。这对应于上图中的PWDN的跳变。对应上边的代码片段:

        gpio_set_level(config->pin_pwdn, 1);
        vTaskDelay(10 / portTICK_PERIOD_MS);
        gpio_set_level(config->pin_pwdn, 0);

PWDN由高到低发生跳变后,再延时(保持)10毫秒。这对应于上图中的t3。t3应大于等于1毫秒,此处则(至少)为10毫秒。对应上边的代码片段:

        gpio_set_level(config->pin_pwdn, 0);
        vTaskDelay(10 / portTICK_PERIOD_MS);

PWDN由高变为低并保持10毫秒之后,RESETB置为低并保持10毫秒。这也对应于上图中的t3,手册要求大于等于1毫秒,代码中则为20(10+10)毫秒。对应上边的代码片段:

        gpio_set_level(config->pin_pwdn, 0);
        vTaskDelay(10 / portTICK_PERIOD_MS);
        ……
        gpio_set_level(config->pin_reset, 0);
        vTaskDelay(10 / portTICK_PERIOD_MS);

之后RESET由低变为高,并保持10毫秒,则是为了满足上图中的t4。手册要求大于等于20毫秒,代码中则为10毫秒,这里有点不符合要求。对应上边的代码片段:

        gpio_set_level(config->pin_reset, 1);
        vTaskDelay(10 / portTICK_PERIOD_MS); 

如果是OV5640内部供电的情况,则情况也是一样。

f395223cc32e43bda4c377154fc16f04.png

这里,乐鑫的代码应该是对两种供电方式都做了兼容,但无论是内部供电还是外部供电的方式,都并未完全契合。

 至此,camera_probe函数的第4段代码就解析完了,下一回继续往下解析该函数后续内容。

 


http://www.kler.cn/a/403348.html

相关文章:

  • gitlab:使用脚本批量下载项目,实现全项目检索
  • Java LinkedList 详解
  • 「三」体验HarmonyOS端云一体化开发模板——使用DevEco Studio直接创建端云一体化工程
  • 构建无障碍的数字世界:深入探讨Web可访问性指南
  • 大数据调度组件之Apache DolphinScheduler
  • CircuitBreaker机制详解:Elasticsearch中的资源管理
  • 2024/11/17周报
  • 网络属性及相关配置常用命令-下篇
  • 腾讯:将LLM排序能力迁移至BERT
  • cesium for unity的使用
  • Flink整合Hudi及使用
  • 视频修复技术和实时在线处理
  • 用Python爬虫“偷窥”1688搜索词推荐:一场数据的奇妙冒险
  • 国内几大网络安全公司介绍 - 网络安全
  • 聊一聊Elasticsearch的索引分片的恢复机制
  • C#无符号整数类型详解:声明、使用及注意事项
  • Android:时间选择器(最下面有效果图)
  • 【设计模式】【创建型模式(Creational Patterns)】之单例模式
  • 实现两个表格的数据传递(类似于穿梭框)
  • 代码随想录---八股文训练营Day40(总结)
  • 【Unity/Animator动画系统】多层动画状态机实现角色的基本移动
  • 散户持股增厚工具:智能T0算法交易
  • ProtonBase × Data for AI Meetup·杭州站
  • 深度学习:神经网络中线性层的使用
  • 网络协议之邮件协议(SMTP、POP3与IMAP)
  • 路由基础(全)