cpu/sam0_common/periph/dma: add ADC sampling via DMA - #22626
Conversation
The `dac_set()` API says > The value is always given as 16-bit value and is internally scaled to the > actual resolution that the DAC unit provides (e.g. 12-bit). We didn't do that scaling before - enable it even though it breaks existing users.
…nce" This reverts commit 5765ebb.
|
This includes the previous two PRs, right? |
|
does only depend on #22261 |
Murdock results❌ FAILED f7f6d3d fixup! test/periph/adc_dma: add test for ADC DMA based sampling
Build failures (1)
Artifacts |
|
|
||
| int main(void) | ||
| { | ||
| adc_continuous_begin(ADC_RES_12BIT); |
There was a problem hiding this comment.
Wouldn't it make sense to move this into adc_dma_setup()?
You already forgot the adc_continuous_stop() after adc_dma_release() - if those always go together, just reduce them to one function.
There was a problem hiding this comment.
adc_continuous_begin is handy because of _adc_configure which is in the non continuous variant tied to direct sampling. I would assume adc_continuous_begin/stop is used already by people, so using DMA is like on top of that. Before I call adc_continuous_, I would rather reuse _adc_configure(). I dont know/remember, why I did not do this. That way, there would be no dependency to adc_continuous.
But using both could be in conflict, because adc_continuous set up ADC already and the DMA could reconfigure it.
| adc_dma_setup(adc_arg.adc, _adc_dma_cb, &adc_arg); | ||
| adc_dma_start(adc_arg.adc, _buf, adc_arg.avg_samples_numof); | ||
|
|
||
| xtimer_msleep(CONFIG_TEST_ADC_SLEEP_SEC * MS_PER_SEC); |
There was a problem hiding this comment.
Is there no way to get a notification / wait until the sample buffer is full?
There was a problem hiding this comment.
The sample buffer (2048) is full every time when a DMA interrupt happens. The interrupt triggers an event to copy the samples to a separate buffer and process them. The second buffer exists so that sampling can continue while processing is ongoing. The xtimer_msleep was for me a cheap alternative to a shell command to stop sampling. The shell is flooded with DMA samples anyway.
There was a problem hiding this comment.
So the DMA is always looping around?
Since you only have a single buffer, how do you know your memcpy is faster than the DMA already writing the next sample to the beginning again?
There was a problem hiding this comment.
In the DMA ISR, samples are copied form sampling to processing buffer, so there are 2 buffers. When processing was too slow the event would still be queued and "ADC: processing too slow\n" prints.
There was a problem hiding this comment.
I assume that DMA does not continue when I am in the ISR
There was a problem hiding this comment.
Swapping buffers is favorable. By using properly, you mean calling dma_prepare from ISR to swap buffers?
I think still the BLOCKACT_BOTH is required, because the DMA could continue until buffers are swapped, so a few newer samples could still override the sampling buffer.
There was a problem hiding this comment.
I would wrap this in adc_dma_continue(), calling dma_prepare and continuing execution from ISR
There was a problem hiding this comment.
You can chain the buffers together, so no additional call to dma_prepare() is needed.
It would still be good to also support single shot operation if one want to only get one buffer full of samples.
There was a problem hiding this comment.
What do you mean by chaining them together?
One shot or n-shot could be realized with calling adc_dma_stop instead of adc_dma_continue from ISR.
There was a problem hiding this comment.
With dma_append_dst() you can specify the next buffer that should be used when the first buffer is full.
If you then chain the last buffer to the first, you get a loop (this is what dma_enable_loop() does)
| @@ -477,3 +526,89 @@ int32_t adc_sample(adc_t line, adc_res_t res) | |||
|
|
|||
| return val; | |||
| } | |||
|
|
|||
| int adc_dma_setup(adc_t line, dma_cb_t cb, void *arg) | |||
There was a problem hiding this comment.
In #20622 I could specify the sampling frequency - here we just use whatever was configured on the board level for all ADC channels?
There was a problem hiding this comment.
Yes whatever is configured with ADC_GCLK_SRC and ADC_PRESCALER. Do I have to do it like in in your PR, or can it stay like this now?
| /** | ||
| * @brief Update the DMA Completion callback context | ||
| * | ||
| * @param dma[in] DMA channel to release |
There was a problem hiding this comment.
| * @param dma[in] DMA channel to release | |
| * @param[in] dma DMA channel to release |
| * @brief Update the DMA Completion callback context | ||
| * | ||
| * @param dma[in] DMA channel to release | ||
| * @param ctx[in] DMA complete callback context |
There was a problem hiding this comment.
| * @param ctx[in] DMA complete callback context | |
| * @param[in] ctx DMA complete callback context |
|
be sure to adjust the PR description to match the actual amount of changes
|
Contribution description
Implement ADC sampling via DMA, using circular DMA descriptors as in #22261.
Testing procedure
BOARD=same54-xpro make -C test/periph/adc_dmaThe test application samples ADC at most possible speed for a given timespan.
Issues/PRs references
Declaration of AI-Tools / LLMs usage:
AI-Tools / LLMs that were used are:
DMAC_BTCTRL_BLOCKACT_INT