@@ -58,19 +58,36 @@ void common_hal_analogbufio_bufferedin_construct(analogbufio_bufferedin_obj_t *s
58
58
float clk_div = (float )ADC_CLOCK_INPUT / (float )sample_rate - 1 ;
59
59
adc_set_clkdiv (clk_div );
60
60
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);
64
63
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
67
65
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 ]);
68
68
// 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);
72
71
// 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 ]);
74
91
75
92
// clear any previous activity
76
93
adc_fifo_drain ();
@@ -86,15 +103,22 @@ void common_hal_analogbufio_bufferedin_deinit(analogbufio_bufferedin_obj_t *self
86
103
return ;
87
104
}
88
105
106
+ // stop DMA
107
+ dma_channel_abort (self -> dma_chan [0 ]);
108
+ dma_channel_abort (self -> dma_chan [1 ]);
109
+
89
110
// Release ADC Pin
90
111
reset_pin_number (self -> pin -> number );
91
112
self -> pin = NULL ;
92
113
93
114
// 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 ]);
95
117
}
96
118
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 ) {
98
122
// RP2040 Implementation Detail
99
123
// Fills the supplied buffer with ADC values using DMA transfer.
100
124
// 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
120
144
121
145
uint32_t sample_count = len / bytes_per_sample ;
122
146
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 ;
143
169
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);
151
172
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 );
161
191
}
162
- // Scale the values to the standard 16 bit range.
163
- buf16 [i ] = (value << 4 ) | (value >> 8 );
164
192
}
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
+
165
219
}
166
- return captured_count ;
167
220
}
0 commit comments