Read ADC using DMA
-
- Posts: 10
- Joined: Sat Mar 21, 2015 1:29 pm
Read ADC using DMA
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
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
Re: Read ADC using DMA
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
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
Re: Read ADC using DMA
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.
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.
- pythoncoder
- Posts: 5956
- Joined: Fri Jul 18, 2014 8:01 am
- Location: UK
- Contact:
Re: Read ADC using DMA
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).
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.
Index to my micropython libraries.
-
- Posts: 10
- Joined: Sat Mar 21, 2015 1:29 pm
Re: Read ADC using DMA
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
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
Re: Read ADC using DMA
Hi Damien,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.
It's been a couple of years, is there any progress on DMA for ADC? ... or for that matter PWM?
Cheers,
- pythoncoder
- Posts: 5956
- Joined: Fri Jul 18, 2014 8:01 am
- Location: UK
- Contact:
Re: Read ADC using DMA
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.
Index to my micropython libraries.
Re: Read ADC using DMA
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
With all due respect, Just because you don't see the application, that does not mean there isn't one
Re: Read ADC using DMA
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 samplesDamien 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.
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.
Re: Read ADC using DMA
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 ..
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 ..