Add pio i2c controller
Add sccb basic init Add basic camera skeleton (no control, no frames, basically nothing)
This commit is contained in:
parent
42edafcf08
commit
4286ab04ee
73
CMakeLists.txt
Normal file
73
CMakeLists.txt
Normal 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
121
pico_sdk_import.cmake
Normal 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
36
src/PicoIris.cpp
Normal 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
323
src/ov2640/camera.cpp
Normal 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
289
src/ov2640/camera.hpp
Normal 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
87
src/ov2640/sccb.cpp
Normal 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, ®, 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, ®, 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
21
src/ov2640/sccb.hpp
Normal 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
146
src/pio/i2c/i2c.pio
Normal 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
154
src/pio/i2c/pio_i2c.c
Normal 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
31
src/pio/i2c/pio_i2c.h
Normal 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
|
||||||
Loading…
x
Reference in New Issue
Block a user