/ 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)