Skip to content

Commit 4e08cf6

Browse files
authored
Merge pull request #4 from adafruit/jpeg-mode
Jpeg mode
2 parents 3adf83d + 8441f09 commit 4e08cf6

7 files changed

+361
-72
lines changed

adafruit_ov2640.py

Lines changed: 101 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@
106106

107107
OV2640_COLOR_RGB = 0
108108
OV2640_COLOR_YUV = 1
109+
OV2640_COLOR_JPEG = 2
109110

110111
_IMAGE_MODE_Y8_DVP_EN = const(0x40)
111112
_IMAGE_MODE_JPEG_EN = const(0x10)
@@ -886,55 +887,70 @@
886887
]
887888
)
888889

889-
# _ov2640_settings_jpeg3 = bytes([
890-
# _BANK_SEL, _BANK_DSP,
891-
# _RESET, _RESET_JPEG | _RESET_DVP,
892-
# _IMAGE_MODE, _IMAGE_MODE_JPEG_EN | _IMAGE_MODE_HREF_VSYNC,
893-
# 0xD7, 0x03,
894-
# 0xE1, 0x77,
895-
# 0xE5, 0x1F,
896-
# 0xD9, 0x10,
897-
# 0xDF, 0x80,
898-
# 0x33, 0x80,
899-
# 0x3C, 0x10,
900-
# 0xEB, 0x30,
901-
# 0xDD, 0x7F,
902-
# _RESET, 0x00,
903-
# ])
904-
905-
_ov2640_settings_yuv422 = bytes(
906-
[
907-
_BANK_SEL,
908-
_BANK_DSP,
909-
_RESET,
910-
_RESET_DVP,
911-
_IMAGE_MODE,
912-
_IMAGE_MODE_YUV422,
913-
0xD7,
914-
0x01,
915-
0xE1,
916-
0x67,
917-
_RESET,
918-
0x00,
919-
]
920-
)
921-
922-
_ov2640_settings_rgb565 = bytes(
923-
[
924-
_BANK_SEL,
925-
_BANK_DSP,
926-
_RESET,
927-
_RESET_DVP,
928-
_IMAGE_MODE,
929-
_IMAGE_MODE_RGB565,
930-
0xD7,
931-
0x03,
932-
0xE1,
933-
0x77,
934-
_RESET,
935-
0x00,
936-
]
937-
)
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+
}
938954

939955

940956
class _RegBits:
@@ -1114,6 +1130,19 @@ def capture(self, buf):
11141130
captured image. Note that this can be a ulab array or a displayio Bitmap.
11151131
"""
11161132
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
11171146

11181147
@property
11191148
def mclk_frequency(self):
@@ -1140,17 +1169,15 @@ def colorspace(self):
11401169
@colorspace.setter
11411170
def colorspace(self, colorspace):
11421171
self._colorspace = colorspace
1143-
self._write_list(
1144-
_ov2640_settings_rgb565
1145-
if colorspace == OV2640_COLOR_RGB
1146-
else _ov2640_settings_yuv422
1147-
)
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)
11481179
# written twice?
1149-
self._write_list(
1150-
_ov2640_settings_rgb565
1151-
if colorspace == OV2640_COLOR_RGB
1152-
else _ov2640_settings_yuv422
1153-
)
1180+
self._write_list(settings)
11541181
time.sleep(0.01)
11551182

11561183
def deinit(self):
@@ -1168,8 +1195,8 @@ def size(self):
11681195
"""Get or set the captured image size, one of the ``OV2640_SIZE_`` constants."""
11691196
return self._size
11701197

1171-
@size.setter
1172-
def size(self, size):
1198+
def _set_size_and_colorspace(self):
1199+
size = self._size
11731200
width, height, ratio = _resolution_info[size]
11741201
offset_x, offset_y, max_x, max_y = _ratio_table[ratio]
11751202
mode = _OV2640_MODE_UXGA
@@ -1190,7 +1217,11 @@ def size(self, size):
11901217
offset_y //= 2
11911218

11921219
self._set_window(mode, offset_x, offset_y, max_x, max_y, width, height)
1220+
1221+
@size.setter
1222+
def size(self, size):
11931223
self._size = size
1224+
self._set_size_and_colorspace()
11941225

11951226
def _set_flip(self):
11961227
bits = 0
@@ -1267,15 +1298,19 @@ def _set_window(
12671298
((height >> 6) & 0x04) | ((width >> 8) & 0x03),
12681299
]
12691300

1270-
pclk_auto = 1
1301+
pclk_auto = 0
12711302
pclk_div = 8
12721303
clk_2x = 0
1273-
clk_div = 7
1304+
clk_div = 0
1305+
1306+
if self._colorspace != OV2640_COLOR_JPEG:
1307+
pclk_auto = 1
1308+
clk_div = 7
12741309

12751310
if mode == _OV2640_MODE_CIF:
12761311
regs = _ov2640_settings_to_cif
1277-
# if pixformat is not jpeg:
1278-
clk_div = 3
1312+
if self._colorspace != OV2640_COLOR_JPEG:
1313+
clk_div = 3
12791314
elif mode == _OV2640_MODE_SVGA:
12801315
regs = _ov2640_settings_to_svga
12811316
else:
@@ -1294,7 +1329,7 @@ def _set_window(
12941329
time.sleep(0.01)
12951330

12961331
# Reestablish colorspace
1297-
self.colorspace = self._colorspace
1332+
self._set_colorspace()
12981333

12991334
# Reestablish test pattern
13001335
if self._test_pattern:

docs/examples.rst

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,39 @@ Ensure your device works with this simple test.
66
.. literalinclude:: ../examples/ov2640_simpletest.py
77
:caption: examples/ov2640_simpletest.py
88
:linenos:
9+
10+
Display an image from the camera on the Kaluga 1.3 board, if it is fitted with an ili9341 display.
11+
12+
.. literalinclude:: ../examples/ov2640_displayio_kaluga1_3_ili9341.py
13+
:caption: ../examples/ov2640_displayio_kaluga1_3_ili9341.py
14+
:linenos:
15+
16+
Display an image from the camera on the Kaluga 1.3 board, if it is fitted with an st7789 display.
17+
18+
.. literalinclude:: ../examples/ov2640_displayio_kaluga1_3_st7789.py
19+
:caption: ../examples/ov2640_displayio_kaluga1_3_st7789.py
20+
:linenos:
21+
22+
Display an image from the camera connected to a Raspberry Pi Pico with an st7789 2" display
23+
24+
.. literalinclude:: ../examples/ov2640_displayio_pico_st7789_2in.py
25+
:caption: ../examples/ov2640_displayio_pico_st7789_2in.py
26+
:linenos:
27+
28+
Save an image to internal flash on Kaluga 1.3
29+
30+
.. literalinclude:: ../examples/ov2640_jpeg_kaluga1_3.py
31+
:caption: ../examples/ov2640_jpeg_kaluga1_3.py
32+
:linenos:
33+
34+
``boot.py`` for the above program
35+
36+
.. literalinclude:: ../examples/ov2640_jpeg_kaluga1_3_boot.py
37+
:caption: ../examples/ov2640_jpeg_kaluga1_3_boot.py
38+
:linenos:
39+
40+
Preview images on LCD then save to SD on Kaluga 1.3 fitted with an ili9341 display
41+
42+
.. literalinclude:: ../examples/ov2640_jpeg_sd_kaluga1_3.py
43+
:caption: ../examples/ov2640_jpeg_sd_kaluga1_3.py
44+
:linenos:

examples/ov2640_displayio_kaluga1_3_ili9341.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@
1212
rotation=90! This demo is for the ili9341. If the display is garbled, try adding
1313
rotation=90, or try modifying it to use ST7799.
1414
15-
The camera included with the Kaluga development kit is the incompatible OV2640,
16-
it won't work.
17-
1815
The audio board must be mounted between the Kaluga and the LCD, it provides the
1916
I2C pull-ups(!)
2017
"""

examples/ov2640_displayio_kaluga1_3_st7789.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@
1313
of straight lines, it may be an ili9341. If it has a bunch of wiggly traces,
1414
it may be an st7789. If in doubt, try both demos.
1515
16-
The camera included with the Kaluga development kit is the incompatible OV2640,
17-
it won't work.
18-
1916
The audio board must be mounted between the Kaluga and the LCD, it provides the
2017
I2C pull-ups(!)
2118
"""

examples/ov2640_jpeg_kaluga1_3.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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: Unlicense
5+
6+
"""
7+
The Kaluga development kit comes in two versions (v1.2 and v1.3); this demo is
8+
tested on v1.3.
9+
10+
The audio board must be mounted between the Kaluga and the LCD, it provides the
11+
I2C pull-ups(!)
12+
13+
You also need to place ov2640_jpeg_kaluga1_3_boot.py at CIRCUITPY/boot.py
14+
and reset the board to make the internal flash readable by CircuitPython.
15+
You can make CIRCUITPY readable from your PC by booting CircuitPython in
16+
safe mode or holding the "MODE" button on the audio daughterboard while
17+
powering on or resetting the board.
18+
"""
19+
20+
import board
21+
import busio
22+
import adafruit_ov2640
23+
24+
25+
bus = busio.I2C(scl=board.CAMERA_SIOC, sda=board.CAMERA_SIOD)
26+
cam = adafruit_ov2640.OV2640(
27+
bus,
28+
data_pins=board.CAMERA_DATA,
29+
clock=board.CAMERA_PCLK,
30+
vsync=board.CAMERA_VSYNC,
31+
href=board.CAMERA_HREF,
32+
mclk=board.CAMERA_XCLK,
33+
mclk_frequency=20_000_000,
34+
size=adafruit_ov2640.OV2640_SIZE_QVGA,
35+
)
36+
37+
pid = cam.product_id
38+
ver = cam.product_version
39+
print(f"Detected pid={pid:x} ver={ver:x}")
40+
# cam.test_pattern = True
41+
42+
cam.colorspace = adafruit_ov2640.OV2640_COLOR_JPEG
43+
b = bytearray(cam.capture_buffer_size)
44+
jpeg = cam.capture(b)
45+
46+
print(f"Captured {len(jpeg)} bytes of jpeg data")
47+
try:
48+
with open("/jpeg.jpg", "wb") as f:
49+
f.write(jpeg)
50+
except OSError as e:
51+
print(e)
52+
print(
53+
"A 'read-only filesystem' error occurs if you did not correctly install\nov2640_jpeg_kaluga1_3_boot.py as CIRCUITPY/boot.py and reset the board"
54+
)
55+
print("Wrote to CIRCUITPY/jpeg.jpg")
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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: Unlicense
5+
"""Use this file as CIRCUITPY/boot.py in conjunction with ov2640_jpeg_kaluga1_3.py
6+
7+
It makes the CIRCUITPY filesystem writable to CircuitPython
8+
(and read-only to the PC) unless the "MODE" button on the audio
9+
daughterboard is held while the board is powered on or reset.
10+
"""
11+
12+
import analogio
13+
import board
14+
import storage
15+
16+
V_MODE = 1.98
17+
V_RECORD = 2.41
18+
19+
a = analogio.AnalogIn(board.IO6)
20+
a_voltage = a.value * a.reference_voltage / 65535
21+
if abs(a_voltage - V_MODE) > 0.05: # If mode is NOT pressed...
22+
print("storage writable by CircuitPython")
23+
storage.remount("/", readonly=False)

0 commit comments

Comments
 (0)