/ adafruit_ov2640.py
adafruit_ov2640.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_ov2640`
   7  ================================================================================
   8  
   9  CircuitPython driver for OV2640 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  * Adafruit's Bus Device library: https:# github.com/adafruit/Adafruit_CircuitPython_BusDevice
  27  """
  28  
  29  # pylint: disable=too-many-lines,unnecessary-lambda-assignment
  30  # imports
  31  
  32  __version__ = "0.0.0+auto.0"
  33  __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_OV7670.git"
  34  
  35  import time
  36  
  37  import digitalio
  38  import imagecapture
  39  import pwmio
  40  from adafruit_bus_device.i2c_device import I2CDevice
  41  
  42  from micropython import const
  43  
  44  CTRLI = const(0x50)
  45  _R_BYPASS = const(0x05)
  46  _QS = const(0x44)
  47  _CTRLI = const(0x50)
  48  _HSIZE = const(0x51)
  49  _VSIZE = const(0x52)
  50  _XOFFL = const(0x53)
  51  _YOFFL = const(0x54)
  52  _VHYX = const(0x55)
  53  _DPRP = const(0x56)
  54  _TEST = const(0x57)
  55  _ZMOW = const(0x5A)
  56  _ZMOH = const(0x5B)
  57  _ZMHH = const(0x5C)
  58  _BPADDR = const(0x7C)
  59  _BPDATA = const(0x7D)
  60  _CTRL2 = const(0x86)
  61  _CTRL3 = const(0x87)
  62  _SIZEL = const(0x8C)
  63  _HSIZE8 = const(0xC0)
  64  _VSIZE8 = const(0xC1)
  65  _CTRL0 = const(0xC2)
  66  _CTRL1 = const(0xC3)
  67  _R_DVP_SP = const(0xD3)
  68  _IMAGE_MODE = const(0xDA)
  69  _RESET = const(0xE0)
  70  _MS_SP = const(0xF0)
  71  _SS_ID = const(0xF7)
  72  _SS_CTRL = const(0xF7)
  73  _MC_BIST = const(0xF9)
  74  _MC_AL = const(0xFA)
  75  _MC_AH = const(0xFB)
  76  _MC_D = const(0xFC)
  77  _P_CMD = const(0xFD)
  78  _P_STATUS = const(0xFE)
  79  _BANK_SEL = const(0xFF)
  80  
  81  _CTRLI_LP_DP = const(0x80)
  82  _CTRLI_ROUND = const(0x40)
  83  
  84  _CTRL0_AEC_EN = const(0x80)
  85  _CTRL0_AEC_SEL = const(0x40)
  86  _CTRL0_STAT_SEL = const(0x20)
  87  _CTRL0_VFIRST = const(0x10)
  88  _CTRL0_YUV422 = const(0x08)
  89  _CTRL0_YUV_EN = const(0x04)
  90  _CTRL0_RGB_EN = const(0x02)
  91  _CTRL0_RAW_EN = const(0x01)
  92  
  93  _CTRL2_DCW_EN = const(0x20)
  94  _CTRL2_SDE_EN = const(0x10)
  95  _CTRL2_UV_ADJ_EN = const(0x08)
  96  _CTRL2_UV_AVG_EN = const(0x04)
  97  _CTRL2_CMX_EN = const(0x01)
  98  
  99  _CTRL3_BPC_EN = const(0x80)
 100  _CTRL3_WPC_EN = const(0x40)
 101  
 102  _R_DVP_SP_AUTO_MODE = const(0x80)
 103  
 104  _R_BYPASS_DSP_EN = const(0x00)
 105  _R_BYPASS_DSP_BYPAS = const(0x01)
 106  
 107  OV2640_COLOR_RGB = 0
 108  OV2640_COLOR_YUV = 1
 109  OV2640_COLOR_JPEG = 2
 110  
 111  _IMAGE_MODE_Y8_DVP_EN = const(0x40)
 112  _IMAGE_MODE_JPEG_EN = const(0x10)
 113  _IMAGE_MODE_YUV422 = const(0x00)
 114  _IMAGE_MODE_RAW10 = const(0x04)
 115  _IMAGE_MODE_RGB565 = const(0x08)
 116  _IMAGE_MODE_HREF_VSYNC = const(0x02)
 117  _IMAGE_MODE_LBYTE_FIRST = const(0x01)
 118  
 119  _RESET_MICROC = const(0x40)
 120  _RESET_SCCB = const(0x20)
 121  _RESET_JPEG = const(0x10)
 122  _RESET_DVP = const(0x04)
 123  _RESET_IPU = const(0x02)
 124  _RESET_CIF = const(0x01)
 125  
 126  _MC_BIST_RESET = const(0x80)
 127  _MC_BIST_BOOT_ROM_SEL = const(0x40)
 128  _MC_BIST_12KB_SEL = const(0x20)
 129  _MC_BIST_12KB_MASK = const(0x30)
 130  _MC_BIST_512KB_SEL = const(0x08)
 131  _MC_BIST_512KB_MASK = const(0x0C)
 132  _MC_BIST_BUSY_BIT_R = const(0x02)
 133  _MC_BIST_MC_RES_ONE_SH_W = const(0x02)
 134  _MC_BIST_LAUNCH = const(0x01)
 135  
 136  
 137  _BANK_DSP = const(0)
 138  _BANK_SENSOR = const(1)
 139  
 140  # Sensor register bank FF=0x01
 141  _GAIN = const(0x00)
 142  _COM1 = const(0x03)
 143  _REG04 = const(0x04)
 144  _REG08 = const(0x08)
 145  _COM2 = const(0x09)
 146  _REG_PID = const(0x0A)
 147  _REG_VER = const(0x0B)
 148  _COM3 = const(0x0C)
 149  _COM4 = const(0x0D)
 150  _AEC = const(0x10)
 151  _CLKRC = const(0x11)
 152  _COM7 = const(0x12)
 153  _COM8 = const(0x13)
 154  _COM9 = const(0x14)  # AGC gain ceiling
 155  _COM10 = const(0x15)
 156  _HSTART = const(0x17)
 157  _HSTOP = const(0x18)
 158  _VSTART = const(0x19)
 159  _VSTOP = const(0x1A)
 160  _MIDH = const(0x1C)
 161  _MIDL = const(0x1D)
 162  _AEW = const(0x24)
 163  _AEB = const(0x25)
 164  _VV = const(0x26)
 165  _REG2A = const(0x2A)
 166  _FRARL = const(0x2B)
 167  _ADDVSL = const(0x2D)
 168  _ADDVSH = const(0x2E)
 169  _YAVG = const(0x2F)
 170  _HSDY = const(0x30)
 171  _HEDY = const(0x31)
 172  _REG32 = const(0x32)
 173  _ARCOM2 = const(0x34)
 174  _REG45 = const(0x45)
 175  _FLL = const(0x46)
 176  _FLH = const(0x47)
 177  _COM19 = const(0x48)
 178  _ZOOMS = const(0x49)
 179  _COM22 = const(0x4B)
 180  _COM25 = const(0x4E)
 181  _BD50 = const(0x4F)
 182  _BD60 = const(0x50)
 183  _REG5D = const(0x5D)
 184  _REG5E = const(0x5E)
 185  _REG5F = const(0x5F)
 186  _REG60 = const(0x60)
 187  _HISTO_LOW = const(0x61)
 188  _HISTO_HIGH = const(0x62)
 189  
 190  _REG04_DEFAULT = const(0x28)
 191  _REG04_HFLIP_IMG = const(0x80)
 192  _REG04_VFLIP_IMG = const(0x40)
 193  _REG04_VREF_EN = const(0x10)
 194  _REG04_HREF_EN = const(0x08)
 195  _REG04_SET = lambda x: (_REG04_DEFAULT | x)
 196  
 197  _COM2_STDBY = const(0x10)
 198  _COM2_OUT_DRIVE_1x = const(0x00)
 199  _COM2_OUT_DRIVE_2x = const(0x01)
 200  _COM2_OUT_DRIVE_3x = const(0x02)
 201  _COM2_OUT_DRIVE_4x = const(0x03)
 202  
 203  _COM3_DEFAULT = const(0x38)
 204  _COM3_BAND_50Hz = const(0x04)
 205  _COM3_BAND_60Hz = const(0x00)
 206  _COM3_BAND_AUTO = const(0x02)
 207  _COM3_BAND_SET = lambda x: (_COM3_DEFAULT | x)
 208  
 209  _COM7_SRST = const(0x80)
 210  _COM7_RES_UXGA = const(0x00)  # UXGA
 211  _COM7_RES_SVGA = const(0x40)  # SVGA
 212  _COM7_RES_CIF = const(0x20)  # CIF
 213  _COM7_ZOOM_EN = const(0x04)  # Enable Zoom
 214  _COM7_COLOR_BAR = const(0x02)  # Enable Color Bar Test
 215  
 216  _COM8_DEFAULT = const(0xC0)
 217  _COM8_BNDF_EN = const(0x20)  # Enable Banding filter
 218  _COM8_AGC_EN = const(0x04)  # AGC Auto/Manual control selection
 219  _COM8_AEC_EN = const(0x01)  # Auto/Manual Exposure control
 220  _COM8_SET = lambda x: (_COM8_DEFAULT | x)
 221  
 222  _COM9_DEFAULT = const(0x08)
 223  _COM9_AGC_GAIN_2x = const(0x00)  # AGC:    2x
 224  _COM9_AGC_GAIN_4x = const(0x01)  # AGC:    4x
 225  _COM9_AGC_GAIN_8x = const(0x02)  # AGC:    8x
 226  _COM9_AGC_GAIN_16x = const(0x03)  # AGC:   16x
 227  _COM9_AGC_GAIN_32x = const(0x04)  # AGC:   32x
 228  _COM9_AGC_GAIN_64x = const(0x05)  # AGC:   64x
 229  _COM9_AGC_GAIN_128x = const(0x06)  # AGC:  128x
 230  _COM9_AGC_SET = lambda x: (_COM9_DEFAULT | (x << 5))
 231  
 232  _COM10_HREF_EN = const(0x80)  # HSYNC changes to HREF
 233  _COM10_HSYNC_EN = const(0x40)  # HREF changes to HSYNC
 234  _COM10_PCLK_FREE = const(0x20)  # PCLK output option: free running PCLK
 235  _COM10_PCLK_EDGE = const(0x10)  # Data is updated at the rising edge of PCLK
 236  _COM10_HREF_NEG = const(0x08)  # HREF negative
 237  _COM10_VSYNC_NEG = const(0x02)  # VSYNC negative
 238  _COM10_HSYNC_NEG = const(0x01)  # HSYNC negative
 239  
 240  _CTRL1_AWB = const(0x08)  # Enable AWB
 241  
 242  _VV_AGC_TH_SET = lambda h, l: ((h << 4) | (l & 0x0F))
 243  
 244  _REG32_UXGA = const(0x36)
 245  _REG32_SVGA = const(0x09)
 246  _REG32_CIF = const(0x89)
 247  
 248  _CLKRC_2X = const(0x80)
 249  _CLKRC_2X_UXGA = const(0x01 | _CLKRC_2X)
 250  _CLKRC_2X_SVGA = _CLKRC_2X
 251  _CLKRC_2X_CIF = _CLKRC_2X
 252  
 253  _OV2640_MODE_CIF = const(0)
 254  _OV2640_MODE_SVGA = const(1)
 255  _OV2640_MODE_UXGA = const(2)
 256  
 257  OV2640_SIZE_96X96 = 0  # 96x96
 258  OV2640_SIZE_QQVGA = 1  # 160x120
 259  OV2640_SIZE_QCIF = 2  # 176x144
 260  OV2640_SIZE_HQVGA = 3  # 240x176
 261  OV2640_SIZE_240X240 = 4  # 240x240
 262  OV2640_SIZE_QVGA = 5  # 320x240
 263  OV2640_SIZE_CIF = 6  # 400x296
 264  OV2640_SIZE_HVGA = 7  # 480x320
 265  OV2640_SIZE_VGA = 8  # 640x480
 266  OV2640_SIZE_SVGA = 9  # 800x600
 267  OV2640_SIZE_XGA = 10  # 1024x768
 268  OV2640_SIZE_HD = 11  # 1280x720
 269  OV2640_SIZE_SXGA = 12  # 1280x1024
 270  OV2640_SIZE_UXGA = 13  # 1600x1200
 271  
 272  _ASPECT_RATIO_4X3 = const(0)
 273  _ASPECT_RATIO_3X2 = const(1)
 274  _ASPECT_RATIO_16X10 = const(2)
 275  _ASPECT_RATIO_5X3 = const(3)
 276  _ASPECT_RATIO_16X9 = const(4)
 277  _ASPECT_RATIO_21X9 = const(5)
 278  _ASPECT_RATIO_5X4 = const(6)
 279  _ASPECT_RATIO_1X1 = const(7)
 280  _ASPECT_RATIO_9X16 = const(8)
 281  
 282  _resolution_info = [
 283      [96, 96, _ASPECT_RATIO_1X1],  # 96x96
 284      [160, 120, _ASPECT_RATIO_4X3],  # QQVGA
 285      [176, 144, _ASPECT_RATIO_5X4],  # QCIF
 286      [240, 176, _ASPECT_RATIO_4X3],  # HQVGA
 287      [240, 240, _ASPECT_RATIO_1X1],  # 240x240
 288      [320, 240, _ASPECT_RATIO_4X3],  # QVGA
 289      [400, 296, _ASPECT_RATIO_4X3],  # CIF
 290      [480, 320, _ASPECT_RATIO_3X2],  # HVGA
 291      [640, 480, _ASPECT_RATIO_4X3],  # VGA
 292      [800, 600, _ASPECT_RATIO_4X3],  # SVGA
 293      [1024, 768, _ASPECT_RATIO_4X3],  # XGA
 294      [1280, 720, _ASPECT_RATIO_16X9],  # HD
 295      [1280, 1024, _ASPECT_RATIO_5X4],  # SXGA
 296      [1600, 1200, _ASPECT_RATIO_4X3],  # UXGA
 297  ]
 298  
 299  _ratio_table = [
 300      # ox,  oy,   mx,   my
 301      [0, 0, 1600, 1200],  # 4x3
 302      [8, 72, 1584, 1056],  # 3x2
 303      [0, 100, 1600, 1000],  # 16x10
 304      [0, 120, 1600, 960],  # 5x3
 305      [0, 150, 1600, 900],  # 16x9
 306      [2, 258, 1596, 684],  # 21x9
 307      [50, 0, 1500, 1200],  # 5x4
 308      [200, 0, 1200, 1200],  # 1x1
 309      [462, 0, 676, 1200],  # 9x16
 310  ]
 311  
 312  # 30fps@24MHz
 313  _ov2640_settings_cif = bytes(
 314      [
 315          _BANK_SEL,
 316          _BANK_DSP,
 317          0x2C,
 318          0xFF,
 319          0x2E,
 320          0xDF,
 321          _BANK_SEL,
 322          _BANK_SENSOR,
 323          0x3C,
 324          0x32,
 325          _CLKRC,
 326          0x01,
 327          _COM2,
 328          _COM2_OUT_DRIVE_3x,
 329          _REG04,
 330          _REG04_DEFAULT,
 331          _COM8,
 332          _COM8_DEFAULT | _COM8_BNDF_EN | _COM8_AGC_EN | _COM8_AEC_EN,
 333          _COM9,
 334          _COM9_AGC_SET(_COM9_AGC_GAIN_8x),
 335          0x2C,
 336          0x0C,
 337          0x33,
 338          0x78,
 339          0x3A,
 340          0x33,
 341          0x3B,
 342          0xFB,
 343          0x3E,
 344          0x00,
 345          0x43,
 346          0x11,
 347          0x16,
 348          0x10,
 349          0x39,
 350          0x92,
 351          0x35,
 352          0xDA,
 353          0x22,
 354          0x1A,
 355          0x37,
 356          0xC3,
 357          0x23,
 358          0x00,
 359          _ARCOM2,
 360          0xC0,
 361          0x06,
 362          0x88,
 363          0x07,
 364          0xC0,
 365          _COM4,
 366          0x87,
 367          0x0E,
 368          0x41,
 369          0x4C,
 370          0x00,
 371          0x4A,
 372          0x81,
 373          0x21,
 374          0x99,
 375          _AEW,
 376          0x40,
 377          _AEB,
 378          0x38,
 379          _VV,
 380          _VV_AGC_TH_SET(8, 2),
 381          0x5C,
 382          0x00,
 383          0x63,
 384          0x00,
 385          _HISTO_LOW,
 386          0x70,
 387          _HISTO_HIGH,
 388          0x80,
 389          0x7C,
 390          0x05,
 391          0x20,
 392          0x80,
 393          0x28,
 394          0x30,
 395          0x6C,
 396          0x00,
 397          0x6D,
 398          0x80,
 399          0x6E,
 400          0x00,
 401          0x70,
 402          0x02,
 403          0x71,
 404          0x94,
 405          0x73,
 406          0xC1,
 407          0x3D,
 408          0x34,
 409          0x5A,
 410          0x57,
 411          _BD50,
 412          0xBB,
 413          _BD60,
 414          0x9C,
 415          _COM7,
 416          _COM7_RES_CIF,
 417          _HSTART,
 418          0x11,
 419          _HSTOP,
 420          0x43,
 421          _VSTART,
 422          0x00,
 423          _VSTOP,
 424          0x25,
 425          _REG32,
 426          0x89,
 427          0x37,
 428          0xC0,
 429          _BD50,
 430          0xCA,
 431          _BD60,
 432          0xA8,
 433          0x6D,
 434          0x00,
 435          0x3D,
 436          0x38,
 437          _BANK_SEL,
 438          _BANK_DSP,
 439          0xE5,
 440          0x7F,
 441          _MC_BIST,
 442          _MC_BIST_RESET | _MC_BIST_BOOT_ROM_SEL,
 443          0x41,
 444          0x24,
 445          _RESET,
 446          _RESET_JPEG | _RESET_DVP,
 447          0x76,
 448          0xFF,
 449          0x33,
 450          0xA0,
 451          0x42,
 452          0x20,
 453          0x43,
 454          0x18,
 455          0x4C,
 456          0x00,
 457          _CTRL3,
 458          _CTRL3_WPC_EN | 0x10,
 459          0x88,
 460          0x3F,
 461          0xD7,
 462          0x03,
 463          0xD9,
 464          0x10,
 465          _R_DVP_SP,
 466          _R_DVP_SP_AUTO_MODE | 0x02,
 467          0xC8,
 468          0x08,
 469          0xC9,
 470          0x80,
 471          _BPADDR,
 472          0x00,
 473          _BPDATA,
 474          0x00,
 475          _BPADDR,
 476          0x03,
 477          _BPDATA,
 478          0x48,
 479          _BPDATA,
 480          0x48,
 481          _BPADDR,
 482          0x08,
 483          _BPDATA,
 484          0x20,
 485          _BPDATA,
 486          0x10,
 487          _BPDATA,
 488          0x0E,
 489          0x90,
 490          0x00,
 491          0x91,
 492          0x0E,
 493          0x91,
 494          0x1A,
 495          0x91,
 496          0x31,
 497          0x91,
 498          0x5A,
 499          0x91,
 500          0x69,
 501          0x91,
 502          0x75,
 503          0x91,
 504          0x7E,
 505          0x91,
 506          0x88,
 507          0x91,
 508          0x8F,
 509          0x91,
 510          0x96,
 511          0x91,
 512          0xA3,
 513          0x91,
 514          0xAF,
 515          0x91,
 516          0xC4,
 517          0x91,
 518          0xD7,
 519          0x91,
 520          0xE8,
 521          0x91,
 522          0x20,
 523          0x92,
 524          0x00,
 525          0x93,
 526          0x06,
 527          0x93,
 528          0xE3,
 529          0x93,
 530          0x05,
 531          0x93,
 532          0x05,
 533          0x93,
 534          0x00,
 535          0x93,
 536          0x04,
 537          0x93,
 538          0x00,
 539          0x93,
 540          0x00,
 541          0x93,
 542          0x00,
 543          0x93,
 544          0x00,
 545          0x93,
 546          0x00,
 547          0x93,
 548          0x00,
 549          0x93,
 550          0x00,
 551          0x96,
 552          0x00,
 553          0x97,
 554          0x08,
 555          0x97,
 556          0x19,
 557          0x97,
 558          0x02,
 559          0x97,
 560          0x0C,
 561          0x97,
 562          0x24,
 563          0x97,
 564          0x30,
 565          0x97,
 566          0x28,
 567          0x97,
 568          0x26,
 569          0x97,
 570          0x02,
 571          0x97,
 572          0x98,
 573          0x97,
 574          0x80,
 575          0x97,
 576          0x00,
 577          0x97,
 578          0x00,
 579          0xA4,
 580          0x00,
 581          0xA8,
 582          0x00,
 583          0xC5,
 584          0x11,
 585          0xC6,
 586          0x51,
 587          0xBF,
 588          0x80,
 589          0xC7,
 590          0x10,
 591          0xB6,
 592          0x66,
 593          0xB8,
 594          0xA5,
 595          0xB7,
 596          0x64,
 597          0xB9,
 598          0x7C,
 599          0xB3,
 600          0xAF,
 601          0xB4,
 602          0x97,
 603          0xB5,
 604          0xFF,
 605          0xB0,
 606          0xC5,
 607          0xB1,
 608          0x94,
 609          0xB2,
 610          0x0F,
 611          0xC4,
 612          0x5C,
 613          _CTRL1,
 614          0xFD,
 615          0x7F,
 616          0x00,
 617          0xE5,
 618          0x1F,
 619          0xE1,
 620          0x67,
 621          0xDD,
 622          0x7F,
 623          _IMAGE_MODE,
 624          0x00,
 625          _RESET,
 626          0x00,
 627          _R_BYPASS,
 628          _R_BYPASS_DSP_EN,
 629      ]
 630  )
 631  
 632  _ov2640_settings_to_cif = bytes(
 633      [
 634          _BANK_SEL,
 635          _BANK_SENSOR,
 636          _COM7,
 637          _COM7_RES_CIF,
 638          # Set the sensor output window
 639          _COM1,
 640          0x0A,
 641          _REG32,
 642          _REG32_CIF,
 643          _HSTART,
 644          0x11,
 645          _HSTOP,
 646          0x43,
 647          _VSTART,
 648          0x00,
 649          _VSTOP,
 650          0x25,
 651          # _CLKRC, 0x00,
 652          _BD50,
 653          0xCA,
 654          _BD60,
 655          0xA8,
 656          0x5A,
 657          0x23,
 658          0x6D,
 659          0x00,
 660          0x3D,
 661          0x38,
 662          0x39,
 663          0x92,
 664          0x35,
 665          0xDA,
 666          0x22,
 667          0x1A,
 668          0x37,
 669          0xC3,
 670          0x23,
 671          0x00,
 672          _ARCOM2,
 673          0xC0,
 674          0x06,
 675          0x88,
 676          0x07,
 677          0xC0,
 678          _COM4,
 679          0x87,
 680          0x0E,
 681          0x41,
 682          0x4C,
 683          0x00,
 684          _BANK_SEL,
 685          _BANK_DSP,
 686          _RESET,
 687          _RESET_DVP,
 688          # Set the sensor resolution (UXGA, SVGA, CIF)
 689          _HSIZE8,
 690          0x32,
 691          _VSIZE8,
 692          0x25,
 693          _SIZEL,
 694          0x00,
 695          # Set the image window size >= output size
 696          _HSIZE,
 697          0x64,
 698          _VSIZE,
 699          0x4A,
 700          _XOFFL,
 701          0x00,
 702          _YOFFL,
 703          0x00,
 704          _VHYX,
 705          0x00,
 706          _TEST,
 707          0x00,
 708          _CTRL2,
 709          _CTRL2_DCW_EN | 0x1D,
 710          _CTRLI,
 711          _CTRLI_LP_DP | 0x00,
 712          # _R_DVP_SP, 0x08,
 713      ]
 714  )
 715  
 716  _ov2640_settings_to_svga = bytes(
 717      [
 718          _BANK_SEL,
 719          _BANK_SENSOR,
 720          _COM7,
 721          _COM7_RES_SVGA,
 722          # Set the sensor output window
 723          _COM1,
 724          0x0A,
 725          _REG32,
 726          _REG32_SVGA,
 727          _HSTART,
 728          0x11,
 729          _HSTOP,
 730          0x43,
 731          _VSTART,
 732          0x00,
 733          _VSTOP,
 734          0x4B,
 735          # _CLKRC, 0x00,
 736          0x37,
 737          0xC0,
 738          _BD50,
 739          0xCA,
 740          _BD60,
 741          0xA8,
 742          0x5A,
 743          0x23,
 744          0x6D,
 745          0x00,
 746          0x3D,
 747          0x38,
 748          0x39,
 749          0x92,
 750          0x35,
 751          0xDA,
 752          0x22,
 753          0x1A,
 754          0x37,
 755          0xC3,
 756          0x23,
 757          0x00,
 758          _ARCOM2,
 759          0xC0,
 760          0x06,
 761          0x88,
 762          0x07,
 763          0xC0,
 764          _COM4,
 765          0x87,
 766          0x0E,
 767          0x41,
 768          0x42,
 769          0x03,
 770          0x4C,
 771          0x00,
 772          _BANK_SEL,
 773          _BANK_DSP,
 774          _RESET,
 775          _RESET_DVP,
 776          # Set the sensor resolution (UXGA, SVGA, CIF)
 777          _HSIZE8,
 778          0x64,
 779          _VSIZE8,
 780          0x4B,
 781          _SIZEL,
 782          0x00,
 783          # Set the image window size >= output size
 784          _HSIZE,
 785          0xC8,
 786          _VSIZE,
 787          0x96,
 788          _XOFFL,
 789          0x00,
 790          _YOFFL,
 791          0x00,
 792          _VHYX,
 793          0x00,
 794          _TEST,
 795          0x00,
 796          _CTRL2,
 797          _CTRL2_DCW_EN | 0x1D,
 798          _CTRLI,
 799          _CTRLI_LP_DP | 0x00,
 800          # _R_DVP_SP, 0x08,
 801      ]
 802  )
 803  
 804  _ov2640_settings_to_uxga = bytes(
 805      [
 806          _BANK_SEL,
 807          _BANK_SENSOR,
 808          _COM7,
 809          _COM7_RES_UXGA,
 810          # Set the sensor output window
 811          _COM1,
 812          0x0F,
 813          _REG32,
 814          _REG32_UXGA,
 815          _HSTART,
 816          0x11,
 817          _HSTOP,
 818          0x75,
 819          _VSTART,
 820          0x01,
 821          _VSTOP,
 822          0x97,
 823          # _CLKRC, 0x00,
 824          0x3D,
 825          0x34,
 826          _BD50,
 827          0xBB,
 828          _BD60,
 829          0x9C,
 830          0x5A,
 831          0x57,
 832          0x6D,
 833          0x80,
 834          0x39,
 835          0x82,
 836          0x23,
 837          0x00,
 838          0x07,
 839          0xC0,
 840          0x4C,
 841          0x00,
 842          0x35,
 843          0x88,
 844          0x22,
 845          0x0A,
 846          0x37,
 847          0x40,
 848          _ARCOM2,
 849          0xA0,
 850          0x06,
 851          0x02,
 852          _COM4,
 853          0xB7,
 854          0x0E,
 855          0x01,
 856          0x42,
 857          0x83,
 858          _BANK_SEL,
 859          _BANK_DSP,
 860          _RESET,
 861          _RESET_DVP,
 862          # Set the sensor resolution (UXGA, SVGA, CIF)
 863          _HSIZE8,
 864          0xC8,
 865          _VSIZE8,
 866          0x96,
 867          _SIZEL,
 868          0x00,
 869          # Set the image window size >= output size
 870          _HSIZE,
 871          0x90,
 872          _VSIZE,
 873          0x2C,
 874          _XOFFL,
 875          0x00,
 876          _YOFFL,
 877          0x00,
 878          _VHYX,
 879          0x88,
 880          _TEST,
 881          0x00,
 882          _CTRL2,
 883          _CTRL2_DCW_EN | 0x1D,
 884          _CTRLI,
 885          0x00,
 886          # _R_DVP_SP, 0x06,
 887      ]
 888  )
 889  
 890  _ov2640_color_settings = {
 891      OV2640_COLOR_JPEG: bytes(
 892          [
 893              _BANK_SEL,
 894              _BANK_DSP,
 895              _RESET,
 896              _RESET_JPEG | _RESET_DVP,
 897              _IMAGE_MODE,
 898              _IMAGE_MODE_JPEG_EN | _IMAGE_MODE_HREF_VSYNC,
 899              0xD7,
 900              0x03,
 901              0xE1,
 902              0x77,
 903              0xE5,
 904              0x1F,
 905              0xD9,
 906              0x10,
 907              0xDF,
 908              0x80,
 909              0x33,
 910              0x80,
 911              0x3C,
 912              0x10,
 913              0xEB,
 914              0x30,
 915              0xDD,
 916              0x7F,
 917              _RESET,
 918              0x00,
 919          ]
 920      ),
 921      OV2640_COLOR_YUV: bytes(
 922          [
 923              _BANK_SEL,
 924              _BANK_DSP,
 925              _RESET,
 926              _RESET_DVP,
 927              _IMAGE_MODE,
 928              _IMAGE_MODE_YUV422,
 929              0xD7,
 930              0x01,
 931              0xE1,
 932              0x67,
 933              _RESET,
 934              0x00,
 935          ]
 936      ),
 937      OV2640_COLOR_RGB: bytes(
 938          [
 939              _BANK_SEL,
 940              _BANK_DSP,
 941              _RESET,
 942              _RESET_DVP,
 943              _IMAGE_MODE,
 944              _IMAGE_MODE_RGB565,
 945              0xD7,
 946              0x03,
 947              0xE1,
 948              0x77,
 949              _RESET,
 950              0x00,
 951          ]
 952      ),
 953  }
 954  
 955  
 956  class _RegBits:
 957      def __init__(self, bank, reg, shift, mask):
 958          self.bank = bank
 959          self.reg = reg
 960          self.shift = shift
 961          self.mask = mask
 962  
 963      def __get__(self, obj, objtype=None):
 964          reg_value = obj._read_bank_register(self.bank, self.reg)
 965          return (reg_value >> self.shift) & self.mask
 966  
 967      def __set__(self, obj, value):
 968          if value & ~self.mask:
 969              raise ValueError(
 970                  f"Value 0x{value:02x} does not fit in mask 0x{self.mask:02x}"
 971              )
 972          reg_value = obj._read_bank_register(self.bank, self.reg)
 973          reg_value &= ~(self.mask << self.shift)
 974          reg_value |= value << self.shift
 975          obj._write_register(self.reg, reg_value)
 976  
 977  
 978  class _SCCBCameraBase:  # pylint: disable=too-few-public-methods
 979      def __init__(self, i2c_bus, i2c_address):
 980          self._i2c_device = I2CDevice(i2c_bus, i2c_address)
 981          self._bank = None
 982  
 983      def _get_reg_bits(self, bank, reg, shift, mask):
 984          return (self._read_bank_register(bank, reg) >> shift) & mask
 985  
 986      def _set_reg_bits(
 987          self, bank, reg, shift, mask, value
 988      ):  #  pylint: disable=too-many-arguments
 989          reg_value = self._read_bank_register(bank, reg)
 990          reg_value &= ~(mask << shift)
 991          reg_value |= value << shift
 992          self._write_register(reg, reg_value)
 993  
 994      def _write_list(self, reg_list):
 995          for i in range(0, len(reg_list), 2):
 996              self._write_register(reg_list[i], reg_list[i + 1])
 997              time.sleep(0.001)
 998  
 999      def _write_bank_register(self, bank, reg, value):
1000          if self._bank != bank:
1001              self._write_register(_BANK_SEL, bank)
1002          self._write_register(reg, value)
1003  
1004      def _read_bank_register(self, bank, reg):
1005          if self._bank != bank:
1006              self._write_register(_BANK_SEL, bank)
1007          result = self._read_register(reg)
1008          return result
1009  
1010      def _write_register(self, reg, value):
1011          if reg == _BANK_SEL:
1012              if self._bank == value:
1013                  return
1014              self._bank = value
1015          # print(f"write_register {reg:02x} {value:02x}")
1016          b = bytearray(2)
1017          b[0] = reg
1018          b[1] = value
1019          with self._i2c_device as i2c:
1020              i2c.write(b)
1021  
1022      def _read_register(self, reg):
1023          b = bytearray(1)
1024          b[0] = reg
1025          with self._i2c_device as i2c:
1026              i2c.write(b)
1027              i2c.readinto(b)
1028          return b[0]
1029  
1030  
1031  class OV2640(_SCCBCameraBase):  # pylint: disable=too-many-instance-attributes
1032      """Library for the OV2640 digital camera"""
1033  
1034      test_pattern = _RegBits(_BANK_SENSOR, _COM7, 1, 1)
1035      gain_ceiling = _RegBits(_BANK_SENSOR, _COM9, 5, 7)
1036      bpc = _RegBits(_BANK_DSP, _CTRL3, 7, 1)
1037      wpc = _RegBits(_BANK_DSP, _CTRL3, 6, 1)
1038      lenc = _RegBits(_BANK_DSP, _CTRL1, 1, 1)
1039  
1040      def __init__(
1041          self,
1042          i2c_bus,
1043          data_pins,
1044          clock,
1045          vsync,
1046          href,
1047          shutdown=None,
1048          reset=None,
1049          mclk=None,
1050          mclk_frequency=20_000_000,
1051          i2c_address=0x30,
1052          size=OV2640_SIZE_QQVGA,
1053      ):  # pylint: disable=too-many-arguments
1054          """
1055          Args:
1056              i2c_bus (busio.I2C): The I2C bus used to configure the OV2640
1057              data_pins (List[microcontroller.Pin]): A list of 8 data pins, in order.
1058              clock (microcontroller.Pin): The pixel clock from the OV2640.
1059              vsync (microcontroller.Pin): The vsync signal from the OV2640.
1060              href (microcontroller.Pin): The href signal from the OV2640, \
1061                  sometimes inaccurately called hsync.
1062              shutdown (Optional[microcontroller.Pin]): If not None, the shutdown
1063                  signal to the camera, also called the powerdown or enable pin.
1064              reset (Optional[microcontroller.Pin]): If not None, the reset signal
1065                  to the camera.
1066              mclk (Optional[microcontroller.Pin]): The pin on which to create a
1067                  master clock signal, or None if the master clock signal is
1068                  already being generated.
1069              mclk_frequency (int): The frequency of the master clock to generate, \
1070                  ignored if mclk is None, requred if it is specified
1071              i2c_address (int): The I2C address of the camera.
1072          """
1073  
1074          # Initialize the master clock
1075          if mclk:
1076              self._mclk_pwm = pwmio.PWMOut(mclk, frequency=mclk_frequency)
1077              self._mclk_pwm.duty_cycle = 32768
1078          else:
1079              self._mclk_pwm = None
1080  
1081          if shutdown:
1082              self._shutdown = digitalio.DigitalInOut(shutdown)
1083              self._shutdown.switch_to_output(True)
1084              time.sleep(0.1)
1085              self._shutdown.switch_to_output(False)
1086              time.sleep(0.3)
1087          else:
1088              self._shutdown = None
1089  
1090          if reset:
1091              self._reset = digitalio.DigitalInOut(reset)
1092              self._reset.switch_to_output(False)
1093              time.sleep(0.1)
1094              self._reset.switch_to_output(True)
1095              time.sleep(0.1)
1096  
1097          super().__init__(i2c_bus, i2c_address)
1098  
1099          self._write_bank_register(_BANK_SENSOR, _COM7, _COM7_SRST)
1100          time.sleep(0.001)
1101  
1102          self._write_list(_ov2640_settings_cif)
1103  
1104          self._colorspace = OV2640_COLOR_RGB
1105          self._w = None
1106          self._h = None
1107          self._size = None
1108          self._test_pattern = False
1109          self.size = size
1110  
1111          self._flip_x = False
1112          self._flip_y = False
1113  
1114          self.gain_ceiling = _COM9_AGC_GAIN_2x
1115          self.bpc = False
1116          self.wpc = True
1117          self.lenc = True
1118  
1119          # self._sensor_init()
1120  
1121          self._imagecapture = imagecapture.ParallelImageCapture(
1122              data_pins=data_pins, clock=clock, vsync=vsync, href=href
1123          )
1124  
1125      def capture(self, buf):
1126          """Capture an image into the buffer.
1127  
1128          Args:
1129              buf (Union[bytearray, memoryview]): A WritableBuffer to contain the \
1130                  captured image.  Note that this can be a ulab array or a displayio Bitmap.
1131          """
1132          self._imagecapture.capture(buf)
1133          if self.colorspace == OV2640_COLOR_JPEG:
1134              eoi = buf.find(b"\xff\xd9")
1135              if eoi != -1:
1136                  # terminate the JPEG data just after the EOI marker
1137                  return memoryview(buf)[: eoi + 2]
1138          return None
1139  
1140      @property
1141      def capture_buffer_size(self):
1142          """Return the size of capture buffer to use with current resolution & colorspace settings"""
1143          if self.colorspace == OV2640_COLOR_JPEG:
1144              return self.width * self.height // 5
1145          return self.width * self.height * 2
1146  
1147      @property
1148      def mclk_frequency(self):
1149          """Get the actual frequency the generated mclk, or None"""
1150          return self._mclk_pwm.frequency if self._mclk_pwm else None
1151  
1152      @property
1153      def width(self):
1154          """Get the image width in pixels.  A buffer of 2*width*height bytes \
1155          stores a whole image."""
1156          return self._w
1157  
1158      @property
1159      def height(self):
1160          """Get the image height in pixels.  A buffer of 2*width*height bytes \
1161          stores a whole image."""
1162          return self._h
1163  
1164      @property
1165      def colorspace(self):
1166          """Get or set the colorspace, one of the ``OV2640_COLOR_`` constants."""
1167          return self._colorspace
1168  
1169      @colorspace.setter
1170      def colorspace(self, colorspace):
1171          self._colorspace = colorspace
1172          self._set_size_and_colorspace()
1173  
1174      def _set_colorspace(self):
1175          colorspace = self._colorspace
1176          settings = _ov2640_color_settings[colorspace]
1177  
1178          self._write_list(settings)
1179          # written twice?
1180          self._write_list(settings)
1181          time.sleep(0.01)
1182  
1183      def deinit(self):
1184          """Deinitialize the camera"""
1185          self._imagecapture.deinit()
1186          if self._mclk_pwm:
1187              self._mclk_pwm.deinit()
1188          if self._shutdown:
1189              self._shutdown.deinit()
1190          if self._reset:
1191              self._reset.deinit()
1192  
1193      @property
1194      def size(self):
1195          """Get or set the captured image size, one of the ``OV2640_SIZE_`` constants."""
1196          return self._size
1197  
1198      def _set_size_and_colorspace(self):
1199          size = self._size
1200          width, height, ratio = _resolution_info[size]
1201          offset_x, offset_y, max_x, max_y = _ratio_table[ratio]
1202          mode = _OV2640_MODE_UXGA
1203          if size <= OV2640_SIZE_CIF:
1204              mode = _OV2640_MODE_CIF
1205              max_x //= 4
1206              max_y //= 4
1207              offset_x //= 4
1208              offset_y //= 4
1209              max_y = min(max_y, 296)
1210  
1211          elif size <= OV2640_SIZE_SVGA:
1212              mode = _OV2640_MODE_SVGA
1213              max_x //= 2
1214              max_y //= 2
1215              offset_x //= 2
1216              offset_y //= 2
1217  
1218          self._set_window(mode, offset_x, offset_y, max_x, max_y, width, height)
1219  
1220      @size.setter
1221      def size(self, size):
1222          self._size = size
1223          self._set_size_and_colorspace()
1224  
1225      def _set_flip(self):
1226          bits = 0
1227          if self._flip_x:
1228              bits |= _REG04_HFLIP_IMG
1229          if self._flip_y:
1230              bits |= _REG04_VFLIP_IMG | _REG04_VREF_EN
1231          self._write_bank_register(_BANK_SENSOR, _REG04, _REG04_SET(bits))
1232  
1233      @property
1234      def flip_x(self):
1235          """Get or set the X-flip flag"""
1236          return self._flip_x
1237  
1238      @flip_x.setter
1239      def flip_x(self, value):
1240          self._flip_x = bool(value)
1241          self._set_flip()
1242  
1243      @property
1244      def flip_y(self):
1245          """Get or set the Y-flip flag"""
1246          return self._flip_y
1247  
1248      @flip_y.setter
1249      def flip_y(self, value):
1250          self._flip_y = bool(value)
1251          self._set_flip()
1252  
1253      @property
1254      def product_id(self):
1255          """Get the product id (PID) register.  The expected value is 0x26."""
1256          return self._read_bank_register(_BANK_SENSOR, _REG_PID)
1257  
1258      @property
1259      def product_version(self):
1260          """Get the version (VER) register.  The expected value is 0x4x."""
1261          return self._read_bank_register(_BANK_SENSOR, _REG_VER)
1262  
1263      def _set_window(
1264          self, mode, offset_x, offset_y, max_x, max_y, width, height
1265      ):  # pylint: disable=too-many-arguments, too-many-locals
1266          self._w = width
1267          self._h = height
1268  
1269          max_x //= 4
1270          max_y //= 4
1271          width //= 4
1272          height //= 4
1273  
1274          win_regs = [
1275              _BANK_SEL,
1276              _BANK_DSP,
1277              _HSIZE,
1278              max_x & 0xFF,
1279              _VSIZE,
1280              max_y & 0xFF,
1281              _XOFFL,
1282              offset_x & 0xFF,
1283              _YOFFL,
1284              offset_y & 0xFF,
1285              _VHYX,
1286              ((max_y >> 1) & 0x80)
1287              | ((offset_y >> 4) & 0x70)
1288              | ((max_x >> 5) & 0x08)
1289              | ((offset_y >> 8) & 0x07),
1290              _TEST,
1291              (max_x >> 2) & 0x80,
1292              _ZMOW,
1293              (width) & 0xFF,
1294              _ZMOH,
1295              (height) & 0xFF,
1296              _ZMHH,
1297              ((height >> 6) & 0x04) | ((width >> 8) & 0x03),
1298          ]
1299  
1300          pclk_auto = 0
1301          pclk_div = 8
1302          clk_2x = 0
1303          clk_div = 0
1304  
1305          if self._colorspace != OV2640_COLOR_JPEG:
1306              pclk_auto = 1
1307              clk_div = 7
1308  
1309          if mode == _OV2640_MODE_CIF:
1310              regs = _ov2640_settings_to_cif
1311              if self._colorspace != OV2640_COLOR_JPEG:
1312                  clk_div = 3
1313          elif mode == _OV2640_MODE_SVGA:
1314              regs = _ov2640_settings_to_svga
1315          else:
1316              regs = _ov2640_settings_to_uxga
1317              pclk_div = 12
1318  
1319          clk = clk_div | (clk_2x << 7)
1320          pclk = pclk_div | (pclk_auto << 7)
1321  
1322          self._write_bank_register(_BANK_DSP, _R_BYPASS, _R_BYPASS_DSP_BYPAS)
1323          self._write_list(regs)
1324          self._write_list(win_regs)
1325          self._write_bank_register(_BANK_SENSOR, _CLKRC, clk)
1326          self._write_bank_register(_BANK_DSP, _R_DVP_SP, pclk)
1327          self._write_register(_R_BYPASS, _R_BYPASS_DSP_EN)
1328          time.sleep(0.01)
1329  
1330          # Reestablish colorspace
1331          self._set_colorspace()
1332  
1333          # Reestablish test pattern
1334          if self._test_pattern:
1335              self.test_pattern = self._test_pattern
1336  
1337      @property
1338      def exposure(self):
1339          """The exposure level of the sensor"""
1340          aec_9_2 = self._get_reg_bits(_BANK_SENSOR, _AEC, 0, 0xFF)
1341          aec_15_10 = self._get_reg_bits(_BANK_SENSOR, _REG45, 0, 0b111111)
1342          aec_1_0 = self._get_reg_bits(_BANK_SENSOR, _REG04, 0, 0b11)
1343  
1344          return aec_1_0 | (aec_9_2 << 2) | (aec_15_10 << 10)
1345  
1346      @exposure.setter
1347      def exposure(self, exposure):
1348          aec_1_0 = exposure & 0x11
1349          aec_9_2 = (exposure >> 2) & 0b11111111
1350          aec_15_10 = exposure >> 10
1351  
1352          self._set_reg_bits(_BANK_SENSOR, _AEC, 0, 0xFF, aec_9_2)
1353          self._set_reg_bits(_BANK_SENSOR, _REG45, 0, 0b111111, aec_15_10)
1354          self._set_reg_bits(_BANK_SENSOR, _REG04, 0, 0b11, aec_1_0)