Skip to content

Commit 4c064c2

Browse files
authored
Merge pull request #7294 from tannewt/analogbufio_rework
Rework the analogbufio API.
2 parents d6c3dfb + f7504ff commit 4c064c2

File tree

6 files changed

+110
-118
lines changed

6 files changed

+110
-118
lines changed

locale/circuitpython.pot

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ msgid "%q must be >= %d"
155155
msgstr ""
156156

157157
#: shared-bindings/analogbufio/BufferedIn.c
158+
msgid "%q must be a bytearray or array of type 'H' or 'B'"
159+
msgstr ""
160+
158161
#: shared-bindings/audiocore/RawSample.c
159162
msgid "%q must be a bytearray or array of type 'h', 'H', 'b', or 'B'"
160163
msgstr ""

ports/raspberrypi/common-hal/analogbufio/BufferedIn.c

Lines changed: 65 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "common-hal/analogbufio/BufferedIn.h"
3434
#include "shared-bindings/analogbufio/BufferedIn.h"
3535
#include "shared-bindings/microcontroller/Pin.h"
36+
#include "shared/runtime/interrupt_char.h"
3637
#include "py/runtime.h"
3738
#include "supervisor/shared/translate/translate.h"
3839
#include "src/rp2_common/hardware_adc/include/hardware/adc.h"
@@ -42,59 +43,32 @@
4243
#define ADC_FIRST_PIN_NUMBER 26
4344
#define ADC_PIN_COUNT 4
4445

45-
void common_hal_analogbufio_bufferedin_construct(analogbufio_bufferedin_obj_t *self, const mcu_pin_obj_t *pin, uint8_t *buffer, uint32_t len, uint8_t bytes_per_sample, bool samples_signed, uint32_t sample_rate) {
46+
#define ADC_CLOCK_INPUT 48000000
47+
#define ADC_MAX_CLOCK_DIV (1 << (ADC_DIV_INT_MSB - ADC_DIV_INT_LSB + 1))
4648

49+
void common_hal_analogbufio_bufferedin_construct(analogbufio_bufferedin_obj_t *self, const mcu_pin_obj_t *pin, uint32_t sample_rate) {
4750
// Make sure pin number is in range for ADC
4851
if (pin->number < ADC_FIRST_PIN_NUMBER || pin->number >= (ADC_FIRST_PIN_NUMBER + ADC_PIN_COUNT)) {
4952
raise_ValueError_invalid_pins();
5053
}
5154

55+
// Validate sample rate here
56+
sample_rate = (uint32_t)mp_arg_validate_int_range(sample_rate, ADC_CLOCK_INPUT / ADC_MAX_CLOCK_DIV, ADC_CLOCK_INPUT / 96, MP_QSTR_sample_rate);
57+
5258
// Set pin and channel
5359
self->pin = pin;
5460
claim_pin(pin);
5561

5662
// TODO: find a way to accept ADC4 for temperature
5763
self->chan = pin->number - ADC_FIRST_PIN_NUMBER;
5864

59-
// Set buffer and length
60-
self->buffer = buffer;
61-
self->len = len;
62-
63-
// Set sample rate - used in read
64-
self->bytes_per_sample = bytes_per_sample;
65-
self->sample_rate = sample_rate;
66-
6765
// Init GPIO for analogue use: hi-Z, no pulls, disable digital input buffer.
66+
// TODO: Make sure we share the ADC well. Right now we just assume it is
67+
// unused.
6868
adc_init();
6969
adc_gpio_init(pin->number);
7070
adc_select_input(self->chan); // chan = pin - 26 ??
7171

72-
// RP2040 Implementation Detail
73-
// Fills the supplied buffer with ADC values using DMA transfer.
74-
// If the buffer is 8-bit, then values are 8-bit shifted and error bit is off.
75-
// If buffer is 16-bit, then values are not shifted and error bit is present.
76-
// Number of transfers is always the number of samples which is the array
77-
// byte length divided by the bytes_per_sample.
78-
79-
// self->bytes_per_sample == 1
80-
uint dma_size = DMA_SIZE_8;
81-
bool show_error_bit = false;
82-
bool shift_sample_8_bits = true;
83-
if (self->bytes_per_sample == 2) {
84-
dma_size = DMA_SIZE_16;
85-
show_error_bit = true;
86-
shift_sample_8_bits = false;
87-
}
88-
89-
// adc_select_input(self->pin->number - ADC_FIRST_PIN_NUMBER);
90-
adc_fifo_setup(
91-
true, // Write each completed conversion to the sample FIFO
92-
true, // Enable DMA data request (DREQ)
93-
1, // DREQ (and IRQ) asserted when at least 1 sample present
94-
show_error_bit, // See the ERR bit
95-
shift_sample_8_bits // Shift each sample to 8 bits when pushing to FIFO
96-
);
97-
9872
// Divisor of 0 -> full speed. Free-running capture with the divider is
9973
// equivalent to pressing the ADC_CS_START_ONCE button once per `div + 1`
10074
// cycles (div not necessarily an integer). Each conversion takes 96
@@ -104,7 +78,8 @@ void common_hal_analogbufio_bufferedin_construct(analogbufio_bufferedin_obj_t *s
10478
// sample rate determines divisor, not zero.
10579

10680
// sample_rate is forced to be >= 1 in shared-bindings
107-
adc_set_clkdiv((float)48000000.0 / (float)self->sample_rate);
81+
float clk_div = (float)ADC_CLOCK_INPUT / (float)sample_rate;
82+
adc_set_clkdiv(clk_div);
10883

10984
// Set up the DMA to start transferring data as soon as it appears in FIFO
11085
uint dma_chan = dma_claim_unused_channel(true);
@@ -114,7 +89,6 @@ void common_hal_analogbufio_bufferedin_construct(analogbufio_bufferedin_obj_t *s
11489
self->cfg = dma_channel_get_default_config(dma_chan);
11590

11691
// Reading from constant address, writing to incrementing byte addresses
117-
channel_config_set_transfer_data_size(&(self->cfg), dma_size);
11892
channel_config_set_read_increment(&(self->cfg), false);
11993
channel_config_set_write_increment(&(self->cfg), true);
12094

@@ -143,14 +117,38 @@ void common_hal_analogbufio_bufferedin_deinit(analogbufio_bufferedin_obj_t *self
143117
dma_channel_unclaim(self->dma_chan);
144118
}
145119

146-
void common_hal_analogbufio_bufferedin_read(analogbufio_bufferedin_obj_t *self) {
120+
uint32_t common_hal_analogbufio_bufferedin_readinto(analogbufio_bufferedin_obj_t *self, uint8_t *buffer, uint32_t len, uint8_t bytes_per_sample) {
121+
// RP2040 Implementation Detail
122+
// Fills the supplied buffer with ADC values using DMA transfer.
123+
// If the buffer is 8-bit, then values are 8-bit shifted and error bit is off.
124+
// If buffer is 16-bit, then values are 12-bit and error bit is present. We
125+
// stretch the 12-bit value to 16-bits and truncate the number of valid
126+
// samples at the first sample with the error bit set.
127+
// Number of transfers is always the number of samples which is the array
128+
// byte length divided by the bytes_per_sample.
129+
uint dma_size = DMA_SIZE_8;
130+
bool show_error_bit = false;
131+
if (bytes_per_sample == 2) {
132+
dma_size = DMA_SIZE_16;
133+
show_error_bit = true;
134+
}
147135

148-
uint32_t cdl = self->len / self->bytes_per_sample;
136+
adc_fifo_setup(
137+
true, // Write each completed conversion to the sample FIFO
138+
true, // Enable DMA data request (DREQ)
139+
1, // DREQ (and IRQ) asserted when at least 1 sample present
140+
show_error_bit, // See the ERR bit
141+
bytes_per_sample == 1 // Shift each sample to 8 bits when pushing to FIFO
142+
);
143+
144+
uint32_t sample_count = len / bytes_per_sample;
145+
146+
channel_config_set_transfer_data_size(&(self->cfg), dma_size);
149147

150148
dma_channel_configure(self->dma_chan, &(self->cfg),
151-
self->buffer, // dst
149+
buffer, // dst
152150
&adc_hw->fifo, // src
153-
cdl, // transfer count
151+
sample_count, // transfer count
154152
true // start immediately
155153
);
156154

@@ -159,9 +157,34 @@ void common_hal_analogbufio_bufferedin_read(analogbufio_bufferedin_obj_t *self)
159157

160158
// Once DMA finishes, stop any new conversions from starting, and clean up
161159
// the FIFO in case the ADC was still mid-conversion.
162-
dma_channel_wait_for_finish_blocking(self->dma_chan);
160+
uint32_t remaining_transfers = sample_count;
161+
while (dma_channel_is_busy(self->dma_chan) &&
162+
!mp_hal_is_interrupted()) {
163+
RUN_BACKGROUND_TASKS;
164+
}
165+
remaining_transfers = dma_channel_hw_addr(self->dma_chan)->transfer_count;
163166

164167
// Clean up
165168
adc_run(false);
169+
// Stopping early so abort.
170+
if (dma_channel_is_busy(self->dma_chan)) {
171+
dma_channel_abort(self->dma_chan);
172+
}
166173
adc_fifo_drain();
174+
175+
size_t captured_count = sample_count - remaining_transfers;
176+
if (dma_size == DMA_SIZE_16) {
177+
uint16_t *buf16 = (uint16_t *)buffer;
178+
for (size_t i = 0; i < captured_count; i++) {
179+
uint16_t value = buf16[i];
180+
// Check the error bit and "truncate" the buffer if there is an error.
181+
if ((value & ADC_FIFO_ERR_BITS) != 0) {
182+
captured_count = i;
183+
break;
184+
}
185+
// Scale the values to the standard 16 bit range.
186+
buf16[i] = (value << 4) | (value >> 8);
187+
}
188+
}
189+
return captured_count;
167190
}

ports/raspberrypi/common-hal/analogbufio/BufferedIn.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,9 @@
4141
typedef struct {
4242
mp_obj_base_t base;
4343
const mcu_pin_obj_t *pin;
44-
uint8_t *buffer;
45-
uint32_t len;
46-
uint8_t bytes_per_sample;
47-
bool samples_signed;
48-
uint32_t sample_rate;
4944
uint8_t chan;
5045
uint dma_chan;
5146
dma_channel_config cfg;
5247
} analogbufio_bufferedin_obj_t;
5348

54-
void bufferedin_init(void);
55-
5649
#endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_ANALOGBUFIO_BUFFEREDIN_H

shared-bindings/analogbufio/BufferedIn.c

Lines changed: 40 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,10 @@
4747
//| import array
4848
//|
4949
//| length = 1000
50-
//| mybuffer = array.array("H", 0x0000 for i in range(length))
50+
//| mybuffer = array.array("H", [0x0000] * length)
5151
//| rate = 500000
52-
//| adcbuf = analogbufio.BufferedIn(board.GP26, mybuffer, rate)
53-
//| adcbuf.read()
52+
//| adcbuf = analogbufio.BufferedIn(board.GP26, sample_rate=rate)
53+
//| adcbuf.readinto(mybuffer)
5454
//| adcbuf.deinit()
5555
//| for i in range(length):
5656
//| print(i, mybuffer[i])
@@ -60,64 +60,30 @@
6060
//| (TODO) Provide mechanism to read CPU Temperature."""
6161
//|
6262

63-
//| def __init__(
64-
//| self, pin: microcontroller.Pin, buffer: WriteableBuffer, *, sample_rate: int = 500000
65-
//| ) -> None:
66-
//| """Create a `BufferedIn` on the given pin. ADC values will be read
67-
//| into the given buffer at the supplied sample_rate. Depending on the
68-
//| buffer typecode, 'b', 'B', 'h', 'H', samples are 8-bit byte-arrays or
69-
//| 16-bit half-words and are signed or unsigned.
70-
//| The ADC most significant bits of the ADC are kept. (See
71-
//| https://docs.circuitpython.org/en/latest/docs/library/array.html)
63+
//| def __init__(self, pin: microcontroller.Pin, *, sample_rate: int) -> None:
64+
//| """Create a `BufferedIn` on the given pin and given sample rate.
7265
//|
7366
//| :param ~microcontroller.Pin pin: the pin to read from
74-
//| :param ~circuitpython_typing.WriteableBuffer buffer: buffer: A buffer for samples
7567
//| :param ~int sample_rate: rate: sampling frequency, in samples per second"""
7668
//| ...
7769
STATIC mp_obj_t analogbufio_bufferedin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
78-
enum { ARG_pin, ARG_buffer, ARG_sample_rate };
70+
enum { ARG_pin, ARG_sample_rate };
7971
static const mp_arg_t allowed_args[] = {
8072
{ MP_QSTR_pin, MP_ARG_OBJ | MP_ARG_REQUIRED },
81-
{ MP_QSTR_buffer, MP_ARG_OBJ | MP_ARG_REQUIRED },
82-
{ MP_QSTR_sample_rate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 500000} },
73+
{ MP_QSTR_sample_rate, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT },
8374
};
8475
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
8576
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
8677

8778
// Validate Pin
8879
const mcu_pin_obj_t *pin = validate_obj_is_free_pin(args[ARG_pin].u_obj);
8980

90-
// Buffer defined and allocated by user
91-
mp_buffer_info_t bufinfo;
92-
mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_READ);
93-
94-
// signed or unsigned, byte per sample
95-
bool signed_samples = bufinfo.typecode == 'b' || bufinfo.typecode == 'h';
96-
uint8_t bytes_per_sample = 1;
97-
98-
// Bytes Per Sample
99-
if (bufinfo.typecode == 'h' || bufinfo.typecode == 'H') {
100-
bytes_per_sample = 2;
101-
} else if (bufinfo.typecode != 'b' && bufinfo.typecode != 'B' && bufinfo.typecode != BYTEARRAY_TYPECODE) {
102-
mp_raise_ValueError_varg(translate("%q must be a bytearray or array of type 'h', 'H', 'b', or 'B'"), MP_QSTR_buffer);
103-
}
104-
105-
// Validate sample rate here
106-
uint32_t sample_rate = (uint32_t)mp_arg_validate_int_range(args[ARG_sample_rate].u_int, 1, 500000, MP_QSTR_sample_rate);
107-
10881
// Create local object
109-
analogbufio_bufferedin_obj_t *self = m_new_obj(analogbufio_bufferedin_obj_t);
82+
analogbufio_bufferedin_obj_t *self = m_new_obj_with_finaliser(analogbufio_bufferedin_obj_t);
11083
self->base.type = &analogbufio_bufferedin_type;
11184

112-
// Call local intereface in ports/common-hal/analogbufio
113-
common_hal_analogbufio_bufferedin_construct(self,
114-
pin,
115-
((uint8_t *)bufinfo.buf),
116-
bufinfo.len,
117-
bytes_per_sample,
118-
signed_samples,
119-
sample_rate
120-
);
85+
// Call local interface in ports/common-hal/analogbufio
86+
common_hal_analogbufio_bufferedin_construct(self, pin, args[ARG_sample_rate].u_int);
12187

12288
return MP_OBJ_FROM_PTR(self);
12389
}
@@ -153,23 +119,46 @@ STATIC mp_obj_t analogbufio_bufferedin___exit__(size_t n_args, const mp_obj_t *a
153119
}
154120
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(analogbufio_bufferedin___exit___obj, 4, 4, analogbufio_bufferedin___exit__);
155121

156-
//| def read(self) -> None:
157-
//| """Fills the provided buffer with ADC voltage values."""
122+
//| def readinto(self, buffer: WriteableBuffer) -> int:
123+
//| """Fills the provided buffer with ADC voltage values.
124+
//|
125+
//| ADC values will be read into the given buffer at the supplied sample_rate.
126+
//| Depending on the buffer typecode, 'B', 'H', samples are 8-bit byte-arrays or
127+
//| 16-bit half-words and are always unsigned.
128+
//| The ADC most significant bits of the ADC are kept. (See
129+
//| https://docs.circuitpython.org/en/latest/docs/library/array.html)
130+
//|
131+
//| :param ~circuitpython_typing.WriteableBuffer buffer: buffer: A buffer for samples"""
158132
//| ...
159133
//|
160-
STATIC mp_obj_t analogbufio_bufferedin_obj_read(mp_obj_t self_in) {
134+
STATIC mp_obj_t analogbufio_bufferedin_obj_readinto(mp_obj_t self_in, mp_obj_t buffer_obj) {
161135
analogbufio_bufferedin_obj_t *self = MP_OBJ_TO_PTR(self_in);
162136
check_for_deinit(self);
163-
common_hal_analogbufio_bufferedin_read(self);
164-
return mp_const_none;
137+
138+
// Buffer defined and allocated by user
139+
mp_buffer_info_t bufinfo;
140+
mp_get_buffer_raise(buffer_obj, &bufinfo, MP_BUFFER_READ);
141+
142+
uint8_t bytes_per_sample = 1;
143+
144+
// Bytes Per Sample
145+
if (bufinfo.typecode == 'H') {
146+
bytes_per_sample = 2;
147+
} else if (bufinfo.typecode != 'B' && bufinfo.typecode != BYTEARRAY_TYPECODE) {
148+
mp_raise_ValueError_varg(translate("%q must be a bytearray or array of type 'H' or 'B'"), MP_QSTR_buffer);
149+
}
150+
151+
mp_uint_t captured = common_hal_analogbufio_bufferedin_readinto(self, bufinfo.buf, bufinfo.len, bytes_per_sample);
152+
return MP_OBJ_NEW_SMALL_INT(captured);
165153
}
166-
MP_DEFINE_CONST_FUN_OBJ_1(analogbufio_bufferedin_read_obj, analogbufio_bufferedin_obj_read);
154+
MP_DEFINE_CONST_FUN_OBJ_2(analogbufio_bufferedin_readinto_obj, analogbufio_bufferedin_obj_readinto);
167155

168156
STATIC const mp_rom_map_elem_t analogbufio_bufferedin_locals_dict_table[] = {
157+
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&analogbufio_bufferedin_deinit_obj) },
169158
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&analogbufio_bufferedin_deinit_obj) },
170159
{ MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) },
171160
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&analogbufio_bufferedin___exit___obj) },
172-
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&analogbufio_bufferedin_read_obj)},
161+
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&analogbufio_bufferedin_readinto_obj)},
173162

174163
};
175164

shared-bindings/analogbufio/BufferedIn.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@
3232

3333
extern const mp_obj_type_t analogbufio_bufferedin_type;
3434

35-
void common_hal_analogbufio_bufferedin_construct(analogbufio_bufferedin_obj_t *self, const mcu_pin_obj_t *pin, uint8_t *buffer, uint32_t len, uint8_t bytes_per_sample, bool samples_signed, uint32_t sample_rate);
35+
void common_hal_analogbufio_bufferedin_construct(analogbufio_bufferedin_obj_t *self, const mcu_pin_obj_t *pin, uint32_t sample_rate);
3636
void common_hal_analogbufio_bufferedin_deinit(analogbufio_bufferedin_obj_t *self);
3737
bool common_hal_analogbufio_bufferedin_deinited(analogbufio_bufferedin_obj_t *self);
38-
void common_hal_analogbufio_bufferedin_read(analogbufio_bufferedin_obj_t *self);
38+
uint32_t common_hal_analogbufio_bufferedin_readinto(analogbufio_bufferedin_obj_t *self, uint8_t *buffer, uint32_t len, uint8_t bytes_per_sample);
3939

4040
#endif // __MICROPY_INCLUDED_SHARED_BINDINGS_ANALOGBUFIO_BUFFEREDIN_H__

shared-bindings/analogbufio/__init__.c

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,22 +40,6 @@
4040
//| call :py:meth:`!deinit` or use a context manager. See
4141
//| :ref:`lifetime-and-contextmanagers` for more info.
4242
//|
43-
//| For example::
44-
//|
45-
//| import analogbufio
46-
//| import array
47-
//| from board import *
48-
//|
49-
//| length = 5000000
50-
//| mybuffer = array.array("H", 0x0000 for i in range(length))
51-
//| adc_in = analogbufio.BufferedIn(GP26, mybuffer, length)
52-
//| analogbufio.read()
53-
//| print(*mybuffer)
54-
//| adc_in.deinit()
55-
//|
56-
//| This example will initialize the the device, read and fill
57-
//| :py:data:`~analogbufio.BufferedIn` to mybuffer
58-
//|
5943
//| TODO: For the essentials of `analogbufio`, see the `CircuitPython Essentials
6044
//| Learn guide <https://learn.adafruit.com/circuitpython-essentials/circuitpython-analogbufio>`_
6145
//|

0 commit comments

Comments
 (0)