Read multiple ADC channel simultaneously
Read multiple ADC channel simultaneously
Hi,
I am currently working in a project where a need to collect data.
I managed to collect data from one of the ADC port that the pyboard has thanks to this code:
import array
import os
import pyb
import utime
adc1=pyb.ADC(pyb.Pin.board.Y12)
buf1=array.array('H',bytearray(30000))
tim=pyb.Timer(6, freq=1000)
adc1.read_timed(buf1,tim)
f1=open('data1.dash','w')
for val in buf1:
s=str(val)
f1.write(s+'\n')
f1.close()
This code enables to read the values of the port Y12 and stock them in a file in the SD card.
I am now trying to do the same but with two ports. But if I use the same code, that will first collect the data from the first port and then collect the data from the second port. Is there a way to collect both data at the same time ?
Thanks for your help
Meide
I am currently working in a project where a need to collect data.
I managed to collect data from one of the ADC port that the pyboard has thanks to this code:
import array
import os
import pyb
import utime
adc1=pyb.ADC(pyb.Pin.board.Y12)
buf1=array.array('H',bytearray(30000))
tim=pyb.Timer(6, freq=1000)
adc1.read_timed(buf1,tim)
f1=open('data1.dash','w')
for val in buf1:
s=str(val)
f1.write(s+'\n')
f1.close()
This code enables to read the values of the port Y12 and stock them in a file in the SD card.
I am now trying to do the same but with two ports. But if I use the same code, that will first collect the data from the first port and then collect the data from the second port. Is there a way to collect both data at the same time ?
Thanks for your help
Meide
Re: Read multiple ADC channel simultaneously
never tried to work with ADC, but I would try following way:
1. set callback function for a timer that will set a flag e.g. time_to_read_values
2. in main loop check for time_to_read_values (don't forget to sleep for a while in the end of the loop)
3. if time_to_read_values flag is set = just read both sequentially and write into file.
Also take a look to ADC channel... does it make life easier? (dunno)
and yes, I would suggest you to use context manager for file stuff like:
1. set callback function for a timer that will set a flag e.g. time_to_read_values
2. in main loop check for time_to_read_values (don't forget to sleep for a while in the end of the loop)
3. if time_to_read_values flag is set = just read both sequentially and write into file.
Also take a look to ADC channel... does it make life easier? (dunno)
and yes, I would suggest you to use context manager for file stuff like:
Code: Select all
with open(...) as f:
print(s, file=f) #in this case there is no need to concatenate with '\n'
Re: Read multiple ADC channel simultaneously
Hi,
Thank you for your answer. Sorry for taking so much time to answer but I come with new things.
Here is the code that I finnaly used:
import array
import os
import pyb
import utime
buf1=array.array('H',bytearray(10000))
buf2=array.array('H',bytearray(10000))
temps=array.array('L',bytearray(20000))
start=utime.ticks_us()
i=0
while i!=5000:
adc2=pyb.ADC(pyb.Pin.board.Y11)
adc1=pyb.ADC(pyb.Pin.board.Y12)
buf1[i]=adc1.read()
buf2[i]=adc2.read()
temps[i]=utime.ticks_us()
i=i+1
end1=utime.ticks_us()
f=open('chanel1.dash','w')
for val in buf1:
s1=str(val)
f.write(s1+'\n')
f.close()
h=open('chanel2.dash','w')
for val in buf2:
s2=str(val)
h.write(s2+'\n')
h.close()
j=open('t.dash','w')
for val in temps:
s3=str(val)
j.write(s3+'\n')
j.close()
end2=utime.ticks_us()
r=open('t1.dash','w')
s4=str(end1-start)
s5=str(end2-start)
r.write(s4+'\n'+s5)
r.close()
So this code enables first to collect all the data and then to stock them in different files. There are one file for the first ADC port (f), one file for the second ADC port (h), one file which contain time for each loop of the "while loop" (j), and a file which containt the time of execution of the "while loop" and the total time of the operation (reading+writing).
After collecting all these data, I began to analyze them and I found several problems...
First, If we make a graph of what we measure in one channel as a function of time, we can see that the results are very strange (I am sending the same triangular signal to the two ports). Here are the graph for the 1200 first value collected:
http://zupimages.net/viewer.php?id=16/50/2fvz.jpg
http://zupimages.net/viewer.php?id=16/50/vo59.jpg
http://zupimages.net/viewer.php?id=16/50/1xer.jpg
As you can see, at the begining the values are collectinf very fast and then it goes slower until a big gap.. And then it begins again.
You can also see that after a certain number of iterations, points which are totally unclear appear.
An other thing really strange, if we look at the gap between the 2 channels (which are receiving the same signal), we will see most part of the time that the gap between the two channels is small. But sometimes we can see big error as you can see in this graph :
http://zupimages.net/viewer.php?id=16/50/y8cm.jpg
(graph of: |Channel1-Channel2| vs Time )
Do you have an idea of what can explain all these errors that I have ?
Some specifications:
-time is in us
-we measure the voltage received by the pyboard which is in mV
-I used both "For loop" and "while loop", problem is the same
-I tried to use your command for writting file and it wasn't working
I know that may be I wasn't clear on some point, so please if you don't understand something just ask me.
I will try to use the same loop for only one channel to see if I have the same error and I will post here my results.
Thank you.
Médé
Thank you for your answer. Sorry for taking so much time to answer but I come with new things.
Here is the code that I finnaly used:
import array
import os
import pyb
import utime
buf1=array.array('H',bytearray(10000))
buf2=array.array('H',bytearray(10000))
temps=array.array('L',bytearray(20000))
start=utime.ticks_us()
i=0
while i!=5000:
adc2=pyb.ADC(pyb.Pin.board.Y11)
adc1=pyb.ADC(pyb.Pin.board.Y12)
buf1[i]=adc1.read()
buf2[i]=adc2.read()
temps[i]=utime.ticks_us()
i=i+1
end1=utime.ticks_us()
f=open('chanel1.dash','w')
for val in buf1:
s1=str(val)
f.write(s1+'\n')
f.close()
h=open('chanel2.dash','w')
for val in buf2:
s2=str(val)
h.write(s2+'\n')
h.close()
j=open('t.dash','w')
for val in temps:
s3=str(val)
j.write(s3+'\n')
j.close()
end2=utime.ticks_us()
r=open('t1.dash','w')
s4=str(end1-start)
s5=str(end2-start)
r.write(s4+'\n'+s5)
r.close()
So this code enables first to collect all the data and then to stock them in different files. There are one file for the first ADC port (f), one file for the second ADC port (h), one file which contain time for each loop of the "while loop" (j), and a file which containt the time of execution of the "while loop" and the total time of the operation (reading+writing).
After collecting all these data, I began to analyze them and I found several problems...
First, If we make a graph of what we measure in one channel as a function of time, we can see that the results are very strange (I am sending the same triangular signal to the two ports). Here are the graph for the 1200 first value collected:
http://zupimages.net/viewer.php?id=16/50/2fvz.jpg
http://zupimages.net/viewer.php?id=16/50/vo59.jpg
http://zupimages.net/viewer.php?id=16/50/1xer.jpg
As you can see, at the begining the values are collectinf very fast and then it goes slower until a big gap.. And then it begins again.
You can also see that after a certain number of iterations, points which are totally unclear appear.
An other thing really strange, if we look at the gap between the 2 channels (which are receiving the same signal), we will see most part of the time that the gap between the two channels is small. But sometimes we can see big error as you can see in this graph :
http://zupimages.net/viewer.php?id=16/50/y8cm.jpg
(graph of: |Channel1-Channel2| vs Time )
Do you have an idea of what can explain all these errors that I have ?
Some specifications:
-time is in us
-we measure the voltage received by the pyboard which is in mV
-I used both "For loop" and "while loop", problem is the same
-I tried to use your command for writting file and it wasn't working
I know that may be I wasn't clear on some point, so please if you don't understand something just ask me.
I will try to use the same loop for only one channel to see if I have the same error and I will post here my results.
Thank you.
Médé
Re: Read multiple ADC channel simultaneously
Don't you want to place code intoMeide wrote:Here is the code that I finnaly used:
Code: Select all
tags? And restore identitation? :roll:
Last edited by kamikaze on Thu Dec 15, 2016 6:22 pm, edited 1 time in total.
- pythoncoder
- Posts: 5956
- Joined: Fri Jul 18, 2014 8:01 am
- Location: UK
- Contact:
Re: Read multiple ADC channel simultaneously
@kamikaze The option isn't available to new posters as a defence against spam.
@Meide A few points. There is no need to initialise the ADC's in a loop. It's more efficient to initialise arrays with a generator. And, as @kamikaze says, it's best to use context managers for files.
The big issue is whether to grab samples in a free running loop, as you are doing, or whether to use a timer. A timer will give consistent sampling intervals but won't be able to sample above about 10KHz. Your method will be faster, but the interval between samples will vary because interrupts will occur at times.
The following keeps your free running approach but addresses the issues above. (EDITED 16th Dec)
@Meide A few points. There is no need to initialise the ADC's in a loop. It's more efficient to initialise arrays with a generator. And, as @kamikaze says, it's best to use context managers for files.
The big issue is whether to grab samples in a free running loop, as you are doing, or whether to use a timer. A timer will give consistent sampling intervals but won't be able to sample above about 10KHz. Your method will be faster, but the interval between samples will vary because interrupts will occur at times.
The following keeps your free running approach but addresses the issues above.
Code: Select all
import array
import os
import pyb
import utime
NSAMPLES = 5000
buf1 = array.array('H', (0 for _ in range(NSAMPLES)))
buf2 = array.array('H', (0 for _ in range(NSAMPLES)))
temps = array.array('L', (0 for _ in range(NSAMPLES)))
adc2=pyb.ADC(pyb.Pin.board.Y11)
adc1=pyb.ADC(pyb.Pin.board.Y12)
start=utime.ticks_us()
for i in range(NSAMPLES):
buf1[i] = adc1.read()
buf2[i] = adc2.read()
temps[i] = utime.ticks_us()
end1=utime.ticks_us()
with open('chanel1.dash','w') as f:
for val in buf1:
s1 = str(val)
_ = f.write(s1+'\n')
with open('chanel2.dash','w') as h:
for val in buf2:
s2 = str(val)
_ = h.write(s2+'\n')
with open('t.dash','w') as j:
for val in temps:
s3 = str(val)
_ = j.write(s3+'\n')
end2=utime.ticks_us()
with open('t1.dash','w') as r:
s4=str(end1 - start)
s5=str(end2 - start)
_ = r.write(s4+'\n'+s5)
Last edited by pythoncoder on Fri Dec 16, 2016 6:02 am, edited 2 times in total.
Peter Hinch
Index to my micropython libraries.
Index to my micropython libraries.
Re: Read multiple ADC channel simultaneously
If you want to read samples at a fixed rate, you can use read_timed, like explained here.
http://docs.micropython.org/en/latest/p ... read_timed
Very simple.
http://docs.micropython.org/en/latest/p ... read_timed
Very simple.
- pythoncoder
- Posts: 5956
- Joined: Fri Jul 18, 2014 8:01 am
- Location: UK
- Contact:
Re: Read multiple ADC channel simultaneously
@Roberthh One issue with read_timed is that it only supports a single ADC. To sample two ADC's you'd need to issue two read_timed statements. This could lead to phase errors between the channels. There is a need for a means of reading two ADC's as near simultaneously as possible and at a precise frequency and, as far as I'm aware, the firmware doesn't support it.
I hit this problem with my experiments with making an electrical network analyser. I used a few tricks to get reasonable phase accuracy but this was only possible because the signal driving the network was available. Getting reasonable phase accuracy from a fast arbitrary incoming signal is difficult.
I hit this problem with my experiments with making an electrical network analyser. I used a few tricks to get reasonable phase accuracy but this was only possible because the signal driving the network was available. Getting reasonable phase accuracy from a fast arbitrary incoming signal is difficult.
Peter Hinch
Index to my micropython libraries.
Index to my micropython libraries.
Re: Read multiple ADC channel simultaneously
@Pythoncoder, - Fully agree, see a similar question I raised
viewtopic.php?f=2&t=2682
It seems a real shame that when running without an operating system underneath that micropython doesn't natively give the option to collect multichannel data fast and accurately. It is one of the potential attraction of the pyboard/wipy etc over the likes of RPi for 'fast...ish' real time control.
Regards
viewtopic.php?f=2&t=2682
It seems a real shame that when running without an operating system underneath that micropython doesn't natively give the option to collect multichannel data fast and accurately. It is one of the potential attraction of the pyboard/wipy etc over the likes of RPi for 'fast...ish' real time control.
Regards
Re: Read multiple ADC channel simultaneously
Hi,
First thank you for all your answers, that help me a lot.
So, I tried the code that you post before. I did several attemps to be sure of the result.
Here are the different results that I had:
Chan1 vs Time and Chan 2 vs Time (2 attempts) :
http://zupimages.net/viewer.php?id=16/50/h0xq.jpg
http://zupimages.net/viewer.php?id=16/50/w328.jpg
Chan1 vs Time, 1 to 1000 data:
http://zupimages.net/viewer.php?id=16/50/lc5f.jpg
http://zupimages.net/viewer.php?id=16/50/dfcr.jpg
Chan 1 vs Time, 1000 to 1500 data:
http://zupimages.net/viewer.php?id=16/50/d60z.jpg
http://zupimages.net/viewer.php?id=16/50/sghv.jpg
So as you can see, the results are really better than before at the beginning and then we have a degradation (with some gaps).
I am really surprised to have these gaps because at the beginning it looks regular and clean..
And I also tried with only one channel with the same loop:
http://zupimages.net/viewer.php?id=16/50/isoo.jpg
We have the same degradation, so the gaps certainly come from the loop...
So I am thinking to put a timer to have more regular results (as you said @Pythoncoder). But I was really surprised when you said a timer could sample at 10KHz. The signal that I am using for the moment is only 150Hz and, as you can see, I have some difficulties to obtain something accurate. And the method that I am using for the moment should be faster than to put a timer no ? So how the Timer method can support 10KHz signal ?
If you have some ideas to improve the code or the method, don't hesitate to tell me and I will make a try and post results here.
If you need more information or data, just ask me.
Thank you again
Meide
First thank you for all your answers, that help me a lot.
So, I tried the code that you post before. I did several attemps to be sure of the result.
Here are the different results that I had:
Chan1 vs Time and Chan 2 vs Time (2 attempts) :
http://zupimages.net/viewer.php?id=16/50/h0xq.jpg
http://zupimages.net/viewer.php?id=16/50/w328.jpg
Chan1 vs Time, 1 to 1000 data:
http://zupimages.net/viewer.php?id=16/50/lc5f.jpg
http://zupimages.net/viewer.php?id=16/50/dfcr.jpg
Chan 1 vs Time, 1000 to 1500 data:
http://zupimages.net/viewer.php?id=16/50/d60z.jpg
http://zupimages.net/viewer.php?id=16/50/sghv.jpg
So as you can see, the results are really better than before at the beginning and then we have a degradation (with some gaps).
I am really surprised to have these gaps because at the beginning it looks regular and clean..
And I also tried with only one channel with the same loop:
http://zupimages.net/viewer.php?id=16/50/isoo.jpg
We have the same degradation, so the gaps certainly come from the loop...
So I am thinking to put a timer to have more regular results (as you said @Pythoncoder). But I was really surprised when you said a timer could sample at 10KHz. The signal that I am using for the moment is only 150Hz and, as you can see, I have some difficulties to obtain something accurate. And the method that I am using for the moment should be faster than to put a timer no ? So how the Timer method can support 10KHz signal ?
If you have some ideas to improve the code or the method, don't hesitate to tell me and I will make a try and post results here.
If you need more information or data, just ask me.
Thank you again
Meide
- pythoncoder
- Posts: 5956
- Joined: Fri Jul 18, 2014 8:01 am
- Location: UK
- Contact:
Re: Read multiple ADC channel simultaneously
I'm afraid I'm not sure what your graphs represent. Consider this code which uses the timer to read the ADC's. This is the best way to ensure consistent timing:
I ran this (with a link between X5 and Y12) then plotted the content of chanel1.dash. This was the outcome:
Code: Select all
import array
import os
import pyb
import utime
import micropython
micropython.alloc_emergency_exception_buf(100)
NSAMPLES = 5000
FREQ = 10 # Hz
buf1 = array.array('H', (0 for _ in range(NSAMPLES)))
buf2 = array.array('H', (0 for _ in range(NSAMPLES)))
temps = array.array('L', (0 for _ in range(NSAMPLES)))
adc2=pyb.ADC(pyb.Pin.board.Y11)
adc1=pyb.ADC(pyb.Pin.board.Y12)
dac1 = pyb.DAC(1) # Pin X5
tim = pyb.Timer(4)
def sawtooth():
for i in range(256):
yield i
while i > 0:
i -= 1
yield i
bufout = bytearray(sawtooth())
dac1.write_timed(bufout, FREQ * len(bufout), mode=pyb.DAC.CIRCULAR)
i = 0
def cb(timer):
global i
buf1[i] = adc1.read()
buf2[i] = adc2.read()
temps[i] = utime.ticks_us()
i += 1
if i >= NSAMPLES:
timer.deinit()
tim.init(freq=5000, callback=cb)
start = utime.ticks_us()
while i < NSAMPLES:
pass
end1 = utime.ticks_us()
print((end1 - start) // NSAMPLES)
maxd = 0
for n, s in enumerate(buf1):
if n > 0:
maxd = max(maxd, abs(s - buf1[n -1]))
print('Maximum difference between consecutive samples: {} theoretical: {}'.format(maxd, 4096//256))
with open('chanel1.dash','w') as f:
for val in buf1:
s1 = str(val)
f.write(s1+'\n')
with open('chanel2.dash','w') as h:
for val in buf2:
s2 = str(val)
h.write(s2+'\n')
with open('t.dash','w') as j:
for val in temps:
s3 = str(val)
j.write(s3+'\n')
end2=utime.ticks_us()
with open('t1.dash','w') as r:
s4=str(end1 - start)
s5=str(end2 - start)
r.write(s4+'\n'+s5)
Peter Hinch
Index to my micropython libraries.
Index to my micropython libraries.