Configure ADC,DMA and Timer to scan multiple ADC channels

I tried to create a script to scan multiple analog channels and write the data via dma into ram. I used MXCube to initilaize the peripherals (ADC1, TIMER6, DMA1_STREAM0).

How the code should work:
Every few seconds (the frequency will be increased) the timer should trigger the adc and convert e.g. 2 channel and the dma transfers the data into ram.

-i disabled D- and I-Cache

-without: SET_BIT(DMA1_Stream0->CR, DMA_SxCR_MINC_Msk); memory location doesn't increase although MXCube configured it hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;

-i tried different memory locations

The DMA works only one time and then doesn't transfer any data while the adc converts values every time the timer creates an interrupt. It looks like there is a DMA flag which isnt't cleared and the dma can't start new transfers. Due to choosing a slow frequency, overruns should not happen.

Similar post where the dma works with setting registers, but without a timer:

[Solved] Using DMA/Fast Communication on portentaH7 - #6 by Sympathic_Cone

Complete Sketch: ADC1_TIMER_DMA.zip (9.9 KB)

You might wish to try :

HAL_ADC_Start_DMA(&hadc1,(uint32_t *)&dma_data,2);

instead of

HAL_ADC_Start_DMA(&hadc1,(uint32_t *) dma_data,2);

and retry to disableD cache.
(only an hypothesis I never used HAL library)

Changing: HAL_ADC_Start_DMA(&hadc1,(uint32_t *)&dma_data,2);

nor disabling D-Cache every iteration doesn't solve the problem that the dma just runs one time.

My five cents:
when DMA is used - there is "cache coherency issue":
Any memory updated by DMA cannot be seen by MCU: it reads from cache. You had to invalidate the cache, to make sure MCU sees the updated memory content ("Cache Policies and Maintenance").

Also, some devices using DMA, e.g. for me ETH controller - they need descriptors in memory for the DMA engine which are not on cached memory. If MCU updates, writes a new descriptor - it sits still in cache, but not yet in real physical memory. If you kick off now a DMA - it will not see this descriptor (DMA uses just real physical memory, MCU writes via caches and updates sit still in cache, not updated in physical memory).

Check, if you should disable DCACHE, use MPU or cache maintenance functions (clear and invalidate).

Finally i found the solution. The DMA was in one shot mode, although it was configured in circular with CubeMX. If i set the bit by myself it works.

CubeMX:  hdma_adc1.Init.Mode = DMA_CIRCULAR;              -doesn't work
CMSIS:   SET_BIT(DMA1_Stream0->CR, DMA_SxCR_CIRC_Msk);    -works

So two lines of code (mem incr. and DMA mode), which were created by CubeMX doesn't work?

The final working code:
ADC1_TIMER_DMA.zip (9.8 KB)

Edit: If you want to use the ADC and DMA in 16 Bit mode:

the line following doesn't work:
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; //doensn't work

instead use:

SET_BIT(DMA1_Stream0->CR, DMA_SxCR_PSIZE_0); //set peripheral size to 16 bit

If you change it the the code should work!

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.