Skip to content

Commit e8a7700

Browse files
committed
add looping to rp2040 analogbufio.BufferedIn
1 parent 2f62612 commit e8a7700

File tree

5 files changed

+151
-70
lines changed

5 files changed

+151
-70
lines changed

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

Lines changed: 103 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -58,19 +58,36 @@ void common_hal_analogbufio_bufferedin_construct(analogbufio_bufferedin_obj_t *s
5858
float clk_div = (float)ADC_CLOCK_INPUT / (float)sample_rate - 1;
5959
adc_set_clkdiv(clk_div);
6060

61-
// Set up the DMA to start transferring data as soon as it appears in FIFO
62-
uint dma_chan = dma_claim_unused_channel(true);
63-
self->dma_chan = dma_chan;
61+
self->dma_chan[0] = dma_claim_unused_channel(true);
62+
self->dma_chan[1] = dma_claim_unused_channel(true);
6463

65-
// Set Config
66-
self->cfg = dma_channel_get_default_config(dma_chan);
64+
// Set up the DMA to start transferring data as soon as it appears in FIFO
6765

66+
// Channel 0 reads from ADC data register and writes to buffer
67+
self->cfg[0] = dma_channel_get_default_config(self->dma_chan[0]);
6868
// Reading from constant address, writing to incrementing byte addresses
69-
channel_config_set_read_increment(&(self->cfg), false);
70-
channel_config_set_write_increment(&(self->cfg), true);
71-
69+
channel_config_set_read_increment(&(self->cfg[0]), false);
70+
channel_config_set_write_increment(&(self->cfg[0]), true);
7271
// Pace transfers based on availability of ADC samples
73-
channel_config_set_dreq(&(self->cfg), DREQ_ADC);
72+
channel_config_set_dreq(&(self->cfg[0]), DREQ_ADC);
73+
channel_config_set_chain_to(&(self->cfg[0]), self->dma_chan[0]);
74+
75+
// If we want to loop, later we'll set channel 0 to chain to channel 1 instead.
76+
// Channel 1 resets channel 0's write address and restarts it
77+
78+
self->cfg[1] = dma_channel_get_default_config(self->dma_chan[1]);
79+
// Read from incrementing address
80+
channel_config_set_read_increment(&(self->cfg[1]), true);
81+
// Write to constant address (data dma write address register)
82+
channel_config_set_write_increment(&(self->cfg[1]), false);
83+
// Writing to 32-bit register
84+
channel_config_set_transfer_data_size(&(self->cfg[1]), DMA_SIZE_32);
85+
// Run as fast as possible
86+
channel_config_set_dreq(&(self->cfg[1]), 0x3F);
87+
// set ring to read one 32-bit value (the starting write address) over and over
88+
channel_config_set_ring(&(self->cfg[1]), false, 2); // ring is 1<<2 = 4 bytes
89+
// Chain to adc channel
90+
channel_config_set_chain_to(&(self->cfg[1]), self->dma_chan[0]);
7491

7592
// clear any previous activity
7693
adc_fifo_drain();
@@ -86,15 +103,22 @@ void common_hal_analogbufio_bufferedin_deinit(analogbufio_bufferedin_obj_t *self
86103
return;
87104
}
88105

106+
// stop DMA
107+
dma_channel_abort(self->dma_chan[0]);
108+
dma_channel_abort(self->dma_chan[1]);
109+
89110
// Release ADC Pin
90111
reset_pin_number(self->pin->number);
91112
self->pin = NULL;
92113

93114
// Release DMA Channel
94-
dma_channel_unclaim(self->dma_chan);
115+
dma_channel_unclaim(self->dma_chan[0]);
116+
dma_channel_unclaim(self->dma_chan[1]);
95117
}
96118

97-
uint32_t common_hal_analogbufio_bufferedin_readinto(analogbufio_bufferedin_obj_t *self, uint8_t *buffer, uint32_t len, uint8_t bytes_per_sample) {
119+
uint8_t *active_buffer;
120+
121+
uint32_t common_hal_analogbufio_bufferedin_readinto(analogbufio_bufferedin_obj_t *self, uint8_t *buffer, uint32_t len, uint8_t bytes_per_sample, bool loop) {
98122
// RP2040 Implementation Detail
99123
// Fills the supplied buffer with ADC values using DMA transfer.
100124
// If the buffer is 8-bit, then values are 8-bit shifted and error bit is off.
@@ -120,48 +144,77 @@ uint32_t common_hal_analogbufio_bufferedin_readinto(analogbufio_bufferedin_obj_t
120144

121145
uint32_t sample_count = len / bytes_per_sample;
122146

123-
channel_config_set_transfer_data_size(&(self->cfg), dma_size);
124-
125-
dma_channel_configure(self->dma_chan, &(self->cfg),
126-
buffer, // dst
127-
&adc_hw->fifo, // src
128-
sample_count, // transfer count
129-
true // start immediately
130-
);
131-
132-
// Start the ADC
133-
adc_run(true);
134-
135-
// Once DMA finishes, stop any new conversions from starting, and clean up
136-
// the FIFO in case the ADC was still mid-conversion.
137-
uint32_t remaining_transfers = sample_count;
138-
while (dma_channel_is_busy(self->dma_chan) &&
139-
!mp_hal_is_interrupted()) {
140-
RUN_BACKGROUND_TASKS;
141-
}
142-
remaining_transfers = dma_channel_hw_addr(self->dma_chan)->transfer_count;
147+
channel_config_set_transfer_data_size(&(self->cfg[0]), dma_size);
148+
149+
if (!loop) { // Set DMA to stop after one one set of transfers
150+
channel_config_set_chain_to(&(self->cfg[0]), self->dma_chan[0]);
151+
dma_channel_configure(self->dma_chan[0], &(self->cfg[0]),
152+
buffer, // dst
153+
&adc_hw->fifo, // src
154+
sample_count, // transfer count
155+
true // start immediately
156+
);
157+
158+
// Start the ADC
159+
adc_run(true);
160+
161+
// Wait for DMA to finish, then stop any new conversions from starting,
162+
// and clean up the FIFO in case the ADC was still mid-conversion.
163+
uint32_t remaining_transfers = sample_count;
164+
while (dma_channel_is_busy(self->dma_chan[0]) &&
165+
!mp_hal_is_interrupted()) {
166+
RUN_BACKGROUND_TASKS;
167+
}
168+
remaining_transfers = dma_channel_hw_addr(self->dma_chan[0])->transfer_count;
143169

144-
// Clean up
145-
adc_run(false);
146-
// Stopping early so abort.
147-
if (dma_channel_is_busy(self->dma_chan)) {
148-
dma_channel_abort(self->dma_chan);
149-
}
150-
adc_fifo_drain();
170+
// Clean up
171+
adc_run(false);
151172

152-
size_t captured_count = sample_count - remaining_transfers;
153-
if (dma_size == DMA_SIZE_16) {
154-
uint16_t *buf16 = (uint16_t *)buffer;
155-
for (size_t i = 0; i < captured_count; i++) {
156-
uint16_t value = buf16[i];
157-
// Check the error bit and "truncate" the buffer if there is an error.
158-
if ((value & ADC_FIFO_ERR_BITS) != 0) {
159-
captured_count = i;
160-
break;
173+
// If we stopped early, stop DMA
174+
if (dma_channel_is_busy(self->dma_chan[0])) {
175+
dma_channel_abort(self->dma_chan[0]);
176+
}
177+
adc_fifo_drain();
178+
179+
// Scale the values to the standard 16 bit range.
180+
size_t captured_count = sample_count - remaining_transfers;
181+
if (dma_size == DMA_SIZE_16) {
182+
uint16_t *buf16 = (uint16_t *)buffer;
183+
for (size_t i = 0; i < captured_count; i++) {
184+
uint16_t value = buf16[i];
185+
// Check the error bit and "truncate" the buffer if there is an error.
186+
if ((value & ADC_FIFO_ERR_BITS) != 0) {
187+
captured_count = i;
188+
break;
189+
}
190+
buf16[i] = (value << 4) | (value >> 8);
161191
}
162-
// Scale the values to the standard 16 bit range.
163-
buf16[i] = (value << 4) | (value >> 8);
164192
}
193+
return captured_count;
194+
} else { // Set DMA to repeat transfers indefinitely
195+
dma_channel_configure(self->dma_chan[1], &(self->cfg[1]),
196+
&dma_hw->ch[self->dma_chan[0]].al2_write_addr_trig, // write address
197+
&active_buffer, // read address
198+
1, // transfer count
199+
false // don't start yet
200+
);
201+
202+
// put the buffer start address into a global so that it can be read by DMA
203+
// and written into channel 0's write address
204+
active_buffer = buffer;
205+
206+
channel_config_set_chain_to(&(self->cfg[0]), self->dma_chan[1]);
207+
dma_channel_configure(self->dma_chan[0], &(self->cfg[0]),
208+
buffer, // write address
209+
&adc_hw->fifo, // read address
210+
sample_count, // transfer count
211+
true // start immediately
212+
);
213+
214+
// Start the ADC
215+
adc_run(true);
216+
217+
return 0;
218+
165219
}
166-
return captured_count;
167220
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@ typedef struct {
1717
mp_obj_base_t base;
1818
const mcu_pin_obj_t *pin;
1919
uint8_t chan;
20-
uint dma_chan;
21-
dma_channel_config cfg;
20+
uint dma_chan[2];
21+
dma_channel_config cfg[2];
2222
} analogbufio_bufferedin_obj_t;

shared-bindings/analogbufio/BufferedIn.c

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
//| ``reference_voltage`` to read the configured setting.
3737
//| (TODO) Provide mechanism to read CPU Temperature."""
3838
//|
39-
4039
//| def __init__(self, pin: microcontroller.Pin, *, sample_rate: int) -> None:
4140
//| """Create a `BufferedIn` on the given pin and given sample rate.
4241
//|
@@ -96,47 +95,53 @@ static mp_obj_t analogbufio_bufferedin___exit__(size_t n_args, const mp_obj_t *a
9695
}
9796
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(analogbufio_bufferedin___exit___obj, 4, 4, analogbufio_bufferedin___exit__);
9897

99-
//| def readinto(self, buffer: WriteableBuffer) -> int:
98+
//| def readinto(self, buffer: WriteableBuffer, loop: bool) -> int:
10099
//| """Fills the provided buffer with ADC voltage values.
101100
//|
102101
//| ADC values will be read into the given buffer at the supplied sample_rate.
103102
//| Depending on the buffer typecode, 'B', 'H', samples are 8-bit byte-arrays or
104103
//| 16-bit half-words and are always unsigned.
105-
//| The ADC most significant bits of the ADC are kept. (See
106-
//| https://docs.circuitpython.org/en/latest/docs/library/array.html)
104+
//| (See https://docs.circuitpython.org/en/latest/docs/library/array.html)
105+
//| For 8-bit samples, the most significant bits of the 12-bit ADC values are kept.
106+
//| For 16-bit samples, if loop=False, the 12-bit ADC values are scaled up to fill the 16 bit range.
107+
//| If loop=True, ADC values are stored without scaling.
107108
//|
108-
//| :param ~circuitpython_typing.WriteableBuffer buffer: buffer: A buffer for samples"""
109+
//| :param ~circuitpython_typing.WriteableBuffer buffer: buffer: A buffer for samples
110+
//| :param ~bool loop: loop: Set to true for continuous conversions, False to fill buffer once then stop
111+
//| """
109112
//| ...
110113
//|
111-
static mp_obj_t analogbufio_bufferedin_obj_readinto(mp_obj_t self_in, mp_obj_t buffer_obj) {
112-
analogbufio_bufferedin_obj_t *self = MP_OBJ_TO_PTR(self_in);
114+
static mp_obj_t analogbufio_bufferedin_obj_readinto(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
115+
enum { ARG_buffer, ARG_loop };
116+
static const mp_arg_t allowed_args[] = {
117+
{ MP_QSTR_buffer, MP_ARG_OBJ | MP_ARG_REQUIRED, {} },
118+
{ MP_QSTR_loop, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} },
119+
};
120+
analogbufio_bufferedin_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
113121
check_for_deinit(self);
114-
122+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
123+
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
124+
mp_obj_t buffer = args[ARG_buffer].u_obj;
115125
// Buffer defined and allocated by user
116126
mp_buffer_info_t bufinfo;
117-
mp_get_buffer_raise(buffer_obj, &bufinfo, MP_BUFFER_READ);
118-
127+
mp_get_buffer_raise(buffer, &bufinfo, MP_BUFFER_READ);
119128
uint8_t bytes_per_sample = 1;
120-
121-
// Bytes Per Sample
122129
if (bufinfo.typecode == 'H') {
123130
bytes_per_sample = 2;
124131
} else if (bufinfo.typecode != 'B' && bufinfo.typecode != BYTEARRAY_TYPECODE) {
125132
mp_raise_ValueError_varg(MP_ERROR_TEXT("%q must be a bytearray or array of type 'H' or 'B'"), MP_QSTR_buffer);
126133
}
127-
128-
mp_uint_t captured = common_hal_analogbufio_bufferedin_readinto(self, bufinfo.buf, bufinfo.len, bytes_per_sample);
134+
mp_uint_t captured = common_hal_analogbufio_bufferedin_readinto(self, bufinfo.buf, bufinfo.len, bytes_per_sample, args[ARG_loop].u_bool);
129135
return MP_OBJ_NEW_SMALL_INT(captured);
130136
}
131-
MP_DEFINE_CONST_FUN_OBJ_2(analogbufio_bufferedin_readinto_obj, analogbufio_bufferedin_obj_readinto);
137+
MP_DEFINE_CONST_FUN_OBJ_KW(analogbufio_bufferedin_readinto_obj, 1, analogbufio_bufferedin_obj_readinto);
132138

133139
static const mp_rom_map_elem_t analogbufio_bufferedin_locals_dict_table[] = {
134140
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&analogbufio_bufferedin_deinit_obj) },
135141
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&analogbufio_bufferedin_deinit_obj) },
136142
{ MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) },
137143
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&analogbufio_bufferedin___exit___obj) },
138-
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&analogbufio_bufferedin_readinto_obj)},
139-
144+
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&analogbufio_bufferedin_readinto_obj)},
140145
};
141146

142147
static MP_DEFINE_CONST_DICT(analogbufio_bufferedin_locals_dict, analogbufio_bufferedin_locals_dict_table);

shared-bindings/analogbufio/BufferedIn.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@ extern const mp_obj_type_t analogbufio_bufferedin_type;
1414
void common_hal_analogbufio_bufferedin_construct(analogbufio_bufferedin_obj_t *self, const mcu_pin_obj_t *pin, uint32_t sample_rate);
1515
void common_hal_analogbufio_bufferedin_deinit(analogbufio_bufferedin_obj_t *self);
1616
bool common_hal_analogbufio_bufferedin_deinited(analogbufio_bufferedin_obj_t *self);
17-
uint32_t common_hal_analogbufio_bufferedin_readinto(analogbufio_bufferedin_obj_t *self, uint8_t *buffer, uint32_t len, uint8_t bytes_per_sample);
17+
uint32_t common_hal_analogbufio_bufferedin_readinto(analogbufio_bufferedin_obj_t *self, uint8_t *buffer, uint32_t len, uint8_t bytes_per_sample, bool loop);
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import analogbufio
2+
import array
3+
import audiocore
4+
import audiopwmio
5+
import board
6+
7+
samples = 100
8+
buffer = array.array("H", [0x0000] * samples)
9+
adc = analogbufio.BufferedIn(board.A0, sample_rate=100000)
10+
11+
adc.readinto(buffer)
12+
13+
print("Sample,Print 1, Print 2,Print 3, Print 4")
14+
for i in range(4):
15+
for j in range(samples):
16+
print(j, "," * (i + 1), buffer[j])
17+
18+
adc.readinto(buffer, loop=True)
19+
20+
print("Sample,Print 1, Print 2,Print 3, Print 4")
21+
for i in range(4):
22+
for j in range(samples):
23+
print(j, "," * (i + 1), buffer[j])

0 commit comments

Comments
 (0)