/ adafruit_ov7670.py
adafruit_ov7670.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_ov7670`
  7  ================================================================================
  8  
  9  CircuitPython driver for OV7670 cameras
 10  
 11  
 12  * Author(s): Jeff Epler
 13  
 14  Implementation Notes
 15  --------------------
 16  
 17  **Hardware:**
 18  
 19  **Software and Dependencies:**
 20  
 21  * Adafruit CircuitPython firmware for the supported boards:
 22    https://github.com/adafruit/circuitpython/releases
 23  * The CircuitPython build for your board must support the ``imagecapture`` module.
 24  * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
 25  """
 26  
 27  # imports
 28  
 29  __version__ = "0.0.0-auto.0"
 30  __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ov7670.git"
 31  
 32  import time
 33  
 34  import digitalio
 35  import imagecapture
 36  import pwmio
 37  from adafruit_bus_device.i2c_device import I2CDevice
 38  
 39  from micropython import const
 40  
 41  # Supported color formats
 42  OV7670_COLOR_RGB = 0  # RGB565 big-endian
 43  OV7670_COLOR_YUV = 1  # YUV/YCbCr 4:2:2 big-endian
 44  
 45  # Supported sizes (VGA division factor) for OV7670_set_size()
 46  OV7670_SIZE_DIV1 = 0  # 640 x 480
 47  OV7670_SIZE_DIV2 = 1  # 320 x 240
 48  OV7670_SIZE_DIV4 = 2  # 160 x 120
 49  OV7670_SIZE_DIV8 = 3  # 80 x 60
 50  OV7670_SIZE_DIV16 = 4  # 40 x 30
 51  
 52  # Test patterns
 53  OV7670_TEST_PATTERN_NONE = 0  # Disable test pattern
 54  OV7670_TEST_PATTERN_SHIFTING_1 = 1  # "Shifting 1" pattern
 55  OV7670_TEST_PATTERN_COLOR_BAR = 2  # 8 color bars
 56  OV7670_TEST_PATTERN_COLOR_BAR_FADE = 3  # Color bars w/fade to white
 57  
 58  # Table of bit patterns for the different supported night modes.
 59  # There's a "same frame rate" option for OV7670 night mode but it
 60  # doesn't seem to do anything useful and can be skipped over.
 61  OV7670_NIGHT_MODE_OFF = 0  # Disable night mode
 62  OV7670_NIGHT_MODE_2 = 0b10100000  # Night mode 1/2 frame rate
 63  OV7670_NIGHT_MODE_4 = 0b11000000  # Night mode 1/4 frame rate
 64  OV7670_NIGHT_MODE_8 = 0b11100000  # Night mode 1/8 frame rate
 65  
 66  OV7670_ADDR = 0x21  # Default I2C address if unspecified
 67  
 68  _OV7670_REG_GAIN = const(0x00)  # AGC gain bits 7:0 (9:8 in VREF)
 69  _OV7670_REG_BLUE = const(0x01)  # AWB blue channel gain
 70  _OV7670_REG_RED = const(0x02)  # AWB red channel gain
 71  _OV7670_REG_VREF = const(0x03)  # Vert frame control bits
 72  _OV7670_REG_COM1 = const(0x04)  # Common control 1
 73  _OV7670_COM1_R656 = const(0x40)  # COM1 enable R656 format
 74  _OV7670_REG_BAVE = const(0x05)  # U/B average level
 75  _OV7670_REG_GbAVE = const(0x06)  # Y/Gb average level
 76  _OV7670_REG_AECHH = const(0x07)  # Exposure value - AEC 15:10 bits
 77  _OV7670_REG_RAVE = const(0x08)  # V/R average level
 78  _OV7670_REG_COM2 = const(0x09)  # Common control 2
 79  _OV7670_COM2_SSLEEP = const(0x10)  # COM2 soft sleep mode
 80  _OV7670_REG_PID = const(0x0A)  # Product ID MSB (read-only)
 81  _OV7670_REG_VER = const(0x0B)  # Product ID LSB (read-only)
 82  _OV7670_REG_COM3 = const(0x0C)  # Common control 3
 83  _OV7670_COM3_SWAP = const(0x40)  # COM3 output data MSB/LSB swap
 84  _OV7670_COM3_SCALEEN = const(0x08)  # COM3 scale enable
 85  _OV7670_COM3_DCWEN = const(0x04)  # COM3 DCW enable
 86  _OV7670_REG_COM4 = const(0x0D)  # Common control 4
 87  _OV7670_REG_COM5 = const(0x0E)  # Common control 5
 88  _OV7670_REG_COM6 = const(0x0F)  # Common control 6
 89  _OV7670_REG_AECH = const(0x10)  # Exposure value 9:2
 90  _OV7670_REG_CLKRC = const(0x11)  # Internal clock
 91  _OV7670_CLK_EXT = const(0x40)  # CLKRC Use ext clock directly
 92  _OV7670_CLK_SCALE = const(0x3F)  # CLKRC Int clock prescale mask
 93  _OV7670_REG_COM7 = const(0x12)  # Common control 7
 94  _OV7670_COM7_RESET = const(0x80)  # COM7 SCCB register reset
 95  _OV7670_COM7_SIZE_MASK = const(0x38)  # COM7 output size mask
 96  _OV7670_COM7_PIXEL_MASK = const(0x05)  # COM7 output pixel format mask
 97  _OV7670_COM7_SIZE_VGA = const(0x00)  # COM7 output size VGA
 98  _OV7670_COM7_SIZE_CIF = const(0x20)  # COM7 output size CIF
 99  _OV7670_COM7_SIZE_QVGA = const(0x10)  # COM7 output size QVGA
100  _OV7670_COM7_SIZE_QCIF = const(0x08)  # COM7 output size QCIF
101  _OV7670_COM7_RGB = const(0x04)  # COM7 pixel format RGB
102  _OV7670_COM7_YUV = const(0x00)  # COM7 pixel format YUV
103  _OV7670_COM7_BAYER = const(0x01)  # COM7 pixel format Bayer RAW
104  _OV7670_COM7_PBAYER = const(0x05)  # COM7 pixel fmt proc Bayer RAW
105  _OV7670_COM7_COLORBAR = const(0x02)  # COM7 color bar enable
106  _OV7670_REG_COM8 = const(0x13)  # Common control 8
107  _OV7670_COM8_FASTAEC = const(0x80)  # COM8 Enable fast AGC/AEC algo,
108  _OV7670_COM8_AECSTEP = const(0x40)  # COM8 AEC step size unlimited
109  _OV7670_COM8_BANDING = const(0x20)  # COM8 Banding filter enable
110  _OV7670_COM8_AGC = const(0x04)  # COM8 AGC (auto gain) enable
111  _OV7670_COM8_AWB = const(0x02)  # COM8 AWB (auto white balance)
112  _OV7670_COM8_AEC = const(0x01)  # COM8 AEC (auto exposure) enable
113  _OV7670_REG_COM9 = const(0x14)  # Common control 9 - max AGC value
114  _OV7670_REG_COM10 = const(0x15)  # Common control 10
115  _OV7670_COM10_HSYNC = const(0x40)  # COM10 HREF changes to HSYNC
116  _OV7670_COM10_PCLK_HB = const(0x20)  # COM10 Suppress PCLK on hblank
117  _OV7670_COM10_HREF_REV = const(0x08)  # COM10 HREF reverse
118  _OV7670_COM10_VS_EDGE = const(0x04)  # COM10 VSYNC chg on PCLK rising
119  _OV7670_COM10_VS_NEG = const(0x02)  # COM10 VSYNC negative
120  _OV7670_COM10_HS_NEG = const(0x01)  # COM10 HSYNC negative
121  _OV7670_REG_HSTART = const(0x17)  # Horiz frame start high bits
122  _OV7670_REG_HSTOP = const(0x18)  # Horiz frame end high bits
123  _OV7670_REG_VSTART = const(0x19)  # Vert frame start high bits
124  _OV7670_REG_VSTOP = const(0x1A)  # Vert frame end high bits
125  _OV7670_REG_PSHFT = const(0x1B)  # Pixel delay select
126  _OV7670_REG_MIDH = const(0x1C)  # Manufacturer ID high byte
127  _OV7670_REG_MIDL = const(0x1D)  # Manufacturer ID low byte
128  _OV7670_REG_MVFP = const(0x1E)  # Mirror / vert-flip enable
129  _OV7670_MVFP_MIRROR = const(0x20)  # MVFP Mirror image
130  _OV7670_MVFP_VFLIP = const(0x10)  # MVFP Vertical flip
131  _OV7670_REG_LAEC = const(0x1F)  # Reserved
132  _OV7670_REG_ADCCTR0 = const(0x20)  # ADC control
133  _OV7670_REG_ADCCTR1 = const(0x21)  # Reserved
134  _OV7670_REG_ADCCTR2 = const(0x22)  # Reserved
135  _OV7670_REG_ADCCTR3 = const(0x23)  # Reserved
136  _OV7670_REG_AEW = const(0x24)  # AGC/AEC upper limit
137  _OV7670_REG_AEB = const(0x25)  # AGC/AEC lower limit
138  _OV7670_REG_VPT = const(0x26)  # AGC/AEC fast mode op region
139  _OV7670_REG_BBIAS = const(0x27)  # B channel signal output bias
140  _OV7670_REG_GbBIAS = const(0x28)  # Gb channel signal output bias
141  _OV7670_REG_EXHCH = const(0x2A)  # Dummy pixel insert MSB
142  _OV7670_REG_EXHCL = const(0x2B)  # Dummy pixel insert LSB
143  _OV7670_REG_RBIAS = const(0x2C)  # R channel signal output bias
144  _OV7670_REG_ADVFL = const(0x2D)  # Insert dummy lines MSB
145  _OV7670_REG_ADVFH = const(0x2E)  # Insert dummy lines LSB
146  _OV7670_REG_YAVE = const(0x2F)  # Y/G channel average value
147  _OV7670_REG_HSYST = const(0x30)  # HSYNC rising edge delay
148  _OV7670_REG_HSYEN = const(0x31)  # HSYNC falling edge delay
149  _OV7670_REG_HREF = const(0x32)  # HREF control
150  _OV7670_REG_CHLF = const(0x33)  # Array current control
151  _OV7670_REG_ARBLM = const(0x34)  # Array ref control - reserved
152  _OV7670_REG_ADC = const(0x37)  # ADC control - reserved
153  _OV7670_REG_ACOM = const(0x38)  # ADC & analog common - reserved
154  _OV7670_REG_OFON = const(0x39)  # ADC offset control - reserved
155  _OV7670_REG_TSLB = const(0x3A)  # Line buffer test option
156  _OV7670_TSLB_NEG = const(0x20)  # TSLB Negative image enable
157  _OV7670_TSLB_YLAST = const(0x04)  # TSLB UYVY or VYUY, see COM13
158  _OV7670_TSLB_AOW = const(0x01)  # TSLB Auto output window
159  _OV7670_REG_COM11 = const(0x3B)  # Common control 11
160  _OV7670_COM11_NIGHT = const(0x80)  # COM11 Night mode
161  _OV7670_COM11_NMFR = const(0x60)  # COM11 Night mode frame rate mask
162  _OV7670_COM11_HZAUTO = const(0x10)  # COM11 Auto detect 50/60 Hz
163  _OV7670_COM11_BAND = const(0x08)  # COM11 Banding filter val select
164  _OV7670_COM11_EXP = const(0x02)  # COM11 Exposure timing control
165  _OV7670_REG_COM12 = const(0x3C)  # Common control 12
166  _OV7670_COM12_HREF = const(0x80)  # COM12 Always has HREF
167  _OV7670_REG_COM13 = const(0x3D)  # Common control 13
168  _OV7670_COM13_GAMMA = const(0x80)  # COM13 Gamma enable
169  _OV7670_COM13_UVSAT = const(0x40)  # COM13 UV saturation auto adj
170  _OV7670_COM13_UVSWAP = const(0x01)  # COM13 UV swap, use w TSLB[3]
171  _OV7670_REG_COM14 = const(0x3E)  # Common control 14
172  _OV7670_COM14_DCWEN = const(0x10)  # COM14 DCW & scaling PCLK enable
173  _OV7670_REG_EDGE = const(0x3F)  # Edge enhancement adjustment
174  _OV7670_REG_COM15 = const(0x40)  # Common control 15
175  _OV7670_COM15_RMASK = const(0xC0)  # COM15 Output range mask
176  _OV7670_COM15_R10F0 = const(0x00)  # COM15 Output range 10 to F0
177  _OV7670_COM15_R01FE = const(0x80)  # COM15              01 to FE
178  _OV7670_COM15_R00FF = const(0xC0)  # COM15              00 to FF
179  _OV7670_COM15_RGBMASK = const(0x30)  # COM15 RGB 555/565 option mask
180  _OV7670_COM15_RGB = const(0x00)  # COM15 Normal RGB out
181  _OV7670_COM15_RGB565 = const(0x10)  # COM15 RGB 565 output
182  _OV7670_COM15_RGB555 = const(0x30)  # COM15 RGB 555 output
183  _OV7670_REG_COM16 = const(0x41)  # Common control 16
184  _OV7670_COM16_AWBGAIN = const(0x08)  # COM16 AWB gain enable
185  _OV7670_REG_COM17 = const(0x42)  # Common control 17
186  _OV7670_COM17_AECWIN = const(0xC0)  # COM17 AEC window must match COM4
187  _OV7670_COM17_CBAR = const(0x08)  # COM17 DSP Color bar enable
188  _OV7670_REG_AWBC1 = const(0x43)  # Reserved
189  _OV7670_REG_AWBC2 = const(0x44)  # Reserved
190  _OV7670_REG_AWBC3 = const(0x45)  # Reserved
191  _OV7670_REG_AWBC4 = const(0x46)  # Reserved
192  _OV7670_REG_AWBC5 = const(0x47)  # Reserved
193  _OV7670_REG_AWBC6 = const(0x48)  # Reserved
194  _OV7670_REG_REG4B = const(0x4B)  # UV average enable
195  _OV7670_REG_DNSTH = const(0x4C)  # De-noise strength
196  _OV7670_REG_MTX1 = const(0x4F)  # Matrix coefficient 1
197  _OV7670_REG_MTX2 = const(0x50)  # Matrix coefficient 2
198  _OV7670_REG_MTX3 = const(0x51)  # Matrix coefficient 3
199  _OV7670_REG_MTX4 = const(0x52)  # Matrix coefficient 4
200  _OV7670_REG_MTX5 = const(0x53)  # Matrix coefficient 5
201  _OV7670_REG_MTX6 = const(0x54)  # Matrix coefficient 6
202  _OV7670_REG_BRIGHT = const(0x55)  # Brightness control
203  _OV7670_REG_CONTRAS = const(0x56)  # Contrast control
204  _OV7670_REG_CONTRAS_CENTER = const(0x57)  # Contrast center
205  _OV7670_REG_MTXS = const(0x58)  # Matrix coefficient sign
206  _OV7670_REG_LCC1 = const(0x62)  # Lens correction option 1
207  _OV7670_REG_LCC2 = const(0x63)  # Lens correction option 2
208  _OV7670_REG_LCC3 = const(0x64)  # Lens correction option 3
209  _OV7670_REG_LCC4 = const(0x65)  # Lens correction option 4
210  _OV7670_REG_LCC5 = const(0x66)  # Lens correction option 5
211  _OV7670_REG_MANU = const(0x67)  # Manual U value
212  _OV7670_REG_MANV = const(0x68)  # Manual V value
213  _OV7670_REG_GFIX = const(0x69)  # Fix gain control
214  _OV7670_REG_GGAIN = const(0x6A)  # G channel AWB gain
215  _OV7670_REG_DBLV = const(0x6B)  # PLL & regulator control
216  _OV7670_REG_AWBCTR3 = const(0x6C)  # AWB control 3
217  _OV7670_REG_AWBCTR2 = const(0x6D)  # AWB control 2
218  _OV7670_REG_AWBCTR1 = const(0x6E)  # AWB control 1
219  _OV7670_REG_AWBCTR0 = const(0x6F)  # AWB control 0
220  _OV7670_REG_SCALING_XSC = const(0x70)  # Test pattern X scaling
221  _OV7670_REG_SCALING_YSC = const(0x71)  # Test pattern Y scaling
222  _OV7670_REG_SCALING_DCWCTR = const(0x72)  # DCW control
223  _OV7670_REG_SCALING_PCLK_DIV = const(0x73)  # DSP scale control clock divide
224  _OV7670_REG_REG74 = const(0x74)  # Digital gain control
225  _OV7670_REG_REG76 = const(0x76)  # Pixel correction
226  _OV7670_REG_SLOP = const(0x7A)  # Gamma curve highest seg slope
227  _OV7670_REG_GAM_BASE = const(0x7B)  # Gamma register base (1 of 15)
228  _OV7670_GAM_LEN = const(15)  # Number of gamma registers
229  _OV7670_R76_BLKPCOR = const(0x80)  # REG76 black pixel corr enable
230  _OV7670_R76_WHTPCOR = const(0x40)  # REG76 white pixel corr enable
231  _OV7670_REG_RGB444 = const(0x8C)  # RGB 444 control
232  _OV7670_R444_ENABLE = const(0x02)  # RGB444 enable
233  _OV7670_R444_RGBX = const(0x01)  # RGB444 word format
234  _OV7670_REG_DM_LNL = const(0x92)  # Dummy line LSB
235  _OV7670_REG_LCC6 = const(0x94)  # Lens correction option 6
236  _OV7670_REG_LCC7 = const(0x95)  # Lens correction option 7
237  _OV7670_REG_HAECC1 = const(0x9F)  # Histogram-based AEC/AGC ctrl 1
238  _OV7670_REG_HAECC2 = const(0xA0)  # Histogram-based AEC/AGC ctrl 2
239  _OV7670_REG_SCALING_PCLK_DELAY = const(0xA2)  # Scaling pixel clock delay
240  _OV7670_REG_BD50MAX = const(0xA5)  # 50 Hz banding step limit
241  _OV7670_REG_HAECC3 = const(0xA6)  # Histogram-based AEC/AGC ctrl 3
242  _OV7670_REG_HAECC4 = const(0xA7)  # Histogram-based AEC/AGC ctrl 4
243  _OV7670_REG_HAECC5 = const(0xA8)  # Histogram-based AEC/AGC ctrl 5
244  _OV7670_REG_HAECC6 = const(0xA9)  # Histogram-based AEC/AGC ctrl 6
245  _OV7670_REG_HAECC7 = const(0xAA)  # Histogram-based AEC/AGC ctrl 7
246  _OV7670_REG_BD60MAX = const(0xAB)  # 60 Hz banding step limit
247  _OV7670_REG_ABLC1 = const(0xB1)  # ABLC enable
248  _OV7670_REG_THL_ST = const(0xB3)  # ABLC target
249  _OV7670_REG_SATCTR = const(0xC9)  # Saturation control
250  
251  _OV7670_REG_LAST = const(_OV7670_REG_SATCTR)  # Maximum register address
252  
253  # Manual output format, RGB, use RGB565 and full 0-255 output range
254  _OV7670_rgb = bytes(
255      [
256          _OV7670_REG_COM7,
257          _OV7670_COM7_RGB,
258          _OV7670_REG_RGB444,
259          0,
260          _OV7670_REG_COM15,
261          _OV7670_COM15_RGB565 | _OV7670_COM15_R00FF,
262      ]
263  )
264  
265  # Manual output format, YUV, use full output range
266  _OV7670_yuv = bytes(
267      [
268          _OV7670_REG_COM7,
269          _OV7670_COM7_YUV,
270          _OV7670_REG_COM15,
271          _OV7670_COM15_R00FF,
272      ]
273  )
274  
275  _OV7670_init = bytes(
276      [
277          _OV7670_REG_TSLB,
278          _OV7670_TSLB_YLAST,  # No auto window
279          _OV7670_REG_COM10,
280          _OV7670_COM10_VS_NEG,  # -VSYNC (req by SAMD PCC)
281          _OV7670_REG_SLOP,
282          0x20,
283          _OV7670_REG_GAM_BASE,
284          0x1C,
285          _OV7670_REG_GAM_BASE + 1,
286          0x28,
287          _OV7670_REG_GAM_BASE + 2,
288          0x3C,
289          _OV7670_REG_GAM_BASE + 3,
290          0x55,
291          _OV7670_REG_GAM_BASE + 4,
292          0x68,
293          _OV7670_REG_GAM_BASE + 5,
294          0x76,
295          _OV7670_REG_GAM_BASE + 6,
296          0x80,
297          _OV7670_REG_GAM_BASE + 7,
298          0x88,
299          _OV7670_REG_GAM_BASE + 8,
300          0x8F,
301          _OV7670_REG_GAM_BASE + 9,
302          0x96,
303          _OV7670_REG_GAM_BASE + 10,
304          0xA3,
305          _OV7670_REG_GAM_BASE + 11,
306          0xAF,
307          _OV7670_REG_GAM_BASE + 12,
308          0xC4,
309          _OV7670_REG_GAM_BASE + 13,
310          0xD7,
311          _OV7670_REG_GAM_BASE + 14,
312          0xE8,
313          _OV7670_REG_COM8,
314          _OV7670_COM8_FASTAEC | _OV7670_COM8_AECSTEP | _OV7670_COM8_BANDING,
315          _OV7670_REG_GAIN,
316          0x00,
317          _OV7670_COM2_SSLEEP,
318          0x00,
319          _OV7670_REG_COM4,
320          0x00,
321          _OV7670_REG_COM9,
322          0x20,  # Max AGC value
323          _OV7670_REG_BD50MAX,
324          0x05,
325          _OV7670_REG_BD60MAX,
326          0x07,
327          _OV7670_REG_AEW,
328          0x75,
329          _OV7670_REG_AEB,
330          0x63,
331          _OV7670_REG_VPT,
332          0xA5,
333          _OV7670_REG_HAECC1,
334          0x78,
335          _OV7670_REG_HAECC2,
336          0x68,
337          0xA1,
338          0x03,  # Reserved register?
339          _OV7670_REG_HAECC3,
340          0xDF,  # Histogram-based AEC/AGC setup
341          _OV7670_REG_HAECC4,
342          0xDF,
343          _OV7670_REG_HAECC5,
344          0xF0,
345          _OV7670_REG_HAECC6,
346          0x90,
347          _OV7670_REG_HAECC7,
348          0x94,
349          _OV7670_REG_COM8,
350          _OV7670_COM8_FASTAEC
351          | _OV7670_COM8_AECSTEP
352          | _OV7670_COM8_BANDING
353          | _OV7670_COM8_AGC
354          | _OV7670_COM8_AEC,
355          _OV7670_REG_COM5,
356          0x61,
357          _OV7670_REG_COM6,
358          0x4B,
359          0x16,
360          0x02,  # Reserved register?
361          _OV7670_REG_MVFP,
362          0x07,  # 0x07,
363          _OV7670_REG_ADCCTR1,
364          0x02,
365          _OV7670_REG_ADCCTR2,
366          0x91,
367          0x29,
368          0x07,  # Reserved register?
369          _OV7670_REG_CHLF,
370          0x0B,
371          0x35,
372          0x0B,  # Reserved register?
373          _OV7670_REG_ADC,
374          0x1D,
375          _OV7670_REG_ACOM,
376          0x71,
377          _OV7670_REG_OFON,
378          0x2A,
379          _OV7670_REG_COM12,
380          0x78,
381          0x4D,
382          0x40,  # Reserved register?
383          0x4E,
384          0x20,  # Reserved register?
385          _OV7670_REG_GFIX,
386          0x5D,
387          _OV7670_REG_REG74,
388          0x19,
389          0x8D,
390          0x4F,  # Reserved register?
391          0x8E,
392          0x00,  # Reserved register?
393          0x8F,
394          0x00,  # Reserved register?
395          0x90,
396          0x00,  # Reserved register?
397          0x91,
398          0x00,  # Reserved register?
399          _OV7670_REG_DM_LNL,
400          0x00,
401          0x96,
402          0x00,  # Reserved register?
403          0x9A,
404          0x80,  # Reserved register?
405          0xB0,
406          0x84,  # Reserved register?
407          _OV7670_REG_ABLC1,
408          0x0C,
409          0xB2,
410          0x0E,  # Reserved register?
411          _OV7670_REG_THL_ST,
412          0x82,
413          0xB8,
414          0x0A,  # Reserved register?
415          _OV7670_REG_AWBC1,
416          0x14,
417          _OV7670_REG_AWBC2,
418          0xF0,
419          _OV7670_REG_AWBC3,
420          0x34,
421          _OV7670_REG_AWBC4,
422          0x58,
423          _OV7670_REG_AWBC5,
424          0x28,
425          _OV7670_REG_AWBC6,
426          0x3A,
427          0x59,
428          0x88,  # Reserved register?
429          0x5A,
430          0x88,  # Reserved register?
431          0x5B,
432          0x44,  # Reserved register?
433          0x5C,
434          0x67,  # Reserved register?
435          0x5D,
436          0x49,  # Reserved register?
437          0x5E,
438          0x0E,  # Reserved register?
439          _OV7670_REG_LCC3,
440          0x04,
441          _OV7670_REG_LCC4,
442          0x20,
443          _OV7670_REG_LCC5,
444          0x05,
445          _OV7670_REG_LCC6,
446          0x04,
447          _OV7670_REG_LCC7,
448          0x08,
449          _OV7670_REG_AWBCTR3,
450          0x0A,
451          _OV7670_REG_AWBCTR2,
452          0x55,
453          _OV7670_REG_MTX1,
454          0x80,
455          _OV7670_REG_MTX2,
456          0x80,
457          _OV7670_REG_MTX3,
458          0x00,
459          _OV7670_REG_MTX4,
460          0x22,
461          _OV7670_REG_MTX5,
462          0x5E,
463          _OV7670_REG_MTX6,
464          0x80,  # 0x40?
465          _OV7670_REG_AWBCTR1,
466          0x11,
467          _OV7670_REG_AWBCTR0,
468          0x9F,  # Or use 0x9E for advance AWB
469          _OV7670_REG_BRIGHT,
470          0x00,
471          _OV7670_REG_CONTRAS,
472          0x40,
473          _OV7670_REG_CONTRAS_CENTER,
474          0x80,  # 0x40?
475      ]
476  )
477  
478  _window = [
479      [9, 162, 2, 2],  # SIZE_DIV1  640x480 VGA
480      [10, 174, 4, 2],  # SIZE_DIV2  320x240 QVGA
481      [11, 186, 2, 2],  # SIZE_DIV4  160x120 QQVGA
482      [12, 210, 0, 2],  # SIZE_DIV8  80x60   ...
483      [15, 252, 3, 2],  # SIZE_DIV16 40x30
484  ]
485  
486  
487  class OV7670:  # pylint: disable=too-many-instance-attributes
488      """Library for the OV7670 digital camera"""
489  
490      def __init__(
491          self,
492          i2c_bus,
493          data0,
494          clock,
495          vsync,
496          href,
497          shutdown=None,
498          reset=None,
499          mclk=None,
500          mclk_frequency=16_000_000,
501          colorspace=OV7670_COLOR_RGB,
502          i2c_address=0x21,
503      ):  # pylint: disable=too-many-arguments
504          """
505          Args:
506              i2c_bus (busio.I2C): The I2C bus used to configure the OV7670
507              i2c_address (int): The I2C address of the camera
508              data0 (microcontroller.Pin): The first of 8 parallel data capture pins
509              clock (microcontroller.Pin): The pixel clock from the OV7670
510              vsync (microcontroller.Pin): The vsync signal from the OV7670
511              href (microcontroller.Pin): The href signal from the OV7670
512              shutdown: The microcontroller.Pin that controls the camera's \
513                  shutdown signal, also called the powerdown or enable pin, or \
514                  None
515              reset: The microcontroller.Pin that controls the camera's reset \
516                  signal, or enable pin, or None
517              mclk: The pin on which to create a master clock signal, or None
518              mclk_frequency: The frequency of the master clock to generate, \
519                  ignored if mclk is None
520              colorspace: The colorspace to operate in
521              size: The size of image to capture
522          """
523          # Initialize the master clock
524          if mclk:
525              self._mclk_pwm = pwmio.PWMOut(mclk, frequency=mclk_frequency)
526              self._mclk_pwm.duty_cycle = 32768
527          else:
528              self._mclk_pwm = None
529  
530          if shutdown:
531              self._shutdown = digitalio.DigitalInOut(shutdown)
532              self._shutdown.switch_to_output(True)
533              time.sleep(0.001)
534              self._shutdown.switch_to_output(False)
535              time.sleep(0.3)
536          else:
537              self._shutdown = None
538  
539          if reset:
540              self._reset = digitalio.DigitalInOut(reset)
541              self._reset.switch_to_output(False)
542              time.sleep(0.001)
543              self._reset.switch_to_output(True)
544  
545          self._i2c_device = I2CDevice(i2c_bus, i2c_address)
546  
547          if not reset:
548              self._write_register(_OV7670_REG_COM7, _OV7670_COM7_RESET)
549  
550          time.sleep(0.001)
551  
552          self._colorspace = None
553          self.colorspace = colorspace
554  
555          self._write_list(_OV7670_init)
556  
557          self._size = None
558          self.size = OV7670_SIZE_DIV8
559  
560          self._test_pattern = None
561          self.test_pattern = OV7670_TEST_PATTERN_NONE
562  
563          self._flip_x = False
564          self._flip_y = False
565  
566          self._night = OV7670_NIGHT_MODE_OFF
567  
568          self._imagecapture = imagecapture.ParallelImageCapture(
569              data0=data0, clock=clock, vsync=vsync, href=href
570          )
571  
572      def capture(self, buf):
573          """Capture an image into the buffer."""
574          self._imagecapture.capture(buf)
575  
576      @property
577      def mclk_frequency(self):
578          """Get the actual frequency the generated mclk, or None"""
579          return self._mclk_pwm.frequency if self._mclk_pwm else None
580  
581      @property
582      def width(self):
583          """Get the image width in pixels.  A buffer of 2*width*height bytes \
584          stores a whole image."""
585          return 640 >> self._size
586  
587      @property
588      def height(self):
589          """Get the image height in pixels.  A buffer of 2*width*height bytes \
590          stores a whole image."""
591          return 480 >> self._size
592  
593      @property
594      def colorspace(self):
595          """Get or set the colorspace"""
596          return self._colorspace
597  
598      @colorspace.setter
599      def colorspace(self, colorspace):
600          self._colorspace = colorspace
601          self._write_list(_OV7670_rgb if colorspace == OV7670_COLOR_RGB else _OV7670_yuv)
602  
603      def deinit(self):
604          """Deinitialize the camera"""
605          if self._mclk_pwm:
606              self._mclk_pwm.deinit()
607          if self._shutdown:
608              self._shutdown.deinit()
609          if self._reset:
610              self._reset.deinit()
611  
612      @property
613      def size(self):
614          """Get or set the captured image size"""
615          return self._size
616  
617      @size.setter
618      def size(self, size):
619          self._frame_control(size, *_window[size])
620          self._size = size
621  
622      @property
623      def test_pattern(self):
624          """Get or set the test pattern"""
625          return self._test_pattern
626  
627      @test_pattern.setter
628      def test_pattern(self, pattern):
629          # Modify only test pattern bits (not scaling bits)
630          xsc = self._read_register(_OV7670_REG_SCALING_XSC) & ~0x80
631          ysc = self._read_register(_OV7670_REG_SCALING_YSC) & ~0x80
632          if pattern & 1:
633              xsc |= 0x80
634          if pattern & 3:
635              ysc |= 0x80
636          # Write modified result back to SCALING_XSC and SCALING_YSC
637          self._write_register(_OV7670_REG_SCALING_XSC, xsc)
638          self._write_register(_OV7670_REG_SCALING_YSC, ysc)
639  
640      def _set_flip(self):
641          mvfp = self._read_register(_OV7670_REG_MVFP)
642          if self._flip_x:
643              mvfp |= _OV7670_MVFP_MIRROR
644          else:
645              mvfp &= ~_OV7670_MVFP_MIRROR
646          if self._flip_y:
647              mvfp |= _OV7670_MVFP_VFLIP
648          else:
649              mvfp &= ~_OV7670_MVFP_VFLIP
650          self._write_register(_OV7670_REG_MVFP, mvfp)
651  
652      @property
653      def flip_x(self):
654          """Get or set the X-flip flag"""
655          return self._flip_x
656  
657      @flip_x.setter
658      def flip_x(self, value):
659          self._flip_x = bool(value)
660          self._set_flip()
661  
662      @property
663      def flip_y(self):
664          """Get or set the Y-flip flag"""
665          return self._flip_y
666  
667      @flip_y.setter
668      def flip_y(self, value):
669          self._flip_y = bool(value)
670          self._set_flip()
671  
672      @property
673      def night(self):
674          """Get or set the night-vision mode"""
675          return self._night
676  
677      @night.setter
678      def night(self, value):
679          com11 = self._read_register(_OV7670_REG_COM11)
680          com11 = (com11 & 0b00011111) | value
681          self._write_register(_OV7670_REG_COM11, com11)
682          self._night = value
683  
684      @property
685      def product_id(self):
686          """Get the product id (PID) register"""
687          return self._read_register(_OV7670_REG_PID)
688  
689      @property
690      def product_version(self):
691          """Get the version (VER) register"""
692          return self._read_register(_OV7670_REG_VER)
693  
694      def _write_list(self, reg_list):
695          for i in range(0, len(reg_list), 2):
696              self._write_register(reg_list[i], reg_list[i + 1])
697              time.sleep(0.001)
698  
699      def _write_register(self, reg, value):
700          b = bytearray(2)
701          b[0] = reg
702          b[1] = value
703          with self._i2c_device as i2c:
704              i2c.write(b)
705  
706      def _read_register(self, reg):
707          b = bytearray(1)
708          b[0] = reg
709          with self._i2c_device as i2c:
710              i2c.write(b)
711              i2c.readinto(b)
712          return b[0]
713  
714      def _frame_control(
715          self, size, vstart, hstart, edge_offset, pclk_delay
716      ):  # pylint: disable=too-many-arguments
717  
718          # Enable downsampling if sub-VGA, and zoom if 1:16 scale
719          value = _OV7670_COM3_DCWEN if (size > OV7670_SIZE_DIV1) else 0
720          if size == OV7670_SIZE_DIV16:
721              value |= _OV7670_COM3_SCALEEN
722          self._write_register(_OV7670_REG_COM3, value)
723  
724          # Enable PCLK division if sub-VGA 2,4,8,16 = 0x19,1A,1B,1C
725          value = (0x18 + size) if (size > OV7670_SIZE_DIV1) else 0
726          self._write_register(_OV7670_REG_COM14, value)
727  
728          # Horiz/vert downsample ratio, 1:8 max (H,V are always equal for now)
729          value = size if (size <= OV7670_SIZE_DIV8) else OV7670_SIZE_DIV8
730          self._write_register(_OV7670_REG_SCALING_DCWCTR, value * 0x11)
731  
732          # Pixel clock divider if sub-VGA
733          value = (0xF0 + size) if (size > OV7670_SIZE_DIV1) else 0x08
734          self._write_register(_OV7670_REG_SCALING_PCLK_DIV, value)
735  
736          # Apply 0.5 digital zoom at 1:16 size (others are downsample only)
737          value = 0x40 if (size == OV7670_SIZE_DIV16) else 0x20  # 0.5, 1.0
738  
739          # Read current SCALING_XSC and SCALING_YSC register values because
740          # test pattern settings are also stored in those registers and we
741          # don't want to corrupt anything there.
742          xsc = self._read_register(_OV7670_REG_SCALING_XSC)
743          ysc = self._read_register(_OV7670_REG_SCALING_YSC)
744          xsc = (xsc & 0x80) | value  # Modify only scaling bits (not test pattern)
745          ysc = (ysc & 0x80) | value
746          # Write modified result back to SCALING_XSC and SCALING_YSC
747          self._write_register(_OV7670_REG_SCALING_XSC, xsc)
748          self._write_register(_OV7670_REG_SCALING_YSC, ysc)
749  
750          # Window size is scattered across multiple registers.
751          # Horiz/vert stops can be automatically calc'd from starts.
752          vstop = vstart + 480
753          hstop = (hstart + 640) % 784
754          self._write_register(_OV7670_REG_HSTART, hstart >> 3)
755          self._write_register(_OV7670_REG_HSTOP, hstop >> 3)
756          self._write_register(
757              _OV7670_REG_HREF,
758              (edge_offset << 6) | ((hstop & 0b111) << 3) | (hstart & 0b111),
759          )
760          self._write_register(_OV7670_REG_VSTART, vstart >> 2)
761          self._write_register(_OV7670_REG_VSTOP, vstop >> 2)
762          self._write_register(_OV7670_REG_VREF, ((vstop & 0b11) << 2) | (vstart & 0b11))
763  
764          self._write_register(_OV7670_REG_SCALING_PCLK_DELAY, pclk_delay)