/ adafruit_ov5640 / __init__.py
__init__.py
   1  # SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
   2  # SPDX-FileCopyrightText: Copyright (c) 2021 Jeff Epler for Adafruit Industries
   3  #
   4  # SPDX-License-Identifier: MIT
   5  """
   6  `adafruit_ov5640`
   7  ================================================================================
   8  
   9  CircuitPython driver for OV5640 Camera
  10  
  11  
  12  * Author(s): Jeff Epler
  13  
  14  Implementation Notes
  15  --------------------
  16  
  17  **Hardware:**
  18  
  19  * ESP32-S2 Kaluga Dev Kit featuring ESP32-S2 WROVER <https://www.adafruit.com/product/4729>
  20  
  21  **Software and Dependencies:**
  22  
  23  * Adafruit CircuitPython firmware for the supported boards:
  24    https://github.com/adafruit/circuitpython/releases
  25  """
  26  
  27  # pylint: disable=too-many-lines
  28  # pylint: disable=too-many-public-methods
  29  # imports
  30  import time
  31  import imagecapture
  32  import pwmio
  33  from adafruit_bus_device.i2c_device import I2CDevice
  34  
  35  try:
  36      from typing import Optional, Sequence, List, Union
  37      from busio import I2C
  38      from microcontroller import Pin
  39      from digitalio import DigitalInOut
  40  except ImportError:
  41      pass
  42  
  43  __version__ = "0.0.0+auto.0"
  44  __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ov5640.git"
  45  
  46  from micropython import const
  47  
  48  OV5640_COLOR_RGB = 0
  49  OV5640_COLOR_YUV = 1
  50  OV5640_COLOR_GRAYSCALE = 2
  51  OV5640_COLOR_JPEG = 3
  52  
  53  # fmt: off
  54  
  55  _SYSTEM_RESET00 = const(0x3000) # Reset for Individual Block
  56  # (0: enable block; 1: reset block)
  57  # Bit[7]: Reset BIST
  58  # Bit[6]: Reset MCU program memory
  59  # Bit[5]: Reset MCU
  60  # Bit[4]: Reset OTP
  61  # Bit[3]: Reset STB
  62  # Bit[2]: Reset d5060
  63  # Bit[1]: Reset timing control
  64  # Bit[0]: Reset array control
  65  
  66  _SYSTEM_RESET02 = const(0x3002) # Reset for Individual Block
  67  # (0: enable block; 1: reset block)
  68  # Bit[7]: Reset VFIFO
  69  # Bit[5]: Reset format
  70  # Bit[4]: Reset JFIFO
  71  # Bit[3]: Reset SFIFO
  72  # Bit[2]: Reset JPG
  73  # Bit[1]: Reset format MUX
  74  # Bit[0]: Reset average
  75  
  76  _CLOCK_ENABLE02 = const(0x3006) # Clock Enable Control
  77  # (0: disable clock; 1: enable clock)
  78  # Bit[7]: Enable PSRAM clock
  79  # Bit[6]: Enable FMT clock
  80  # Bit[5]: Enable JPEG 2x clock
  81  # Bit[3]: Enable JPEG clock
  82  # Bit[1]: Enable format MUX clock
  83  # Bit[0]: Enable average clock
  84  
  85  _SYSTEM_CTROL0 = const(0x3008)
  86  # Bit[7]: Software reset
  87  # Bit[6]: Software power down
  88  # Bit[5]: Reserved
  89  # Bit[4]: SRB clock SYNC enable
  90  # Bit[3]: Isolation suspend select
  91  # Bit[2:0]: Not used
  92  
  93  _CHIP_ID_HIGH = const(0x300A)
  94  
  95  _DRIVE_CAPABILITY = const(0x302C)
  96  # Bit[7:6]:
  97  #          00: 1x
  98  #          01: 2x
  99  #          10: 3x
 100  #          11: 4x
 101  
 102  _SC_PLLS_CTRL0 = const(0x303A)
 103  # Bit[7]: PLLS bypass
 104  _SC_PLLS_CTRL1 = const(0x303B)
 105  # Bit[4:0]: PLLS multiplier
 106  _SC_PLLS_CTRL2 = const(0x303C)
 107  # Bit[6:4]: PLLS charge pump control
 108  # Bit[3:0]: PLLS system divider
 109  _SC_PLLS_CTRL3 = const(0x303D)
 110  # Bit[5:4]: PLLS pre-divider
 111  #          00: 1
 112  #          01: 1.5
 113  #          10: 2
 114  #          11: 3
 115  # Bit[2]: PLLS root-divider - 1
 116  # Bit[1:0]: PLLS seld5
 117  #          00: 1
 118  #          01: 1
 119  #          10: 2
 120  #          11: 2.5
 121  
 122  # AEC/AGC control functions
 123  _AEC_PK_MANUAL = const(0x3503)
 124  # AEC Manual Mode Control
 125  # Bit[7:6]: Reserved
 126  # Bit[5]: Gain delay option
 127  #         Valid when 0x3503[4]=1’b0
 128  #         0: Delay one frame latch
 129  #         1: One frame latch
 130  # Bit[4:2]: Reserved
 131  # Bit[1]: AGC manual
 132  #         0: Auto enable
 133  #         1: Manual enable
 134  # Bit[0]: AEC manual
 135  #         0: Auto enable
 136  #         1: Manual enable
 137  
 138  # gain = {0x350A[1:0], 0x350B[7:0]} / 16
 139  
 140  
 141  _X_ADDR_ST_H = const(0x3800)
 142  # Bit[3:0]: X address start[11:8]
 143  _X_ADDR_ST_L = const(0x3801)
 144  # Bit[7:0]: X address start[7:0]
 145  _Y_ADDR_ST_H = const(0x3802)
 146  # Bit[2:0]: Y address start[10:8]
 147  _Y_ADDR_ST_L = const(0x3803)
 148  # Bit[7:0]: Y address start[7:0]
 149  _X_ADDR_END_H = const(0x3804)
 150  # Bit[3:0]: X address end[11:8]
 151  _X_ADDR_END_L = const(0x3805)
 152  # Bit[7:0]:
 153  _Y_ADDR_END_H = const(0x3806)
 154  # Bit[2:0]: Y address end[10:8]
 155  _Y_ADDR_END_L = const(0x3807)
 156  # Bit[7:0]:
 157  # Size after scaling
 158  _X_OUTPUT_SIZE_H = const(0x3808)
 159  # Bit[3:0]: DVP output horizontal width[11:8]
 160  _X_OUTPUT_SIZE_L = const(0x3809)
 161  # Bit[7:0]:
 162  _Y_OUTPUT_SIZE_H = const(0x380A)
 163  # Bit[2:0]: DVP output vertical height[10:8]
 164  _Y_OUTPUT_SIZE_L = const(0x380B)
 165  # Bit[7:0]:
 166  _X_TOTAL_SIZE_H = const(0x380C)
 167  # Bit[3:0]: Total horizontal size[11:8]
 168  _X_TOTAL_SIZE_L = const(0x380D)
 169  # Bit[7:0]:
 170  _Y_TOTAL_SIZE_H = const(0x380E)
 171  # Bit[7:0]: Total vertical size[15:8]
 172  _Y_TOTAL_SIZE_L = const(0x380F)
 173  # Bit[7:0]:
 174  _X_OFFSET_H = const(0x3810)
 175  # Bit[3:0]: ISP horizontal offset[11:8]
 176  _X_OFFSET_L = const(0x3811)
 177  # Bit[7:0]:
 178  _Y_OFFSET_H = const(0x3812)
 179  # Bit[2:0]: ISP vertical offset[10:8]
 180  _Y_OFFSET_L = const(0x3813)
 181  # Bit[7:0]:
 182  _X_INCREMENT = const(0x3814)
 183  # Bit[7:4]: Horizontal odd subsample increment
 184  # Bit[3:0]: Horizontal even subsample increment
 185  _Y_INCREMENT = const(0x3815)
 186  # Bit[7:4]: Vertical odd subsample increment
 187  # Bit[3:0]: Vertical even subsample increment
 188  # Size before scaling
 189  # X_INPUT_SIZE = const(   (X_ADDR_END - X_ADDR_ST + 1 - (2 * X_OFFSET)))
 190  # Y_INPUT_SIZE = const(   (Y_ADDR_END - Y_ADDR_ST + 1 - (2 * Y_OFFSET)))
 191  
 192  # mirror and flip registers
 193  _TIMING_TC_REG20 = const(0x3820)
 194  # Timing Control Register
 195  # Bit[2:1]: Vertical flip enable
 196  #         00: Normal
 197  #         11: Vertical flip
 198  # Bit[0]: Vertical binning enable
 199  _TIMING_TC_REG21 = const(0x3821)
 200  # Timing Control Register
 201  # Bit[5]: Compression Enable
 202  # Bit[2:1]: Horizontal mirror enable
 203  #         00: Normal
 204  #         11: Horizontal mirror
 205  # Bit[0]: Horizontal binning enable
 206  
 207  _PCLK_RATIO = const(0x3824)
 208  # Bit[4:0]: PCLK ratio manual
 209  
 210  # frame control registers
 211  _FRAME_CTRL01 = const(
 212      0x4201
 213  )
 214  # Control Passed Frame Number When both ON and OFF number set to 0x00,frame
 215  # control is in bypass mode
 216  # Bit[7:4]: Not used
 217  # Bit[3:0]: Frame ON number
 218  _FRAME_CTRL02 = const(
 219      0x4202
 220  )
 221  # Control Masked Frame Number When both ON and OFF number set to 0x00,frame
 222  # control is in bypass mode
 223  # Bit[7:4]: Not used
 224  # BIT[3:0]: Frame OFF number
 225  
 226  # format control registers
 227  _FORMAT_CTRL00 = const(0x4300)
 228  
 229  _CLOCK_POL_CONTROL = const(0x4740)
 230  # Bit[5]: PCLK polarity 0: active low
 231  #          1: active high
 232  # Bit[3]: Gate PCLK under VSYNC
 233  # Bit[2]: Gate PCLK under HREF
 234  # Bit[1]: HREF polarity
 235  #          0: active low
 236  #          1: active high
 237  # Bit[0] VSYNC polarity
 238  #          0: active low
 239  #          1: active high
 240  
 241  _ISP_CONTROL_01 = const(0x5001)
 242  # Bit[5]: Scale enable
 243  #          0: Disable
 244  #          1: Enable
 245  
 246  # output format control registers
 247  _FORMAT_CTRL = const(0x501F)
 248  # Format select
 249  # Bit[2:0]:
 250  #  000: YUV422
 251  #  001: RGB
 252  #  010: Dither
 253  #  011: RAW after DPC
 254  #  101: RAW after CIP
 255  
 256  # ISP top control registers
 257  _PRE_ISP_TEST_SETTING_1 = const(0x503D)
 258  # Bit[7]: Test enable
 259  #         0: Test disable
 260  #         1: Color bar enable
 261  # Bit[6]: Rolling
 262  # Bit[5]: Transparent
 263  # Bit[4]: Square black and white
 264  # Bit[3:2]: Color bar style
 265  #         00: Standard 8 color bar
 266  #         01: Gradual change at vertical mode 1
 267  #         10: Gradual change at horizontal
 268  #         11: Gradual change at vertical mode 2
 269  # Bit[1:0]: Test select
 270  #         00: Color bar
 271  #         01: Random data
 272  #         10: Square data
 273  #         11: Black image
 274  
 275  # exposure = {0x3500[3:0], 0x3501[7:0], 0x3502[7:0]} / 16 × tROW
 276  
 277  _SCALE_CTRL_1 = const(0x5601)
 278  # Bit[6:4]: HDIV RW
 279  #          DCW scale times
 280  #          000: DCW 1 time
 281  #          001: DCW 2 times
 282  #          010: DCW 4 times
 283  #          100: DCW 8 times
 284  #          101: DCW 16 times
 285  #          Others: DCW 16 times
 286  # Bit[2:0]: VDIV RW
 287  #          DCW scale times
 288  #          000: DCW 1 time
 289  #          001: DCW 2 times
 290  #          010: DCW 4 times
 291  #          100: DCW 8 times
 292  #          101: DCW 16 times
 293  #          Others: DCW 16 times
 294  
 295  _SCALE_CTRL_2 = const(0x5602)
 296  # X_SCALE High Bits
 297  _SCALE_CTRL_3 = const(0x5603)
 298  # X_SCALE Low Bits
 299  _SCALE_CTRL_4 = const(0x5604)
 300  # Y_SCALE High Bits
 301  _SCALE_CTRL_5 = const(0x5605)
 302  # Y_SCALE Low Bits
 303  _SCALE_CTRL_6 = const(0x5606)
 304  # Bit[3:0]: V Offset
 305  
 306  _VFIFO_CTRL0C = const(0x460C)
 307  # Bit[1]: PCLK manual enable
 308  #          0: Auto
 309  #          1: Manual by PCLK_RATIO
 310  
 311  _VFIFO_X_SIZE_H = const(0x4602)
 312  _VFIFO_X_SIZE_L = const(0x4603)
 313  _VFIFO_Y_SIZE_H = const(0x4604)
 314  _VFIFO_Y_SIZE_L = const(0x4605)
 315  
 316  _COMPRESSION_CTRL00 = const(0x4400)
 317  _COMPRESSION_CTRL01 = const(0x4401)
 318  _COMPRESSION_CTRL02 = const(0x4402)
 319  _COMPRESSION_CTRL03 = const(0x4403)
 320  _COMPRESSION_CTRL04 = const(0x4404)
 321  _COMPRESSION_CTRL05 = const(0x4405)
 322  _COMPRESSION_CTRL06 = const(0x4406)
 323  _COMPRESSION_CTRL07 = const(0x4407)
 324  # Bit[5:0]: QS
 325  _COMPRESSION_ISI_CTRL = const(0x4408)
 326  _COMPRESSION_CTRL09 = const(0x4409)
 327  _COMPRESSION_CTRL0A = const(0x440A)
 328  _COMPRESSION_CTRL0B = const(0x440B)
 329  _COMPRESSION_CTRL0C = const(0x440C)
 330  _COMPRESSION_CTRL0D = const(0x440D)
 331  _COMPRESSION_CTRL0E = const(0x440E)
 332  
 333  _TEST_COLOR_BAR = const(0xC0)
 334  # Enable Color Bar roling Test
 335  
 336  _AEC_PK_MANUAL_AGC_MANUALEN = const(0x02)
 337  # Enable AGC Manual enable
 338  _AEC_PK_MANUAL_AEC_MANUALEN = const(0x01)
 339  # Enable AEC Manual enable
 340  
 341  _TIMING_TC_REG20_VFLIP = const(0x06)
 342  # Vertical flip enable
 343  _TIMING_TC_REG21_HMIRROR = const(0x06)
 344  # Horizontal mirror enable
 345  
 346  OV5640_SIZE_96X96 = 0  # 96x96
 347  OV5640_SIZE_QQVGA = 1  # 160x120
 348  OV5640_SIZE_QCIF = 2  # 176x144
 349  OV5640_SIZE_HQVGA = 3  # 240x176
 350  OV5640_SIZE_240X240 = 4  # 240x240
 351  OV5640_SIZE_QVGA = 5  # 320x240
 352  OV5640_SIZE_CIF = 6  # 400x296
 353  OV5640_SIZE_HVGA = 7  # 480x320
 354  OV5640_SIZE_VGA = 8  # 640x480
 355  OV5640_SIZE_SVGA = 9  # 800x600
 356  OV5640_SIZE_XGA = 10  # 1024x768
 357  OV5640_SIZE_HD = 11  # 1280x720
 358  OV5640_SIZE_SXGA = 12  # 1280x1024
 359  OV5640_SIZE_UXGA = 13  # 1600x1200
 360  OV5640_SIZE_QHDA = 14  # 2560x1440
 361  OV5640_SIZE_WQXGA = 15  # 2560x1600
 362  OV5640_SIZE_PFHD = 16  # 1088x1920
 363  OV5640_SIZE_QSXGA = 17  # 2560x1920
 364  
 365  _ASPECT_RATIO_4X3 = const(0)
 366  _ASPECT_RATIO_3X2 = const(1)
 367  _ASPECT_RATIO_16X10 = const(2)
 368  _ASPECT_RATIO_5X3 = const(3)
 369  _ASPECT_RATIO_16X9 = const(4)
 370  _ASPECT_RATIO_21X9 = const(5)
 371  _ASPECT_RATIO_5X4 = const(6)
 372  _ASPECT_RATIO_1X1 = const(7)
 373  _ASPECT_RATIO_9X16 = const(8)
 374  
 375  _resolution_info = [
 376      [96, 96, _ASPECT_RATIO_1X1],  # 96x96
 377      [160, 120, _ASPECT_RATIO_4X3],  # QQVGA
 378      [176, 144, _ASPECT_RATIO_5X4],  # QCIF
 379      [240, 176, _ASPECT_RATIO_4X3],  # HQVGA
 380      [240, 240, _ASPECT_RATIO_1X1],  # 240x240
 381      [320, 240, _ASPECT_RATIO_4X3],  # QVGA
 382      [400, 296, _ASPECT_RATIO_4X3],  # CIF
 383      [480, 320, _ASPECT_RATIO_3X2],  # HVGA
 384      [640, 480, _ASPECT_RATIO_4X3],  # VGA
 385      [800, 600, _ASPECT_RATIO_4X3],  # SVGA
 386      [1024, 768, _ASPECT_RATIO_4X3],  # XGA
 387      [1280, 720, _ASPECT_RATIO_16X9],  # HD
 388      [1280, 1024, _ASPECT_RATIO_5X4],  # SXGA
 389      [1600, 1200, _ASPECT_RATIO_4X3],  # UXGA
 390      [2560, 1440, _ASPECT_RATIO_16X9], # QHD
 391      [2560, 1600, _ASPECT_RATIO_16X10], # WQXGA
 392      [1088, 1920, _ASPECT_RATIO_9X16], # Portrait FHD
 393      [2560, 1920, _ASPECT_RATIO_4X3], # QSXGA
 394  
 395  ]
 396  
 397  
 398  _ratio_table = [
 399      #  mw,   mh,  sx,  sy,   ex,   ey, ox, oy,   tx,   ty
 400      [2560, 1920, 0, 0, 2623, 1951, 32, 16, 2844, 1968],  # 4x3
 401      [2560, 1704, 0, 110, 2623, 1843, 32, 16, 2844, 1752],  # 3x2
 402      [2560, 1600, 0, 160, 2623, 1791, 32, 16, 2844, 1648],  # 16x10
 403      [2560, 1536, 0, 192, 2623, 1759, 32, 16, 2844, 1584],  # 5x3
 404      [2560, 1440, 0, 240, 2623, 1711, 32, 16, 2844, 1488],  # 16x9
 405      [2560, 1080, 0, 420, 2623, 1531, 32, 16, 2844, 1128],  # 21x9
 406      [2400, 1920, 80, 0, 2543, 1951, 32, 16, 2684, 1968],  # 5x4
 407      [1920, 1920, 320, 0, 2543, 1951, 32, 16, 2684, 1968],  # 1x1
 408      [1088, 1920, 736, 0, 1887, 1951, 32, 16, 1884, 1968],  # 9x16
 409  ]
 410  
 411  _pll_pre_div2x_factors = [1, 1, 2, 3, 4, 1.5, 6, 2.5, 8]
 412  _pll_pclk_root_div_factors = [1,2,4,8]
 413  
 414  _REG_DLY = const(0xFFFF)
 415  _REGLIST_TAIL = const(0x0000)
 416  
 417  _OV5640_STAT_FIRMWAREBAD = const(0x7F)
 418  _OV5640_STAT_STARTUP = const(0x7E)
 419  _OV5640_STAT_IDLE = const(0x70)
 420  _OV5640_STAT_FOCUSING = const(0x00)
 421  _OV5640_STAT_FOCUSED = const(0x10)
 422  
 423  _OV5640_CMD_TRIGGER_AUTOFOCUS = const(0x03)
 424  _OV5640_CMD_AUTO_AUTOFOCUS = const(0x04)
 425  _OV5640_CMD_RELEASE_FOCUS = const(0x08)
 426  _OV5640_CMD_AF_SET_VCM_STEP = const(0x1A)
 427  _OV5640_CMD_AF_GET_VCM_STEP = const(0x1B)
 428  
 429  _OV5640_CMD_MAIN = const(0x3022)
 430  _OV5640_CMD_ACK = const(0x3023)
 431  _OV5640_CMD_PARA0 = const(0x3024)
 432  _OV5640_CMD_PARA1 = const(0x3025)
 433  _OV5640_CMD_PARA2 = const(0x3026)
 434  _OV5640_CMD_PARA3 = const(0x3027)
 435  _OV5640_CMD_PARA4 = const(0x3028)
 436  _OV5640_CMD_FW_STATUS = const(0x3029)
 437  
 438  
 439  _sensor_default_regs = [
 440      _SYSTEM_CTROL0, 0x82,  # software reset
 441      _REG_DLY, 10,  # delay 10ms
 442      _SYSTEM_CTROL0, 0x42,  # power down
 443      # enable pll
 444      0x3103, 0x13,
 445      # io direction
 446      0x3017, 0xFF,
 447      0x3018, 0xFF,
 448      _DRIVE_CAPABILITY, 0xC3,
 449      _CLOCK_POL_CONTROL, 0x21,
 450      0x4713, 0x02,  # jpg mode select
 451      _ISP_CONTROL_01, 0x83,  # turn color matrix, awb and SDE
 452      # sys reset
 453      _SYSTEM_RESET00, 0x00, # enable all blocks
 454      _SYSTEM_RESET02, 0x1C, # reset jfifo, sfifo, jpg, fmux, avg
 455      # clock enable
 456      0x3004, 0xFF,
 457      _CLOCK_ENABLE02, 0xC3,
 458      # isp control
 459      0x5000, 0xA7,
 460      _ISP_CONTROL_01, 0xA3,  # +scaling?
 461      0x5003, 0x08,  # special_effect
 462      # unknown
 463      0x370C, 0x02,  #!!IMPORTANT
 464      0x3634, 0x40,  #!!IMPORTANT
 465      # AEC/AGC
 466      0x3A02, 0x03,
 467      0x3A03, 0xD8,
 468      0x3A08, 0x01,
 469      0x3A09, 0x27,
 470      0x3A0A, 0x00,
 471      0x3A0B, 0xF6,
 472      0x3A0D, 0x04,
 473      0x3A0E, 0x03,
 474      0x3A0F, 0x30,  # ae_level
 475      0x3A10, 0x28,  # ae_level
 476      0x3A11, 0x60,  # ae_level
 477      0x3A13, 0x43,
 478      0x3A14, 0x03,
 479      0x3A15, 0xD8,
 480      0x3A18, 0x00,  # gainceiling
 481      0x3A19, 0xF8,  # gainceiling
 482      0x3A1B, 0x30,  # ae_level
 483      0x3A1E, 0x26,  # ae_level
 484      0x3A1F, 0x14,  # ae_level
 485      # vcm debug
 486      0x3600, 0x08,
 487      0x3601, 0x33,
 488      # 50/60Hz
 489      0x3C01, 0xA4,
 490      0x3C04, 0x28,
 491      0x3C05, 0x98,
 492      0x3C06, 0x00,
 493      0x3C07, 0x08,
 494      0x3C08, 0x00,
 495      0x3C09, 0x1C,
 496      0x3C0A, 0x9C,
 497      0x3C0B, 0x40,
 498      0x460C, 0x22,  # disable jpeg footer
 499      # BLC
 500      0x4001, 0x02,
 501      0x4004, 0x02,
 502      # AWB
 503      0x5180, 0xFF,
 504      0x5181, 0xF2,
 505      0x5182, 0x00,
 506      0x5183, 0x14,
 507      0x5184, 0x25,
 508      0x5185, 0x24,
 509      0x5186, 0x09,
 510      0x5187, 0x09,
 511      0x5188, 0x09,
 512      0x5189, 0x75,
 513      0x518A, 0x54,
 514      0x518B, 0xE0,
 515      0x518C, 0xB2,
 516      0x518D, 0x42,
 517      0x518E, 0x3D,
 518      0x518F, 0x56,
 519      0x5190, 0x46,
 520      0x5191, 0xF8,
 521      0x5192, 0x04,
 522      0x5193, 0x70,
 523      0x5194, 0xF0,
 524      0x5195, 0xF0,
 525      0x5196, 0x03,
 526      0x5197, 0x01,
 527      0x5198, 0x04,
 528      0x5199, 0x12,
 529      0x519A, 0x04,
 530      0x519B, 0x00,
 531      0x519C, 0x06,
 532      0x519D, 0x82,
 533      0x519E, 0x38,
 534      # color matrix (Saturation)
 535      0x5381, 0x1E,
 536      0x5382, 0x5B,
 537      0x5383, 0x08,
 538      0x5384, 0x0A,
 539      0x5385, 0x7E,
 540      0x5386, 0x88,
 541      0x5387, 0x7C,
 542      0x5388, 0x6C,
 543      0x5389, 0x10,
 544      0x538A, 0x01,
 545      0x538B, 0x98,
 546      # CIP control (Sharpness)
 547      0x5300, 0x10,  # sharpness
 548      0x5301, 0x10,  # sharpness
 549      0x5302, 0x18,  # sharpness
 550      0x5303, 0x19,  # sharpness
 551      0x5304, 0x10,
 552      0x5305, 0x10,
 553      0x5306, 0x08,  # denoise
 554      0x5307, 0x16,
 555      0x5308, 0x40,
 556      0x5309, 0x10,  # sharpness
 557      0x530A, 0x10,  # sharpness
 558      0x530B, 0x04,  # sharpness
 559      0x530C, 0x06,  # sharpness
 560      # GAMMA
 561      0x5480, 0x01,
 562      0x5481, 0x00,
 563      0x5482, 0x1E,
 564      0x5483, 0x3B,
 565      0x5484, 0x58,
 566      0x5485, 0x66,
 567      0x5486, 0x71,
 568      0x5487, 0x7D,
 569      0x5488, 0x83,
 570      0x5489, 0x8F,
 571      0x548A, 0x98,
 572      0x548B, 0xA6,
 573      0x548C, 0xB8,
 574      0x548D, 0xCA,
 575      0x548E, 0xD7,
 576      0x548F, 0xE3,
 577      0x5490, 0x1D,
 578      # Special Digital Effects (SDE) (UV adjust)
 579      0x5580, 0x06,  # enable brightness and contrast
 580      0x5583, 0x40,  # special_effect
 581      0x5584, 0x10,  # special_effect
 582      0x5586, 0x20,  # contrast
 583      0x5587, 0x00,  # brightness
 584      0x5588, 0x00,  # brightness
 585      0x5589, 0x10,
 586      0x558A, 0x00,
 587      0x558B, 0xF8,
 588      0x501D, 0x40,  # enable manual offset of contrast
 589      # power on
 590      0x3008, 0x02,
 591      # 50Hz
 592      0x3C00, 0x04,
 593      #_REG_DLY, 300,
 594  ]
 595  
 596  
 597  
 598  _reset_awb = [
 599      _ISP_CONTROL_01, 0x83,  # turn color matrix, awb and SDE
 600      # sys reset
 601      _SYSTEM_RESET00, 0x00, # enable all blocks
 602      _SYSTEM_RESET02, 0x1C, # reset jfifo, sfifo, jpg, fmux, avg
 603      # clock enable
 604      #0x3004, 0xFF,
 605      #_CLOCK_ENABLE02, 0xC3,
 606      # isp control
 607      0x5000, 0xA7,
 608      _ISP_CONTROL_01, 0xA3,  # +scaling?
 609      0x5003, 0x08,  # special_effect
 610      # unknown
 611      0x370C, 0x02,  #!!IMPORTANT
 612      0x3634, 0x40,  #!!IMPORTANT
 613      # AEC/AGC
 614      0x3A02, 0x03,
 615      0x3A03, 0xD8,
 616      0x3A08, 0x01,
 617      0x3A09, 0x27,
 618      0x3A0A, 0x00,
 619      0x3A0B, 0xF6,
 620      0x3A0D, 0x04,
 621      0x3A0E, 0x03,
 622      0x3A0F, 0x30,  # ae_level
 623      0x3A10, 0x28,  # ae_level
 624      0x3A11, 0x60,  # ae_level
 625      0x3A13, 0x43,
 626      0x3A14, 0x03,
 627      0x3A15, 0xD8,
 628      0x3A18, 0x00,  # gainceiling
 629      0x3A19, 0xF8,  # gainceiling
 630      0x3A1B, 0x30,  # ae_level
 631      0x3A1E, 0x26,  # ae_level
 632      0x3A1F, 0x14,  # ae_level
 633      # vcm debug
 634      0x3600, 0x08,
 635      0x3601, 0x33,
 636      # 50/60Hz
 637      0x3C01, 0xA4,
 638      0x3C04, 0x28,
 639      0x3C05, 0x98,
 640      0x3C06, 0x00,
 641      0x3C07, 0x08,
 642      0x3C08, 0x00,
 643      0x3C09, 0x1C,
 644      0x3C0A, 0x9C,
 645      0x3C0B, 0x40,
 646      0x460C, 0x22,  # disable jpeg footer
 647      # BLC
 648      0x4001, 0x02,
 649      0x4004, 0x02,
 650      # AWB
 651      0x5180, 0xFF,
 652      0x5181, 0xF2,
 653      0x5182, 0x00,
 654      0x5183, 0x14,
 655      0x5184, 0x25,
 656      0x5185, 0x24,
 657      0x5186, 0x09,
 658      0x5187, 0x09,
 659      0x5188, 0x09,
 660      0x5189, 0x75,
 661      0x518A, 0x54,
 662      0x518B, 0xE0,
 663      0x518C, 0xB2,
 664      0x518D, 0x42,
 665      0x518E, 0x3D,
 666      0x518F, 0x56,
 667      0x5190, 0x46,
 668      0x5191, 0xF8,
 669      0x5192, 0x04,
 670      0x5193, 0x70,
 671      0x5194, 0xF0,
 672      0x5195, 0xF0,
 673      0x5196, 0x03,
 674      0x5197, 0x01,
 675      0x5198, 0x04,
 676      0x5199, 0x12,
 677      0x519A, 0x04,
 678      0x519B, 0x00,
 679      0x519C, 0x06,
 680      0x519D, 0x82,
 681      0x519E, 0x38,
 682      # color matrix (Saturation)
 683      0x5381, 0x1E,
 684      0x5382, 0x5B,
 685      0x5383, 0x08,
 686      0x5384, 0x0A,
 687      0x5385, 0x7E,
 688      0x5386, 0x88,
 689      0x5387, 0x7C,
 690      0x5388, 0x6C,
 691      0x5389, 0x10,
 692      0x538A, 0x01,
 693      0x538B, 0x98,
 694      # CIP control (Sharpness)
 695      0x5300, 0x10,  # sharpness
 696      0x5301, 0x10,  # sharpness
 697      0x5302, 0x18,  # sharpness
 698      0x5303, 0x19,  # sharpness
 699      0x5304, 0x10,
 700      0x5305, 0x10,
 701      0x5306, 0x08,  # denoise
 702      0x5307, 0x16,
 703      0x5308, 0x40,
 704      0x5309, 0x10,  # sharpness
 705      0x530A, 0x10,  # sharpness
 706      0x530B, 0x04,  # sharpness
 707      0x530C, 0x06,  # sharpness
 708      # GAMMA
 709      0x5480, 0x01,
 710      0x5481, 0x00,
 711      0x5482, 0x1E,
 712      0x5483, 0x3B,
 713      0x5484, 0x58,
 714      0x5485, 0x66,
 715      0x5486, 0x71,
 716      0x5487, 0x7D,
 717      0x5488, 0x83,
 718      0x5489, 0x8F,
 719      0x548A, 0x98,
 720      0x548B, 0xA6,
 721      0x548C, 0xB8,
 722      0x548D, 0xCA,
 723      0x548E, 0xD7,
 724      0x548F, 0xE3,
 725      0x5490, 0x1D,
 726      # Special Digital Effects (SDE) (UV adjust)
 727      0x5580, 0x06,  # enable brightness and contrast
 728      0x5583, 0x40,  # special_effect
 729      0x5584, 0x10,  # special_effect
 730      0x5586, 0x20,  # contrast
 731      0x5587, 0x00,  # brightness
 732      0x5588, 0x00,  # brightness
 733      0x5589, 0x10,
 734      0x558A, 0x00,
 735      0x558B, 0xF8,
 736      0x501D, 0x40,  # enable manual offset of contrast
 737  ]
 738  _sensor_format_jpeg = [
 739      _FORMAT_CTRL, 0x00,  # YUV422
 740      _FORMAT_CTRL00, 0x30,  # YUYV
 741      _SYSTEM_RESET02, 0x00,  # enable everything
 742      _CLOCK_ENABLE02, 0xFF,  # enable all clocks
 743      0x471C, 0x50,  # 0xd0 to 0x50 !!!
 744  ]
 745  
 746  _sensor_format_raw = [
 747      _FORMAT_CTRL, 0x03,  # RAW (DPC)
 748      _FORMAT_CTRL00, 0x00,  # RAW
 749  ]
 750  
 751  _sensor_format_grayscale = [
 752      _FORMAT_CTRL, 0x00,  # YUV422
 753      _FORMAT_CTRL00, 0x10,  # Y8
 754  ]
 755  
 756  _sensor_format_yuv422 = [
 757      _FORMAT_CTRL, 0x00,  # YUV422
 758      _FORMAT_CTRL00, 0x30,  # YUYV
 759  ]
 760  
 761  _sensor_format_rgb565 = [
 762      _FORMAT_CTRL, 0x01,  # RGB
 763      _FORMAT_CTRL00, 0x61,  # RGB565 (BGR)
 764      _SYSTEM_RESET02, 0x1C, # reset jfifo, sfifo, jpg, fmux, avg
 765      _CLOCK_ENABLE02, 0xC3, # reset to how it was before (no jpg clock)
 766  
 767  ]
 768  
 769  _ov5640_color_settings = {
 770      OV5640_COLOR_RGB: _sensor_format_rgb565,
 771      OV5640_COLOR_YUV: _sensor_format_yuv422,
 772      OV5640_COLOR_GRAYSCALE: _sensor_format_grayscale,
 773      OV5640_COLOR_JPEG: _sensor_format_jpeg,
 774  }
 775  
 776  _contrast_settings = [
 777      [0x20, 0x00], #  0
 778      [0x24, 0x10], # +1
 779      [0x28, 0x18], # +2
 780      [0x2c, 0x1c], # +3
 781      [0x14, 0x14], # -3
 782      [0x18, 0x18], # -2
 783      [0x1c, 0x1c], # -1
 784  ]
 785  
 786  _sensor_saturation_levels = [
 787      [0x1D, 0x60, 0x03, 0x0C, 0x78, 0x84, 0x7D, 0x6B, 0x12, 0x01, 0x98],  # 0
 788      [0x1D, 0x60, 0x03, 0x0D, 0x84, 0x91, 0x8A, 0x76, 0x14, 0x01, 0x98],  # +1
 789      [0x1D, 0x60, 0x03, 0x0E, 0x90, 0x9E, 0x96, 0x80, 0x16, 0x01, 0x98],  # +2
 790      [0x1D, 0x60, 0x03, 0x10, 0x9C, 0xAC, 0xA2, 0x8B, 0x17, 0x01, 0x98],  # +3
 791      [0x1D, 0x60, 0x03, 0x11, 0xA8, 0xB9, 0xAF, 0x96, 0x19, 0x01, 0x98],  # +4
 792      [0x1D, 0x60, 0x03, 0x07, 0x48, 0x4F, 0x4B, 0x40, 0x0B, 0x01, 0x98],  # -4
 793      [0x1D, 0x60, 0x03, 0x08, 0x54, 0x5C, 0x58, 0x4B, 0x0D, 0x01, 0x98],  # -3
 794      [0x1D, 0x60, 0x03, 0x0A, 0x60, 0x6A, 0x64, 0x56, 0x0E, 0x01, 0x98],  # -2
 795      [0x1D, 0x60, 0x03, 0x0B, 0x6C, 0x77, 0x70, 0x60, 0x10, 0x01, 0x98],  # -1
 796  ]
 797  
 798  _sensor_ev_levels = [
 799      [0x38, 0x30, 0x61, 0x38, 0x30, 0x10], #  0
 800      [0x40, 0x38, 0x71, 0x40, 0x38, 0x10], # +1
 801      [0x50, 0x48, 0x90, 0x50, 0x48, 0x20], # +2
 802      [0x60, 0x58, 0xa0, 0x60, 0x58, 0x20], # +3
 803      [0x10, 0x08, 0x10, 0x08, 0x20, 0x10], # -3
 804      [0x20, 0x18, 0x41, 0x20, 0x18, 0x10], # -2
 805      [0x30, 0x28, 0x61, 0x30, 0x28, 0x10], # -1
 806  ]
 807  
 808  OV5640_WHITE_BALANCE_AUTO = 0
 809  OV5640_WHITE_BALANCE_SUNNY = 1
 810  OV5640_WHITE_BALANCE_FLUORESCENT = 2
 811  OV5640_WHITE_BALANCE_CLOUDY = 3
 812  OV5640_WHITE_BALANCE_INCANDESCENT = 4
 813  
 814  _light_registers = [0x3406, 0x3400, 0x3401, 0x3402, 0x3403, 0x3404, 0x3405]
 815  _light_modes = [
 816      [0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00], # auto
 817      [0x01, 0x06, 0x1c, 0x04, 0x00, 0x04, 0xf3], # sunny
 818      [0x01, 0x05, 0x48, 0x04, 0x00, 0x07, 0xcf], # office / fluorescent
 819      [0x01, 0x06, 0x48, 0x04, 0x00, 0x04, 0xd3], # cloudy
 820      [0x01, 0x04, 0x10, 0x04, 0x00, 0x08, 0x40], # home / incandescent
 821  
 822  ]
 823  
 824  OV5640_SPECIAL_EFFECT_NONE = 0
 825  OV5640_SPECIAL_EFFECT_NEGATIVE = 1
 826  OV5640_SPECIAL_EFFECT_GRAYSCALE = 2
 827  OV5640_SPECIAL_EFFECT_RED_TINT = 3
 828  OV5640_SPECIAL_EFFECT_GREEN_TINT = 4
 829  OV5640_SPECIAL_EFFECT_BLUE_TINT = 5
 830  OV5640_SPECIAL_EFFECT_SEPIA = 6
 831  
 832  _sensor_special_effects = [
 833      [0x06, 0x40, 0x10, 0x08],  # Normal
 834      [0x46, 0x40, 0x28, 0x08],  # Negative
 835      [0x1E, 0x80, 0x80, 0x08],  # Grayscale
 836      [0x1E, 0x80, 0xC0, 0x08],  # Red Tint
 837      [0x1E, 0x60, 0x60, 0x08],  # Green Tint
 838      [0x1E, 0xA0, 0x40, 0x08],  # Blue Tint
 839      [0x1E, 0x40, 0xA0, 0x08],  # Sepia
 840  ]
 841  
 842  _sensor_regs_gamma0 = [
 843      0x5480, 0x01,
 844      0x5481, 0x08,
 845      0x5482, 0x14,
 846      0x5483, 0x28,
 847      0x5484, 0x51,
 848      0x5485, 0x65,
 849      0x5486, 0x71,
 850      0x5487, 0x7D,
 851      0x5488, 0x87,
 852      0x5489, 0x91,
 853      0x548A, 0x9A,
 854      0x548B, 0xAA,
 855      0x548C, 0xB8,
 856      0x548D, 0xCD,
 857      0x548E, 0xDD,
 858      0x548F, 0xEA,
 859      0x5490, 0x1D,
 860  ]
 861  
 862  sensor_regs_gamma1 = [
 863      0x5480, 0x1,
 864      0x5481, 0x0,
 865      0x5482, 0x1E,
 866      0x5483, 0x3B,
 867      0x5484, 0x58,
 868      0x5485, 0x66,
 869      0x5486, 0x71,
 870      0x5487, 0x7D,
 871      0x5488, 0x83,
 872      0x5489, 0x8F,
 873      0x548A, 0x98,
 874      0x548B, 0xA6,
 875      0x548C, 0xB8,
 876      0x548D, 0xCA,
 877      0x548E, 0xD7,
 878      0x548F, 0xE3,
 879      0x5490, 0x1D,
 880  ]
 881  
 882  sensor_regs_awb0 = [
 883      0x5180, 0xFF,
 884      0x5181, 0xF2,
 885      0x5182, 0x00,
 886      0x5183, 0x14,
 887      0x5184, 0x25,
 888      0x5185, 0x24,
 889      0x5186, 0x09,
 890      0x5187, 0x09,
 891      0x5188, 0x09,
 892      0x5189, 0x75,
 893      0x518A, 0x54,
 894      0x518B, 0xE0,
 895      0x518C, 0xB2,
 896      0x518D, 0x42,
 897      0x518E, 0x3D,
 898      0x518F, 0x56,
 899      0x5190, 0x46,
 900      0x5191, 0xF8,
 901      0x5192, 0x04,
 902      0x5193, 0x70,
 903      0x5194, 0xF0,
 904      0x5195, 0xF0,
 905      0x5196, 0x03,
 906      0x5197, 0x01,
 907      0x5198, 0x04,
 908      0x5199, 0x12,
 909      0x519A, 0x04,
 910      0x519B, 0x00,
 911      0x519C, 0x06,
 912      0x519D, 0x82,
 913      0x519E, 0x38,
 914  ]
 915  # fmt: on
 916  
 917  
 918  class _RegBits:
 919      def __init__(self, reg: int, shift: int, mask: int) -> None:
 920          self.reg = reg
 921          self.shift = shift
 922          self.mask = mask
 923  
 924      def __get__(self, obj: "OV5640", objtype: Optional[type] = None) -> int:
 925          reg_value = obj._read_register(self.reg)
 926          return (reg_value >> self.shift) & self.mask
 927  
 928      def __set__(self, obj: "OV5640", value: int) -> None:
 929          if value & ~self.mask:
 930              raise ValueError(
 931                  f"Value 0x{value:02x} does not fit in mask 0x{self.mask:02x}"
 932              )
 933          reg_value = obj._read_register(self.reg)
 934          reg_value &= ~(self.mask << self.shift)
 935          reg_value |= value << self.shift
 936          obj._write_register(self.reg, reg_value)
 937  
 938  
 939  class _RegBits16:
 940      def __init__(self, reg: int, shift: int, mask: int) -> None:
 941          self.reg = reg
 942          self.shift = shift
 943          self.mask = mask
 944  
 945      def __get__(self, obj: "OV5640", objtype: Optional[type] = None) -> int:
 946          reg_value = obj._read_register16(self.reg)
 947          return (reg_value >> self.shift) & self.mask
 948  
 949      def __set__(self, obj: "OV5640", value: int) -> None:
 950          if value & ~self.mask:
 951              raise ValueError(
 952                  f"Value 0x{value:02x} does not fit in mask 0x{self.mask:02x}"
 953              )
 954          reg_value = obj._read_register16(self.reg)
 955          reg_value &= ~(self.mask << self.shift)
 956          reg_value |= value << self.shift
 957          obj._write_register16(self.reg, reg_value)
 958  
 959  
 960  class _SCCB16CameraBase:  # pylint: disable=too-few-public-methods
 961      _finalize_firmware_load = (
 962          0x3022,
 963          0x00,
 964          0x3023,
 965          0x00,
 966          0x3024,
 967          0x00,
 968          0x3025,
 969          0x00,
 970          0x3026,
 971          0x00,
 972          0x3027,
 973          0x00,
 974          0x3028,
 975          0x00,
 976          0x3029,
 977          0x7F,
 978          0x3000,
 979          0x00,
 980      )
 981  
 982      def __init__(self, i2c_bus: I2C, i2c_address: int) -> None:
 983          self._i2c_device = I2CDevice(i2c_bus, i2c_address)
 984          self._bank = None
 985  
 986      def _write_register(self, reg: int, value: int) -> None:
 987          b = bytearray(3)
 988          b[0] = reg >> 8
 989          b[1] = reg & 0xFF
 990          b[2] = value
 991          with self._i2c_device as i2c:
 992              i2c.write(b)
 993  
 994      def _write_addr_reg(self, reg: int, x_value: int, y_value: int) -> None:
 995          self._write_register16(reg, x_value)
 996          self._write_register16(reg + 2, y_value)
 997  
 998      def _write_register16(self, reg: int, value: int) -> None:
 999          self._write_register(reg, value >> 8)
1000          self._write_register(reg + 1, value & 0xFF)
1001  
1002      def _read_register(self, reg: int) -> int:
1003          b = bytearray(2)
1004          b[0] = reg >> 8
1005          b[1] = reg & 0xFF
1006          with self._i2c_device as i2c:
1007              i2c.write(b)
1008              i2c.readinto(b, end=1)
1009          return b[0]
1010  
1011      def _read_register16(self, reg: int) -> int:
1012          high = self._read_register(reg)
1013          low = self._read_register(reg + 1)
1014          return (high << 8) | low
1015  
1016      def _write_list(self, reg_list: Sequence[int]) -> None:
1017          for i in range(0, len(reg_list), 2):
1018              register = reg_list[i]
1019              value = reg_list[i + 1]
1020              if register == _REG_DLY:
1021                  time.sleep(value / 1000)
1022              else:
1023                  self._write_register(register, value)
1024  
1025      def _write_reg_bits(self, reg: int, mask: int, enable: bool) -> None:
1026          val = val = self._read_register(reg)
1027          if enable:
1028              val |= mask
1029          else:
1030              val &= ~mask
1031          self._write_register(reg, val)
1032  
1033  
1034  class OV5640(_SCCB16CameraBase):  # pylint: disable=too-many-instance-attributes
1035      """Control & Capture Images from an OV5640 Camera"""
1036  
1037      def __init__(
1038          self,
1039          i2c_bus: I2C,
1040          data_pins: List[Pin],
1041          clock: Pin,
1042          vsync: Pin,
1043          href: Pin,
1044          shutdown: Optional[DigitalInOut] = None,
1045          reset: Optional[DigitalInOut] = None,
1046          mclk: Optional[Pin] = None,
1047          mclk_frequency: int = 20_000_000,
1048          i2c_address: int = 0x3C,
1049          size: int = OV5640_SIZE_QQVGA,
1050          init_autofocus: bool = True,
1051      ):  # pylint: disable=too-many-arguments
1052          """
1053          Args:
1054              i2c_bus (busio.I2C): The I2C bus used to configure the OV5640
1055              data_pins (List[microcontroller.Pin]): A list of 8 data pins, in order.
1056              clock (microcontroller.Pin): The pixel clock from the OV5640.
1057              vsync (microcontroller.Pin): The vsync signal from the OV5640.
1058              href (microcontroller.Pin): The href signal from the OV5640, \
1059                  sometimes inaccurately called hsync.
1060              shutdown (Optional[digitalio.DigitalInOut]): If not None, the shutdown
1061                  signal to the camera, also called the powerdown or enable pin.
1062              reset (Optional[digitalio.DigitalInOut]): If not None, the reset signal
1063                  to the camera.
1064              mclk (Optional[microcontroller.Pin]): The pin on which to create a
1065                  master clock signal, or None if the master clock signal is
1066                  already being generated.
1067              mclk_frequency (int): The frequency of the master clock to generate, \
1068                  ignored if mclk is None, requred if it is specified.
1069                  Note that the OV5640 requires a very low jitter clock,
1070                  so only specific (microcontroller-dependent) values may
1071                  work reliably.  On the ESP32-S2, a 20MHz clock can be generated
1072                  with sufficiently low jitter.
1073              i2c_address (int): The I2C address of the camera.
1074              size (int): The captured image size
1075              init_autofocus (bool): initialize autofocus
1076          """
1077  
1078          # Initialize the master clock
1079          if mclk:
1080              self._mclk_pwm = pwmio.PWMOut(mclk, frequency=mclk_frequency)
1081              self._mclk_pwm.duty_cycle = 32768
1082          else:
1083              self._mclk_pwm = None
1084  
1085          if reset:
1086              self._reset = reset
1087              self._reset.switch_to_output(False)
1088          else:
1089              self._reset = None
1090  
1091          if shutdown:
1092              self._shutdown = shutdown
1093              self._shutdown.switch_to_output(True)
1094              time.sleep(0.005)  # t2, 5ms stability
1095              self._shutdown.switch_to_output(False)
1096          else:
1097              self._shutdown = None
1098  
1099          if self._reset:
1100              time.sleep(0.001)  # t3, 1ms delay from pwdn
1101              self._reset.switch_to_output(True)
1102              time.sleep(0.02)
1103  
1104          # Now that the master clock is running, we can initialize i2c comms
1105          super().__init__(i2c_bus, i2c_address)
1106  
1107          self._write_list(_sensor_default_regs)
1108  
1109          self._imagecapture = imagecapture.ParallelImageCapture(
1110              data_pins=data_pins, clock=clock, vsync=vsync, href=href
1111          )
1112  
1113          self._colorspace = OV5640_COLOR_RGB
1114          self._flip_x = False
1115          self._flip_y = False
1116          self._w = None
1117          self._h = None
1118          self._size = None
1119          self._test_pattern = False
1120          self._binning = False
1121          self._scale = False
1122          self._ev = 0
1123          self._white_balance = 0
1124          self.size = size
1125  
1126          if init_autofocus:
1127              self.autofocus_init()
1128  
1129      chip_id = _RegBits16(_CHIP_ID_HIGH, 0, 0xFFFF)
1130  
1131      def autofocus_init_from_file(self, filename):
1132          """Initialize the autofocus engine from a .bin file"""
1133          with open(filename, mode="rb") as file:
1134              firmware = file.read()
1135          self.autofocus_init_from_bitstream(firmware)
1136  
1137      def autofocus_init_from_bitstream(self, firmware: bytes):
1138          """Initialize the autofocus engine from a bytestring"""
1139          self._write_register(0x3000, 0x20)  # reset autofocus coprocessor
1140          time.sleep(0.01)
1141  
1142          arr = bytearray(256)
1143          with self._i2c_device as i2c:
1144              for offset in range(0, len(firmware), 254):
1145                  num_firmware_bytes = min(254, len(firmware) - offset)
1146                  reg = offset + 0x8000
1147                  arr[0] = reg >> 8
1148                  arr[1] = reg & 0xFF
1149                  arr[2 : 2 + num_firmware_bytes] = firmware[
1150                      offset : offset + num_firmware_bytes
1151                  ]
1152                  i2c.write(arr, end=2 + num_firmware_bytes)
1153  
1154          self._write_list(self._finalize_firmware_load)
1155          for _ in range(100):
1156              if self.autofocus_status == _OV5640_STAT_IDLE:
1157                  break
1158              time.sleep(0.01)
1159          else:
1160              raise RuntimeError("Timed out after trying to load autofocus firmware")
1161  
1162      def autofocus_init(self):
1163          """Initialize the autofocus engine from ov5640_autofocus.bin"""
1164          if "/" in __file__:
1165              binfile = (
1166                  __file__.rsplit("/", 1)[0].rsplit(".", 1)[0] + "/ov5640_autofocus.bin"
1167              )
1168          else:
1169              binfile = "ov5640_autofocus.bin"
1170          print(binfile)
1171          return self.autofocus_init_from_file(binfile)
1172  
1173      @property
1174      def autofocus_status(self):
1175          """Read the camera autofocus status register"""
1176          return self._read_register(_OV5640_CMD_FW_STATUS)
1177  
1178      def _send_autofocus_command(self, command, msg):  # pylint: disable=unused-argument
1179          self._write_register(_OV5640_CMD_ACK, 0x01)  # clear command ack
1180          self._write_register(_OV5640_CMD_MAIN, command)  # send command
1181          for _ in range(1000):
1182              if self._read_register(_OV5640_CMD_ACK) == 0x0:  # command is finished
1183                  return True
1184              time.sleep(0.01)
1185          return False
1186  
1187      def autofocus(self) -> list[int]:
1188          """Perform an autofocus operation.
1189  
1190          If all elements of the list are 0, the autofocus operation failed. Otherwise,
1191          if at least one element is nonzero, the operation succeeded.
1192  
1193          In principle the elements correspond to 5 autofocus regions, if configured."""
1194          if not self._send_autofocus_command(_OV5640_CMD_RELEASE_FOCUS, "release focus"):
1195              return [False] * 5
1196          if not self._send_autofocus_command(_OV5640_CMD_TRIGGER_AUTOFOCUS, "autofocus"):
1197              return [False] * 5
1198          zone_focus = [self._read_register(_OV5640_CMD_PARA0 + i) for i in range(5)]
1199          print(f"zones focused: {zone_focus}")
1200          return zone_focus
1201  
1202      @property
1203      def autofocus_vcm_step(self):
1204          """Get the voice coil motor step location"""
1205          if not self._send_autofocus_command(
1206              _OV5640_CMD_AF_GET_VCM_STEP, "get vcm step"
1207          ):
1208              return None
1209          return self._read_register(_OV5640_CMD_PARA4)
1210  
1211      @autofocus_vcm_step.setter
1212      def autofocus_vcm_step(self, step):
1213          """Get the voice coil motor step location, from 0 to 255"""
1214          if not 0 <= step <= 255:
1215              raise RuntimeError("VCM step must be 0 to 255")
1216          self._write_register(_OV5640_CMD_PARA3, 0x00)
1217          self._write_register(_OV5640_CMD_PARA4, step)
1218          self._send_autofocus_command(_OV5640_CMD_AF_SET_VCM_STEP, "set vcm step")
1219  
1220      def capture(self, buf: Union[bytearray, memoryview]) -> None:
1221          """Capture an image into the buffer.
1222  
1223          Args:
1224              buf (Union[bytearray, memoryview]): A WritableBuffer to contain the \
1225                  captured image.  Note that this can be a ulab array or a displayio Bitmap.
1226          """
1227          self._imagecapture.capture(buf)
1228          if self.colorspace == OV5640_COLOR_JPEG:
1229              eoi = buf.find(b"\xff\xd9")
1230              if eoi != -1:
1231                  # terminate the JPEG data just after the EOI marker
1232                  return memoryview(buf)[: eoi + 2]
1233          return None
1234  
1235      @property
1236      def capture_buffer_size(self) -> int:
1237          """Return the size of capture buffer to use with current resolution & colorspace settings"""
1238          if self.colorspace == OV5640_COLOR_JPEG:
1239              return self.width * self.height // self.quality
1240          if self.colorspace == OV5640_COLOR_GRAYSCALE:
1241              return self.width * self.height
1242          return self.width * self.height * 2
1243  
1244      @property
1245      def mclk_frequency(self) -> Optional[int]:
1246          """Get the actual frequency the generated mclk, or None"""
1247          return self._mclk_pwm.frequency if self._mclk_pwm else None
1248  
1249      @property
1250      def width(self) -> int:
1251          """Get the image width in pixels."""
1252          return self._w
1253  
1254      @property
1255      def height(self) -> int:
1256          """Get the image height in pixels."""
1257          return self._h
1258  
1259      @property
1260      def colorspace(self) -> int:
1261          """Get or set the colorspace, one of the ``OV5640_COLOR_`` constants."""
1262          return self._colorspace
1263  
1264      @colorspace.setter
1265      def colorspace(self, colorspace: int) -> None:
1266          self._colorspace = colorspace
1267          self._set_size_and_colorspace()
1268  
1269      def _set_image_options(self) -> None:  # pylint: disable=too-many-branches
1270          reg20 = reg21 = reg4514 = reg4514_test = 0
1271          if self.colorspace == OV5640_COLOR_JPEG:
1272              reg21 |= 0x20
1273  
1274          if self._binning:
1275              reg20 |= 1
1276              reg21 |= 1
1277              reg4514_test |= 4
1278          else:
1279              reg20 |= 0x40
1280  
1281          if self._flip_y:
1282              reg20 |= 0x06
1283              reg4514_test |= 1
1284  
1285          if self._flip_x:
1286              reg21 |= 0x06
1287              reg4514_test |= 2
1288  
1289          if reg4514_test == 0:
1290              reg4514 = 0x88
1291          elif reg4514_test == 1:
1292              reg4514 = 0x00
1293          elif reg4514_test == 2:
1294              reg4514 = 0xBB
1295          elif reg4514_test == 3:
1296              reg4514 = 0x00
1297          elif reg4514_test == 4:
1298              reg4514 = 0xAA
1299          elif reg4514_test == 5:
1300              reg4514 = 0xBB
1301          elif reg4514_test == 6:
1302              reg4514 = 0xBB
1303          elif reg4514_test == 7:
1304              reg4514 = 0xAA
1305  
1306          self._write_register(_TIMING_TC_REG20, reg20)
1307          self._write_register(_TIMING_TC_REG21, reg21)
1308          self._write_register(0x4514, reg4514)
1309  
1310          if self._binning:
1311              self._write_register(0x4520, 0x0B)
1312              self._write_register(_X_INCREMENT, 0x31)
1313              self._write_register(_Y_INCREMENT, 0x31)
1314          else:
1315              self._write_register(0x4520, 0x10)
1316              self._write_register(_X_INCREMENT, 0x11)
1317              self._write_register(_Y_INCREMENT, 0x11)
1318  
1319      def _set_colorspace(self) -> None:
1320          colorspace = self._colorspace
1321          settings = _ov5640_color_settings[colorspace]
1322  
1323          self._write_list(settings)
1324  
1325      def deinit(self) -> None:
1326          """Deinitialize the camera"""
1327          self._imagecapture.deinit()
1328          if self._mclk_pwm:
1329              self._mclk_pwm.deinit()
1330          if self._shutdown:
1331              self._shutdown.deinit()
1332          if self._reset:
1333              self._reset.deinit()
1334  
1335      @property
1336      def size(self) -> int:
1337          """Get or set the captured image size, one of the ``OV5640_SIZE_`` constants."""
1338          return self._size
1339  
1340      def _set_size_and_colorspace(self) -> None:  # pylint: disable=too-many-locals
1341          size = self._size
1342          width, height, ratio = _resolution_info[size]
1343          self._w = width
1344          self._h = height
1345          (
1346              max_width,
1347              max_height,
1348              start_x,
1349              start_y,
1350              end_x,
1351              end_y,
1352              offset_x,
1353              offset_y,
1354              total_x,
1355              total_y,
1356          ) = _ratio_table[ratio]
1357  
1358          self._binning = (width <= max_width // 2) and (height <= max_height // 2)
1359          self._scale = not (
1360              (width == max_width and height == max_height)
1361              or (width == max_width // 2 and height == max_height // 2)
1362          )
1363  
1364          self._write_addr_reg(_X_ADDR_ST_H, start_x, start_y)
1365          self._write_addr_reg(_X_ADDR_END_H, end_x, end_y)
1366          self._write_addr_reg(_X_OUTPUT_SIZE_H, width, height)
1367  
1368          if not self._binning:
1369              self._write_addr_reg(_X_TOTAL_SIZE_H, total_x, total_y)
1370              self._write_addr_reg(_X_OFFSET_H, offset_x, offset_y)
1371          else:
1372              if width > 920:
1373                  self._write_addr_reg(_X_TOTAL_SIZE_H, total_x - 200, total_y // 2)
1374              else:
1375                  self._write_addr_reg(_X_TOTAL_SIZE_H, 2060, total_y // 2)
1376              self._write_addr_reg(_X_OFFSET_H, offset_x // 2, offset_y // 2)
1377  
1378          self._write_reg_bits(_ISP_CONTROL_01, 0x20, self._scale)
1379  
1380          self._set_image_options()
1381  
1382          if self.colorspace == OV5640_COLOR_JPEG:
1383              sys_mul = 200
1384              if size < OV5640_SIZE_QVGA:
1385                  sys_mul = 160
1386              if size < OV5640_SIZE_XGA:
1387                  sys_mul = 180
1388              self._set_pll(False, sys_mul, 4, 2, False, 2, True, 4)
1389          else:
1390              self._set_pll(False, 32, 1, 1, False, 1, True, 4)
1391  
1392          self._set_colorspace()
1393  
1394      def _set_pll(  # pylint: disable=too-many-arguments
1395          self,
1396          bypass: bool,
1397          multiplier: int,
1398          sys_div: int,
1399          pre_div: int,
1400          root_2x: bool,
1401          pclk_root_div: int,
1402          pclk_manual: bool,
1403          pclk_div: int,
1404      ) -> None:
1405          if (  # pylint: disable=too-many-boolean-expressions
1406              multiplier > 252
1407              or multiplier < 4
1408              or sys_div > 15
1409              or pre_div > 8
1410              or pclk_div > 31
1411              or pclk_root_div > 3
1412          ):
1413              raise ValueError("Invalid argument to internal function")
1414  
1415          self._write_register(0x3039, 0x80 if bypass else 0)
1416          self._write_register(0x3034, 0x1A)
1417          self._write_register(0x3035, 1 | ((sys_div & 0xF) << 4))
1418          self._write_register(0x3036, multiplier & 0xFF)
1419          self._write_register(0x3037, (pre_div & 0xF) | (0x10 if root_2x else 0))
1420          self._write_register(0x3108, (pclk_root_div & 3) << 4 | 0x06)
1421          self._write_register(0x3824, pclk_div & 0x1F)
1422          self._write_register(0x460C, 0x22 if pclk_manual else 0x22)
1423          self._write_register(0x3103, 0x13)
1424  
1425      @size.setter
1426      def size(self, size: int) -> None:
1427          self._size = size
1428          self._set_size_and_colorspace()
1429  
1430      @property
1431      def flip_x(self) -> bool:
1432          """Get or set the X-flip flag"""
1433          return self._flip_x
1434  
1435      @flip_x.setter
1436      def flip_x(self, value: bool) -> None:
1437          self._flip_x = bool(value)
1438          self._set_image_options()
1439  
1440      @property
1441      def flip_y(self) -> bool:
1442          """Get or set the Y-flip flag"""
1443          return self._flip_y
1444  
1445      @flip_y.setter
1446      def flip_y(self, value: bool) -> None:
1447          self._flip_y = bool(value)
1448          self._set_image_options()
1449  
1450      @property
1451      def test_pattern(self) -> bool:
1452          """Set to True to enable a test pattern, False to enable normal image capture"""
1453          return self._test_pattern
1454  
1455      @test_pattern.setter
1456      def test_pattern(self, value: bool) -> None:
1457          self._test_pattern = value
1458          self._write_register(_PRE_ISP_TEST_SETTING_1, value << 7)
1459  
1460      @property
1461      def saturation(self) -> int:
1462          """Get or set the saturation value, from -4 to +4."""
1463          return self._saturation
1464  
1465      @saturation.setter
1466      def saturation(self, value: int) -> None:
1467          if not -4 <= value <= 4:
1468              raise ValueError(
1469                  "Invalid saturation {value}, use a value from -4..4 inclusive"
1470              )
1471          for offset, reg_value in enumerate(_sensor_saturation_levels[value]):
1472              self._write_register(0x5381 + offset, reg_value)
1473          self._saturation = value
1474  
1475      @property
1476      def effect(self) -> int:
1477          """Get or set the special effect, one of the ``OV5640_SPECIAL_EFFECT_`` constants"""
1478          return self._effect
1479  
1480      @effect.setter
1481      def effect(self, value: int) -> None:
1482          for reg_addr, reg_value in zip(
1483              (0x5580, 0x5583, 0x5584, 0x5003), _sensor_special_effects[value]
1484          ):
1485              self._write_register(reg_addr, reg_value)
1486          self._effect = value
1487  
1488      @property
1489      def quality(self) -> int:
1490          """Controls the JPEG quality.  Valid range is from 2..55 inclusive"""
1491          return self._read_register(_COMPRESSION_CTRL07) & 0x3F
1492  
1493      @quality.setter
1494      def quality(self, value: int) -> None:
1495          if not 2 <= value < 55:
1496              raise ValueError(
1497                  f"Invalid quality value {value}, use a value from 2..55 inclusive"
1498              )
1499          self._write_register(_COMPRESSION_CTRL07, value & 0x3F)
1500  
1501      def _write_group_3_settings(self, settings):
1502          self._write_register(0x3212, 0x3)  # start group 3
1503          self._write_list(settings)
1504          self._write_register(0x3212, 0x13)  # end group 3
1505          self._write_register(0x3212, 0xA3)  # launch group 3
1506  
1507      @property
1508      def brightness(self) -> int:
1509          """Sensor brightness adjustment, from -4 to 4 inclusive"""
1510          brightness_abs = self._read_register(0x5587) >> 4
1511          brightness_neg = self._read_register(0x5588) & 8
1512          if brightness_neg:
1513              return -brightness_abs
1514          return brightness_abs
1515  
1516      @brightness.setter
1517      def brightness(self, value: int) -> None:
1518          if not -4 <= value <= 4:
1519              raise ValueError(
1520                  "Invalid brightness value {value}, use a value from -4..4 inclusive"
1521              )
1522          self._write_group_3_settings(
1523              [0x5587, abs(value) << 4, 0x5588, 0x9 if value < 0 else 0x1]
1524          )
1525  
1526      @property
1527      def contrast(self) -> int:
1528          """Sensor contrast adjustment, from -4 to 4 inclusive"""
1529          contrast_abs = self._read_register(0x5587) >> 4
1530          contrast_neg = self._read_register(0x5588) & 8
1531          if contrast_neg:
1532              return -contrast_abs
1533          return contrast_abs
1534  
1535      @contrast.setter
1536      def contrast(self, value: int) -> None:
1537          if not -3 <= value <= 3:
1538              raise ValueError(
1539                  "Invalid contrast value {value}, use a value from -3..3 inclusive"
1540              )
1541          setting = _contrast_settings[value]
1542          self._write_group_3_settings([0x5586, setting[0], 0x5585, setting[1]])
1543  
1544      @property
1545      def exposure_value(self) -> int:
1546          """Sensor exposure (EV) adjustment, from -4 to 4 inclusive"""
1547          return self._ev
1548  
1549      @exposure_value.setter
1550      def exposure_value(self, value: int) -> None:
1551          if not -3 <= value <= 3:
1552              raise ValueError(
1553                  "Invalid exposure value (EV) {value}, use a value from -4..4 inclusive"
1554              )
1555          for offset, reg_value in enumerate(_sensor_ev_levels[value]):
1556              self._write_register(0x5381 + offset, reg_value)
1557  
1558      @property
1559      def white_balance(self) -> int:
1560          """The white balance setting, one of the ``OV5640_WHITE_BALANCE_*`` constants"""
1561          return self._white_balance
1562  
1563      @white_balance.setter
1564      def white_balance(self, value: int) -> None:
1565          if not OV5640_WHITE_BALANCE_AUTO <= value <= OV5640_WHITE_BALANCE_INCANDESCENT:
1566              raise ValueError(
1567                  "Invalid exposure value (EV) {value}, "
1568                  "use one of the OV5640_WHITE_BALANCE_* constants"
1569              )
1570          self._write_register(0x3212, 0x3)  # start group 3
1571          for reg_addr, reg_value in zip(_light_registers, _light_modes[value]):
1572              self._write_register(reg_addr, reg_value)
1573          self._write_register(0x3212, 0x13)  # end group 3
1574          self._write_register(0x3212, 0xA3)  # launch group 3
1575  
1576      @property
1577      def night_mode(self) -> bool:
1578          """Enable or disable the night mode setting of the sensor"""
1579          return bool(self._read_register(0x3A00) & 0x04)
1580  
1581      @night_mode.setter
1582      def night_mode(self, value: bool) -> None:
1583          self._write_reg_bits(0x3A00, 0x04, value)