From e75648fa5e7cc870b29759eb49b94bc04e5d0686 Mon Sep 17 00:00:00 2001 From: lady ada Date: Sat, 16 Oct 2021 22:43:13 -0400 Subject: [PATCH 1/7] add powerdown (reduce current greatly) --- adafruit_ov5640.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/adafruit_ov5640.py b/adafruit_ov5640.py index 95cdad6..3fc55e5 100644 --- a/adafruit_ov5640.py +++ b/adafruit_ov5640.py @@ -99,6 +99,9 @@ # gain = {0x350A[1:0], 0x350B[7:0]} / 16 +_STROBE_CTRL = const(0x3b00) +_FREX_MODE = const(0x3b07) +_PAD_OUTPUT_ENABLE00 = const(0x3016) _X_ADDR_ST_H = const(0x3800) # Bit[3:0]: X address start[11:8] @@ -1259,3 +1262,11 @@ def night_mode(self): @night_mode.setter def night_mode(self, value): self._write_reg_bits(0x3A00, 0x04, value) + + @property + def powerdown(self): + return bool(self._read_register(_SYSTEM_CTROL0) & 0x40) + + @powerdown.setter + def powerdown(self, value): + self._write_reg_bits(_SYSTEM_CTROL0, 0x40, bool(value)) From ee91db69c5992e7314a4e1bf49f2a4529af42264 Mon Sep 17 00:00:00 2001 From: lady ada Date: Sat, 16 Oct 2021 22:57:33 -0400 Subject: [PATCH 2/7] add sekret test modes --- adafruit_ov5640.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/adafruit_ov5640.py b/adafruit_ov5640.py index 3fc55e5..cf6e687 100644 --- a/adafruit_ov5640.py +++ b/adafruit_ov5640.py @@ -1134,9 +1134,12 @@ def test_pattern(self): return self._test_pattern @test_pattern.setter - def test_pattern(self, value: bool) -> None: + def test_pattern(self, value) -> None: + if type(value) is bool: + self._write_register(_PRE_ISP_TEST_SETTING_1, value << 7) + else: + self._write_register(_PRE_ISP_TEST_SETTING_1, 1 << 7 | value) self._test_pattern = value - self._write_register(_PRE_ISP_TEST_SETTING_1, value << 7) @property def saturation(self): From 934c6e51903cbbc26a71890959d8cd8d69041704 Mon Sep 17 00:00:00 2001 From: lady ada Date: Sat, 16 Oct 2021 22:58:36 -0400 Subject: [PATCH 3/7] tweak startup --- adafruit_ov5640.py | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/adafruit_ov5640.py b/adafruit_ov5640.py index cf6e687..5e2d359 100644 --- a/adafruit_ov5640.py +++ b/adafruit_ov5640.py @@ -849,31 +849,38 @@ def __init__( i2c_address (int): The I2C address of the camera. """ - # Initialize the master clock - if mclk: - self._mclk_pwm = pwmio.PWMOut(mclk, frequency=mclk_frequency) - self._mclk_pwm.duty_cycle = 32768 - else: - self._mclk_pwm = None - + # we're supposed to go into shutdown if we can, first if shutdown: self._shutdown = digitalio.DigitalInOut(shutdown) self._shutdown.switch_to_output(True) - time.sleep(0.1) - self._shutdown.switch_to_output(False) - time.sleep(0.3) else: self._shutdown = None if reset: self._reset = digitalio.DigitalInOut(reset) self._reset.switch_to_output(False) - time.sleep(0.1) - self._reset.switch_to_output(True) - time.sleep(0.1) else: self._reset = None + time.sleep(0.1) + + # Initialize the master clock + if mclk: + self._mclk_pwm = pwmio.PWMOut(mclk, frequency=mclk_frequency) + self._mclk_pwm.duty_cycle = 32768 + else: + self._mclk_pwm = None + + if self._shutdown: + self._shutdown.switch_to_output(False) + time.sleep(0.1) + + + if self._reset: + self._reset.switch_to_output(True) + + time.sleep(0.1) + # Now that the master clock is running, we can initialize i2c comms super().__init__(i2c_bus, i2c_address) From 4464ea41e39066f5666415af3301955d541f11e4 Mon Sep 17 00:00:00 2001 From: lady ada Date: Sat, 16 Oct 2021 22:59:59 -0400 Subject: [PATCH 4/7] valval --- adafruit_ov5640.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_ov5640.py b/adafruit_ov5640.py index 5e2d359..2f05ed4 100644 --- a/adafruit_ov5640.py +++ b/adafruit_ov5640.py @@ -800,7 +800,7 @@ def _write_list(self, reg_list): self._write_register(register, value) def _write_reg_bits(self, reg, mask, enable): - val = val = self._read_register(reg) + val = self._read_register(reg) if enable: val |= mask else: From dd3d3e41178b0b0e96997eca6ced897a7e701051 Mon Sep 17 00:00:00 2001 From: lady ada Date: Sun, 17 Oct 2021 00:37:06 -0400 Subject: [PATCH 5/7] add strobe/LED support! (note NOT ALL CAMS HAVE THIS PIN CONNECTED UP!) --- adafruit_ov5640.py | 51 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/adafruit_ov5640.py b/adafruit_ov5640.py index 2f05ed4..871f744 100644 --- a/adafruit_ov5640.py +++ b/adafruit_ov5640.py @@ -101,7 +101,19 @@ _STROBE_CTRL = const(0x3b00) _FREX_MODE = const(0x3b07) +_FREX_REQUEST = const(0x3B08) _PAD_OUTPUT_ENABLE00 = const(0x3016) +_PAD_OUTPUT_VALUE00 = const(0x3019) +_PAD_SELECT00 = const(0x301C) + +FREX_MODE_0 = 1 +FREX_MODE_1 = 2 +FREX_MODE_ROLL = 3 + +STROBE_MODE_XENON = 0 +STROBE_MODE_LED1 = 1 +STROBE_MODE_LED2 = 2 +STROBE_MODE_LED3 = 3 _X_ADDR_ST_H = const(0x3800) # Bit[3:0]: X address start[11:8] @@ -902,6 +914,7 @@ def __init__( self._ev = 0 self._white_balance = 0 self.size = size + self._strobe_enabled = False chip_id = _RegBits16(_CHIP_ID_HIGH, 0, 0xFFFF) @@ -1019,6 +1032,7 @@ def deinit(self): self._shutdown.deinit() if self._reset: self._reset.deinit() + self.powerdown = True @property def size(self): @@ -1280,3 +1294,40 @@ def powerdown(self): @powerdown.setter def powerdown(self, value): self._write_reg_bits(_SYSTEM_CTROL0, 0x40, bool(value)) + + def strobe_config(self, enabled, pulse_invert, frex_mode, strobe_mode): + # set the FREX mode rolling + if frex_mode not in (FREX_MODE_0, FREX_MODE_1, FREX_MODE_ROLL): + raise ValueError("Frex mode must be 0, 1 or 2 (rolling)") + self._write_register(_FREX_MODE, frex_mode) + # set the output pin + self._write_register(_PAD_OUTPUT_ENABLE00, 0x2) + # set the pulse invert, mode and enable + if strobe_mode not in (STROBE_MODE_XENON, STROBE_MODE_LED1, STROBE_MODE_LED2, STROBE_MODE_LED3): + raise ValueError("Strobe mode must be 0~3") + self._write_register(_STROBE_CTRL, + (bool(enabled) << 7) | + (bool(pulse_invert) << 6) | + (strobe_mode & 0x3)) + + #print("strobe reg: ", hex(self._read_register(_STROBE_CTRL))) + #print("pad00 reg: ", hex(self._read_register(_PAD_OUTPUT_ENABLE00))) + #print("clock00 reg: ", hex(self._read_register(0x3004))) + + @property + def strobe_request(self): + return bool(self._read_register(_FREX_REQUEST)) + + @strobe_request.setter + def strobe_request(self, value): + self._write_register(_FREX_REQUEST, bool(value)) + + @property + def strobe_pin(self): + return bool(self._read_register(_PAD_OUTPUT_VALUE00)) + + @strobe_pin.setter + def strobe_pin(self, value): + self._write_register(_PAD_OUTPUT_ENABLE00, 0x02) + self._write_register(_PAD_SELECT00, 0x02) + self._write_register(_PAD_OUTPUT_VALUE00, bool(value) << 1) From ab3c5809ae8aed55b9a62445892e5ca44483af4e Mon Sep 17 00:00:00 2001 From: lady ada Date: Sun, 17 Oct 2021 17:02:49 -0400 Subject: [PATCH 6/7] autofocus working --- adafruit_ov5640.py | 73 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/adafruit_ov5640.py b/adafruit_ov5640.py index 871f744..8f1443b 100644 --- a/adafruit_ov5640.py +++ b/adafruit_ov5640.py @@ -722,6 +722,27 @@ 0x519D, 0x82, 0x519E, 0x38, ] + +_autofocus_firmware_load = ( + 0x3022, 0x00, + 0x3023, 0x00, + 0x3024, 0x00, + 0x3025, 0x00, + 0x3026, 0x00, + 0x3027, 0x00, + 0x3028, 0x00, + 0x3029, 0x7f, + 0x3000, 0x00, +) + +AUTOFOCUS_STAT_FIRMWAREBAD = 0x7F +AUTOFOCUS_STAT_STARTUP = 0x7E +AUTOFOCUS_STAT_IDLE = 0x70 +AUTOFOCUS_STAT_FOCUSING = 0x00 +AUTOFOCUS_STAT_FOCUSED = 0x10 + +autofocus_firmware = b'\x02\x0f\xd6\x02\n9\xc2\x01""\x00\x02\x0f\xb2\xe5\x1fpr\xf5\x1e\xd25\xff\xef%\xe0$N\xf8\xe4\xf6\x08\xf6\x0f\xbf4\xf2\x90\x0e\x93\xe4\x93\xff\xe5K\xc3\x9fP\x04\x7f\x05\x80\x02\x7f\xfbx\xbd\xa6\x07\x12\x0f\x04@\x04\x7f\x03\x80\x02\x7f0x\xbc\xa6\x07\xe6\x18\xf6\x08\xe6x\xb9\xf6x\xbc\xe6x\xba\xf6x\xbfv3\xe4\x08\xf6x\xb8v\x01uJ\x02x\xb6\xf6\x08\xf6t\xffx\xc1\xf6\x08\xf6u\x1f\x01x\xbc\xe6u\xf0\x05\xa4\xf5K\x12\n\xff\xc27"x\xb8\xe6\xd3\x94\x00@\x02\x16"\xe5\x1f\xb4\x05#\xe4\xf5\x1f\xc2\x01x\xb6\xe6\xfe\x08\xe6\xffxN\xa6\x06\x08\xa6\x07\xa27\xe43\xf5<\x900(\xf0u\x1e\x10\xd25"\xe5Ku\xf0\x05\x84x\xbc\xf6\x90\x0e\x8c\xe4\x93\xff%\xe0$\n\xf8\xe6\xfc\x08\xe6\xfdx\xbc\xe6%\xe0$N\xf8\xa6\x04\x08\xa6\x05\xef\x12\x0f\x0b\xd3x\xb7\x96\xee\x18\x96@\rx\xbc\xe6x\xb9\xf6x\xb6\xa6\x06\x08\xa6\x07\x90\x0e\x8c\xe4\x93\x12\x0f\x0b\xc3x\xc2\x96\xee\x18\x96P\rx\xbc\xe6x\xba\xf6x\xc1\xa6\x06\x08\xa6\x07x\xb6\xe6\xfe\x08\xe6\xc3x\xc2\x96\xff\xee\x18\x96x\xc3\xf6\x08\xa6\x07\x90\x0e\x95\xe4\x18\x12\x0e\xe9@\x02\xd27x\xbc\xe6\x08&\x08\xf6\xe5\x1fd\x01pJ\xe6\xc3x\xc0\x12\x0e\xdf@\x05\x12\x0e\xda@9\x12\x0f\x02@\x04\x7f\xfe\x80\x02\x7f\x02x\xbd\xa6\x07x\xb9\xe6$\x03x\xbf\xf6x\xb9\xe6$\xfdx\xc0\xf6\x12\x0f\x02@\x06x\xc0\xe6\xff\x80\x04x\xbf\xe6\xffx\xbe\xa6\x07u\x1f\x02x\xb8v\x01\x02\x02J\xe5\x1fd\x02`\x03\x02\x02*x\xbe\xe6\xff\xc3x\xc0\x12\x0e\xe0@\x08\x12\x0e\xdaP\x03\x02\x02(\x12\x0f\x02@\x04\x7f\xff\x80\x02\x7f\x01x\xbd\xa6\x07x\xb9\xe6\x04x\xbf\xf6x\xb9\xe6\x14x\xc0\xf6\x18\x12\x0f\x04@\x04\xe6\xff\x80\x02\x7f\x00x\xbf\xa6\x07\xd3\x08\xe6d\x80\x94\x80@\x04\xe6\xff\x80\x02\x7f\x00x\xc0\xa6\x07\xc3\x18\xe6d\x80\x94\xb3P\x04\xe6\xff\x80\x02\x7f3x\xbf\xa6\x07\xc3\x08\xe6d\x80\x94\xb3P\x04\xe6\xff\x80\x02\x7f3x\xc0\xa6\x07\x12\x0f\x02@\x06x\xc0\xe6\xff\x80\x04x\xbf\xe6\xffx\xbe\xa6\x07u\x1f\x03x\xb8v\x01\x80 \xe5\x1fd\x03p&x\xbe\xe6\xff\xc3x\xc0\x12\x0e\xe0@\x05\x12\x0e\xda@\tx\xb9\xe6x\xbe\xf6u\x1f\x04x\xbe\xe6u\xf0\x05\xa4\xf5K\x02\n\xff\xe5\x1f\xb4\x04\x10\x90\x0e\x94\xe4x\xc3\x12\x0e\xe9@\x02\xd27u\x1f\x05"0\x01\x03\x02\x04\xc00\x02\x03\x02\x04\xc0\x90Q\xa5\xe0x\x93\xf6\xa3\xe0\x08\xf6\xa3\xe0\x08\xf6\xe5\x1fp\xa3\xe0\xf5?\xa3\xe0\xf5@\xa3\xe0\xf5<\xd24\xe5A\x12\x06\xb1\t1\x03\t5\x04\t;\x05\t>\x06\tA\x07\tJ\x08\t[\x12\ts\x18\t\x89\x19\t^\x1a\tj\x1b\t\xad\x80\t\xb2\x81\n\x1d\x8f\n\t\x90\n\x1d\x91\n\x1d\x92\n\x1d\x93\n\x1d\x94\n\x1d\x98\n\x17\x9f\n\x1a\xec\x00\x00\n8\x12\x0ft"\x12\x0ft\xd2\x03"\xd2\x03"\xc2\x03"\xa27\xe43\xf5<\x02\n\x1d\xc2\x01\xc2\x02\xc2\x03\x12\r\ru\x1ep\xd25\x02\n\x1d\x02\n\x04\x85@J\x85/\x85=.\x12\x0fF\x80\x1fu"\x00u#\x01t\xff\xf5-\xf5,\xf5+\xf5*\x12\x0fF\x85-@\x85,?\x85+>\x85*=\xe4\xf5<\x80p\x12\x0f\x16\x80k\x85=E\x85>F\xe5G\xc3\x13\xff\xe5E\xc3\x9fP\x02\x8fE\xe5H\xc3\x13\xff\xe5F\xc3\x9fP\x02\x8fF\xe5G\xc3\x13\xff\xfd\xe5E-\xfd\xe43\xfc\xe5D\x12\x0f\x90@\x05\xe5D\x9f\xf5E\xe5H\xc3\x13\xff\xfd\xe5F-\xfd\xe43\xfc\xe5C\x12\x0f\x90@\x05\xe5C\x9f\xf5F\x12\x06\xd7\x80\x14\x85@H\x85?G\x85>F\x85=E\x80\x06\x02\x06\xd7\x12\r~\x900$\xe5=\xf0\xa3\xe5>\xf0\xa3\xe5?\xf0\xa3\xe5@\xf0\xa3\xe5<\xf0\x900#\xe4\xf0"\xc0\xe0\xc0\x83\xc0\x82\xc0\xd0\x90?\x0c\xe0\xf52\xe520\xe3t06f\x90`\x19\xe0\xf5\n\xa3\xe0\xf5\x0b\x90`\x1d\xe0\xf5\x14\xa3\xe0\xf5\x15\x90`!\xe0\xf5\x0c\xa3\xe0\xf5\r\x90`)\xe0\xf5\x0e\xa3\xe0\xf5\x0f\x90`1\xe0\xf5\x10\xa3\xe0\xf5\x11\x90`9\xe0\xf5\x12\xa3\xe0\xf5\x130\x01\x0603\x03\xd3\x80\x01\xc3\x92\t0\x02\x0603\x03\xd3\x80\x01\xc3\x92\n03\x0c0\x03\t \x02\x06 \x01\x03\xd3\x80\x01\xc3\x92\x0b\x900\x01\xe0D@\xf0\xe0T\xbf\xf0\xe520\xe1\x1404\x11\x900"\xe0\xf5\x08\xe4\xf00\x00\x03\xd3\x80\x01\xc3\x92\x08\xe520\xe5\x12\x90V\xa1\xe0\xf5\t01\t0\x05\x03\xd3\x80\x01\xc3\x92\r\x90?\x0c\xe52\xf0\xd0\xd0\xd0\x82\xd0\x83\xd0\xe02\x90\x0e~\xe4\x93\xfet\x01\x93\xff\xc3\x90\x0e|t\x01\x93\x9f\xff\xe4\x93\x9e\xfe\xe4\x8f;\x8e:\xf59\xf58\xab;\xaa:\xa99\xa88\xafK\xfc\xfd\xfe\x12\x05(\x12\r\xe1\xe4{\xff\xfa\xf9\xf8\x12\x05\xb3\x12\r\xe1\x90\x0ei\xe4\x12\r\xf6\x12\r\xe1\xe4\x85J7\xf56\xf55\xf54\xaf7\xae6\xad5\xac4\xa3\x12\r\xf6\x8f7\x8e6\x8d5\x8c4\xe5;E7\xf5;\xe5:E6\xf5:\xe59E5\xf59\xe58E4\xf58\xe4\xf5"\xf5#\x85;1\x85:0\x859/\x858.\x02\x0fF\xe0\xa3\xe0u\xf0\x02\xa4\xff\xae\xf0\xc3\x08\xe6\x9f\xf6\x18\xe6\x9e\xf6"\xff\xe5\xf04`\x8f\x82\xf5\x83\xec\xf0"xR~\x00\xe6\xfc\x08\xe6\xfd\x02\x04\xc1\xe4\xfc\xfd\x12\x06\x99x\\\xe6\xc3\x13\xfe\x08\xe6\x13"xR\xe6\xfe\x08\xe6\xff\xe4\xfc\xfd"\xe7\xc4\xf8T\xf0\xc8h\xf7\t\xe7\xc4T\x0fH\xf7"\xe6\xfc\xedu\xf0\x04\xa4"\x12\x06|\x8fH\x8eG\x8dF\x8cE"\xe0\xfe\xa3\xe0\xfd\xee\xf6\xed\x08\xf6"\x13\xff\xc3\xe6\x9f\xff\x18\xe6\x9e\xfe"\xe6\xc3\x13\xf7\x08\xe6\x13\t\xf7"\xad9\xac8\xfa\xf9\xf8\x12\x05(\x8f;\x8e:\x8d9\x8c8\xab7\xaa6\xa95\xa84"\x93\xff\xe4\xfc\xfd\xfe\x12\x05(\x8f7\x8e6\x8d5\x8c4"x\x84\xe6\xfe\x08\xe6\xff\xe4\x8f7\x8e6\xf55\xf54"\x90\x0e\x8c\xe4\x93%\xe0$\n\xf8\xe6\xfe\x08\xe6\xff"\xe6\xfe\x08\xe6\xff\xe4\x8f;\x8e:\xf59\xf58"xN\xe6\xfe\x08\xe6\xff"\xef%\xe0$N\xf8\xe6\xfc\x08\xe6\xfd"x\x89\xef&\xf6\x18\xe46\xf6"u\x89\x03u\xa8\x01u\xb8\x04u4\xffu5\x0eu6\x15u7\r\x12\x0e\x9a\x12\x00\t\x12\x0f\x16\x12\x00\x06\xd2\x00\xd24\xd2\xafu4\xffu5\x0eu6Iu7\x03\x12\x0e\x9a0\x08\t\xc24\x12\x08\xcb\xc2\x08\xd240\x0b\t\xc26\x12\x02l\xc2\x0b\xd260\t\t\xc26\x12\x00\x0e\xc2\t\xd260\x0e\x03\x12\x06\xd705\xd3\x900)\xe5\x1e\xf0\xb4\x10\x05\x900#\xe4\xf0\xc25\x80\xc1\xe4\xf5K\x90\x0ez\x93\xff\xe4\x8f7\xf56\xf55\xf54\xaf7\xae6\xad5\xac4\x90\x0ej\x12\r\xf6\x8f7\x8e6\x8d5\x8c4\x90\x0er\x12\x06|\xefE7\xf57\xeeE6\xf56\xedE5\xf55\xecE4\xf54\xe4\xf5"\xf5#\x8571\x8560\x855/\x854.\x12\x0fF\xe4\xf5"\xf5#\x90\x0er\x12\r\xea\x12\x0fF\xe4\xf5"\xf5#\x90\x0en\x12\r\xea\x02\x0fF\xe5@$\xf2\xf57\xe5?4C\xf56\xe5>4\xa2\xf55\xe5=4(\xf54\xe57\xff\xe4\xfe\xfd\xfcx\x18\x12\x06i\x8f@\x8e?\x8d>\x8c=\xe57T\xa0\xff\xe56\xfe\xe4\xfd\xfcx\x07\x12\x06Vx\x10\x12\x0f\x9a\xe4\xff\xfe\xe55\xfd\xe4\xfcx\x0e\x12\x06V\x12\x0f\x9d\xe4\xff\xfe\xfd\xe54\xfcx\x18\x12\x06Vx\x08\x12\x0f\x9a"\x8f;\x8e:\x8d9\x8c8"\x12\x06|\x8f1\x8e0\x8d/\x8c."\x93\xf9\xf8\x02\x06i\x00\x00\x00\x00\x12\x01\x17\x081\x15STD \x13\x01\x10\x01V@\x1a0)~\x000\x04 \xdf0\x05@\xbfP\x03\x00\xfdP\'\x01\xfe`\x00\x11\x00?\x050\x00?\x06"\x00?\x01*\x00?\x02\x00\x006\x06\x07\x00?\x0b\x0f\xf0\x00\x00\x00\x000\x01@\xbf0\x01\x00\xbf0)p\x00:\x00\x00\xff:\x00\x00\xff6\x036\x02ADX \x18\x10\n\x04\x04\x00\x03\xffd\x00\x00\x80\x00\x00\x00\x00\x00\x00\x02\x04\x06\x06\x00\x03Q\x00zP<(\x1e\x10\x10P-(\x16\x10\x10\x02\x00\x10\x0c\x10\x04\x0cn\x06\x05\x00\xa5Z\x00\xae5\xaf6\xe4\xfd\xed\xc3\x957P3\x12\x0f\xe2\xe4\x93\xf58t\x01\x93\xf59E8`#\x859\x82\x858\x83\xe0\xfc\x12\x0f\xe2t\x03\x93R\x04\x12\x0f\xe2t\x02\x93B\x04\x859\x82\x858\x83\xec\xf0\r\x80\xc7"x\xbe\xe6\xd3\x08\xff\xe6d\x80\xf8\xefd\x80\x98"\x93\xff~\x00\xe6\xfc\x08\xe6\xfd\x12\x04\xc1x\xc1\xe6\xfc\x08\xe6\xfd\xd3\xef\x9d\xee\x9c"x\xbd\xd3\xe6d\x80\x94\x80"%\xe0$\n\xf8\xe6\xfe\x08\xe6\xff"\xe5<\xd3\x94\x00@\x0b\x90\x0e\x88\x12\x0b\xf1\x90\x0e\x86\x80\t\x90\x0e\x82\x12\x0b\xf1\x90\x0e\x80\xe4\x93\xf5D\xa3\xe4\x93\xf5C\xd2\x060\x06\x03\xd3\x80\x01\xc3\x92\x0e"\xa2\xaf\x922\xc2\xaf\xe5#E"\x90\x0e]`\x0e\x12\x0f\xcb\xe0\xf5,\x12\x0f\xc8\xe0\xf5-\x80\x0c\x12\x0f\xcb\xe50\xf0\x12\x0f\xc8\xe51\xf0\xa22\x92\xaf"\xd2\x01\xc2\x02\xe4\xf5\x1f\xf5\x1e\xd25\xd23\xd26\xd2\x01\xc2\x02\xf5\x1f\xf5\x1e\xd25\xd23"\xfb\xd3\xed\x9bt\x80\xf8l\x98"\x12\x06i\xe5@/\xf5@\xe5?>\xf5?\xe5>=\xf5>\xe5=<\xf5="\xc0\xe0\xc0\x83\xc0\x82\x90?\r\xe0\xf53\xe53\xf0\xd0\x82\xd0\x83\xd0\xe02\x90\x0e_\xe4\x93\xfet\x01\x93\xf5\x82\x8e\x83"x\x7f\xe4\xf6\xd8\xfdu\x81\xcd\x02\x0c\x98\x8f\x82\x8e\x83u\xf0\x04\xed\x02\x06\xa5' + # fmt: on @@ -1331,3 +1352,55 @@ def strobe_pin(self, value): self._write_register(_PAD_OUTPUT_ENABLE00, 0x02) self._write_register(_PAD_SELECT00, 0x02) self._write_register(_PAD_OUTPUT_VALUE00, bool(value) << 1) + + def autofocus_init(self): + self._write_register(0x3000, 0x20) # reset + # load firmware + for addr,val in enumerate(autofocus_firmware): + #print(hex(addr), hex(val)) + self._write_register(0x8000+addr, val) + self._write_list(_autofocus_firmware_load) + for _ in range(100): + if self.autofocus_status == AUTOFOCUS_STAT_IDLE: + break + time.sleep(0.01) + else: + raise RuntimeError("Timed out after trying to load autofocus firmware") + return True + + @property + def autofocus_status(self): + return self._read_register(0x3029) + + def autofocus(self): + print("Auto focusing?") + self._write_register(0x3023, 0x01) # clear command ack + self._write_register(0x3022, 0x08) # release focus + for _ in range(100): + if self._read_register(0x3032) == 0x0: # command is finished + break + time.sleep(0.01) + else: + raise RuntimeError("Timed out trying to run autofocus") + + self._write_register(0x3023, 0x01) + self._write_register(0x3022, 0x04) + for _ in range(100): + if self._read_register(0x3032) == 0x0: # command is finished + break + time.sleep(0.01) + else: + raise RuntimeError("Timed out trying to run autofocus") + + """ + for _ in range(100): + focus_stat = self._read_register(0x3029) + print("Focus status 0x3029:", hex(focus_stat)) + if focus_stat == 0x10: # we focused + break + time.sleep(0.01) + else: + raise RuntimeError("Timed out trying to autofocus") + """ + + return True From f33a3a656ea45f8d4f722280205ddfc7799e837d Mon Sep 17 00:00:00 2001 From: lady ada Date: Sun, 17 Oct 2021 17:27:18 -0400 Subject: [PATCH 7/7] move firmware to bin file --- adafruit_ov5640.py | 25 ++++++------------------- examples/ov5640_autofocus.bin | Bin 0 -> 4077 bytes 2 files changed, 6 insertions(+), 19 deletions(-) create mode 100644 examples/ov5640_autofocus.bin diff --git a/adafruit_ov5640.py b/adafruit_ov5640.py index 8f1443b..5b43bc2 100644 --- a/adafruit_ov5640.py +++ b/adafruit_ov5640.py @@ -741,8 +741,6 @@ AUTOFOCUS_STAT_FOCUSING = 0x00 AUTOFOCUS_STAT_FOCUSED = 0x10 -autofocus_firmware = b'\x02\x0f\xd6\x02\n9\xc2\x01""\x00\x02\x0f\xb2\xe5\x1fpr\xf5\x1e\xd25\xff\xef%\xe0$N\xf8\xe4\xf6\x08\xf6\x0f\xbf4\xf2\x90\x0e\x93\xe4\x93\xff\xe5K\xc3\x9fP\x04\x7f\x05\x80\x02\x7f\xfbx\xbd\xa6\x07\x12\x0f\x04@\x04\x7f\x03\x80\x02\x7f0x\xbc\xa6\x07\xe6\x18\xf6\x08\xe6x\xb9\xf6x\xbc\xe6x\xba\xf6x\xbfv3\xe4\x08\xf6x\xb8v\x01uJ\x02x\xb6\xf6\x08\xf6t\xffx\xc1\xf6\x08\xf6u\x1f\x01x\xbc\xe6u\xf0\x05\xa4\xf5K\x12\n\xff\xc27"x\xb8\xe6\xd3\x94\x00@\x02\x16"\xe5\x1f\xb4\x05#\xe4\xf5\x1f\xc2\x01x\xb6\xe6\xfe\x08\xe6\xffxN\xa6\x06\x08\xa6\x07\xa27\xe43\xf5<\x900(\xf0u\x1e\x10\xd25"\xe5Ku\xf0\x05\x84x\xbc\xf6\x90\x0e\x8c\xe4\x93\xff%\xe0$\n\xf8\xe6\xfc\x08\xe6\xfdx\xbc\xe6%\xe0$N\xf8\xa6\x04\x08\xa6\x05\xef\x12\x0f\x0b\xd3x\xb7\x96\xee\x18\x96@\rx\xbc\xe6x\xb9\xf6x\xb6\xa6\x06\x08\xa6\x07\x90\x0e\x8c\xe4\x93\x12\x0f\x0b\xc3x\xc2\x96\xee\x18\x96P\rx\xbc\xe6x\xba\xf6x\xc1\xa6\x06\x08\xa6\x07x\xb6\xe6\xfe\x08\xe6\xc3x\xc2\x96\xff\xee\x18\x96x\xc3\xf6\x08\xa6\x07\x90\x0e\x95\xe4\x18\x12\x0e\xe9@\x02\xd27x\xbc\xe6\x08&\x08\xf6\xe5\x1fd\x01pJ\xe6\xc3x\xc0\x12\x0e\xdf@\x05\x12\x0e\xda@9\x12\x0f\x02@\x04\x7f\xfe\x80\x02\x7f\x02x\xbd\xa6\x07x\xb9\xe6$\x03x\xbf\xf6x\xb9\xe6$\xfdx\xc0\xf6\x12\x0f\x02@\x06x\xc0\xe6\xff\x80\x04x\xbf\xe6\xffx\xbe\xa6\x07u\x1f\x02x\xb8v\x01\x02\x02J\xe5\x1fd\x02`\x03\x02\x02*x\xbe\xe6\xff\xc3x\xc0\x12\x0e\xe0@\x08\x12\x0e\xdaP\x03\x02\x02(\x12\x0f\x02@\x04\x7f\xff\x80\x02\x7f\x01x\xbd\xa6\x07x\xb9\xe6\x04x\xbf\xf6x\xb9\xe6\x14x\xc0\xf6\x18\x12\x0f\x04@\x04\xe6\xff\x80\x02\x7f\x00x\xbf\xa6\x07\xd3\x08\xe6d\x80\x94\x80@\x04\xe6\xff\x80\x02\x7f\x00x\xc0\xa6\x07\xc3\x18\xe6d\x80\x94\xb3P\x04\xe6\xff\x80\x02\x7f3x\xbf\xa6\x07\xc3\x08\xe6d\x80\x94\xb3P\x04\xe6\xff\x80\x02\x7f3x\xc0\xa6\x07\x12\x0f\x02@\x06x\xc0\xe6\xff\x80\x04x\xbf\xe6\xffx\xbe\xa6\x07u\x1f\x03x\xb8v\x01\x80 \xe5\x1fd\x03p&x\xbe\xe6\xff\xc3x\xc0\x12\x0e\xe0@\x05\x12\x0e\xda@\tx\xb9\xe6x\xbe\xf6u\x1f\x04x\xbe\xe6u\xf0\x05\xa4\xf5K\x02\n\xff\xe5\x1f\xb4\x04\x10\x90\x0e\x94\xe4x\xc3\x12\x0e\xe9@\x02\xd27u\x1f\x05"0\x01\x03\x02\x04\xc00\x02\x03\x02\x04\xc0\x90Q\xa5\xe0x\x93\xf6\xa3\xe0\x08\xf6\xa3\xe0\x08\xf6\xe5\x1fp\xa3\xe0\xf5?\xa3\xe0\xf5@\xa3\xe0\xf5<\xd24\xe5A\x12\x06\xb1\t1\x03\t5\x04\t;\x05\t>\x06\tA\x07\tJ\x08\t[\x12\ts\x18\t\x89\x19\t^\x1a\tj\x1b\t\xad\x80\t\xb2\x81\n\x1d\x8f\n\t\x90\n\x1d\x91\n\x1d\x92\n\x1d\x93\n\x1d\x94\n\x1d\x98\n\x17\x9f\n\x1a\xec\x00\x00\n8\x12\x0ft"\x12\x0ft\xd2\x03"\xd2\x03"\xc2\x03"\xa27\xe43\xf5<\x02\n\x1d\xc2\x01\xc2\x02\xc2\x03\x12\r\ru\x1ep\xd25\x02\n\x1d\x02\n\x04\x85@J\x85/\x85=.\x12\x0fF\x80\x1fu"\x00u#\x01t\xff\xf5-\xf5,\xf5+\xf5*\x12\x0fF\x85-@\x85,?\x85+>\x85*=\xe4\xf5<\x80p\x12\x0f\x16\x80k\x85=E\x85>F\xe5G\xc3\x13\xff\xe5E\xc3\x9fP\x02\x8fE\xe5H\xc3\x13\xff\xe5F\xc3\x9fP\x02\x8fF\xe5G\xc3\x13\xff\xfd\xe5E-\xfd\xe43\xfc\xe5D\x12\x0f\x90@\x05\xe5D\x9f\xf5E\xe5H\xc3\x13\xff\xfd\xe5F-\xfd\xe43\xfc\xe5C\x12\x0f\x90@\x05\xe5C\x9f\xf5F\x12\x06\xd7\x80\x14\x85@H\x85?G\x85>F\x85=E\x80\x06\x02\x06\xd7\x12\r~\x900$\xe5=\xf0\xa3\xe5>\xf0\xa3\xe5?\xf0\xa3\xe5@\xf0\xa3\xe5<\xf0\x900#\xe4\xf0"\xc0\xe0\xc0\x83\xc0\x82\xc0\xd0\x90?\x0c\xe0\xf52\xe520\xe3t06f\x90`\x19\xe0\xf5\n\xa3\xe0\xf5\x0b\x90`\x1d\xe0\xf5\x14\xa3\xe0\xf5\x15\x90`!\xe0\xf5\x0c\xa3\xe0\xf5\r\x90`)\xe0\xf5\x0e\xa3\xe0\xf5\x0f\x90`1\xe0\xf5\x10\xa3\xe0\xf5\x11\x90`9\xe0\xf5\x12\xa3\xe0\xf5\x130\x01\x0603\x03\xd3\x80\x01\xc3\x92\t0\x02\x0603\x03\xd3\x80\x01\xc3\x92\n03\x0c0\x03\t \x02\x06 \x01\x03\xd3\x80\x01\xc3\x92\x0b\x900\x01\xe0D@\xf0\xe0T\xbf\xf0\xe520\xe1\x1404\x11\x900"\xe0\xf5\x08\xe4\xf00\x00\x03\xd3\x80\x01\xc3\x92\x08\xe520\xe5\x12\x90V\xa1\xe0\xf5\t01\t0\x05\x03\xd3\x80\x01\xc3\x92\r\x90?\x0c\xe52\xf0\xd0\xd0\xd0\x82\xd0\x83\xd0\xe02\x90\x0e~\xe4\x93\xfet\x01\x93\xff\xc3\x90\x0e|t\x01\x93\x9f\xff\xe4\x93\x9e\xfe\xe4\x8f;\x8e:\xf59\xf58\xab;\xaa:\xa99\xa88\xafK\xfc\xfd\xfe\x12\x05(\x12\r\xe1\xe4{\xff\xfa\xf9\xf8\x12\x05\xb3\x12\r\xe1\x90\x0ei\xe4\x12\r\xf6\x12\r\xe1\xe4\x85J7\xf56\xf55\xf54\xaf7\xae6\xad5\xac4\xa3\x12\r\xf6\x8f7\x8e6\x8d5\x8c4\xe5;E7\xf5;\xe5:E6\xf5:\xe59E5\xf59\xe58E4\xf58\xe4\xf5"\xf5#\x85;1\x85:0\x859/\x858.\x02\x0fF\xe0\xa3\xe0u\xf0\x02\xa4\xff\xae\xf0\xc3\x08\xe6\x9f\xf6\x18\xe6\x9e\xf6"\xff\xe5\xf04`\x8f\x82\xf5\x83\xec\xf0"xR~\x00\xe6\xfc\x08\xe6\xfd\x02\x04\xc1\xe4\xfc\xfd\x12\x06\x99x\\\xe6\xc3\x13\xfe\x08\xe6\x13"xR\xe6\xfe\x08\xe6\xff\xe4\xfc\xfd"\xe7\xc4\xf8T\xf0\xc8h\xf7\t\xe7\xc4T\x0fH\xf7"\xe6\xfc\xedu\xf0\x04\xa4"\x12\x06|\x8fH\x8eG\x8dF\x8cE"\xe0\xfe\xa3\xe0\xfd\xee\xf6\xed\x08\xf6"\x13\xff\xc3\xe6\x9f\xff\x18\xe6\x9e\xfe"\xe6\xc3\x13\xf7\x08\xe6\x13\t\xf7"\xad9\xac8\xfa\xf9\xf8\x12\x05(\x8f;\x8e:\x8d9\x8c8\xab7\xaa6\xa95\xa84"\x93\xff\xe4\xfc\xfd\xfe\x12\x05(\x8f7\x8e6\x8d5\x8c4"x\x84\xe6\xfe\x08\xe6\xff\xe4\x8f7\x8e6\xf55\xf54"\x90\x0e\x8c\xe4\x93%\xe0$\n\xf8\xe6\xfe\x08\xe6\xff"\xe6\xfe\x08\xe6\xff\xe4\x8f;\x8e:\xf59\xf58"xN\xe6\xfe\x08\xe6\xff"\xef%\xe0$N\xf8\xe6\xfc\x08\xe6\xfd"x\x89\xef&\xf6\x18\xe46\xf6"u\x89\x03u\xa8\x01u\xb8\x04u4\xffu5\x0eu6\x15u7\r\x12\x0e\x9a\x12\x00\t\x12\x0f\x16\x12\x00\x06\xd2\x00\xd24\xd2\xafu4\xffu5\x0eu6Iu7\x03\x12\x0e\x9a0\x08\t\xc24\x12\x08\xcb\xc2\x08\xd240\x0b\t\xc26\x12\x02l\xc2\x0b\xd260\t\t\xc26\x12\x00\x0e\xc2\t\xd260\x0e\x03\x12\x06\xd705\xd3\x900)\xe5\x1e\xf0\xb4\x10\x05\x900#\xe4\xf0\xc25\x80\xc1\xe4\xf5K\x90\x0ez\x93\xff\xe4\x8f7\xf56\xf55\xf54\xaf7\xae6\xad5\xac4\x90\x0ej\x12\r\xf6\x8f7\x8e6\x8d5\x8c4\x90\x0er\x12\x06|\xefE7\xf57\xeeE6\xf56\xedE5\xf55\xecE4\xf54\xe4\xf5"\xf5#\x8571\x8560\x855/\x854.\x12\x0fF\xe4\xf5"\xf5#\x90\x0er\x12\r\xea\x12\x0fF\xe4\xf5"\xf5#\x90\x0en\x12\r\xea\x02\x0fF\xe5@$\xf2\xf57\xe5?4C\xf56\xe5>4\xa2\xf55\xe5=4(\xf54\xe57\xff\xe4\xfe\xfd\xfcx\x18\x12\x06i\x8f@\x8e?\x8d>\x8c=\xe57T\xa0\xff\xe56\xfe\xe4\xfd\xfcx\x07\x12\x06Vx\x10\x12\x0f\x9a\xe4\xff\xfe\xe55\xfd\xe4\xfcx\x0e\x12\x06V\x12\x0f\x9d\xe4\xff\xfe\xfd\xe54\xfcx\x18\x12\x06Vx\x08\x12\x0f\x9a"\x8f;\x8e:\x8d9\x8c8"\x12\x06|\x8f1\x8e0\x8d/\x8c."\x93\xf9\xf8\x02\x06i\x00\x00\x00\x00\x12\x01\x17\x081\x15STD \x13\x01\x10\x01V@\x1a0)~\x000\x04 \xdf0\x05@\xbfP\x03\x00\xfdP\'\x01\xfe`\x00\x11\x00?\x050\x00?\x06"\x00?\x01*\x00?\x02\x00\x006\x06\x07\x00?\x0b\x0f\xf0\x00\x00\x00\x000\x01@\xbf0\x01\x00\xbf0)p\x00:\x00\x00\xff:\x00\x00\xff6\x036\x02ADX \x18\x10\n\x04\x04\x00\x03\xffd\x00\x00\x80\x00\x00\x00\x00\x00\x00\x02\x04\x06\x06\x00\x03Q\x00zP<(\x1e\x10\x10P-(\x16\x10\x10\x02\x00\x10\x0c\x10\x04\x0cn\x06\x05\x00\xa5Z\x00\xae5\xaf6\xe4\xfd\xed\xc3\x957P3\x12\x0f\xe2\xe4\x93\xf58t\x01\x93\xf59E8`#\x859\x82\x858\x83\xe0\xfc\x12\x0f\xe2t\x03\x93R\x04\x12\x0f\xe2t\x02\x93B\x04\x859\x82\x858\x83\xec\xf0\r\x80\xc7"x\xbe\xe6\xd3\x08\xff\xe6d\x80\xf8\xefd\x80\x98"\x93\xff~\x00\xe6\xfc\x08\xe6\xfd\x12\x04\xc1x\xc1\xe6\xfc\x08\xe6\xfd\xd3\xef\x9d\xee\x9c"x\xbd\xd3\xe6d\x80\x94\x80"%\xe0$\n\xf8\xe6\xfe\x08\xe6\xff"\xe5<\xd3\x94\x00@\x0b\x90\x0e\x88\x12\x0b\xf1\x90\x0e\x86\x80\t\x90\x0e\x82\x12\x0b\xf1\x90\x0e\x80\xe4\x93\xf5D\xa3\xe4\x93\xf5C\xd2\x060\x06\x03\xd3\x80\x01\xc3\x92\x0e"\xa2\xaf\x922\xc2\xaf\xe5#E"\x90\x0e]`\x0e\x12\x0f\xcb\xe0\xf5,\x12\x0f\xc8\xe0\xf5-\x80\x0c\x12\x0f\xcb\xe50\xf0\x12\x0f\xc8\xe51\xf0\xa22\x92\xaf"\xd2\x01\xc2\x02\xe4\xf5\x1f\xf5\x1e\xd25\xd23\xd26\xd2\x01\xc2\x02\xf5\x1f\xf5\x1e\xd25\xd23"\xfb\xd3\xed\x9bt\x80\xf8l\x98"\x12\x06i\xe5@/\xf5@\xe5?>\xf5?\xe5>=\xf5>\xe5=<\xf5="\xc0\xe0\xc0\x83\xc0\x82\x90?\r\xe0\xf53\xe53\xf0\xd0\x82\xd0\x83\xd0\xe02\x90\x0e_\xe4\x93\xfet\x01\x93\xf5\x82\x8e\x83"x\x7f\xe4\xf6\xd8\xfdu\x81\xcd\x02\x0c\x98\x8f\x82\x8e\x83u\xf0\x04\xed\x02\x06\xa5' - # fmt: on @@ -1353,12 +1351,13 @@ def strobe_pin(self, value): self._write_register(_PAD_SELECT00, 0x02) self._write_register(_PAD_OUTPUT_VALUE00, bool(value) << 1) - def autofocus_init(self): + def autofocus_init(self, firmfilename): self._write_register(0x3000, 0x20) # reset - # load firmware - for addr,val in enumerate(autofocus_firmware): - #print(hex(addr), hex(val)) - self._write_register(0x8000+addr, val) + with open(firmfilename, mode='rb') as file: + firmware = file.read() + for addr,val in enumerate(firmware): + self._write_register(0x8000+addr, val) + self._write_list(_autofocus_firmware_load) for _ in range(100): if self.autofocus_status == AUTOFOCUS_STAT_IDLE: @@ -1373,7 +1372,6 @@ def autofocus_status(self): return self._read_register(0x3029) def autofocus(self): - print("Auto focusing?") self._write_register(0x3023, 0x01) # clear command ack self._write_register(0x3022, 0x08) # release focus for _ in range(100): @@ -1392,15 +1390,4 @@ def autofocus(self): else: raise RuntimeError("Timed out trying to run autofocus") - """ - for _ in range(100): - focus_stat = self._read_register(0x3029) - print("Focus status 0x3029:", hex(focus_stat)) - if focus_stat == 0x10: # we focused - break - time.sleep(0.01) - else: - raise RuntimeError("Timed out trying to autofocus") - """ - return True diff --git a/examples/ov5640_autofocus.bin b/examples/ov5640_autofocus.bin new file mode 100644 index 0000000000000000000000000000000000000000..06e4b5adbc39a1058b79faf9cdc0e57445228280 GIT binary patch literal 4077 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 PbR1{{0aX4dX?gR%8vtA( literal 0 HcmV?d00001