Skip to content

Commit a084124

Browse files
authored
Merge pull request #5540 from jepler/continuous-capture
ParallelImageCapture: Add continuous capture on espressif
2 parents 0dfb7a9 + f498cfa commit a084124

File tree

9 files changed

+205
-31
lines changed

9 files changed

+205
-31
lines changed

locale/circuitpython.pot

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,7 @@ msgstr ""
347347
msgid "64 bit types"
348348
msgstr ""
349349

350+
#: ports/atmel-samd/common-hal/alarm/pin/PinAlarm.c
350351
#: ports/atmel-samd/common-hal/countio/Counter.c
351352
#: ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c
352353
msgid "A hardware interrupt channel is already in use"
@@ -625,6 +626,10 @@ msgstr ""
625626
msgid "Buffer too short by %d bytes"
626627
msgstr ""
627628

629+
#: ports/espressif/common-hal/imagecapture/ParallelImageCapture.c
630+
msgid "Buffers must be same size"
631+
msgstr ""
632+
628633
#: ports/atmel-samd/common-hal/paralleldisplay/ParallelBus.c
629634
#: ports/espressif/common-hal/paralleldisplay/ParallelBus.c
630635
#: ports/nrf/common-hal/paralleldisplay/ParallelBus.c
@@ -1587,6 +1592,10 @@ msgstr ""
15871592
msgid "No available clocks"
15881593
msgstr ""
15891594

1595+
#: ports/espressif/common-hal/imagecapture/ParallelImageCapture.c
1596+
msgid "No capture in progress"
1597+
msgstr ""
1598+
15901599
#: shared-bindings/_bleio/PacketBuffer.c
15911600
msgid "No connection: length cannot be determined"
15921601
msgstr ""
@@ -1607,6 +1616,7 @@ msgstr ""
16071616
msgid "No hardware support on clk pin"
16081617
msgstr ""
16091618

1619+
#: ports/atmel-samd/common-hal/alarm/pin/PinAlarm.c
16101620
#: ports/atmel-samd/common-hal/frequencyio/FrequencyIn.c
16111621
#: ports/atmel-samd/common-hal/pulseio/PulseIn.c
16121622
msgid "No hardware support on pin"
@@ -1727,7 +1737,6 @@ msgstr ""
17271737
msgid "Only connectable advertisements can be directed"
17281738
msgstr ""
17291739

1730-
#: ports/atmel-samd/common-hal/alarm/pin/PinAlarm.c
17311740
#: ports/stm/common-hal/alarm/pin/PinAlarm.c
17321741
msgid "Only edge detection is available on this hardware"
17331742
msgstr ""
@@ -1822,6 +1831,7 @@ msgstr ""
18221831
msgid "Permission denied"
18231832
msgstr ""
18241833

1834+
#: ports/atmel-samd/common-hal/alarm/pin/PinAlarm.c
18251835
#: ports/stm/common-hal/alarm/pin/PinAlarm.c
18261836
msgid "Pin cannot wake from Deep Sleep"
18271837
msgstr ""
@@ -2175,6 +2185,10 @@ msgstr ""
21752185
msgid "The sample's signedness does not match the mixer's"
21762186
msgstr ""
21772187

2188+
#: shared-module/imagecapture/ParallelImageCapture.c
2189+
msgid "This microcontroller does not support continuous capture."
2190+
msgstr ""
2191+
21782192
#: shared-module/paralleldisplay/ParallelBus.c
21792193
msgid ""
21802194
"This microcontroller only supports data0=, not data_pins=, because it "
@@ -3880,6 +3894,7 @@ msgstr ""
38803894
msgid "pow() with 3 arguments requires integers"
38813895
msgstr ""
38823896

3897+
#: ports/espressif/boards/adafruit_feather_esp32s2/mpconfigboard.h
38833898
#: ports/espressif/boards/adafruit_feather_esp32s2_nopsram/mpconfigboard.h
38843899
#: ports/espressif/boards/adafruit_feather_esp32s2_tftback_nopsram/mpconfigboard.h
38853900
#: ports/espressif/boards/adafruit_funhouse/mpconfigboard.h

ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -151,14 +151,14 @@ static void setup_dma(DmacDescriptor *descriptor, size_t count, uint32_t *buffer
151151
descriptor->DESCADDR.reg = 0;
152152
}
153153

154-
#include <string.h>
155-
156-
void common_hal_imagecapture_parallelimagecapture_capture(imagecapture_parallelimagecapture_obj_t *self, void *buffer, size_t bufsize) {
154+
void common_hal_imagecapture_parallelimagecapture_singleshot_capture(imagecapture_parallelimagecapture_obj_t *self, mp_obj_t buffer) {
155+
mp_buffer_info_t bufinfo;
156+
mp_get_buffer_raise(buffer, &bufinfo, MP_BUFFER_RW);
157157

158158
uint8_t dma_channel = dma_allocate_channel();
159159

160-
uint32_t *dest = buffer;
161-
size_t count = bufsize / 4; // PCC receives 4 bytes (2 pixels) at a time
160+
uint32_t *dest = bufinfo.buf;
161+
size_t count = bufinfo.len / 4; // PCC receives 4 bytes (2 pixels) at a time
162162

163163
turn_on_event_system();
164164

ports/espressif/common-hal/imagecapture/ParallelImageCapture.c

Lines changed: 66 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ void common_hal_imagecapture_parallelimagecapture_construct(imagecapture_paralle
7979
}
8080

8181
void common_hal_imagecapture_parallelimagecapture_deinit(imagecapture_parallelimagecapture_obj_t *self) {
82+
cam_deinit();
83+
84+
self->buffer1 = NULL;
85+
self->buffer2 = NULL;
86+
8287
reset_pin_number(self->data_clock);
8388
self->data_clock = NO_PIN;
8489

@@ -102,28 +107,73 @@ bool common_hal_imagecapture_parallelimagecapture_deinited(imagecapture_parallel
102107
return self->data_clock == NO_PIN;
103108
}
104109

105-
void common_hal_imagecapture_parallelimagecapture_capture(imagecapture_parallelimagecapture_obj_t *self, void *buffer, size_t bufsize) {
106-
size_t size = bufsize / 2; // count is in pixels
107-
if (size != self->config.size || buffer != self->config.frame1_buffer) {
108-
cam_deinit();
109-
self->config.size = bufsize / 2; // count is in pixels(?)
110-
self->config.frame1_buffer = buffer;
111-
112-
cam_init(&self->config);
113-
cam_start();
114-
} else {
115-
cam_give(buffer);
110+
void common_hal_imagecapture_parallelimagecapture_continuous_capture_start(imagecapture_parallelimagecapture_obj_t *self, mp_obj_t buffer1, mp_obj_t buffer2) {
111+
if (buffer1 == self->buffer1 && buffer2 == self->buffer2) {
112+
return;
113+
}
114+
115+
mp_buffer_info_t bufinfo1, bufinfo2 = {};
116+
mp_get_buffer_raise(buffer1, &bufinfo1, MP_BUFFER_RW);
117+
if (buffer2 != mp_const_none) {
118+
mp_get_buffer_raise(buffer2, &bufinfo2, MP_BUFFER_RW);
119+
if (bufinfo1.len != bufinfo2.len) {
120+
mp_raise_ValueError(translate("Buffers must be same size"));
121+
}
116122
}
117123

124+
self->buffer1 = buffer1;
125+
self->buffer2 = buffer2;
126+
127+
128+
cam_deinit();
129+
self->config.size = bufinfo1.len / 2; // count is in pixels
130+
self->config.frame1_buffer = bufinfo1.buf;
131+
self->config.frame2_buffer = bufinfo2.buf;
132+
self->buffer_to_give = NULL;
133+
134+
cam_init(&self->config);
135+
cam_start();
136+
}
137+
138+
void common_hal_imagecapture_parallelimagecapture_continuous_capture_stop(imagecapture_parallelimagecapture_obj_t *self) {
139+
cam_deinit();
140+
self->buffer1 = self->buffer2 = NULL;
141+
self->buffer_to_give = NULL;
142+
}
143+
144+
STATIC void common_hal_imagecapture_parallelimagecapture_continuous_capture_give_frame(imagecapture_parallelimagecapture_obj_t *self) {
145+
if (self->buffer_to_give) {
146+
cam_give(self->buffer_to_give);
147+
self->buffer_to_give = NULL;
148+
}
149+
}
150+
151+
mp_obj_t common_hal_imagecapture_parallelimagecapture_continuous_capture_get_frame(imagecapture_parallelimagecapture_obj_t *self) {
152+
if (self->buffer1 == NULL) {
153+
mp_raise_RuntimeError(translate("No capture in progress"));
154+
}
155+
common_hal_imagecapture_parallelimagecapture_continuous_capture_give_frame(self);
156+
118157
while (!cam_ready()) {
119158
RUN_BACKGROUND_TASKS;
120159
if (mp_hal_is_interrupted()) {
121-
self->config.size = 0; // force re-init next time
122-
cam_stop();
123-
return;
160+
return mp_const_none;
124161
}
125162
}
126163

127-
uint8_t *unused;
128-
cam_take(&unused); // this just "returns" buffer
164+
cam_take(&self->buffer_to_give);
165+
166+
if (self->buffer_to_give == self->config.frame1_buffer) {
167+
return self->buffer1;
168+
}
169+
if (self->buffer_to_give == self->config.frame2_buffer) {
170+
return self->buffer2;
171+
}
172+
173+
return mp_const_none; // should be unreachable
174+
}
175+
176+
void common_hal_imagecapture_parallelimagecapture_singleshot_capture(imagecapture_parallelimagecapture_obj_t *self, mp_obj_t buffer) {
177+
common_hal_imagecapture_parallelimagecapture_continuous_capture_start(self, buffer, mp_const_none);
178+
common_hal_imagecapture_parallelimagecapture_continuous_capture_get_frame(self);
129179
}

ports/espressif/common-hal/imagecapture/ParallelImageCapture.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
#pragma once
2828

29+
#include "py/obj.h"
2930
#include "shared-bindings/imagecapture/ParallelImageCapture.h"
3031
#include "cam.h"
3132

@@ -36,4 +37,6 @@ struct imagecapture_parallelimagecapture_obj {
3637
gpio_num_t vertical_sync;
3738
gpio_num_t horizontal_reference;
3839
uint8_t data_count;
40+
mp_obj_t buffer1, buffer2;
41+
uint8_t *buffer_to_give;
3942
};

ports/raspberrypi/common-hal/imagecapture/ParallelImageCapture.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,10 @@ bool common_hal_imagecapture_parallelimagecapture_deinited(imagecapture_parallel
137137
return common_hal_rp2pio_statemachine_deinited(&self->state_machine);
138138
}
139139

140-
void common_hal_imagecapture_parallelimagecapture_capture(imagecapture_parallelimagecapture_obj_t *self, void *buffer, size_t bufsize) {
140+
void common_hal_imagecapture_parallelimagecapture_singleshot_capture(imagecapture_parallelimagecapture_obj_t *self, mp_obj_t buffer) {
141+
mp_buffer_info_t bufinfo;
142+
mp_get_buffer_raise(buffer, &bufinfo, MP_BUFFER_RW);
143+
141144
PIO pio = self->state_machine.pio;
142145
uint sm = self->state_machine.state_machine;
143146
uint8_t offset = rp2pio_statemachine_program_offset(&self->state_machine);
@@ -149,7 +152,7 @@ void common_hal_imagecapture_parallelimagecapture_capture(imagecapture_paralleli
149152
pio_sm_exec(pio, sm, pio_encode_jmp(offset));
150153
pio_sm_set_enabled(pio, sm, true);
151154

152-
common_hal_rp2pio_statemachine_readinto(&self->state_machine, buffer, bufsize, 4);
155+
common_hal_rp2pio_statemachine_readinto(&self->state_machine, bufinfo.buf, bufinfo.len, 4);
153156

154157
pio_sm_set_enabled(pio, sm, false);
155158
}

py/circuitpy_defns.mk

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,7 @@ $(filter $(SRC_PATTERNS), \
466466
digitalio/DriveMode.c \
467467
digitalio/Pull.c \
468468
fontio/Glyph.c \
469+
imagecapture/ParallelImageCapture.c \
469470
math/__init__.c \
470471
microcontroller/ResetReason.c \
471472
microcontroller/RunMode.c \
@@ -535,6 +536,7 @@ SRC_SHARED_MODULE_ALL = \
535536
gamepadshift/GamePadShift.c \
536537
gamepadshift/__init__.c \
537538
getpass/__init__.c \
539+
imagecapture/ParallelImageCapture.c \
538540
ipaddress/IPv4Address.c \
539541
ipaddress/__init__.c \
540542
keypad/__init__.c \

shared-bindings/imagecapture/ParallelImageCapture.c

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
*/
2626

2727
#include "py/obj.h"
28+
#include "py/objproperty.h"
2829
#include "py/runtime.h"
2930

3031
#include "shared/runtime/context_manager_helpers.h"
@@ -82,20 +83,70 @@ STATIC mp_obj_t imagecapture_parallelimagecapture_make_new(const mp_obj_type_t *
8283
return self;
8384
}
8485

85-
//| def capture(self, buffer: WriteableBuffer, width: int, height: int, bpp: int=16) -> None:
86-
//| """Capture a single frame into the given buffer"""
86+
//| def capture(self, buffer: WriteableBuffer) -> WriteableBuffer:
87+
//| """Capture a single frame into the given buffer.
88+
//|
89+
//| This will stop a continuous-mode capture, if one is in progress."""
8790
//| ...
8891
//|
8992
STATIC mp_obj_t imagecapture_parallelimagecapture_capture(mp_obj_t self_in, mp_obj_t buffer) {
9093
imagecapture_parallelimagecapture_obj_t *self = (imagecapture_parallelimagecapture_obj_t *)self_in;
91-
mp_buffer_info_t bufinfo;
92-
mp_get_buffer_raise(buffer, &bufinfo, MP_BUFFER_RW);
93-
common_hal_imagecapture_parallelimagecapture_capture(self, bufinfo.buf, bufinfo.len);
94+
common_hal_imagecapture_parallelimagecapture_singleshot_capture(self, buffer);
9495

95-
return mp_const_none;
96+
return buffer;
9697
}
9798
STATIC MP_DEFINE_CONST_FUN_OBJ_2(imagecapture_parallelimagecapture_capture_obj, imagecapture_parallelimagecapture_capture);
9899

100+
//| def continuous_capture_start(self, buffer1: WriteableBuffer, buffer2: WriteableBuffer, /) -> None:
101+
//| """Begin capturing into the given buffers in the background.
102+
//|
103+
//| Call `continuous_capture_get_frame` to get the next available
104+
//| frame, and `continuous_capture_stop` to stop capturing.
105+
//|
106+
//| Until `continuous_capture_stop` (or `deinit`) is called, the
107+
//| `ParallelImageCapture` object keeps references to ``buffer1`` and
108+
//| ``buffer2``, so the objects will not be garbage collected."""
109+
//| ...
110+
//|
111+
STATIC mp_obj_t imagecapture_parallelimagecapture_continuous_capture_start(mp_obj_t self_in, mp_obj_t buffer1, mp_obj_t buffer2) {
112+
imagecapture_parallelimagecapture_obj_t *self = (imagecapture_parallelimagecapture_obj_t *)self_in;
113+
common_hal_imagecapture_parallelimagecapture_continuous_capture_start(self, buffer1, buffer2);
114+
115+
return mp_const_none;
116+
}
117+
STATIC MP_DEFINE_CONST_FUN_OBJ_3(imagecapture_parallelimagecapture_continuous_capture_start_obj, imagecapture_parallelimagecapture_continuous_capture_start);
118+
119+
//| def continuous_capture_get_frame(self) -> WriteableBuffer:
120+
//| """Return the next available frame, one of the two buffers passed to `continuous_capture_start`"""
121+
//| ...
122+
//|
123+
STATIC mp_obj_t imagecapture_parallelimagecapture_continuous_capture_get_frame(mp_obj_t self_in) {
124+
imagecapture_parallelimagecapture_obj_t *self = (imagecapture_parallelimagecapture_obj_t *)self_in;
125+
return common_hal_imagecapture_parallelimagecapture_continuous_capture_get_frame(self);
126+
}
127+
STATIC MP_DEFINE_CONST_FUN_OBJ_1(imagecapture_parallelimagecapture_continuous_capture_get_frame_obj, imagecapture_parallelimagecapture_continuous_capture_get_frame);
128+
129+
130+
131+
//| def continuous_capture_stop(self) -> None:
132+
//| """Stop continuous capture.
133+
//|
134+
//| Calling this method also causes the object to release its
135+
//| references to the buffers passed to `continuous_capture_start`,
136+
//| potentially allowing the objects to be garbage collected."""
137+
//| ...
138+
//|
139+
STATIC mp_obj_t imagecapture_parallelimagecapture_continuous_capture_stop(mp_obj_t self_in) {
140+
imagecapture_parallelimagecapture_obj_t *self = (imagecapture_parallelimagecapture_obj_t *)self_in;
141+
common_hal_imagecapture_parallelimagecapture_continuous_capture_stop(self);
142+
143+
return mp_const_none;
144+
}
145+
STATIC MP_DEFINE_CONST_FUN_OBJ_1(imagecapture_parallelimagecapture_continuous_capture_stop_obj, imagecapture_parallelimagecapture_continuous_capture_stop);
146+
147+
148+
149+
99150
//| def deinit(self) -> None:
100151
//| """Deinitialize this instance"""
101152
//| ...
@@ -134,6 +185,9 @@ STATIC const mp_rom_map_elem_t imagecapture_parallelimagecapture_locals_dict_tab
134185
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&imagecapture_parallelimagecapture___exit___obj) },
135186

136187
{ MP_ROM_QSTR(MP_QSTR_capture), MP_ROM_PTR(&imagecapture_parallelimagecapture_capture_obj) },
188+
{ MP_ROM_QSTR(MP_QSTR_continuous_capture_start), MP_ROM_PTR(&imagecapture_parallelimagecapture_continuous_capture_start_obj) },
189+
{ MP_ROM_QSTR(MP_QSTR_continuous_capture_stop), MP_ROM_PTR(&imagecapture_parallelimagecapture_continuous_capture_stop_obj) },
190+
{ MP_ROM_QSTR(MP_QSTR_continuous_capture_get_frame), MP_ROM_PTR(&imagecapture_parallelimagecapture_continuous_capture_get_frame_obj) },
137191
};
138192

139193
STATIC MP_DEFINE_CONST_DICT(imagecapture_parallelimagecapture_locals_dict, imagecapture_parallelimagecapture_locals_dict_table);

shared-bindings/imagecapture/ParallelImageCapture.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,7 @@ void common_hal_imagecapture_parallelimagecapture_construct(imagecapture_paralle
4040
const mcu_pin_obj_t *horizontal_sync);
4141
void common_hal_imagecapture_parallelimagecapture_deinit(imagecapture_parallelimagecapture_obj_t *self);
4242
bool common_hal_imagecapture_parallelimagecapture_deinited(imagecapture_parallelimagecapture_obj_t *self);
43-
void common_hal_imagecapture_parallelimagecapture_capture(imagecapture_parallelimagecapture_obj_t *self, void *buffer, size_t bufsize);
43+
void common_hal_imagecapture_parallelimagecapture_singleshot_capture(imagecapture_parallelimagecapture_obj_t *self, mp_obj_t buffer);
44+
void common_hal_imagecapture_parallelimagecapture_continuous_capture_start(imagecapture_parallelimagecapture_obj_t *self, mp_obj_t buffer1, mp_obj_t buffer2);
45+
void common_hal_imagecapture_parallelimagecapture_continuous_capture_stop(imagecapture_parallelimagecapture_obj_t *self);
46+
mp_obj_t common_hal_imagecapture_parallelimagecapture_continuous_capture_get_frame(imagecapture_parallelimagecapture_obj_t *self);
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* This file is part of the Micro Python project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2019 Scott Shawcroft for Adafruit Industries
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
#include "shared-bindings/imagecapture/ParallelImageCapture.h"
28+
#include "py/runtime.h"
29+
30+
// If the continuous-capture mode isn't supported, then this default (weak) implementation will raise exceptions for you
31+
__attribute__((weak))
32+
void common_hal_imagecapture_parallelimagecapture_continuous_capture_start(imagecapture_parallelimagecapture_obj_t *self, mp_obj_t buffer1, mp_obj_t buffer2) {
33+
mp_raise_NotImplementedError(translate("This microcontroller does not support continuous capture."));
34+
}
35+
36+
__attribute__((weak))
37+
void common_hal_imagecapture_parallelimagecapture_continuous_capture_stop(imagecapture_parallelimagecapture_obj_t *self) {
38+
mp_raise_NotImplementedError(translate("This microcontroller does not support continuous capture."));
39+
}
40+
41+
__attribute__((weak))
42+
mp_obj_t common_hal_imagecapture_parallelimagecapture_continuous_capture_get_frame(imagecapture_parallelimagecapture_obj_t *self) {
43+
mp_raise_NotImplementedError(translate("This microcontroller does not support continuous capture."));
44+
}

0 commit comments

Comments
 (0)