Adafruit_CircuitPython_OV2640/adafruit_ov2640.py

1301 lines
29 KiB
Python

# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
# SPDX-FileCopyrightText: Copyright (c) 2021 Jeff Epler for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
`adafruit_ov2640`
================================================================================
CircuitPython driver for OV2640 Camera.
* Author(s): Jeff Epler
Implementation Notes
--------------------
**Hardware:**
* `ESP32-S2 Kaluga Dev Kit featuring ESP32-S2 WROVER <https://www.adafruit.com/product/4729>`_
**Software and Dependencies:**
* Adafruit CircuitPython firmware for the supported boards:
https://github.com/adafruit/circuitpython/releases
* Adafruit's Bus Device library: https:# github.com/adafruit/Adafruit_CircuitPython_BusDevice
"""
# pylint: disable=too-many-lines
# imports
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_OV7670.git"
import time
import digitalio
import imagecapture
import pwmio
from adafruit_bus_device.i2c_device import I2CDevice
from micropython import const
CTRLI = const(0x50)
_R_BYPASS = const(0x05)
_QS = const(0x44)
_CTRLI = const(0x50)
_HSIZE = const(0x51)
_VSIZE = const(0x52)
_XOFFL = const(0x53)
_YOFFL = const(0x54)
_VHYX = const(0x55)
_DPRP = const(0x56)
_TEST = const(0x57)
_ZMOW = const(0x5A)
_ZMOH = const(0x5B)
_ZMHH = const(0x5C)
_BPADDR = const(0x7C)
_BPDATA = const(0x7D)
_CTRL2 = const(0x86)
_CTRL3 = const(0x87)
_SIZEL = const(0x8C)
_HSIZE8 = const(0xC0)
_VSIZE8 = const(0xC1)
_CTRL0 = const(0xC2)
_CTRL1 = const(0xC3)
_R_DVP_SP = const(0xD3)
_IMAGE_MODE = const(0xDA)
_RESET = const(0xE0)
_MS_SP = const(0xF0)
_SS_ID = const(0xF7)
_SS_CTRL = const(0xF7)
_MC_BIST = const(0xF9)
_MC_AL = const(0xFA)
_MC_AH = const(0xFB)
_MC_D = const(0xFC)
_P_CMD = const(0xFD)
_P_STATUS = const(0xFE)
_BANK_SEL = const(0xFF)
_CTRLI_LP_DP = const(0x80)
_CTRLI_ROUND = const(0x40)
_CTRL0_AEC_EN = const(0x80)
_CTRL0_AEC_SEL = const(0x40)
_CTRL0_STAT_SEL = const(0x20)
_CTRL0_VFIRST = const(0x10)
_CTRL0_YUV422 = const(0x08)
_CTRL0_YUV_EN = const(0x04)
_CTRL0_RGB_EN = const(0x02)
_CTRL0_RAW_EN = const(0x01)
_CTRL2_DCW_EN = const(0x20)
_CTRL2_SDE_EN = const(0x10)
_CTRL2_UV_ADJ_EN = const(0x08)
_CTRL2_UV_AVG_EN = const(0x04)
_CTRL2_CMX_EN = const(0x01)
_CTRL3_BPC_EN = const(0x80)
_CTRL3_WPC_EN = const(0x40)
_R_DVP_SP_AUTO_MODE = const(0x80)
_R_BYPASS_DSP_EN = const(0x00)
_R_BYPASS_DSP_BYPAS = const(0x01)
OV2640_COLOR_RGB = 0
OV2640_COLOR_YUV = 1
_IMAGE_MODE_Y8_DVP_EN = const(0x40)
_IMAGE_MODE_JPEG_EN = const(0x10)
_IMAGE_MODE_YUV422 = const(0x00)
_IMAGE_MODE_RAW10 = const(0x04)
_IMAGE_MODE_RGB565 = const(0x08)
_IMAGE_MODE_HREF_VSYNC = const(0x02)
_IMAGE_MODE_LBYTE_FIRST = const(0x01)
_RESET_MICROC = const(0x40)
_RESET_SCCB = const(0x20)
_RESET_JPEG = const(0x10)
_RESET_DVP = const(0x04)
_RESET_IPU = const(0x02)
_RESET_CIF = const(0x01)
_MC_BIST_RESET = const(0x80)
_MC_BIST_BOOT_ROM_SEL = const(0x40)
_MC_BIST_12KB_SEL = const(0x20)
_MC_BIST_12KB_MASK = const(0x30)
_MC_BIST_512KB_SEL = const(0x08)
_MC_BIST_512KB_MASK = const(0x0C)
_MC_BIST_BUSY_BIT_R = const(0x02)
_MC_BIST_MC_RES_ONE_SH_W = const(0x02)
_MC_BIST_LAUNCH = const(0x01)
_BANK_DSP = const(0)
_BANK_SENSOR = const(1)
# Sensor register bank FF=0x01
_GAIN = const(0x00)
_COM1 = const(0x03)
_REG04 = const(0x04)
_REG08 = const(0x08)
_COM2 = const(0x09)
_REG_PID = const(0x0A)
_REG_VER = const(0x0B)
_COM3 = const(0x0C)
_COM4 = const(0x0D)
_AEC = const(0x10)
_CLKRC = const(0x11)
_COM7 = const(0x12)
_COM8 = const(0x13)
_COM9 = const(0x14) # AGC gain ceiling
_COM10 = const(0x15)
_HSTART = const(0x17)
_HSTOP = const(0x18)
_VSTART = const(0x19)
_VSTOP = const(0x1A)
_MIDH = const(0x1C)
_MIDL = const(0x1D)
_AEW = const(0x24)
_AEB = const(0x25)
_VV = const(0x26)
_REG2A = const(0x2A)
_FRARL = const(0x2B)
_ADDVSL = const(0x2D)
_ADDVSH = const(0x2E)
_YAVG = const(0x2F)
_HSDY = const(0x30)
_HEDY = const(0x31)
_REG32 = const(0x32)
_ARCOM2 = const(0x34)
_REG45 = const(0x45)
_FLL = const(0x46)
_FLH = const(0x47)
_COM19 = const(0x48)
_ZOOMS = const(0x49)
_COM22 = const(0x4B)
_COM25 = const(0x4E)
_BD50 = const(0x4F)
_BD60 = const(0x50)
_REG5D = const(0x5D)
_REG5E = const(0x5E)
_REG5F = const(0x5F)
_REG60 = const(0x60)
_HISTO_LOW = const(0x61)
_HISTO_HIGH = const(0x62)
_REG04_DEFAULT = const(0x28)
_REG04_HFLIP_IMG = const(0x80)
_REG04_VFLIP_IMG = const(0x40)
_REG04_VREF_EN = const(0x10)
_REG04_HREF_EN = const(0x08)
_REG04_SET = lambda x: (_REG04_DEFAULT | x)
_COM2_STDBY = const(0x10)
_COM2_OUT_DRIVE_1x = const(0x00)
_COM2_OUT_DRIVE_2x = const(0x01)
_COM2_OUT_DRIVE_3x = const(0x02)
_COM2_OUT_DRIVE_4x = const(0x03)
_COM3_DEFAULT = const(0x38)
_COM3_BAND_50Hz = const(0x04)
_COM3_BAND_60Hz = const(0x00)
_COM3_BAND_AUTO = const(0x02)
_COM3_BAND_SET = lambda x: (_COM3_DEFAULT | x)
_COM7_SRST = const(0x80)
_COM7_RES_UXGA = const(0x00) # UXGA
_COM7_RES_SVGA = const(0x40) # SVGA
_COM7_RES_CIF = const(0x20) # CIF
_COM7_ZOOM_EN = const(0x04) # Enable Zoom
_COM7_COLOR_BAR = const(0x02) # Enable Color Bar Test
_COM8_DEFAULT = const(0xC0)
_COM8_BNDF_EN = const(0x20) # Enable Banding filter
_COM8_AGC_EN = const(0x04) # AGC Auto/Manual control selection
_COM8_AEC_EN = const(0x01) # Auto/Manual Exposure control
_COM8_SET = lambda x: (_COM8_DEFAULT | x)
_COM9_DEFAULT = const(0x08)
_COM9_AGC_GAIN_2x = const(0x00) # AGC: 2x
_COM9_AGC_GAIN_4x = const(0x01) # AGC: 4x
_COM9_AGC_GAIN_8x = const(0x02) # AGC: 8x
_COM9_AGC_GAIN_16x = const(0x03) # AGC: 16x
_COM9_AGC_GAIN_32x = const(0x04) # AGC: 32x
_COM9_AGC_GAIN_64x = const(0x05) # AGC: 64x
_COM9_AGC_GAIN_128x = const(0x06) # AGC: 128x
_COM9_AGC_SET = lambda x: (_COM9_DEFAULT | (x << 5))
_COM10_HREF_EN = const(0x80) # HSYNC changes to HREF
_COM10_HSYNC_EN = const(0x40) # HREF changes to HSYNC
_COM10_PCLK_FREE = const(0x20) # PCLK output option: free running PCLK
_COM10_PCLK_EDGE = const(0x10) # Data is updated at the rising edge of PCLK
_COM10_HREF_NEG = const(0x08) # HREF negative
_COM10_VSYNC_NEG = const(0x02) # VSYNC negative
_COM10_HSYNC_NEG = const(0x01) # HSYNC negative
_CTRL1_AWB = const(0x08) # Enable AWB
_VV_AGC_TH_SET = lambda h, l: ((h << 4) | (l & 0x0F))
_REG32_UXGA = const(0x36)
_REG32_SVGA = const(0x09)
_REG32_CIF = const(0x89)
_CLKRC_2X = const(0x80)
_CLKRC_2X_UXGA = const(0x01 | _CLKRC_2X)
_CLKRC_2X_SVGA = _CLKRC_2X
_CLKRC_2X_CIF = _CLKRC_2X
_OV2640_MODE_CIF = const(0)
_OV2640_MODE_SVGA = const(1)
_OV2640_MODE_UXGA = const(2)
OV2640_SIZE_96X96 = 0 # 96x96
OV2640_SIZE_QQVGA = 1 # 160x120
OV2640_SIZE_QCIF = 2 # 176x144
OV2640_SIZE_HQVGA = 3 # 240x176
OV2640_SIZE_240X240 = 4 # 240x240
OV2640_SIZE_QVGA = 5 # 320x240
OV2640_SIZE_CIF = 6 # 400x296
OV2640_SIZE_HVGA = 7 # 480x320
OV2640_SIZE_VGA = 8 # 640x480
OV2640_SIZE_SVGA = 9 # 800x600
OV2640_SIZE_XGA = 10 # 1024x768
OV2640_SIZE_HD = 11 # 1280x720
OV2640_SIZE_SXGA = 12 # 1280x1024
OV2640_SIZE_UXGA = 13 # 1600x1200
_ASPECT_RATIO_4X3 = const(0)
_ASPECT_RATIO_3X2 = const(1)
_ASPECT_RATIO_16X10 = const(2)
_ASPECT_RATIO_5X3 = const(3)
_ASPECT_RATIO_16X9 = const(4)
_ASPECT_RATIO_21X9 = const(5)
_ASPECT_RATIO_5X4 = const(6)
_ASPECT_RATIO_1X1 = const(7)
_ASPECT_RATIO_9X16 = const(8)
_resolution_info = [
[96, 96, _ASPECT_RATIO_1X1], # 96x96
[160, 120, _ASPECT_RATIO_4X3], # QQVGA
[176, 144, _ASPECT_RATIO_5X4], # QCIF
[240, 176, _ASPECT_RATIO_4X3], # HQVGA
[240, 240, _ASPECT_RATIO_1X1], # 240x240
[320, 240, _ASPECT_RATIO_4X3], # QVGA
[400, 296, _ASPECT_RATIO_4X3], # CIF
[480, 320, _ASPECT_RATIO_3X2], # HVGA
[640, 480, _ASPECT_RATIO_4X3], # VGA
[800, 600, _ASPECT_RATIO_4X3], # SVGA
[1024, 768, _ASPECT_RATIO_4X3], # XGA
[1280, 720, _ASPECT_RATIO_16X9], # HD
[1280, 1024, _ASPECT_RATIO_5X4], # SXGA
[1600, 1200, _ASPECT_RATIO_4X3], # UXGA
]
_ratio_table = [
# ox, oy, mx, my
[0, 0, 1600, 1200], # 4x3
[8, 72, 1584, 1056], # 3x2
[0, 100, 1600, 1000], # 16x10
[0, 120, 1600, 960], # 5x3
[0, 150, 1600, 900], # 16x9
[2, 258, 1596, 684], # 21x9
[50, 0, 1500, 1200], # 5x4
[200, 0, 1200, 1200], # 1x1
[462, 0, 676, 1200], # 9x16
]
# 30fps@24MHz
_ov2640_settings_cif = bytes(
[
_BANK_SEL,
_BANK_DSP,
0x2C,
0xFF,
0x2E,
0xDF,
_BANK_SEL,
_BANK_SENSOR,
0x3C,
0x32,
_CLKRC,
0x01,
_COM2,
_COM2_OUT_DRIVE_3x,
_REG04,
_REG04_DEFAULT,
_COM8,
_COM8_DEFAULT | _COM8_BNDF_EN | _COM8_AGC_EN | _COM8_AEC_EN,
_COM9,
_COM9_AGC_SET(_COM9_AGC_GAIN_8x),
0x2C,
0x0C,
0x33,
0x78,
0x3A,
0x33,
0x3B,
0xFB,
0x3E,
0x00,
0x43,
0x11,
0x16,
0x10,
0x39,
0x92,
0x35,
0xDA,
0x22,
0x1A,
0x37,
0xC3,
0x23,
0x00,
_ARCOM2,
0xC0,
0x06,
0x88,
0x07,
0xC0,
_COM4,
0x87,
0x0E,
0x41,
0x4C,
0x00,
0x4A,
0x81,
0x21,
0x99,
_AEW,
0x40,
_AEB,
0x38,
_VV,
_VV_AGC_TH_SET(8, 2),
0x5C,
0x00,
0x63,
0x00,
_HISTO_LOW,
0x70,
_HISTO_HIGH,
0x80,
0x7C,
0x05,
0x20,
0x80,
0x28,
0x30,
0x6C,
0x00,
0x6D,
0x80,
0x6E,
0x00,
0x70,
0x02,
0x71,
0x94,
0x73,
0xC1,
0x3D,
0x34,
0x5A,
0x57,
_BD50,
0xBB,
_BD60,
0x9C,
_COM7,
_COM7_RES_CIF,
_HSTART,
0x11,
_HSTOP,
0x43,
_VSTART,
0x00,
_VSTOP,
0x25,
_REG32,
0x89,
0x37,
0xC0,
_BD50,
0xCA,
_BD60,
0xA8,
0x6D,
0x00,
0x3D,
0x38,
_BANK_SEL,
_BANK_DSP,
0xE5,
0x7F,
_MC_BIST,
_MC_BIST_RESET | _MC_BIST_BOOT_ROM_SEL,
0x41,
0x24,
_RESET,
_RESET_JPEG | _RESET_DVP,
0x76,
0xFF,
0x33,
0xA0,
0x42,
0x20,
0x43,
0x18,
0x4C,
0x00,
_CTRL3,
_CTRL3_WPC_EN | 0x10,
0x88,
0x3F,
0xD7,
0x03,
0xD9,
0x10,
_R_DVP_SP,
_R_DVP_SP_AUTO_MODE | 0x02,
0xC8,
0x08,
0xC9,
0x80,
_BPADDR,
0x00,
_BPDATA,
0x00,
_BPADDR,
0x03,
_BPDATA,
0x48,
_BPDATA,
0x48,
_BPADDR,
0x08,
_BPDATA,
0x20,
_BPDATA,
0x10,
_BPDATA,
0x0E,
0x90,
0x00,
0x91,
0x0E,
0x91,
0x1A,
0x91,
0x31,
0x91,
0x5A,
0x91,
0x69,
0x91,
0x75,
0x91,
0x7E,
0x91,
0x88,
0x91,
0x8F,
0x91,
0x96,
0x91,
0xA3,
0x91,
0xAF,
0x91,
0xC4,
0x91,
0xD7,
0x91,
0xE8,
0x91,
0x20,
0x92,
0x00,
0x93,
0x06,
0x93,
0xE3,
0x93,
0x05,
0x93,
0x05,
0x93,
0x00,
0x93,
0x04,
0x93,
0x00,
0x93,
0x00,
0x93,
0x00,
0x93,
0x00,
0x93,
0x00,
0x93,
0x00,
0x93,
0x00,
0x96,
0x00,
0x97,
0x08,
0x97,
0x19,
0x97,
0x02,
0x97,
0x0C,
0x97,
0x24,
0x97,
0x30,
0x97,
0x28,
0x97,
0x26,
0x97,
0x02,
0x97,
0x98,
0x97,
0x80,
0x97,
0x00,
0x97,
0x00,
0xA4,
0x00,
0xA8,
0x00,
0xC5,
0x11,
0xC6,
0x51,
0xBF,
0x80,
0xC7,
0x10,
0xB6,
0x66,
0xB8,
0xA5,
0xB7,
0x64,
0xB9,
0x7C,
0xB3,
0xAF,
0xB4,
0x97,
0xB5,
0xFF,
0xB0,
0xC5,
0xB1,
0x94,
0xB2,
0x0F,
0xC4,
0x5C,
_CTRL1,
0xFD,
0x7F,
0x00,
0xE5,
0x1F,
0xE1,
0x67,
0xDD,
0x7F,
_IMAGE_MODE,
0x00,
_RESET,
0x00,
_R_BYPASS,
_R_BYPASS_DSP_EN,
]
)
_ov2640_settings_to_cif = bytes(
[
_BANK_SEL,
_BANK_SENSOR,
_COM7,
_COM7_RES_CIF,
# Set the sensor output window
_COM1,
0x0A,
_REG32,
_REG32_CIF,
_HSTART,
0x11,
_HSTOP,
0x43,
_VSTART,
0x00,
_VSTOP,
0x25,
# _CLKRC, 0x00,
_BD50,
0xCA,
_BD60,
0xA8,
0x5A,
0x23,
0x6D,
0x00,
0x3D,
0x38,
0x39,
0x92,
0x35,
0xDA,
0x22,
0x1A,
0x37,
0xC3,
0x23,
0x00,
_ARCOM2,
0xC0,
0x06,
0x88,
0x07,
0xC0,
_COM4,
0x87,
0x0E,
0x41,
0x4C,
0x00,
_BANK_SEL,
_BANK_DSP,
_RESET,
_RESET_DVP,
# Set the sensor resolution (UXGA, SVGA, CIF)
_HSIZE8,
0x32,
_VSIZE8,
0x25,
_SIZEL,
0x00,
# Set the image window size >= output size
_HSIZE,
0x64,
_VSIZE,
0x4A,
_XOFFL,
0x00,
_YOFFL,
0x00,
_VHYX,
0x00,
_TEST,
0x00,
_CTRL2,
_CTRL2_DCW_EN | 0x1D,
_CTRLI,
_CTRLI_LP_DP | 0x00,
# _R_DVP_SP, 0x08,
]
)
_ov2640_settings_to_svga = bytes(
[
_BANK_SEL,
_BANK_SENSOR,
_COM7,
_COM7_RES_SVGA,
# Set the sensor output window
_COM1,
0x0A,
_REG32,
_REG32_SVGA,
_HSTART,
0x11,
_HSTOP,
0x43,
_VSTART,
0x00,
_VSTOP,
0x4B,
# _CLKRC, 0x00,
0x37,
0xC0,
_BD50,
0xCA,
_BD60,
0xA8,
0x5A,
0x23,
0x6D,
0x00,
0x3D,
0x38,
0x39,
0x92,
0x35,
0xDA,
0x22,
0x1A,
0x37,
0xC3,
0x23,
0x00,
_ARCOM2,
0xC0,
0x06,
0x88,
0x07,
0xC0,
_COM4,
0x87,
0x0E,
0x41,
0x42,
0x03,
0x4C,
0x00,
_BANK_SEL,
_BANK_DSP,
_RESET,
_RESET_DVP,
# Set the sensor resolution (UXGA, SVGA, CIF)
_HSIZE8,
0x64,
_VSIZE8,
0x4B,
_SIZEL,
0x00,
# Set the image window size >= output size
_HSIZE,
0xC8,
_VSIZE,
0x96,
_XOFFL,
0x00,
_YOFFL,
0x00,
_VHYX,
0x00,
_TEST,
0x00,
_CTRL2,
_CTRL2_DCW_EN | 0x1D,
_CTRLI,
_CTRLI_LP_DP | 0x00,
# _R_DVP_SP, 0x08,
]
)
_ov2640_settings_to_uxga = bytes(
[
_BANK_SEL,
_BANK_SENSOR,
_COM7,
_COM7_RES_UXGA,
# Set the sensor output window
_COM1,
0x0F,
_REG32,
_REG32_UXGA,
_HSTART,
0x11,
_HSTOP,
0x75,
_VSTART,
0x01,
_VSTOP,
0x97,
# _CLKRC, 0x00,
0x3D,
0x34,
_BD50,
0xBB,
_BD60,
0x9C,
0x5A,
0x57,
0x6D,
0x80,
0x39,
0x82,
0x23,
0x00,
0x07,
0xC0,
0x4C,
0x00,
0x35,
0x88,
0x22,
0x0A,
0x37,
0x40,
_ARCOM2,
0xA0,
0x06,
0x02,
_COM4,
0xB7,
0x0E,
0x01,
0x42,
0x83,
_BANK_SEL,
_BANK_DSP,
_RESET,
_RESET_DVP,
# Set the sensor resolution (UXGA, SVGA, CIF)
_HSIZE8,
0xC8,
_VSIZE8,
0x96,
_SIZEL,
0x00,
# Set the image window size >= output size
_HSIZE,
0x90,
_VSIZE,
0x2C,
_XOFFL,
0x00,
_YOFFL,
0x00,
_VHYX,
0x88,
_TEST,
0x00,
_CTRL2,
_CTRL2_DCW_EN | 0x1D,
_CTRLI,
0x00,
# _R_DVP_SP, 0x06,
]
)
# _ov2640_settings_jpeg3 = bytes([
# _BANK_SEL, _BANK_DSP,
# _RESET, _RESET_JPEG | _RESET_DVP,
# _IMAGE_MODE, _IMAGE_MODE_JPEG_EN | _IMAGE_MODE_HREF_VSYNC,
# 0xD7, 0x03,
# 0xE1, 0x77,
# 0xE5, 0x1F,
# 0xD9, 0x10,
# 0xDF, 0x80,
# 0x33, 0x80,
# 0x3C, 0x10,
# 0xEB, 0x30,
# 0xDD, 0x7F,
# _RESET, 0x00,
# ])
_ov2640_settings_yuv422 = bytes(
[
_BANK_SEL,
_BANK_DSP,
_RESET,
_RESET_DVP,
_IMAGE_MODE,
_IMAGE_MODE_YUV422,
0xD7,
0x01,
0xE1,
0x67,
_RESET,
0x00,
]
)
_ov2640_settings_rgb565 = bytes(
[
_BANK_SEL,
_BANK_DSP,
_RESET,
_RESET_DVP,
_IMAGE_MODE,
_IMAGE_MODE_RGB565,
0xD7,
0x03,
0xE1,
0x77,
_RESET,
0x00,
]
)
class _RegBits:
def __init__(self, bank, reg, shift, mask):
self.bank = bank
self.reg = reg
self.shift = shift
self.mask = mask
def __get__(self, obj, objtype=None):
reg_value = obj._read_bank_register(self.bank, self.reg)
return (reg_value >> self.shift) & self.mask
def __set__(self, obj, value):
if value & ~self.mask:
raise ValueError(
f"Value 0x{value:02x} does not fit in mask 0x{self.mask:02x}"
)
reg_value = obj._read_bank_register(self.bank, self.reg)
reg_value &= ~(self.mask << self.shift)
reg_value |= value << self.shift
obj._write_register(self.reg, reg_value)
class _SCCBCameraBase: # pylint: disable=too-few-public-methods
def __init__(self, i2c_bus, i2c_address):
self._i2c_device = I2CDevice(i2c_bus, i2c_address)
self._bank = None
def _get_reg_bits(self, bank, reg, shift, mask):
return (self._read_bank_register(bank, reg) >> shift) & mask
def _set_reg_bits(
self, bank, reg, shift, mask, value
): # pylint: disable=too-many-arguments
reg_value = self._read_bank_register(bank, reg)
reg_value &= ~(mask << shift)
reg_value |= value << shift
self._write_register(reg, reg_value)
def _write_list(self, reg_list):
for i in range(0, len(reg_list), 2):
self._write_register(reg_list[i], reg_list[i + 1])
time.sleep(0.001)
def _write_bank_register(self, bank, reg, value):
if self._bank != bank:
self._write_register(_BANK_SEL, bank)
self._write_register(reg, value)
def _read_bank_register(self, bank, reg):
if self._bank != bank:
self._write_register(_BANK_SEL, bank)
result = self._read_register(reg)
return result
def _write_register(self, reg, value):
if reg == _BANK_SEL:
if self._bank == value:
return
self._bank = value
# print(f"write_register {reg:02x} {value:02x}")
b = bytearray(2)
b[0] = reg
b[1] = value
with self._i2c_device as i2c:
i2c.write(b)
def _read_register(self, reg):
b = bytearray(1)
b[0] = reg
with self._i2c_device as i2c:
i2c.write(b)
i2c.readinto(b)
return b[0]
class OV2640(_SCCBCameraBase): # pylint: disable=too-many-instance-attributes
"""Library for the OV2640 digital camera"""
test_pattern = _RegBits(_BANK_SENSOR, _COM7, 1, 1)
gain_ceiling = _RegBits(_BANK_SENSOR, _COM9, 5, 7)
bpc = _RegBits(_BANK_DSP, _CTRL3, 7, 1)
wpc = _RegBits(_BANK_DSP, _CTRL3, 6, 1)
lenc = _RegBits(_BANK_DSP, _CTRL1, 1, 1)
def __init__(
self,
i2c_bus,
data_pins,
clock,
vsync,
href,
shutdown=None,
reset=None,
mclk=None,
mclk_frequency=20_000_000,
i2c_address=0x30,
size=OV2640_SIZE_QQVGA,
): # pylint: disable=too-many-arguments
"""
Args:
i2c_bus (busio.I2C): The I2C bus used to configure the OV2640
data_pins (List[microcontroller.Pin]): A list of 8 data pins, in order.
clock (microcontroller.Pin): The pixel clock from the OV2640.
vsync (microcontroller.Pin): The vsync signal from the OV2640.
href (microcontroller.Pin): The href signal from the OV2640, \
sometimes inaccurately called hsync.
shutdown (Optional[microcontroller.Pin]): If not None, the shutdown
signal to the camera, also called the powerdown or enable pin.
reset (Optional[microcontroller.Pin]): If not None, the reset signal
to the camera.
mclk (Optional[microcontroller.Pin]): The pin on which to create a
master clock signal, or None if the master clock signal is
already being generated.
mclk_frequency (int): The frequency of the master clock to generate, \
ignored if mclk is None, requred if it is specified
i2c_address (int): The I2C address of the camera.
"""
# Initialize the master clock
if mclk:
self._mclk_pwm = pwmio.PWMOut(mclk, frequency=mclk_frequency)
self._mclk_pwm.duty_cycle = 32768
else:
self._mclk_pwm = None
if shutdown:
self._shutdown = digitalio.DigitalInOut(shutdown)
self._shutdown.switch_to_output(True)
time.sleep(0.1)
self._shutdown.switch_to_output(False)
time.sleep(0.3)
else:
self._shutdown = None
if reset:
self._reset = digitalio.DigitalInOut(reset)
self._reset.switch_to_output(False)
time.sleep(0.1)
self._reset.switch_to_output(True)
time.sleep(0.1)
super().__init__(i2c_bus, i2c_address)
self._write_bank_register(_BANK_SENSOR, _COM7, _COM7_SRST)
time.sleep(0.001)
self._write_list(_ov2640_settings_cif)
self._colorspace = OV2640_COLOR_RGB
self._w = None
self._h = None
self._size = None
self._test_pattern = False
self.size = size
self._flip_x = False
self._flip_y = False
self.gain_ceiling = _COM9_AGC_GAIN_2x
self.bpc = False
self.wpc = True
self.lenc = True
# self._sensor_init()
self._imagecapture = imagecapture.ParallelImageCapture(
data_pins=data_pins, clock=clock, vsync=vsync, href=href
)
def capture(self, buf):
"""Capture an image into the buffer.
Args:
buf (Union[bytearray, memoryview]): A WritableBuffer to contain the \
captured image. Note that this can be a ulab array or a displayio Bitmap.
"""
self._imagecapture.capture(buf)
@property
def mclk_frequency(self):
"""Get the actual frequency the generated mclk, or None"""
return self._mclk_pwm.frequency if self._mclk_pwm else None
@property
def width(self):
"""Get the image width in pixels. A buffer of 2*width*height bytes \
stores a whole image."""
return self._w
@property
def height(self):
"""Get the image height in pixels. A buffer of 2*width*height bytes \
stores a whole image."""
return self._h
@property
def colorspace(self):
"""Get or set the colorspace, one of the ``OV2640_COLOR_`` constants."""
return self._colorspace
@colorspace.setter
def colorspace(self, colorspace):
self._colorspace = colorspace
self._write_list(
_ov2640_settings_rgb565
if colorspace == OV2640_COLOR_RGB
else _ov2640_settings_yuv422
)
# written twice?
self._write_list(
_ov2640_settings_rgb565
if colorspace == OV2640_COLOR_RGB
else _ov2640_settings_yuv422
)
time.sleep(0.01)
def deinit(self):
"""Deinitialize the camera"""
self._imagecapture.deinit()
if self._mclk_pwm:
self._mclk_pwm.deinit()
if self._shutdown:
self._shutdown.deinit()
if self._reset:
self._reset.deinit()
@property
def size(self):
"""Get or set the captured image size, one of the ``OV2640_SIZE_`` constants."""
return self._size
@size.setter
def size(self, size):
width, height, ratio = _resolution_info[size]
offset_x, offset_y, max_x, max_y = _ratio_table[ratio]
mode = _OV2640_MODE_UXGA
if size <= OV2640_SIZE_CIF:
mode = _OV2640_MODE_CIF
max_x //= 4
max_y //= 4
offset_x //= 4
offset_y //= 4
if max_y > 296:
max_y = 296
elif size <= OV2640_SIZE_SVGA:
mode = _OV2640_MODE_SVGA
max_x //= 2
max_y //= 2
offset_x //= 2
offset_y //= 2
self._set_window(mode, offset_x, offset_y, max_x, max_y, width, height)
self._size = size
def _set_flip(self):
bits = 0
if self._flip_x:
bits |= _REG04_HFLIP_IMG
if self._flip_y:
bits |= _REG04_VFLIP_IMG | _REG04_VREF_EN
self._write_bank_register(_BANK_SENSOR, _REG04, _REG04_SET(bits))
@property
def flip_x(self):
"""Get or set the X-flip flag"""
return self._flip_x
@flip_x.setter
def flip_x(self, value):
self._flip_x = bool(value)
self._set_flip()
@property
def flip_y(self):
"""Get or set the Y-flip flag"""
return self._flip_y
@flip_y.setter
def flip_y(self, value):
self._flip_y = bool(value)
self._set_flip()
@property
def product_id(self):
"""Get the product id (PID) register. The expected value is 0x26."""
return self._read_bank_register(_BANK_SENSOR, _REG_PID)
@property
def product_version(self):
"""Get the version (VER) register. The expected value is 0x4x."""
return self._read_bank_register(_BANK_SENSOR, _REG_VER)
def _set_window(
self, mode, offset_x, offset_y, max_x, max_y, width, height
): # pylint: disable=too-many-arguments, too-many-locals
self._w = width
self._h = height
max_x //= 4
max_y //= 4
width //= 4
height //= 4
win_regs = [
_BANK_SEL,
_BANK_DSP,
_HSIZE,
max_x & 0xFF,
_VSIZE,
max_y & 0xFF,
_XOFFL,
offset_x & 0xFF,
_YOFFL,
offset_y & 0xFF,
_VHYX,
((max_y >> 1) & 0x80)
| ((offset_y >> 4) & 0x70)
| ((max_x >> 5) & 0x08)
| ((offset_y >> 8) & 0x07),
_TEST,
(max_x >> 2) & 0x80,
_ZMOW,
(width) & 0xFF,
_ZMOH,
(height) & 0xFF,
_ZMHH,
((height >> 6) & 0x04) | ((width >> 8) & 0x03),
]
pclk_auto = 1
pclk_div = 8
clk_2x = 0
clk_div = 7
if mode == _OV2640_MODE_CIF:
regs = _ov2640_settings_to_cif
# if pixformat is not jpeg:
clk_div = 3
elif mode == _OV2640_MODE_SVGA:
regs = _ov2640_settings_to_svga
else:
regs = _ov2640_settings_to_uxga
pclk_div = 12
clk = clk_div | (clk_2x << 7)
pclk = pclk_div | (pclk_auto << 7)
self._write_bank_register(_BANK_DSP, _R_BYPASS, _R_BYPASS_DSP_BYPAS)
self._write_list(regs)
self._write_list(win_regs)
self._write_bank_register(_BANK_SENSOR, _CLKRC, clk)
self._write_bank_register(_BANK_DSP, _R_DVP_SP, pclk)
self._write_register(_R_BYPASS, _R_BYPASS_DSP_EN)
time.sleep(0.01)
# Reestablish colorspace
self.colorspace = self._colorspace
# Reestablish test pattern
if self._test_pattern:
self.test_pattern = self._test_pattern