Read ADC using DMA

The official pyboard running MicroPython.
This is the reference design and main target board for MicroPython.
You can buy one at the store.
Target audience: Users with a pyboard.
felix.letkemann
Posts: 10
Joined: Sat Mar 21, 2015 1:29 pm

Read ADC using DMA

Post by felix.letkemann » Sat Mar 21, 2015 8:13 pm

Hi there! :)
About a week ago I started using the MicroPython board that I got because of the kickstarter campaign and that wasn't in use yet. The reason why I decided to use the PyBoard in this case is the very fast and precice analog-digital-converter provided by the STM32F405RG.
You might guess what the problem was: Like some other guys in this forum I couldn't get the ADC to work as fast as I need it. I'm not sure about the exact requirements but I think the required sampling rate should be something between 100kHz and 500kHz. Also I need to be able to do something else with the CPU.
After some searching in the web and trying out the examples from the tutorial I realized that the only way to do it seems to use DMA to transfer the results from the ADC to the memory. I have seen some posts in this forum where this was recommended to people that need the same thing I do. But nowhere I could find any tutorial about how to use DMA. So I hope you guys can help me.

First I thought that the function read_timed(buf,freq) uses DMA to transfer the samples to the memory. Then I tried to estimate the overhead (beacause of the python code) by giving the function a frequency and a buffer size and dividing it. After a few tries I noticed that it becomes nonlinear at high sampling rates. What made me very curious is the fact that I could set the frequency to a number that is higher than the maximum sampling frequency and the function took nearly the same amount of time as in the case of 100kHz sampling rate.
That is why I downloaded the code from github and startet reading it. After a few moments I realized that none of the ADC-related function of micropython can use the DMA, even though there are quite nice functions to use the DMA in the driver file (micropython/stmhal/hal/src/stm32f4xx_hal_adc.c).

So, using C I think it could be very hard to get the DMA to work with ADC as source. So my first thought was to use inline C like using inline assembler and simply call the functions in the driver file. Also I found out that there seems to be the possibility to use inline C because of a post on kickstarter. But I couldn't find any helpful tutorial or nice examples. Does anybody of you know how to inline C? Or if that would be a solution of my problem?
An alternative would be to change the firmware - but I think that could be quite a hard job and I hope I can avoid doing that.

An other possibility would be to use the inline-assembler to set the right registers to start the ADC using DMA. I think that should be possible but it would require to find out which registers I have to set by eating the manual or reverse engeneering the driver.
Also I don't really understand how the memory is protected: If the DMA controller can write his bytes to the memory, how to prevent that he overrides some data required by the python interpreter and vice versa?

Is there any other way to increase the sampling rate? Do you think it would be hard to extend the firmware with a read_timed function that uses DMA?

What were your solutions to the problem with the slow ADC?

Thanks for answering!

Yours sincerely,
Felix

Damien
Site Admin
Posts: 647
Joined: Mon Dec 09, 2013 5:02 pm

Re: Read ADC using DMA

Post by Damien » Mon Mar 23, 2015 9:49 pm

I've had a look at the timing of the components of the read_timed function. The ADC sampling itself takes around 22us per sample. This is a fixed value independent of how fast you want to sample. The rest of the loop takes about 1us (excluding the waiting for the timer to expire, which is 0 at maximum speed).

So, currently the maximum frequency you can read at is about 44kHz. That's not great :(

I'll see what I can do to fix it.

Ultimately this function will support DMA transfer. I just didn't get round to implementing it yet :)

Damien
Site Admin
Posts: 647
Joined: Mon Dec 09, 2013 5:02 pm

Re: Read ADC using DMA

Post by Damien » Mon Mar 23, 2015 10:41 pm

I have improved the ADC.read_timed() function so that you can go up to about 750kHz sample rate while retaining precision (with 12 bit resolution, 15 cycle conversion time). The problem was that the ADC was being turned off and then back on between each sample! So now it's just turned on at the start, then off at the very end of the read_timed() call.

Is this enough for your current needs?

As I said, I intend to support DMA but it's a bit of work to implement so won't be done right away.

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Read ADC using DMA

Post by pythoncoder » Tue Mar 24, 2015 8:52 am

That's great - I've been bumping up against that sample rate limitation too. Another issue with the ADC read_timed() is that it doesn't seem to be able to work concurrently with the DAC's write_timed() method: I was using the latter to generate test waveforms for the ADC. As soon as I ran read_timed() the test waveform stopped. This necessitated blowing the dust off my ancient sine wave generator ;)

I think the problem of DAC and ADC concurrency needs looking at as a whole - there is an oustanding issue on github (#1129).
Peter Hinch
Index to my micropython libraries.

felix.letkemann
Posts: 10
Joined: Sat Mar 21, 2015 1:29 pm

Re: Read ADC using DMA

Post by felix.letkemann » Wed Mar 25, 2015 6:18 pm

Hello Damien,
thank you very much for solving my problem! I've already seen you commit on github and a minute ago I flashed the new version to my PyBoard.
Now I will try out how this works by sampling some data from my gamma spectrometer.
I think it would be nice to get an error if I call read_timed with a frequency higher than possible. The function seems to behave the same, no matter if I call the it with 700000 or 800000 as parameter.
750 kHz is more than enough for this purpose. So thank you very much again!

Yours sincerely,
Felix

ebike
Posts: 55
Joined: Thu Jul 16, 2015 9:36 pm

Re: Read ADC using DMA

Post by ebike » Fri Jul 17, 2015 1:34 am

Damien wrote:
As I said, I intend to support DMA but it's a bit of work to implement so won't be done right away.
Hi Damien,

It's been a couple of years, is there any progress on DMA for ADC? ... or for that matter PWM?

Cheers,

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Read ADC using DMA

Post by pythoncoder » Fri Jul 17, 2015 8:37 am

See my comment to http://forum.micropython.org/viewtopic.php?f=2&t=811. I'm unconvinced that reading an ADC at a high uncontrolled rate is actually very useful. Still less sending data to PWM.
Peter Hinch
Index to my micropython libraries.

ebike
Posts: 55
Joined: Thu Jul 16, 2015 9:36 pm

Re: Read ADC using DMA

Post by ebike » Fri Jul 17, 2015 9:18 pm

Likewise, see my comment in the same place regarding filtering .. http://forum.micropython.org/viewtopic. ... 4651#p4651
With all due respect, Just because you don't see the application, that does not mean there isn't one ;)

manitou
Posts: 73
Joined: Wed Feb 25, 2015 12:15 am

Re: Read ADC using DMA

Post by manitou » Fri Aug 07, 2015 3:40 pm

Damien wrote:I have improved the ADC.read_timed() function so that you can go up to about 750kHz sample rate while retaining precision (with 12 bit resolution, 15 cycle conversion time). The problem was that the ADC was being turned off and then back on between each sample! So now it's just turned on at the start, then off at the very end of the read_timed() call.
FWIW, I hacked the firmware adc.c to use systick (cycle counter) to measure the ADC conversion time in read_timed(). I commented out the wait on timer6, so it's just waiting for the ADC sample to complete. Instead of returning the ADC values, it returns the elapsed cycles for each sample. Here are the cycle counts for 16 samples

array('H',[3484, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151])

So @168mhz, 151 ticks is 0.9 us. So that's over 1 Msps. The datasheet suggests ADC max rate is 2 Msps. The first sample takes about 20.4us and includes the time for turning on the ADC.

ebike
Posts: 55
Joined: Thu Jul 16, 2015 9:36 pm

Re: Read ADC using DMA

Post by ebike » Fri Aug 07, 2015 8:10 pm

This is all good progress :)

Still would prefer a DMA solution instead of wasting cpu cycles on waiting for timers though.
Also, could the ADC not be turned on in the init call rather than in the read? Then you would have all samples the same rate ..

Post Reply