@@ -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}
0 commit comments