Add pio i2c controller

Add sccb basic init
Add basic camera skeleton (no control, no frames, basically nothing)
This commit is contained in:
Jakub Wiśnia 2025-03-05 18:10:36 +01:00
parent 42edafcf08
commit 4286ab04ee
10 changed files with 1281 additions and 0 deletions

73
CMakeLists.txt Normal file
View File

@ -0,0 +1,73 @@
# Generated Cmake Pico project file
cmake_minimum_required(VERSION 3.13)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Initialise pico_sdk from installed location
# (note this can come from environment, CMake cache etc)
# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work ==
if(WIN32)
set(USERHOME $ENV{USERPROFILE})
else()
set(USERHOME $ENV{HOME})
endif()
set(sdkVersion 2.1.1)
set(toolchainVersion 14_2_Rel1)
set(picotoolVersion 2.1.1)
set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake)
if (EXISTS ${picoVscode})
include(${picoVscode})
endif()
# ====================================================================================
set(PICO_BOARD pico2 CACHE STRING "Board type")
# Pull in Raspberry Pi Pico SDK (must be before project)
include(pico_sdk_import.cmake)
project(PicoIris C CXX ASM)
# Initialise the Raspberry Pi Pico SDK
pico_sdk_init()
# Add executable. Default name is the project name, version 0.1
add_executable(PicoIris src/PicoIris.cpp )
pico_set_program_name(PicoIris "PicoIris")
pico_set_program_version(PicoIris "0.1")
# Generate PIO header
pico_generate_pio_header(PicoIris ${CMAKE_CURRENT_LIST_DIR}/src/pio/i2c/i2c.pio)
# Modify the below lines to enable/disable output over UART/USB
pico_enable_stdio_uart(PicoIris 0)
pico_enable_stdio_usb(PicoIris 1)
# Add the standard library to the build
target_link_libraries(PicoIris
pico_stdlib)
# Add the standard include files to the build
target_include_directories(PicoIris PRIVATE
${CMAKE_CURRENT_LIST_DIR}
src/
src/ov2640/
)
# Add any user requested libraries
target_link_libraries(PicoIris
hardware_spi
hardware_i2c
hardware_dma
hardware_pio
hardware_interp
hardware_timer
hardware_clocks
)
pico_add_extra_outputs(PicoIris)

121
pico_sdk_import.cmake Normal file
View File

@ -0,0 +1,121 @@
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
# This can be dropped into an external project to help locate this SDK
# It should be include()ed prior to project()
# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd.
#
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
# following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
endif ()
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
endif ()
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
endif ()
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG))
set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG})
message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')")
endif ()
if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG)
set(PICO_SDK_FETCH_FROM_GIT_TAG "master")
message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG")
endif()
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK")
if (NOT PICO_SDK_PATH)
if (PICO_SDK_FETCH_FROM_GIT)
include(FetchContent)
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
if (PICO_SDK_FETCH_FROM_GIT_PATH)
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
endif ()
FetchContent_Declare(
pico_sdk
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}
)
if (NOT pico_sdk)
message("Downloading Raspberry Pi Pico SDK")
# GIT_SUBMODULES_RECURSE was added in 3.17
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0")
FetchContent_Populate(
pico_sdk
QUIET
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}
GIT_SUBMODULES_RECURSE FALSE
SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src
BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build
SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild
)
else ()
FetchContent_Populate(
pico_sdk
QUIET
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}
SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src
BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build
SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild
)
endif ()
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
endif ()
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
else ()
message(FATAL_ERROR
"SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
)
endif ()
endif ()
get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
if (NOT EXISTS ${PICO_SDK_PATH})
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
endif ()
set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
endif ()
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
include(${PICO_SDK_INIT_CMAKE_FILE})

36
src/PicoIris.cpp Normal file
View File

@ -0,0 +1,36 @@
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/spi.h"
#include "hardware/i2c.h"
#include "hardware/dma.h"
#include "hardware/pio.h"
#include "hardware/interp.h"
#include "hardware/timer.h"
#include "hardware/clocks.h"
#include "hardware/uart.h"
#include <ov2640/camera.hpp>
camera_config_t left_eye_config = {
.pin_pwdn = -1, /*!< GPIO pin for camera power down line */ //? Also called PWDN, or set to -1 and tie to GND
.pin_reset = -1, /*!< GPIO pin for camera reset line */ //? Cam reset, or set to -1 and tie to 3.3V
.pin_xclk = 0, /*!< GPIO pin for camera XCLK line */ //? in theory could be shared or perhaps ommited?
.pin_sccb_sda = 0, /*!< GPIO pin for camera SDA line */
.pin_sccb_scl = 0, /*!< GPIO pin for camera SCL line */
.pin_data_base = 0, /*!< this pin + 7 consecutive will be used D0-D7 */
.pin_vsync = 0, /*!< GPIO pin for camera VSYNC line */
.pin_href = 0, /*!< GPIO pin for camera HREF line */
.pin_pclk = 0, /*!< GPIO pin for camera PCLK line */
.xclk_freq_hz = 0, /*!< Frequency of XCLK signal, in Hz. */ //! Figure out the highest it can go to
.sccb_ctrl = 0, /* Select i2c controller ctrl: 0 - i2c0, 1 - i2c1, 2 - pio0, 3 - pio1, 4 - pio2 */
};
int main()
{
stdio_init_all();
while (true) {
printf("Hello, world!\n");
sleep_ms(1000);
}
}

323
src/ov2640/camera.cpp Normal file
View File

@ -0,0 +1,323 @@
#include <camera.hpp>
#include <sccb.hpp>
#include "pico/stdlib.h"
register_val_t OV2640_init[] = {
// Ideas from rp2040_ov2640-main repo
// OV2640 camera initialization after reset
// WIP STUFF, don't take this seriously yet
{OV2640_REG_RA_DLMT, OV2640_RA_DLMT_DSP}, // DSP bank select 0
{0x2C, 0xFF}, // Reserved
{0x2E, 0xDF}, // Reserved
{OV2640_REG_RA_DLMT, OV2640_RA_DLMT_SENSOR}, // Sensor bank sel 1
{0x3C, 0x32}, // Reserved
{OV2640_REG1_CLKRC, 0x00}, // Clock doubler OFF
{OV2640_REG1_COM2, OV2640_COM2_DRIVE_2X}, // 2X drive select
{OV2640_REG1_REG04, // Mirror + ?
OV2640_REG04_HFLIP | 0x20 | OV2640_REG04_HREF0},
{OV2640_REG1_COM8, 0xC0 | OV2640_COM8_BANDING | OV2640_COM8_AGC_AUTO |
OV2640_COM8_EXP_AUTO},
{OV2640_REG1_COM9, OV2640_COM9_AGC_GAIN_8X | 0x08},
{0x2C, 0x0c}, // Reserved
{0x33, 0x78}, // Reserved
{0x3A, 0x33}, // Reserved
{0x3B, 0xfB}, // Reserved
{0x3E, 0x00}, // Reserved
{0x43, 0x11}, // Reserved
{0x16, 0x10}, // Reserved
{0x4A, 0x81}, // Reserved
{0x21, 0x99}, // Reserved
//{OV2640_REG1_AEW, 0x40}, // High range for AEC/AGC
//{OV2640_REG1_AEB, 0x38}, // Low range for AEC/AGC
{OV2640_REG1_AEW, 0xFF}, // High range for AEC/AGC
{OV2640_REG1_AEB, 0x00}, // Low range for AEC/AGC
{OV2640_REG1_VV, 0x82}, // Fast mode thresholds
{0x5C, 0x00}, // Reserved
{0x63, 0x00}, // Reserved
{OV2640_REG1_FLL, 0x3F}, // Frame length adjustment LSBs
{OV2640_REG1_COM3, 0x38 | OV2640_COM3_BANDING_50HZ},
{OV2640_REG1_HISTO_LOW, 0x70},
{OV2640_REG1_HISTO_HIGH, 0x80},
{0x7C, 0x05}, // Reserved
{0x20, 0x80}, // Reserved
{0x28, 0x30}, // Reserved
{0x6C, 0x00}, // Reserved
{0x6D, 0x80}, // Reserved
{0x6E, 0x00}, // Reserved
{0x70, 0x02}, // Reserved
{0x71, 0x94}, // Reserved
{0x73, 0xC1}, // Reserved
{0x3D, 0x34}, // Reserved
{0x5A, 0x57}, // Reserved
{OV2640_REG1_COM7, OV2640_COM7_RES_UXGA},
{OV2640_REG1_CLKRC, 0x00}, // Clock doubler off
{OV2640_REG1_HREFST, 0x11}, // Horiz window start MSB
{OV2640_REG1_HREFEND, 0x75}, // Horiz window end MSB
{OV2640_REG1_VSTRT, 0x01}, // Vert window start MSB
{OV2640_REG1_VEND, 0x97}, // Vert window end MSB
{OV2640_REG1_REG32, 0x36}, // Horiz window LSBs
{OV2640_REG1_COM1, 0x0F}, // Vert window LSBs
{0x37, 0x40}, // Reserved
{OV2640_REG1_BD50, 0xBB}, // 50 Hz banding AEC MSBs
{OV2640_REG1_BD60, 0x9C}, // 60 Hz banding AEC MSBs
{0x5A, 0x57}, // Reserved
{0x6D, 0x80}, // Reserved
{0x6D, 0x38}, // Reserved (2nd ref in a row?)
{0x39, 0x02}, // Reserved
{0x35, 0x88}, // Reserved
{0x22, 0x0A}, // Reserved
{0x37, 0x40}, // Reserved
{0x23, 0x00}, // Reserved
{OV2640_REG1_ARCOM2, 0xA0}, // ?
{0x36, 0x1A}, // Reserved
{0x06, 0x02}, // Reserved
{0x07, 0xC0}, // Reserved
{OV2640_REG1_COM4, 0xB7},
{0x0E, 0x01}, // Reserved
{0x4C, 0x00}, // Reserved
{OV2640_REG_RA_DLMT, OV2640_RA_DLMT_DSP}, // DSP bank select 0
{0xE5, 0x7F}, // Reserved
{OV2640_REG0_MC_BIST, OV2640_MC_BIST_RESET | OV2640_MC_BIST_BOOTROM},
{0x41, 0x24}, // Reserved
{OV2640_REG0_RESET, OV2640_RESET_JPEG | OV2640_RESET_DVP},
{0x76, 0xFF}, // Reserved
{0x33, 0xA0}, // Reserved
{0x42, 0x20}, // Reserved
{0x43, 0x18}, // Reserved
{0x4C, 0x00}, // Reserved
{OV2640_REG0_CTRL3, OV2640_CTRL3_BPC | OV2640_CTRL3_WPC | 0x10},
{0x88, 0x3F}, // Reserved
{0xD7, 0x03}, // Reserved
{0xD9, 0x10}, // Reserved
{OV2640_REG0_R_DVP_SP, OV2640_R_DVP_SP_AUTO | 0x02},
{0xC8, 0x08}, // Reserved
{0xC9, 0x80}, // Reserved
{OV2640_REG0_BPDATA, 0x00},
{OV2640_REG0_BPADDR, 0x03},
{OV2640_REG0_BPDATA, 0x48},
{OV2640_REG0_BPADDR, 0x08},
{OV2640_REG0_BPDATA, 0x20},
{OV2640_REG0_BPDATA, 0x10},
{OV2640_REG0_BPDATA, 0x0E},
{0x90, 0x00}, // Reserved (addr/data?)
{0x91, 0x0E}, // Reserved
{0x91, 0x1A}, // Reserved
{0x91, 0x31}, // Reserved
{0x91, 0x5A}, // Reserved
{0x91, 0x69}, // Reserved
{0x91, 0x75}, // Reserved
{0x91, 0x7E}, // Reserved
{0x91, 0x88}, // Reserved
{0x91, 0x8F}, // Reserved
{0x91, 0x96}, // Reserved
{0x91, 0xA3}, // Reserved
{0x91, 0xaf}, // Reserved
{0x91, 0xc4}, // Reserved
{0x91, 0xd7}, // Reserved
{0x91, 0xe8}, // Reserved
{0x91, 0x20}, // Reserved
{0x92, 0x00}, // Reserved (addr/data?)
{0x93, 0x06}, // Reserved
{0x93, 0xe3}, // Reserved
{0x93, 0x02}, // Reserved
{0x93, 0x02}, // Reserved
{0x93, 0x00}, // Reserved
{0x93, 0x04}, // Reserved
{0x93, 0x00}, // Reserved
{0x93, 0x03}, // Reserved
{0x93, 0x00}, // Reserved
{0x93, 0x00}, // Reserved
{0x93, 0x00}, // Reserved
{0x93, 0x00}, // Reserved
{0x93, 0x00}, // Reserved
{0x93, 0x00}, // Reserved
{0x93, 0x00}, // Reserved (end data?)
{0x96, 0x00}, // Reserved (addr/data?)
{0x97, 0x08}, // Reserved
{0x97, 0x19}, // Reserved
{0x97, 0x02}, // Reserved
{0x97, 0x0c}, // Reserved
{0x97, 0x24}, // Reserved
{0x97, 0x30}, // Reserved
{0x97, 0x28}, // Reserved
{0x97, 0x26}, // Reserved
{0x97, 0x02}, // Reserved
{0x97, 0x98}, // Reserved
{0x97, 0x80}, // Reserved
{0x97, 0x00}, // Reserved
{0x97, 0x00}, // Reserved
{OV2640_REG0_CTRL1, (uint8_t)~OV2640_CTRL1_DG},
{OV2640_REG_RA_DLMT, OV2640_RA_DLMT_DSP}, // DSP bank select 0
{0xBA, 0xDC}, // Reserved
{0xBB, 0x08}, // Reserved
{0xB6, 0x24}, // Reserved
{0xB8, 0x33}, // Reserved
{0xB7, 0x20}, // Reserved
{0xB9, 0x30}, // Reserved
{0xB3, 0xB4}, // Reserved
{0xB4, 0xCA}, // Reserved
{0xB5, 0x43}, // Reserved
{0xB0, 0x5C}, // Reserved
{0xB1, 0x4F}, // Reserved
{0xB2, 0x06}, // Reserved
{0xC7, 0x00}, // Reserved
{0xC6, 0x51}, // Reserved
{0xC5, 0x11}, // Reserved
{0xC4, 0x9C}, // Reserved
{0xBF, 0x00}, // Reserved
{0xBC, 0x64}, // Reserved
{0xA6, 0x00}, // Reserved (addr/data?)
{0xA7, 0x1E}, // Reserved
{0xA7, 0x6b}, // Reserved
{0xA7, 0x47}, // Reserved
{0xA7, 0x33}, // Reserved
{0xA7, 0x00}, // Reserved
{0xA7, 0x23}, // Reserved
{0xA7, 0x2E}, // Reserved
{0xA7, 0x85}, // Reserved
{0xA7, 0x42}, // Reserved
{0xA7, 0x33}, // Reserved
{0xA7, 0x00}, // Reserved
{0xA7, 0x23}, // Reserved
{0xA7, 0x1B}, // Reserved
{0xA7, 0x74}, // Reserved
{0xA7, 0x42}, // Reserved
{0xA7, 0x33}, // Reserved
{0xA7, 0x00}, // Reserved
{0xA7, 0x23}, // Reserved
{OV2640_REG0_HSIZE8, 0xC8}, // Horiz size MSBs
{OV2640_REG0_VSIZE8, 0x96}, // Vert size MSBs
{OV2640_REG0_SIZEL, 0x00}, // Size bits
{OV2640_REG0_CTRL2, OV2640_CTRL2_DCW | OV2640_CTRL2_SDE |
OV2640_CTRL2_UV_ADJ | OV2640_CTRL2_UV_AVG |
OV2640_CTRL2_CMX},
{OV2640_REG0_CTRLI, OV2640_CTRLI_LP_DP | 0x82}, // H/V dividers
{OV2640_REG0_HSIZE, 0x90}, // H_SIZE low bits
{OV2640_REG0_VSIZE, 0x2C}, // V_SIZE low bits
{OV2640_REG0_XOFFL, 0x00}, // OFFSET_X LSBs
{OV2640_REG0_YOFFL, 0x00}, // OFFSET_Y LSBs
{OV2640_REG0_VHYX, 0x88}, // V/H/Y/X MSBs
{OV2640_REG0_ZMOW, 0x50}, // OUTW low bits
{OV2640_REG0_ZMOH, 0x3C}, // OUTH low bits
{OV2640_REG0_ZMHH, 0x00}, // OUTW/H high bits
{OV2640_REG0_R_DVP_SP, 0x04}, // Manual DVP PCLK
{0x7F, 0x00}, // Reserved
//{OV2640_REG0_IMAGE_MODE, 0x00}, // YUV MSB first
{0xE5, 0x1F}, // Reserved
{0xE1, 0x67}, // Reserved
{OV2640_REG0_RESET, 0x00}, // Reset nothing?
{0xDD, 0x7F}, // Reserved
{OV2640_REG0_R_BYPASS, OV2640_R_BYPASS_DSP_ENABLE},
{OV2640_REG_RA_DLMT, OV2640_RA_DLMT_DSP}, // DSP bank select 0
{OV2640_REG0_RESET, OV2640_RESET_DVP},
{OV2640_REG0_HSIZE8, 0xC8}, // Image horiz size MSBs
{OV2640_REG0_VSIZE8, 0x96}, // Image vert size MSBs
{OV2640_REG0_CTRL2, OV2640_CTRL2_DCW | OV2640_CTRL2_SDE |
OV2640_CTRL2_UV_ADJ | OV2640_CTRL2_UV_AVG |
OV2640_CTRL2_CMX},
{OV2640_REG0_CTRLI, OV2640_CTRLI_LP_DP | 0x12},
{OV2640_REG0_HSIZE, 0x90}, // H_SIZE low bits
{OV2640_REG0_VSIZE, 0x2C}, // V_SIZE low bits
{OV2640_REG0_XOFFL, 0x00}, // OFFSET_X low bits
{OV2640_REG0_YOFFL, 0x00}, // OFFSET_y low bits
{OV2640_REG0_VHYX, 0x88}, // V/H/Y/X high bits
{OV2640_REG0_TEST, 0x00},
{OV2640_REG0_ZMOW, 0x50}, // OUTW low bits
{OV2640_REG0_ZMOH, 0x3C}, // OUTH low bits
{OV2640_REG0_ZMHH, 0x00}, // OUTW/H high bits
{OV2640_REG0_R_DVP_SP, 0x04}, // Manual DVP PCLK
{0xE0, 0x00}, // Reset nothing?
{OV2640_REG_RA_DLMT, OV2640_RA_DLMT_DSP}, // DSP bank select 0
{OV2640_REG0_R_BYPASS, OV2640_R_BYPASS_DSP_ENABLE},
{OV2640_REG0_IMAGE_MODE, OV2640_IMAGE_MODE_DVP_RGB565},
{OV2640_REG0_IMAGE_MODE,
OV2640_IMAGE_MODE_DVP_RGB565 | OV2640_IMAGE_MODE_BYTE_SWAP},
{0x98, 0x00}, // Reserved
{0x99, 0x00}, // Reserved
{0x00, 0x00}, // Reserved
{OV2640_REG_RA_DLMT, OV2640_RA_DLMT_DSP}, // DSP bank select 0
{OV2640_REG0_RESET, OV2640_RESET_DVP},
{OV2640_REG0_HSIZE8, 0xC8}, // H_SIZE high bits
{OV2640_REG0_VSIZE8, 0x96}, // V_SIZE high bits
{OV2640_REG0_CTRL2, OV2640_CTRL2_DCW | OV2640_CTRL2_SDE |
OV2640_CTRL2_UV_ADJ | OV2640_CTRL2_UV_AVG |
OV2640_CTRL2_CMX},
{OV2640_REG0_CTRLI, OV2640_CTRLI_LP_DP | 0x09},
{OV2640_REG0_HSIZE, 0x90}, // H_SIZE low bits
{OV2640_REG0_VSIZE, 0x2C}, // V_SIZE low bits
{OV2640_REG0_XOFFL, 0x00}, // OFFSET_X low bits
{OV2640_REG0_YOFFL, 0x00}, // OFFSET_Y low bits
{OV2640_REG0_VHYX, 0x88}, // V/H/Y/X high bits
{OV2640_REG0_TEST, 0x00},
{OV2640_REG0_ZMOW, 0xA0}, // OUTW low bits
{OV2640_REG0_ZMOH, 0x78}, // OUTH low bits
{OV2640_REG0_ZMHH, 0x00}, // OUTW/H high bits
{OV2640_REG0_R_DVP_SP, 0x02}, // Manual DVP PCLK setting
{OV2640_REG0_RESET, 0x00}
}; // Go
// TODO: figure out how to set to JPEG and 260x260
/*OV2640_qqvga[] =
{// Configure OV2640 for QQVGA output
{OV2640_REG_RA_DLMT, OV2640_RA_DLMT_DSP}, // DSP bank select 0
{OV2640_REG0_RESET, OV2640_RESET_DVP},
{OV2640_REG0_HSIZE8, 0x64}, // HSIZE high bits
{OV2640_REG0_VSIZE8, 0x4B}, // VSIZE high bits
{OV2640_REG0_CTRL2, OV2640_CTRL2_DCW | OV2640_CTRL2_SDE |
OV2640_CTRL2_UV_AVG | OV2640_CTRL2_CMX},
{OV2640_REG0_CTRLI, OV2640_CTRLI_LP_DP | 0x12},
{OV2640_REG0_HSIZE, 0xC8}, // H_SIZE low bits
{OV2640_REG0_VSIZE, 0x96}, // V_SIZE low bits
{OV2640_REG0_XOFFL, 0x00}, // OFFSET_X low bits
{OV2640_REG0_YOFFL, 0x00}, // OFFSET_Y low bits
{OV2640_REG0_VHYX, 0x00}, // V/H/Y/X high bits
{OV2640_REG0_TEST, 0x00}, // ?
{OV2640_REG0_ZMOW, 0x28}, // OUTW low bits
{OV2640_REG0_ZMOH, 0x1E}, // OUTH low bits
{OV2640_REG0_ZMHH, 0x00}, // OUTW/H high bits
{OV2640_REG0_R_DVP_SP, 0x08}, // Manual DVP PCLK setting
{OV2640_REG0_RESET, 0x00}}, // Go
OV2640_rgb[] = {{OV2640_REG_RA_DLMT,
OV2640_RA_DLMT_DSP}, // DSP bank select 0
{OV2640_REG0_RESET, OV2640_RESET_DVP},
{OV2640_REG0_IMAGE_MODE, OV2640_IMAGE_MODE_DVP_RGB565 |
OV2640_IMAGE_MODE_BYTE_SWAP},
{0xD7, 0x03}, // Mystery init values
{0xE1, 0x77}, // seen in other examples
{OV2640_REG0_RESET, 0x00}}, // Go
OV2640_yuv[] = {
{OV2640_REG_RA_DLMT, OV2640_RA_DLMT_DSP}, // DSP bank select 0
{OV2640_REG0_RESET, OV2640_RESET_DVP},
{OV2640_REG0_IMAGE_MODE,
OV2640_IMAGE_MODE_DVP_YUV | OV2640_IMAGE_MODE_BYTE_SWAP},
{0xD7, 0x01}, // Mystery init values
{0xE1, 0x67}, // seen in other examples
{OV2640_REG0_RESET, 0x00}
}; // Go*/
OV2640::OV2640(camera_config_t config) {
this->config = config;
}
int OV2640::begin() {
// Initialize peripherals for parallel+I2C camera:
//! TODO: start XCLK before SCCB init
this->sccb = SCCB(this->config.pin_sccb_sda,this->config.pin_sccb_scl);
this->sccb.begin(this->config.sccb_ctrl);
// ENABLE AND/OR RESET CAMERA --------------------------------------------
// Unsure of camera startup time from beginning of input clock.
// Let's guess it's similar to tS:REG (300 ms) from datasheet.
sleep_ms(300000);
// Perform a soft reset (we dont wanna waste GPIO on wiring in reset pins)
this->sccb.writeRegister(OV2640_REG_RA_DLMT, OV2640_RA_DLMT_SENSOR); // Bank select 1
this->sccb.writeRegister(OV2640_REG1_COM7, OV2640_COM7_SRST); // System reset
sleep_ms(1); // Datasheet: tS:RESET = 1 ms
// Init main camera settings
this->sccb.writeList(OV2640_init, sizeof OV2640_init / sizeof OV2640_init[0]);
return 0;
}
// TODO: add all of the specific controls like brightness and all of that scheiße

289
src/ov2640/camera.hpp Normal file
View File

@ -0,0 +1,289 @@
// TODO: Add XCLK settings
typedef struct {
int pin_pwdn; /*!< GPIO pin for camera power down line */ //? Also called PWDN, or set to -1 and tie to GND
int pin_reset; /*!< GPIO pin for camera reset line */ //? Cam reset, or set to -1 and tie to 3.3V
int pin_xclk; /*!< GPIO pin for camera XCLK line */ //? in theory could be shared or perhaps ommited?
int pin_sccb_sda; /*!< GPIO pin for camera SDA line */
int pin_sccb_scl; /*!< GPIO pin for camera SCL line */
int pin_data_base; /*!< this pin + 7 consecutive will be used D0-D7 */
int pin_vsync; /*!< GPIO pin for camera VSYNC line */
int pin_href; /*!< GPIO pin for camera HREF line */
int pin_pclk; /*!< GPIO pin for camera PCLK line */
int xclk_freq_hz; /*!< Frequency of XCLK signal, in Hz. */ //! Figure out the highest it can go to
uint8_t sccb_ctrl; /* Select i2c controller ctrl: 0 - i2c0, 1 - i2c1, 2 - pio0, 3 - pio1, 4 - pio2 */
} camera_config_t;
typedef struct {
uint8_t reg; ///< Register address
uint8_t value; ///< Value to store
} register_val_t;
class OV2640 {
public:
OV2640(camera_config_t config);
~OV2640();
int begin();
private:
camera_config_t config;
SCCB sccb;
};
//? Refer to https://github.com/adafruit/Adafruit_ImageCapture/blob/main/src/Adafruit_iCap_OV2640.h to check whatever this shit below even is??
#define OV2640_ADDR 0x30
#define OV2640_REG_RA_DLMT 0xFF //< Register bank select
#define OV2640_RA_DLMT_DSP 0x00 //< Bank 0 - DSP address
#define OV2640_RA_DLMT_SENSOR 0x01 //< Bank 1 - Sensor address
// OV2640 register bank 0 -- DSP address
// These register names are preceded by 'REG0' as a bank-select reminder
#define OV2640_REG0_R_BYPASS 0x05 //< Bypass DSP
#define OV2640_R_RESERVED_MASK 0xFE //< Reserved bits
#define OV2640_R_BYPASS_MASK 0x01 //< DSP mask:
#define OV2640_R_BYPASS_DSP_ENABLE 0x00 //< Enable DSP
#define OV2640_R_BYPASS_DSP_BYPASS 0x01 //< Bypass DSP
#define OV2640_REG0_QS 0x44 //< Quantization scale factor
#define OV2640_REG0_CTRLI 0x50 //< ?
#define OV2640_CTRLI_LP_DP 0x80 //< LP_DP bit
#define OV2640_CTRLI_ROUND 0x40 //< Round bit
#define OV2640_CTRLI_V_DIV_MASK 0x38 //< V_DIVIDER mask (3 bits)
#define OV2640_CTRLI_H_DIV_MASK 0x07 //< H_DIVIDER mask (3 bits)
#define OV2640_REG0_HSIZE 0x51 //< H_SIZE[7:0] (real/4)
#define OV2640_REG0_VSIZE 0x52 //< V_SIZE[7:0] (real/4)
#define OV2640_REG0_XOFFL 0x53 //< OFFSET_X[7:0]
#define OV2640_REG0_YOFFL 0x54 //< OFFSET_Y[7:0]
#define OV2640_REG0_VHYX 0x55 //< V/H/X/Y size/offset high bits
#define OV2640_VHYX_V_SIZE_8 0x80 //< V_SIZE[8] bit
#define OV2640_VHYX_OFFSET_Y_MASK 0x70 //< OFFSET_Y[10:8] mask
#define OV2640_VHYX_H_SIZE_8 0x08 //< H_SIZE[8] bit
#define OV2640_VHYX_OFFSET_X_MASK 0x07 //< OFFSET_X[10:8] mask
#define OV2640_REG0_DPRP 0x56 //< ?
#define OV2640_DPRP_DP_SELY_MASK 0xF0 //< DP_SELY mask
#define OV2640_DPRP_DP_SELX_MASK 0x0F //< DP_SELX mask
#define OV2640_REG0_TEST 0x57 //< ?
#define OV2640_TEST_H_SIZE_9 0x80 //< H_SIZE[9] bit
#define OV2640_TEST_RESERVED_MASK 0x7F //< Reserved bits
#define OV2640_REG0_ZMOW 0x5A //< OUTW[7:0] (real/4)
#define OV2640_REG0_ZMOH 0x5B //< OUTH[7:0] (real/4)
#define OV2640_REG0_ZMHH 0x5C //< Zoom speed and more
#define OV2640_ZMHH_ZMSPD_MASK 0xF0 //< ZMSPD (zoom speed)
#define OV2640_ZMHH_OUTH_8 0x40 //< OUTH[8] bit
#define OV2640_ZMHH_OUTW_MASK 0x03 //< OUTW[9:8]
#define OV2640_REG0_BPADDR 0x7C //< SDE indirect reg access: addr
#define OV2640_REG0_BPDATA 0x7D //< SDE indirect reg access: data
#define OV2640_REG0_CTRL2 0x86 //< Module enable:
#define OV2640_CTRL2_DCW 0x20 //< DCW enable
#define OV2640_CTRL2_SDE 0x10 //< SDE enable
#define OV2640_CTRL2_UV_ADJ 0x08 //< UV_ADJ enable
#define OV2640_CTRL2_UV_AVG 0x04 //< UV_AVG enable
#define OV2640_CTRL2_CMX 0x01 //< CMX enable
#define OV2640_REG0_CTRL3 0x87 //< Module enable, continued
#define OV2640_CTRL3_BPC 0x80 //< BPC enable
#define OV2640_CTRL3_WPC 0x40 //< WPC enable
#define OV2640_CTRL3_RESERVED_MASK 0x3F //< Reserved bits
#define OV2640_REG0_SIZEL 0x8C //< HSIZE, VSIZE more bits
#define OV2640_SIZEL_HSIZE11 0x40 //< HSIZE[11]
#define OV2640_SIZEL_HSIZE_MASK 0x38 //< HSIZE[2:0]
#define OV2640_SIZEL_VSIZE_MASK 0x07 //< VSIZE[2:0]
#define OV2640_REG0_HSIZE8 0xC0 //< Image horiz size HSIZE[10:3]
#define OV2640_REG0_VSIZE8 0xC1 //< Image vert size VSIZE[10:3]
#define OV2640_REG0_CTRL0 0xC2 //< Module enable, continued
#define OV2640_CTRL0_AEC_EN 0x80 //< AEC_EN enable
#define OV2640_CTRL0_AEC_SEL 0x40 //< AEC_SEL enable
#define OV2640_CTRL0_STAT_SEL 0x20 //< STAT_SEL enable
#define OV2640_CTRL0_VFIRST 0x10 //< VFIRST enable
#define OV2640_CTRL0_YUV422 0x08 //< YUV422 enable
#define OV2640_CTRL0_YUV_EN 0x04 //< YUV_EN enable
#define OV2640_CTRL0_RGB_EN 0x02 //< RGB_EN enable
#define OV2640_CTRL0_RAW_EN 0x01 //< RAW_EN enable
#define OV2640_REG0_CTRL1 0xC3 //< Module enable, continued
#define OV2640_CTRL1_CIP 0x80 //< CIP enable
#define OV2640_CTRL1_DMY 0x40 //< DMY enable
#define OV2640_CTRL1_RAW_GMA 0x20 //< RAW_GMA enable
#define OV2640_CTRL1_DG 0x10 //< DG enable
#define OV2640_CTRL1_AWB 0x08 //< AWB enable
#define OV2640_CTRL1_AWB_GAIN 0x04 //< AWB_GAIN enable
#define OV2640_CTRL1_LENC 0x02 //< LENC enable
#define OV2640_CTRL1_PRE 0x01 //< PRE enable
#define OV2640_REG0_R_DVP_SP 0xD3 //< DVP selections
#define OV2640_R_DVP_SP_AUTO 0x80 //< Auto DVP mode
#define OV2640_R_DVP_SP_PCLK_MASK 0x7F //< Manual DVP PCLK mask
#define OV2640_REG0_IMAGE_MODE 0xDA //< Image output format select
#define OV2640_IMAGE_MODE_Y8 0x40 //< Y8 enable for DVP
#define OV2640_IMAGE_MODE_JPEG 0x10 //< JPEG output enable mask
#define OV2640_IMAGE_MODE_DVP_MASK 0x0C //< DVP output format mask
#define OV2640_IMAGE_MODE_DVP_YUV 0x00 //< YUV422
#define OV2640_IMAGE_MODE_DVP_RAW10 0x04 //< RAW10 (DVP)
#define OV2640_IMAGE_MODE_DVP_RGB565 0x08 //< RGB565
#define OV2640_IMAGE_MODE_JPEG_HREF 0x02 //< HREF timing select in JPEG mode
#define OV2640_IMAGE_MODE_BYTE_SWAP 0x01 //< Byte swap enable for DVP
#define OV2640_REG0_RESET 0xE0 //< Reset
#define OV2640_RESET_MCU 0x40 //< Microcontroller reset
#define OV2640_RESET_SCCB 0x20 //< SCCB reset
#define OV2640_RESET_JPEG 0x10 //< JPEG reset
#define OV2640_RESET_DVP 0x04 //< DVP reset
#define OV2640_RESET_IPU 0x02 //< IPU reset
#define OV2640_RESET_CIF 0x01 //< CIF reset
#define OV2640_REG0_MS_SP 0xF0 //< SCCB host speed
#define OV2640_REG0_SS_ID 0xF7 //< SCCB periph ID
#define OV2640_REG0_SS_CTRL 0xF8 //< SCCB periph control 1:
#define OV2640_SS_CTRL_ADDR 0x20 //< Address auto-increment
#define OV2640_SS_CTRL_SCCB 0x08 //< SCCB enable
#define OV2640_SS_CTRL_DELAY 0x04 //< Delay SCCB main clock
#define OV2640_SS_CTRL_ACCESS 0x02 //< Enable SCCB host access
#define OV2640_SS_CTRL_SENSOR 0x01 //< Enable sensor pass-through
#define OV2640_REG0_MC_BIST 0xF9 //< ?
#define OV2640_MC_BIST_RESET 0x80 //< MCU reset
#define OV2640_MC_BIST_BOOTROM 0x40 //< Boot ROM select
#define OV2640_MC_BIST_12K_1 0x20 //< R/W 1 error for 12KB mem
#define OV2640_MC_BIST_12K_0 0x10 //< R/W 0 error for 12KB mem
#define OV2640_MC_BIST_512_1 0x08 //< R/W 1 error for 512B mem
#define OV2640_MC_BIST_512_0 0x04 //< R/W 0 error for 512B mem
#define OV2640_MC_BIST_BUSY 0x02 //< R=BISY busy, W=MCU reset
#define OV2640_MC_BIST_LAUNCH 0x01 //< Launch BIST
#define OV2640_REG0_MC_AL 0xFA //< Program mem ptr addr low byte
#define OV2640_REG0_MC_AH 0xFB //< Program mem ptr addr high byte
#define OV2640_REG0_MC_D 0xFC //< Program mem ptr access address
#define OV2640_REG0_P_CMD 0xFD //< SCCB protocol command register
#define OV2640_REG0_P_STATUS 0xFE //< SCCB protocol status register
// OV2640 register bank 1 -- Sensor address
// These register names are preceded by 'REG1' as a bank-select reminder
#define OV2640_REG1_GAIN 0x00 //< AGC gain control LSBs
#define OV2640_REG1_COM1 0x03 //< Common control 1
#define OV2640_COM1_DFRAME_MASK 0xC0 //< Dummy frame control mask
#define OV2640_COM1_DFRAME_1 0x40 //< Allow 1 dummy frame
#define OV2640_COM1_DFRAME_4 0x80 //< Allow 4 dummy frames
#define OV2640_COM1_DFRAME_7 0xC0 //< Allow 7 dummy frames
#define OV2640_COM1_VEND_MASK 0x0C //< Vert window end line LSBs
#define OV2640_COM1_VSTRT_MASK 0x03 //< Vert window start line LSBs
#define OV2640_REG1_REG04 0x04 //< Register 04
#define OV2640_REG04_HFLIP 0x80 //< Horizontal mirror
#define OV2640_REG04_VFLIP 0x40 //< Vertical mirror
#define OV2640_REG04_VREF0 0x10 //< VREF[0]
#define OV2640_REG04_HREF0 0x08 //< HREF[0]
#define OV2640_REG04_AEC_MASK 0x03 //< AEC[1:0]
#define OV2640_REG1_REG08 0x08 //< Register 08 (frame exposure)
#define OV2640_REG1_COM2 0x09 //< Common control 2
#define OV2640_COM2_RESERVED_MASK 0xE8 //< Reserved bits
#define OV2640_COM2_STANDBY 0x10 //< Standby mode
#define OV2640_COM2_PINUSE 0x04 //< PWDN/RESETB as SLVS/SLHS mask
#define OV2640_COM2_DRIVE_MASK 0x03 //< Output drive select mask
#define OV2640_COM2_DRIVE_1X 0x00 //< 1x
#define OV2640_COM2_DRIVE_3X 0x01 //< 3x (sic)
#define OV2640_COM2_DRIVE_2X 0x02 //< 2x (sic)
#define OV2640_COM2_DRIVE_4X 0x03 //< 4x
#define OV2640_REG1_PIDH 0x0A //< Product ID MSB (read only)
#define OV2640_REG1_PIDL 0x0B //< Product ID LSB (read only)
#define OV2640_REG1_COM3 0x0C //< Common control 3
#define OV2640_COM3_RESERVED_MASK 0xF8 //< Reserved bits
#define OV2640_COM3_BANDING_MASK 0x04 //< Manual banding bit mask
#define OV2640_COM3_BANDING_60HZ 0x00 //< 60 Hz
#define OV2640_COM3_BANDING_50HZ 0x04 //< 50 Hz
#define OV2640_COM3_AUTO_BANDING 0x02 //< Auto-set banding
#define OV2640_COM3_SNAPSHOT 0x01 //< Snapshot option
#define OV2640_REG1_COM4 0x0D //< Common control 4
#define OV2640_COM4_RESERVED_MASK 0xF8 //< Reserved bits
#define OV2640_COM4_CLOCK_PIN_STATUS 0x04 //< Clock output power pin status
#define OV2640_REG1_AEC 0x10 //< AEC[9:2] auto exposure ctrl
#define OV2640_REG1_CLKRC 0x11 //< Clock rate control
#define OV2640_CLKRC_DOUBLE 0x80 //< Internal freq doubler on/off
#define OV2640_CLKRC_DIV_MASK 0x3F //< Clock divider mask
#define OV2640_REG1_COM7 0x12 //< Common control 7:
#define OV2640_COM7_SRST 0x80 //< System reset
#define OV2640_COM7_RES_MASK 0x70 //< Resolution mask
#define OV2640_COM7_RES_UXGA 0x00 //< UXGA (full size) mode
#define OV2640_COM7_RES_CIF 0x10 //< CIF mode
#define OV2640_COM7_RES_SVGA 0x40 //< SVGA mode
#define OV2640_COM7_ZOOM 0x04 //< Zoom mode
#define OV2640_COM7_COLORBAR 0x02 //< Color bar test pattern enable
#define OV2640_REG1_COM8 0x13 //< Common control 8:
#define OV2640_COM8_RESERVED_MASK 0xDA //< Reserved bits
#define OV2640_COM8_BANDING 0x20 //< Banding filter on
#define OV2640_COM8_AGC_AUTO 0x04 //< Auto gain
#define OV2640_COM8_EXP_AUTO 0x01 //< Auto exposure
#define OV2640_REG1_COM9 0x14 //< Common control 9:
#define OV2640_COM9_AGC_GAIN_MASK 0xE0 //< AGC gain ceiling mask, GH[2:0]
#define OV2640_COM9_AGC_GAIN_2X 0x00 //< 2x
#define OV2640_COM9_AGC_GAIN_4X 0x20 //< 4x
#define OV2640_COM9_AGC_GAIN_8X 0x40 //< 8x
#define OV2640_COM9_AGC_GAIN_16X 0x60 //< 16x
#define OV2640_COM9_AGC_GAIN_32X 0x80 //< 32x
#define OV2640_COM9_AGC_GAIN_64X 0xA0 //< 64x
#define OV2640_COM9_AGC_GAIN_128X 0xC0 //< 128x
#define OV2640_COM9_RESERVED_MASK 0x1F //< Reserved bits
#define OV2640_REG1_COM10 0x15 //< Common control 10:
#define OV2640_COM10_CHSYNC_SWAP_MASK 0x80 //< CHSYNC pin output swap mask
#define OV2640_COM10_CHSYNC_CHSYNC 0x00 //< CHSYNC
#define OV2640_COM10_CHSYNC_HREF 0x80 //< HREF
#define OV2640_COM10_HREF_SWAP_MASK 0x40 //< HREF pin output swap mask
#define OV2640_COM10_HREF_HREF 0x00 //< HREF
#define OV2640_COM10_HREF_CHSYNC 0x40 //< CHSYNC
#define OV2640_COM10_PCLK_MASK 0x20 //< PCLK output selection mask
#define OV2640_COM10_PCLK_ALWAYS 0x00 //< PCLK always output
#define OV2640_COM10_PCLK_HREF 0x20 //< PCLK qualified by HREF
#define OV2640_COM10_PCLK_EDGE_MASK 0x10 //< PCLK edge selection mask
#define OV2640_COM10_PCLK_FALLING 0x00 //< Data updated on falling PCLK
#define OV2640_COM10_PCLK_RISING 0x10 //< Data updated on rising PCLK
#define OV2640_COM10_HREF_MASK 0x08 //< HREF polarity mask
#define OV2640_COM10_HREF_POSITIVE 0x00 //< Positive HREF
#define OV2640_COM10_HREF_NEGATIVE 0x08 //< Negative HREF for data valid
#define OV2640_COM10_VSYNC_MASK 0x02 //< VSYNC polarity mask
#define OV2640_COM10_VSYNC_POSITIVE 0x00 //< Positive VSYNC
#define OV2640_COM10_VSYNC_NEGATIVE 0x02 //< Negative VSYNC
#define OV2640_COM10_HSYNC_MASK 0x01 //< HSYNC polarity mask
#define OV2640_COM10_HSYNC_POSITIVE 0x00 //< Positive HSYNC
#define OV2640_COM10_HSYNC_NEGATIVE 0x01 //< Negative HSYNC
#define OV2640_REG1_HREFST 0x17 //< Horizontal window start MSB
#define OV2640_REG1_HREFEND 0x18 //< Horizontal window end MSB
#define OV2640_REG1_VSTRT 0x19 //< Vertical window line start MSB
#define OV2640_REG1_VEND 0x1A //< Vertical window line end MSB
#define OV2640_REG1_MIDH 0x1C //< Manufacturer ID MSB (RO=0x7F)
#define OV2640_REG1_MIDL 0x1D //< Manufacturer ID LSB (RO=0xA2)
#define OV2640_REG1_AEW 0x24 //< Luminance signal high range
#define OV2640_REG1_AEB 0x25 //< Luminance signal low range
#define OV2640_REG1_VV 0x26 //< Fast mode large step threshold
#define OV2640_VV_HIGH_MASK 0xF0 //< High threshold mask
#define OV2640_VV_LOW_MASK 0x0F //< Low threshold mask
#define OV2640_REG1_REG2A 0x2A //< Register 2A
#define OV2640_REG2A_LINE_MASK 0xF0 //< Line interval adjust MSBs
#define OV2640_REG2A_HSYNC_END_MASK 0x0C //< HSYNC timing end point MSBs
#define OV2640_REG2A_HSYNC_START_MASK 0x03 //< HSYNC timing start point MSBs
#define OV2640_REG1_FRARL 0x2B //< Line interval adjust LSB
#define OV2640_REG1_ADDVSL 0x2D //< VSYNC pulse width LSB
#define OV2640_REG1_ADDVSH 0x2E //< VSYNC pulse width MSB
#define OV2640_REG1_YAVG 0x2F //< Luminance average
#define OV2640_REG1_HSDY 0x30 //< HSYNC pos+width start LSB
#define OV2640_REG1_HEDY 0x31 //< HSYNC pos+width end LSB
#define OV2640_REG1_REG32 0x32 //< Common control 32
#define OV2640_REG32_PCLK_MASK 0xC0 //< Pixel clock divide option mask
#define OV2640_REG32_PCLK_DIV1 0x00 //< No effect on PCLK
#define OV2640_REG32_PCLK_DIV2 0x80 //< PCLK frequency / 2
#define OV2640_REG32_PCLK_DIV4 0xC0 //< PCLK frequency / 4
#define OV2640_REG32_HREFEND_MASK 0x38 //< HREFEND LSBs
#define OV2640_REG32_HREFST_MASK 0x07 //< HREFST LSBs
#define OV2640_REG1_ARCOM2 0x34 //< ?
#define OV2640_ARCOM2_ZOOM 0x04 //< Zoom window horiz start point
#define OV2640_REG1_REG45 0x45 //< Register 45:
#define OV2640_REG45_AGC_MASK 0xC0 //< AGC[9:8] highest gain control
#define OV2640_REG45_AEC_MASK 0x3F //< AEC[15:10] AEC MSBs
#define OV2640_REG1_FLL 0x46 //< Frame length adjustment LSBs
#define OV2640_REG1_FLH 0x47 //< Frame length adjustment MSBs
#define OV2640_REG1_COM19 0x48 //< Frame length adjustment MSBs
#define OV2640_COM19_ZOOM_MASK 0x03 //< Zoom mode vert window LSBs
#define OV2640_REG1_ZOOMS 0x49 //< Zoom mode vert window MSB
#define OV2640_REG1_COM22 0x4B //< Common control 22 (flash strobe)
#define OV2640_REG1_COM25 0x4E //< Common control 25
#define OV2640_COM25_50HZ_MASK 0xC0 //< 50 Hz banding AEC MSBs
#define OV2640_COM25_60HZ_MASK 0x30 //< 60 Hz banding AEC MSBs
#define OV2640_REG1_BD50 0x4F //< 50 Hz banding AEC LSBs
#define OV2640_REG1_BD60 0x50 //< 60 Hz banding AEC LSBs
#define OV2640_REG1_REG5D 0x5D //< AVGsel[7:0] 16-zone avg weight
#define OV2640_REG1_REG5E 0x5E //< AVGsel[15:8]
#define OV2640_REG1_REG5F 0x5F //< AVGsel[23:16]
#define OV2640_REG1_REG60 0x60 //< AVGsel[31:24]
#define OV2640_REG1_HISTO_LOW 0x61 //< Histogram low level
#define OV2640_REG1_HISTO_HIGH 0x62 //< Histogram high level

87
src/ov2640/sccb.cpp Normal file
View File

@ -0,0 +1,87 @@
#include <sccb.hpp>
#include "hardware/i2c.h"
#include "pico/stdlib.h"
#include "src/pio/i2c/pio_i2c.h"
SCCB::SCCB(uint8_t sda_pin, uint8_t scl_pin) {
this->sda = sda_pin;
this->scl = scl_pin;
}
SCCB::~SCCB() {}
// ctrl: 0 - i2c0, 1 - i2c1, 2 - pio0, 3 - pio1, 4 - pio2
// TODO: add proper return values when failure
// TODO: panic if scl != sda + 1
int SCCB::begin(uint8_t ctrl)
{
// Set up XCLK out unless it's a self-clocking camera. This must be done
// BEFORE any I2C commands, as cam may require clock for I2C timing.
this->ctrl = ctrl;
if (ctrl < 2) {
this->i2cc = ctrl == 0 ? i2c0 : i2c1;
i2c_init(this->i2cc, 100 * 1000);
gpio_set_function(this->sda, GPIO_FUNC_I2C);
gpio_set_function(this->scl, GPIO_FUNC_I2C);
gpio_pull_up(this->sda);
gpio_pull_up(this->scl);
}
else {
PIO pio;
switch (ctrl) {
case 2:
this->pio = pio0;
break;
case 3:
this->pio = pio1;
break;
case 4:
this->pio = pio2;
break;
default:
this->pio = pio0;
break;
};
uint sm = pio_claim_unused_sm(this->pio, true);
uint offset = pio_add_program(this->pio, &i2c_program);
i2c_program_init(this->pio, this->sm, offset, this->sda, this->scl);
}
return 0;
}
int SCCB::readRegister(uint8_t reg)
{
uint8_t buf[1];
if (this->ctrl < 2) {
i2c_write_blocking(this->i2cc, OV2640_ADDR, &reg, 1, true); // TODO: check if true or false is correct here
i2c_read_blocking(this->i2cc, OV2640_ADDR, buf, 1, false); // false - finished with bus
}else {
pio_i2c_write_blocking(this->pio, this->sm, OV2640_ADDR, &reg, 1);
pio_i2c_read_blocking(this->pio, this->sm, OV2640_ADDR, buf, 1);
}
return buf[0];
}
void SCCB::writeRegister(uint8_t reg, uint8_t value)
{
uint8_t buf[2];
buf[0] = reg;
buf[1] = value;
if (this->ctrl < 2) {
i2c_write_blocking(this->i2cc, OV2640_ADDR, buf, 2, false);
}else {
pio_i2c_write_blocking(this->pio, this->sm, OV2640_ADDR, buf, 2);
}
}
void SCCB::writeList(const register_val_t *cfg,
uint16_t len)
{
for (int i = 0; i < len; i++)
{
writeRegister(cfg[i].reg, cfg[i].value);
sleep_ms(1000); // Some cams require, else lockup on init
}
}

21
src/ov2640/sccb.hpp Normal file
View File

@ -0,0 +1,21 @@
#include <camera.hpp>
class SCCB {
public:
SCCB(uint8_t sda_pin = 0, uint8_t scl_pin = 0);
~SCCB(); // Destructor
int begin(uint8_t ctrl);
int readRegister(uint8_t reg);
void writeRegister(uint8_t reg, uint8_t value);
void writeList(const register_val_t *cfg, uint16_t len);
private:
uint8_t sda;
uint8_t scl;
uint ctrl;
i2c_inst_t* i2cc;
pio_hw_t* pio;
uint sm;
};

146
src/pio/i2c/i2c.pio Normal file
View File

@ -0,0 +1,146 @@
;
; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
;
; SPDX-License-Identifier: BSD-3-Clause
;
.pio_version 0 // only requires PIO version 0
.program i2c
.side_set 1 opt pindirs
; TX Encoding:
; | 15:10 | 9 | 8:1 | 0 |
; | Instr | Final | Data | NAK |
;
; If Instr has a value n > 0, then this FIFO word has no
; data payload, and the next n + 1 words will be executed as instructions.
; Otherwise, shift out the 8 data bits, followed by the ACK bit.
;
; The Instr mechanism allows stop/start/repstart sequences to be programmed
; by the processor, and then carried out by the state machine at defined points
; in the datastream.
;
; The "Final" field should be set for the final byte in a transfer.
; This tells the state machine to ignore a NAK: if this field is not
; set, then any NAK will cause the state machine to halt and interrupt.
;
; Autopull should be enabled, with a threshold of 16.
; Autopush should be enabled, with a threshold of 8.
; The TX FIFO should be accessed with halfword writes, to ensure
; the data is immediately available in the OSR.
;
; Pin mapping:
; - Input pin 0 is SDA, 1 is SCL (if clock stretching used)
; - Jump pin is SDA
; - Side-set pin 0 is SCL
; - Set pin 0 is SDA
; - OUT pin 0 is SDA
; - SCL must be SDA + 1 (for wait mapping)
;
; The OE outputs should be inverted in the system IO controls!
; (It's possible for the inversion to be done in this program,
; but costs 2 instructions: 1 for inversion, and one to cope
; with the side effect of the MOV on TX shift counter.)
do_nack:
jmp y-- entry_point ; Continue if NAK was expected
irq wait 0 rel ; Otherwise stop, ask for help
do_byte:
set x, 7 ; Loop 8 times
bitloop:
out pindirs, 1 [7] ; Serialise write data (all-ones if reading)
nop side 1 [2] ; SCL rising edge
wait 1 pin, 1 [4] ; Allow clock to be stretched
in pins, 1 [7] ; Sample read data in middle of SCL pulse
jmp x-- bitloop side 0 [7] ; SCL falling edge
; Handle ACK pulse
out pindirs, 1 [7] ; On reads, we provide the ACK.
nop side 1 [7] ; SCL rising edge
wait 1 pin, 1 [7] ; Allow clock to be stretched
jmp pin do_nack side 0 [2] ; Test SDA for ACK/NAK, fall through if ACK
public entry_point:
.wrap_target
out x, 6 ; Unpack Instr count
out y, 1 ; Unpack the NAK ignore bit
jmp !x do_byte ; Instr == 0, this is a data record.
out null, 32 ; Instr > 0, remainder of this OSR is invalid
do_exec:
out exec, 16 ; Execute one instruction per FIFO word
jmp x-- do_exec ; Repeat n + 1 times
.wrap
% c-sdk {
#include "hardware/clocks.h"
#include "hardware/gpio.h"
static inline void i2c_program_init(PIO pio, uint sm, uint offset, uint pin_sda, uint pin_scl) {
assert(pin_scl == pin_sda + 1);
pio_sm_config c = i2c_program_get_default_config(offset);
// IO mapping
sm_config_set_out_pins(&c, pin_sda, 1);
sm_config_set_set_pins(&c, pin_sda, 1);
sm_config_set_in_pins(&c, pin_sda);
sm_config_set_sideset_pins(&c, pin_scl);
sm_config_set_jmp_pin(&c, pin_sda);
sm_config_set_out_shift(&c, false, true, 16);
sm_config_set_in_shift(&c, false, true, 8);
float div = (float)clock_get_hz(clk_sys) / (32 * 100000);
sm_config_set_clkdiv(&c, div);
// Try to avoid glitching the bus while connecting the IOs. Get things set
// up so that pin is driven down when PIO asserts OE low, and pulled up
// otherwise.
gpio_pull_up(pin_scl);
gpio_pull_up(pin_sda);
uint32_t both_pins = (1u << pin_sda) | (1u << pin_scl);
pio_sm_set_pins_with_mask(pio, sm, both_pins, both_pins);
pio_sm_set_pindirs_with_mask(pio, sm, both_pins, both_pins);
pio_gpio_init(pio, pin_sda);
gpio_set_oeover(pin_sda, GPIO_OVERRIDE_INVERT);
pio_gpio_init(pio, pin_scl);
gpio_set_oeover(pin_scl, GPIO_OVERRIDE_INVERT);
pio_sm_set_pins_with_mask(pio, sm, 0, both_pins);
// Clear IRQ flag before starting, and make sure flag doesn't actually
// assert a system-level interrupt (we're using it as a status flag)
pio_set_irq0_source_enabled(pio, (enum pio_interrupt_source) ((uint) pis_interrupt0 + sm), false);
pio_set_irq1_source_enabled(pio, (enum pio_interrupt_source) ((uint) pis_interrupt0 + sm), false);
pio_interrupt_clear(pio, sm);
// Configure and start SM
pio_sm_init(pio, sm, offset + i2c_offset_entry_point, &c);
pio_sm_set_enabled(pio, sm, true);
}
%}
.program set_scl_sda
.side_set 1 opt
; Assemble a table of instructions which software can select from, and pass
; into the FIFO, to issue START/STOP/RSTART. This isn't intended to be run as
; a complete program.
set pindirs, 0 side 0 [7] ; SCL = 0, SDA = 0
set pindirs, 1 side 0 [7] ; SCL = 0, SDA = 1
set pindirs, 0 side 1 [7] ; SCL = 1, SDA = 0
set pindirs, 1 side 1 [7] ; SCL = 1, SDA = 1
% c-sdk {
// Define order of our instruction table
enum {
I2C_SC0_SD0 = 0,
I2C_SC0_SD1,
I2C_SC1_SD0,
I2C_SC1_SD1
};
%}

154
src/pio/i2c/pio_i2c.c Normal file
View File

@ -0,0 +1,154 @@
/**
* Copyright (c) 2021 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "pio_i2c.hpp"
const int PIO_I2C_ICOUNT_LSB = 10;
const int PIO_I2C_FINAL_LSB = 9;
const int PIO_I2C_DATA_LSB = 1;
const int PIO_I2C_NAK_LSB = 0;
bool pio_i2c_check_error(PIO pio, uint sm) {
return pio_interrupt_get(pio, sm);
}
void pio_i2c_resume_after_error(PIO pio, uint sm) {
pio_sm_drain_tx_fifo(pio, sm);
pio_sm_exec(pio, sm, (pio->sm[sm].execctrl & PIO_SM0_EXECCTRL_WRAP_BOTTOM_BITS) >> PIO_SM0_EXECCTRL_WRAP_BOTTOM_LSB);
pio_interrupt_clear(pio, sm);
}
void pio_i2c_rx_enable(PIO pio, uint sm, bool en) {
if (en)
hw_set_bits(&pio->sm[sm].shiftctrl, PIO_SM0_SHIFTCTRL_AUTOPUSH_BITS);
else
hw_clear_bits(&pio->sm[sm].shiftctrl, PIO_SM0_SHIFTCTRL_AUTOPUSH_BITS);
}
static inline void pio_i2c_put16(PIO pio, uint sm, uint16_t data) {
while (pio_sm_is_tx_fifo_full(pio, sm))
;
// some versions of GCC dislike this
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
#endif
*(io_rw_16 *)&pio->txf[sm] = data;
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
}
// If I2C is ok, block and push data. Otherwise fall straight through.
void pio_i2c_put_or_err(PIO pio, uint sm, uint16_t data) {
while (pio_sm_is_tx_fifo_full(pio, sm))
if (pio_i2c_check_error(pio, sm))
return;
if (pio_i2c_check_error(pio, sm))
return;
// some versions of GCC dislike this
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
#endif
*(io_rw_16 *)&pio->txf[sm] = data;
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
}
uint8_t pio_i2c_get(PIO pio, uint sm) {
return (uint8_t)pio_sm_get(pio, sm);
}
void pio_i2c_start(PIO pio, uint sm) {
pio_i2c_put_or_err(pio, sm, 1u << PIO_I2C_ICOUNT_LSB); // Escape code for 2 instruction sequence
pio_i2c_put_or_err(pio, sm, set_scl_sda_program_instructions[I2C_SC1_SD0]); // We are already in idle state, just pull SDA low
pio_i2c_put_or_err(pio, sm, set_scl_sda_program_instructions[I2C_SC0_SD0]); // Also pull clock low so we can present data
}
void pio_i2c_stop(PIO pio, uint sm) {
pio_i2c_put_or_err(pio, sm, 2u << PIO_I2C_ICOUNT_LSB);
pio_i2c_put_or_err(pio, sm, set_scl_sda_program_instructions[I2C_SC0_SD0]); // SDA is unknown; pull it down
pio_i2c_put_or_err(pio, sm, set_scl_sda_program_instructions[I2C_SC1_SD0]); // Release clock
pio_i2c_put_or_err(pio, sm, set_scl_sda_program_instructions[I2C_SC1_SD1]); // Release SDA to return to idle state
};
void pio_i2c_repstart(PIO pio, uint sm) {
pio_i2c_put_or_err(pio, sm, 3u << PIO_I2C_ICOUNT_LSB);
pio_i2c_put_or_err(pio, sm, set_scl_sda_program_instructions[I2C_SC0_SD1]);
pio_i2c_put_or_err(pio, sm, set_scl_sda_program_instructions[I2C_SC1_SD1]);
pio_i2c_put_or_err(pio, sm, set_scl_sda_program_instructions[I2C_SC1_SD0]);
pio_i2c_put_or_err(pio, sm, set_scl_sda_program_instructions[I2C_SC0_SD0]);
}
static void pio_i2c_wait_idle(PIO pio, uint sm) {
// Finished when TX runs dry or SM hits an IRQ
pio->fdebug = 1u << (PIO_FDEBUG_TXSTALL_LSB + sm);
while (!(pio->fdebug & 1u << (PIO_FDEBUG_TXSTALL_LSB + sm) || pio_i2c_check_error(pio, sm)))
tight_loop_contents();
}
int pio_i2c_write_blocking(PIO pio, uint sm, uint8_t addr, uint8_t *txbuf, uint len) {
int err = 0;
pio_i2c_start(pio, sm);
pio_i2c_rx_enable(pio, sm, false);
pio_i2c_put16(pio, sm, (addr << 2) | 1u);
while (len && !pio_i2c_check_error(pio, sm)) {
if (!pio_sm_is_tx_fifo_full(pio, sm)) {
--len;
pio_i2c_put_or_err(pio, sm, (*txbuf++ << PIO_I2C_DATA_LSB) | ((len == 0) << PIO_I2C_FINAL_LSB) | 1u);
}
}
pio_i2c_stop(pio, sm);
pio_i2c_wait_idle(pio, sm);
if (pio_i2c_check_error(pio, sm)) {
err = -1;
pio_i2c_resume_after_error(pio, sm);
pio_i2c_stop(pio, sm);
}
return err;
}
int pio_i2c_read_blocking(PIO pio, uint sm, uint8_t addr, uint8_t *rxbuf, uint len) {
int err = 0;
pio_i2c_start(pio, sm);
pio_i2c_rx_enable(pio, sm, true);
while (!pio_sm_is_rx_fifo_empty(pio, sm))
(void)pio_i2c_get(pio, sm);
pio_i2c_put16(pio, sm, (addr << 2) | 3u);
uint32_t tx_remain = len; // Need to stuff 0xff bytes in to get clocks
bool first = true;
while ((tx_remain || len) && !pio_i2c_check_error(pio, sm)) {
if (tx_remain && !pio_sm_is_tx_fifo_full(pio, sm)) {
--tx_remain;
pio_i2c_put16(pio, sm, (0xffu << 1) | (tx_remain ? 0 : (1u << PIO_I2C_FINAL_LSB) | (1u << PIO_I2C_NAK_LSB)));
}
if (!pio_sm_is_rx_fifo_empty(pio, sm)) {
if (first) {
// Ignore returned address byte
(void)pio_i2c_get(pio, sm);
first = false;
}
else {
--len;
*rxbuf++ = pio_i2c_get(pio, sm);
}
}
}
pio_i2c_stop(pio, sm);
pio_i2c_wait_idle(pio, sm);
if (pio_i2c_check_error(pio, sm)) {
err = -1;
pio_i2c_resume_after_error(pio, sm);
pio_i2c_stop(pio, sm);
}
return err;
}

31
src/pio/i2c/pio_i2c.h Normal file
View File

@ -0,0 +1,31 @@
/**
* Copyright (c) 2021 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PIO_I2C_H
#define _PIO_I2C_H
#include "i2c.pio.h"
// ----------------------------------------------------------------------------
// Low-level functions
void pio_i2c_start(PIO pio, uint sm);
void pio_i2c_stop(PIO pio, uint sm);
void pio_i2c_repstart(PIO pio, uint sm);
bool pio_i2c_check_error(PIO pio, uint sm);
void pio_i2c_resume_after_error(PIO pio, uint sm);
// If I2C is ok, block and push data. Otherwise fall straight through.
void pio_i2c_put_or_err(PIO pio, uint sm, uint16_t data);
uint8_t pio_i2c_get(PIO pio, uint sm);
// ----------------------------------------------------------------------------
// Transaction-level functions
int pio_i2c_write_blocking(PIO pio, uint sm, uint8_t addr, uint8_t *txbuf, uint len);
int pio_i2c_read_blocking(PIO pio, uint sm, uint8_t addr, uint8_t *rxbuf, uint len);
#endif