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