Skip to content

Commit 2c300b0

Browse files
committed
Add a "direct LCD access" example
The "direct LCD access" example shows how to copy the image directly to an LCD of the right resolution. It achieves a somewhat higher framerate (4.1fps vs 2.5fps) but is not compatible with displayio.
1 parent 756ac6b commit 2c300b0

File tree

1 file changed

+176
-0
lines changed

1 file changed

+176
-0
lines changed
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
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+
The v1.3 development kit's LCD can have one of two chips, the ili9341 or
14+
st7789. Furthermore, there are at least 2 ILI9341 variants, which differ
15+
by rotation. This example is written for one if the ILI9341 variants,
16+
the one which usually uses rotation=90 to get a landscape display.
17+
18+
This example also requires an SD card breakout wired as follows:
19+
* IO18: SD Clock Input
20+
* IO17: SD Serial Output (MISO)
21+
* IO14: SD Serial Input (MOSI)
22+
* IO12: SD Chip Select
23+
24+
Insert a CircuitPython-compatible SD card before powering on the Kaluga.
25+
Press the "Record" button on the audio daughterboard to take a photo.
26+
"""
27+
28+
import os
29+
import struct
30+
import time
31+
32+
import analogio
33+
import board
34+
import busio
35+
import displayio
36+
import sdcardio
37+
import storage
38+
from adafruit_ili9341 import ILI9341
39+
import adafruit_ov2640
40+
41+
V_MODE = 1.98
42+
V_RECORD = 2.41
43+
44+
a = analogio.AnalogIn(board.IO6)
45+
46+
# Release any resources currently in use for the displays
47+
displayio.release_displays()
48+
49+
spi = busio.SPI(MOSI=board.LCD_MOSI, clock=board.LCD_CLK)
50+
display_bus = displayio.FourWire(
51+
spi,
52+
command=board.LCD_D_C,
53+
chip_select=board.LCD_CS,
54+
reset=board.LCD_RST,
55+
baudrate=80_000_000,
56+
)
57+
_INIT_SEQUENCE = (
58+
b"\x01\x80\x80" # Software reset then delay 0x80 (128ms)
59+
b"\xEF\x03\x03\x80\x02"
60+
b"\xCF\x03\x00\xC1\x30"
61+
b"\xED\x04\x64\x03\x12\x81"
62+
b"\xE8\x03\x85\x00\x78"
63+
b"\xCB\x05\x39\x2C\x00\x34\x02"
64+
b"\xF7\x01\x20"
65+
b"\xEA\x02\x00\x00"
66+
b"\xc0\x01\x23" # Power control VRH[5:0]
67+
b"\xc1\x01\x10" # Power control SAP[2:0];BT[3:0]
68+
b"\xc5\x02\x3e\x28" # VCM control
69+
b"\xc7\x01\x86" # VCM control2
70+
b"\x36\x01\x40" # Memory Access Control
71+
b"\x37\x01\x00" # Vertical scroll zero
72+
b"\x3a\x01\x55" # COLMOD: Pixel Format Set
73+
b"\xb1\x02\x00\x18" # Frame Rate Control (In Normal Mode/Full Colors)
74+
b"\xb6\x03\x08\x82\x27" # Display Function Control
75+
b"\xF2\x01\x00" # 3Gamma Function Disable
76+
b"\x26\x01\x01" # Gamma curve selected
77+
b"\xe0\x0f\x0F\x31\x2B\x0C\x0E\x08\x4E\xF1\x37\x07\x10\x03\x0E\x09\x00" # Set Gamma
78+
b"\xe1\x0f\x00\x0E\x14\x03\x11\x07\x31\xC1\x48\x08\x0F\x0C\x31\x36\x0F" # Set Gamma
79+
b"\x11\x80\x78" # Exit Sleep then delay 0x78 (120ms)
80+
b"\x29\x80\x78" # Display on then delay 0x78 (120ms)
81+
)
82+
83+
display = displayio.Display(display_bus, _INIT_SEQUENCE, width=320, height=240)
84+
85+
bus = busio.I2C(scl=board.CAMERA_SIOC, sda=board.CAMERA_SIOD)
86+
cam = adafruit_ov2640.OV2640(
87+
bus,
88+
data_pins=board.CAMERA_DATA,
89+
clock=board.CAMERA_PCLK,
90+
vsync=board.CAMERA_VSYNC,
91+
href=board.CAMERA_HREF,
92+
mclk=board.CAMERA_XCLK,
93+
mclk_frequency=20_000_000,
94+
size=adafruit_ov2640.OV2640_SIZE_QVGA,
95+
)
96+
97+
cam.flip_x = False
98+
cam.flip_y = True
99+
pid = cam.product_id
100+
ver = cam.product_version
101+
print(f"Detected pid={pid:x} ver={ver:x}")
102+
# cam.test_pattern = True
103+
104+
bitmap = displayio.Bitmap(320, 240, 65536)
105+
106+
display.auto_refresh = False
107+
108+
sd_spi = busio.SPI(clock=board.IO18, MOSI=board.IO14, MISO=board.IO17)
109+
sd_cs = board.IO12
110+
sdcard = sdcardio.SDCard(sd_spi, sd_cs)
111+
vfs = storage.VfsFat(sdcard)
112+
storage.mount(vfs, "/sd")
113+
114+
115+
def exists(filename):
116+
try:
117+
os.stat(filename)
118+
return True
119+
except OSError as e:
120+
return False
121+
122+
123+
_image_counter = 0
124+
125+
126+
def open_next_image():
127+
global _image_counter
128+
while True:
129+
filename = f"/sd/img{_image_counter:04d}.jpg"
130+
_image_counter += 1
131+
if exists(filename):
132+
continue
133+
print("#", filename)
134+
return open(filename, "wb")
135+
136+
137+
def capture_image():
138+
old_size = cam.size
139+
old_colorspace = cam.colorspace
140+
exposure = cam.exposure
141+
try:
142+
cam.size = adafruit_ov2640.OV2640_SIZE_UXGA
143+
cam.colorspace = adafruit_ov2640.OV2640_COLOR_JPEG
144+
cam.exposure = exposure
145+
b = bytearray(cam.capture_buffer_size)
146+
jpeg = cam.capture(b)
147+
148+
print(f"Captured {len(jpeg)} bytes of jpeg data")
149+
with open_next_image() as f:
150+
f.write(jpeg)
151+
finally:
152+
cam.size = old_size
153+
cam.colorspace = old_colorspace
154+
cam.exposure = exposure
155+
156+
157+
def main():
158+
display.auto_refresh = False
159+
display_bus.send(42, struct.pack(">hh", 0, 319))
160+
display_bus.send(43, struct.pack(">hh", 0, 239))
161+
t0 = time.monotonic_ns()
162+
while True:
163+
a_voltage = a.value * a.reference_voltage / 65535
164+
record_pressed = abs(a_voltage - V_RECORD) < 0.05
165+
if record_pressed:
166+
capture_image()
167+
cam.capture(bitmap)
168+
display_bus.send(44, bitmap)
169+
t1 = time.monotonic_ns()
170+
print(1e9 / (t1 - t0), "fps")
171+
t0 = t1
172+
# bitmap.dirty()
173+
# display.refresh(minimum_frames_per_second=0)
174+
175+
176+
main()

0 commit comments

Comments
 (0)