8 ADC input @20 KHz

Dear Community Members,

I am trying to implement a fast 8 channel Analog capture using on board ADC on BBB. I know there are only 7 available on pin header, but as I am starting off & learning I am ok with count of 7. :slight_smile:

I just wanted to have an opinion about the approach to opt for, What would be the most optimum (& preferably faster to implement) option to achieve it?

I have read quite a few articles and thread (& more confused ) and currently I am using libpruio for implementing it but as of now even after configuring libpruio I am only getting 900 - 1000 samples per seconds.

So, is it my configuration wrong or for such high speed I should opt for external ADC? or something else?

I am completely NEW to EMBEDDED domain. So please pardon me.

It would be great if someone could guide me, precisely towards some direction.

Sincerely,
Rathin

Well essentially, you probably need a review of your code. If itā€™s large, past it on a pastebin site, and give us a link here. Also ALL code would be best. bitā€™s and pieces wonā€™t be enough.

Hi,
Thank u very much for prompt response. I am using the default C wrapper example from libpruio and modified it. HEre are the changes and the paste bin

Modifications:

  1. changed steps - 8
  2. Made delay = 0,
  3. Start sample delay = 0
    Paste bin: http://pastebin.com/yBHdN548

OK, so while the executable is running. How much CPU is being used ? The reason I ask may be less than obvious, but Iā€™m asking because I do not know what type pruIo is. But the assignment of this type looks remarkably similar to mmap(). e.g. a file mapped to a region in memory.

So a few things to take notice of.

First, youā€™re making a call to select() every while loop iteration. That is to say: every 1ms, assuming the OS lets you. select() I believe is a system call, and can slow things down. If the call to the file descriptor object is blocking. It can slow things down quite considerably. Here, again, Iā€™m not familiar with TJFā€™s library to know. One potential ā€œfixā€ might be to change the file descriptor object to O_NONBLOCK using fcntl(). Then perhaps look into using epoll() which has none of the performance issues that select(), or poll() have. Which is something I do not experience with personally, but have been reading a lot about lately for my own purposes.

Second, you making a call to every while loop cycle to fprintf() which has to format 8 variables down to every 1ms. fprintf(), Iā€™m not very familiar with, but if it is similar to printf() this can be another performance hit. With that said, Iā€™m not sure how you could avoid this. That is to say, I know what the function does, I just have never read the actual implementation code . . . so could not tell you how it does, what it does. Iā€™m assuming, it behaves exactly like printf(), but allows one to send this formatted output to an alternate ā€œfile streamā€. Without . . .heh . . . ā€œmagic hand wavingā€.

Thirdly, and Iā€™m still not quite sure how often this happens. Because I have not even a clue what the TEMP_FAILURE_RETRY macro does. Here, Im assuming it returns some condition based on the state of the file descriptor( ready read, etc ). But once you break out of the while loop, into the outside do loop, you have cough 3 what I think are also system calls to tcgetattr(). But once again, Iā€™m not all that familiar with termios either . . .

Anyway, are you seeing a pattern here ? For very performant code, system calls are baaaaaaaaad. Did I emphasize that enough ? This is why many people when working with file descriptors often use mmap() Or rather is one very good reason why. Unfortunately Iā€™m also not all that familiar with the ADC on this platform either . . . However, assuming there is a sysfs pseudo file available for the ADC, using mmap() to map to it should not be all that difficult. After that, I think the majority of the difficulty would be working out timing. Or in other words it is very possible using mmap() on the file descriptor to the ADC and just pumping that data straight into the termios buffer may be too fast.

Anyway, sorry I could not answer your question better . . . but hopefully I did give you a few things to consider. Perhaps TJF can elaborate on some of the points I made, and prove / disprove what Iā€™m assuming in many cases.

OK, so while the executable is running. How much CPU is being used ? The reason I ask may be less than obvious, but Iā€™m asking because I do not know what type pruIo is. But the assignment of this type looks remarkably similar to mmap(). e.g. a file mapped to a region in memory.

err . . .heh , What I was getting at here is that if the executable is using more than say 5% CPU. Then there is a good possibility that the file descriptor object is non blocking. But if the executable is using a small amount of CPU, then it most likely is a non blocking file descriptor. Which in English means it is probably the majority of the ā€œslownessā€ problem.

Another one of those days . . .

If the executable is using a small amount of CPU, it is very likely the file descriptor is a blocking descriptor, and very likely to be the majority of the performance problem.

Looking at the code, and seeing the use of select() though I would assume the file descriptor is non blocking. But assuming anything, often has a way of coming back and biting oneā€™s backside.

So, I got bored, and decided to do some ā€œscrewing aroundā€. But took me a while because I had not used, or setup the ADC peripheral before, but . . . here is some quick and dirty code, using std API stuff - Which breaks all the rules I spoke of in my second post heh. But still pretty darned fast. Iā€™d be happier if I could use mmap() directly on /sys/bus/iio/devices/iio:device0/in_voltage0_raw, but either Iā€™ve been trying to use an improper mode of operation. Or mmap() on that file location is not supported. So, might be forced to use mmap() on /dev/mem . ā€¦ug!

#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>

void read_adc(int fd)
{
char adc[5] = {0};
int len = read(fd, adc, sizeof(adc - 1));
adc[len] =ā€˜\0ā€™;
printf("%s ", adc);
}

int main()
{
const char *fname = ā€œ/sys/bus/iio/devices/iio:device0/in_voltage0_rawā€;
int count = 0;

while( count++ <= 1000){
int fd = open(fname, O_CREAT | O_RDONLY);
if(fd == -1){
printf(ā€œerror: %s\nā€, strerror(errno));
exit(1);
}

if(count % 15 == 0 && count != 0)
printf(ā€œ\nā€);

read_adc(fd);

close(fd);
}
printf(ā€œ\nā€);

return 0;
}

Output:
debian@beaglebone:~$ time ./test
4016 4020 4007 4010 4008 4010 4005 4005 4013 4015 4015 4011 4008 4001
4010 4006 4006 4011 4007 4011 4003 4009 4006 4005 3995 3999 4005 4000 3997
4008 4016 4009 3997 4008 4011 4013 4011 4012 4009 4006 3997 4007 4007 3999
4014 4014 4007 4015 4008 4011 4012 4012 4007 4013 4016 4017 4016 4016 4013
4006 4013 4015 4006 4013 4009 4003 4011 4011 4017 4015 4008 4005 3998 4000
4002 4011 4012 4010 4013 4009 4010 4010 4010 4010 4011 4008 4007 4007 4007
4013 3998 4006 4007 4003 3999 4009 4005 4013 4018 4019 4014 4012 4003 4008
4010 4017 3999 4002 4008 4008 4008 4004 4010 4008 3996 4002 4009 4014 4011
4012 4006 4007 4004 4000 4008 4008 4010 4008 4007 4006 4003 4007 4000 4008
4003 4010 4003 4006 4006 3996 4001 4003 4000 4002 3996 4007 4003 4011 4013
4012 4017 4013 4008 4002 4010 4005 4013 4016 4014 4012 4015 4013 4013 4007
4011 4008 4010 4014 4016 4016 4015 4018 4013 4016 4005 3997 4007 3998 4007
4002 4004 4009 4007 4010 4010 4001 4005 4008 4003 4010 4012 4013 4006 3994
4014 4006 4008 4009 4014 4015 4012 4012 4013 4010 4012 4014 4015 4020 4017
4017 4013 4010 4020 4014 4012 4010 4008 3997 4002 4001 4012 4010 4006 4016
4010 4004 4004 4004 4003 4004 4004 3999 4003 4007 4008 4006 4005 4006 4007
4013 4013 4011 4010 4010 4005 3996 4002 4008 4012 4012 4007 4006 4000 3999
4009 4005 4013 4010 4008 4012 4011 4015 4016 4017 4013 4009 4008 4007 4006
4015 4012 4010 4013 4011 4016 4012 4013 4012 4009 4007 3998 3995 3998 4004
4003 4011 4009 4001 3998 4007 4008 4001 4008 4008 4012 4013 4007 3998 3999
4015 4007 3997 4005 4009 4007 4002 4009 4015 4012 4007 4009 4013 4017 4015
4010 4005 4011 4017 4015 4010 4013 4012 4004 4008 4012 4010 4014 4015 4013
4016 4016 4016 4007 4009 4011 4010 4009 4010 4010 4004 4004 4002 4002 4011
4008 4007 4004 4010 4006 4008 4007 4005 4006 4008 4002 4009 4013 4011 4006
4010 4015 4014 4016 4018 4015 4017 4015 4015 4018 4016 4013 4011 4013 4016
4017 3998 4012 4006 4008 4014 4011 4003 4012 4018 4014 4012 4005 3994 4000
4004 4003 4007 4012 4012 4005 4005 4006 4008 4001 3999 4004 4000 4004 4000
4011 4010 4005 4002 4009 4007 4002 4013 4015 4020 4012 4011 4011 4002 4011
4008 4013 4009 4012 4006 4011 4009 4015 4013 4009 4003 4008 4003 4005 4009
4017 4001 4010 4012 4009 4009 4007 4010 4007 4006 4004 4003 4008 4008 4003
4012 4009 4007 4008 4005 4006 4000 3995 4000 4007 4006 4003 4004 4009 4004
4008 4020 4010 4009 4006 4008 4015 4018 4017 4011 4014 4013 4010 4009 4012
4011 4009 4016 4017 4016 4011 4005 4007 4012 4014 4014 4005 4004 3998 4000
4009 4009 4005 3996 4006 3999 4002 4009 4014 4011 4009 4008 4008 4012 4013
4003 4007 4010 4012 4012 4010 4010 4010 4014 4017 4016 4007 4020 4013 4007
4019 4015 4014 4016 4016 4016 4009 4004 4000 4009 4012 4010 4015 4015 4015
4016 4011 4013 4008 4012 4009 4007 4006 4005 4006 4012 4004 4011 4015 4009
4010 4014 4004 3998 4008 4010 4004 4003 4008 4013 4013 4009 4007 4004 4009
4014 4013 4016 4012 4010 4011 4013 4013 4013 4008 4017 4015 4015 4011 4014
4016 4007 4013 4014 4009 3999 4008 4003 4005 4009 4014 4007 4003 3993 3999
3999 4005 4013 4003 4011 4009 4011 4010 4006 4002 4006 3998 4001 4003 4009
4011 4011 4016 4013 4018 4013 4010 4003 4000 4006 4015 4011 4017 4017 4016
4010 4017 4017 4016 4012 4016 4015 4013 4012 4013 4009 4003 4010 4006 4012
4004 4013 4001 4000 4005 3997 4004 4008 4012 4008 4005 3996 4003 3999 4006
4016 4002 4000 4002 4008 4009 4006 3995 4005 4008 4013 4012 4012 4009 4007
4013 4006 4018 4012 4005 4016 4014 4012 4011 4007 4014 4016 4015 4014 4013
4005 4007 4013 4008 4009 4002 4012 4011 4015 4013 4012 4010 4004 4007 4005
4013 4008 4013 4014 4014 4004 4012 4012 4012 4007 4009 4006 4010 4013 4014
4015 4006 4008 4009 4007 4004 4011 4005 4009 4007 4020 4015 4012 4005 4008
4021 4018 4016 4013 4003 4015 4015 4019 4014 4013 4003 4007 4009 4014 4015
3999 4016 4007 4008 4004 4008 4011 4003 4011 4012 4009 4006 4009 4008 4005
4002 4005 4011 4008 4011 4009 4016 4014 4010 4012 4016 4015 4011 4010 4009
4010 4011 4019 4018 4015 4017 4010 4000 4009 4015 4017 4017 4013 4017 4012
4011 4016 4014 3999 4008 4006 4006 4011 4010 4001 4000 4008 3998 4007 4013
4008 4007 4011 4008 4011 4007 4013 4012 4014 4009 4006 4006 4007 4000 4002
4012 4011 4008 4010 4004 4015 4013 4015 4014 4006 4019 4012 4007 4016 4016
4005 4009 4011 4010 4010 4004 4013 4014 4015 4015 4016 4011 4014 4009 4004
4015 4013 4009 4011 4009 4009 4006 4008 4005 3995 4001 4007 4007 4008 4008
4008 4008 4004 4006 4011 4010 4002 4011 4013 4009 4003 4000 4006 4011 4010
4015 4020 4016 4017 4017 4016 4016 4015 4014 4013 4010 4012 4006 4002 4000
4017 4016 4013 4010 4013 4008 4011 4010 4006 4002 4007 4009 4010 4011 4007
4011 4012 4014 4003 4003 4012 4010 4014 4013 4010 4007 3994 4000 4006 4002
3999 4006 4004 4008 4011 4005 4005 4010 4008 4012 4014 4017 4015 4015 4001
4011 4006 4015 4017 4013 4015 4014 4018 4016 4012 4011 4009 4011 4014 4015
4008 4009 4009 4006 4011 4010 4008 4010 4005 4006 4005 4005 4009 4003 4009
4008 4010 4011 4010 4012 4007 4009 4007 4000 4004 4008 4008 4013 4016 4017
4019 4016 4021 4018 4018 4012 4009 4007 4015 4015 4012 4013

real 0m0.328s
user 0m0.010s
sys 0m0.250s

Oh, and that is using one shot mode. I did not think to use continuous. BUt I really wanted to see how slow using open(), read(), and close() really were. Because I had done a lot of reading on the subject, but never actually used it( I always used mmap() ).

Dear William,

Amazing, i m awed by your rigorous treatment of the topic :slight_smile: and as you said I ran a test of that program, which proved that you are correct!! the program ( mine one) is taking 10.2% to 11% of CPU!! which proves that its in blocking mode.
and I am not aware of O_NONBlOCK and mmapā€¦ I will read it up and see but for that do you recommend and preferred resources?

and thanks for the code I will run it and let you know. but there are few hick ups:

  1. My uname -a out put is : Linux beaglebone 3.8.13-bone72 #1 SMP Tue Jun 16 21:36:04 UTC 2015 armv7l GNU/Linux

  2. your kernel has preemption i.eā€¦ it is RT patched / enabled: mine is not!!
    So are you using official Debian image & patched it or r you using some other image?

  3. I dont know how to turn off systemd?
    can you guide me!!

Once again thanks a LOT!! :slight_smile:

Hi,

I tried running your code and I was successful and unsuccessful both!! :slight_smile:

Success: I could compile code it compiled and when I put ā€œtime ./testā€ I gave me following output

real 0m0.011s
user 0m0.000s
sys 0m0.008s

unsuccessful: it didnā€™t show any readings!!
My guess is it has something to do with my kernel preemtion, isnā€™t it?

Well, Iā€™ll probably write up a quick blog post on it tomorrow. So I can link it to others when / if needed to. How i setup the ADC and all that. In the mean time, you can read the last 5 posts Iā€™ve made here: http://www.embeddedhobbyist.com/ . . .let me look . . .

Beaglebone black ā€“ Setup / customization guide which is actually very brief( I wanted to write more, but didnt have the time ) covers how to disable systemd. HOWEVER, Iā€™d test with systemd, and maybe without. It may very well have no impact on code execution. Iā€™ve disabled it because Iā€™m used to the ā€œold schoolā€ sysv init daemon.

Beaglebone black ā€“ Updating device tree files is pretty close to an exact steps of how to upgrade to a newer kernel, and then recompile the peripheral device tree files.

Well it might be youā€™re running in continuous mode. That much I do not know, but it could be difference in kernels ? I could not say.

I pretty much followed this guide for ADC setup http://processors.wiki.ti.com/index.php/Linux_Core_ADC_Userā€™s_Guide#Usage

Amazing, i m awed by your rigorous treatment of the topic :slight_smile: and as you said I ran a test of that program, which proved that you are correct!! the program ( mine one) is taking 10.2% to 11% of CPU!! which proves that its in blocking mode.

Well actually if my assumption is correct that would be non blocking mode. blocking mode reads, sees nothing, puts the code to sleep for a little bit, then reads again. But while this process is waiting for itā€™s successful read, it blocks all other execution in that process.

Non blocking, reads, sees nothing, and then continues execution. So for example if you have two reads back to back on two different channels, it would not matter in what order the data was ready. The first channel to have data would very likely ā€œdisplayā€ that data first. But in blocking mode. EVERY read after the current read has to wait until it is successful.

This is why blocking uses less CPU, while non blocking uses more. 10-11% could possibly still be non blocking though, and it not so much as a rule, as much as possible indicator. 20% CPU, iā€™d probably bet on it. ~10% . . .is hit and miss. I can tell you that using blocking reads with socketcan ( CANBUS peripheral ), uses like 0-2% CPU . . .

Yeah, I saw those posts of yours :slight_smile: Okk, So till you post your blog, I will do the following,

  1. Read your last 5 posts
  2. try updating my Debian
  3. implement your customization
  4. read the ADC page that you have sent!!

Thanks :slight_smile:

btw can you suggest me a book or and link where I can learn kind of coding you r doing, embedded coding I guess!! The basics and other stuff!!

Once again thanks a lot!! :slight_smile:

Rathin

Non blocking, reads, sees nothing, and then continues execution. So for example if you have two reads back to back on two different channels, it would not matter in what order the data was ready. The first channel to have data would very likely ā€œdisplayā€ that data first. But in blocking mode. EVERY read after the current read has to wait until it is successful.

But if that is the case, than data would come randomly to me!! 2nd channel 1st and than maybe 4th channel, dont you think its risky? specially in my case, I want to analyze the data, filter it if required & reconstruct the waveform!! :slight_smile: your explanation is scaring me :-p

Well, Iā€™ve been coding since the early '90ā€™s . . .and have been using Debian for about that long too. 93-94 somewhere in there. Anyway . .

The key for me since Iā€™ve programmed using many different languages is being able to find good documentation. Passed that, knowing what keywords to use when searching the internet. See, as experienced as I am with programming, and Linux, I can not remember exactly how to do everything. But I do usually know what is possible, and how to go about finding out exactly how . . .

http://www.amazon.com/Exploring-BeagleBone-Techniques-Building-Embedded/dp/1118935128 this is the only book I know enough about to recommend. But Iā€™ve barely read any of it. Many people says itā€™s excellent, and I know how DR Molloy is from reading some of his stuff, and watching several of his embedded electronics videos in my down time.

Passed that, search the internet for ā€œbest C programming booksā€, and just start reading those that are available for free off the internet. Also, try to stay away from ā€œhow to sitesā€ or tutorials, unless you know one is good. As they tend to teach bad habits . . .But no one is perfect.

With all that in mind, when it comes to programming, and Iā€™m in the zone. Iā€™m like a bulldog. Nothing stops me :slight_smile:

But if that is the case, than data would come randomly to me!! 2nd channel 1st and than maybe 4th channel, dont you think its risky? specially in my case, I want to analyze the data, filter it if required & reconstruct the waveform!! :slight_smile: your explanation is scaring me :-p

No not really, as weā€™re talking about micro or nano seconds. Also, one of the serious electronics engineers around who knows exactly how the hardware works might be able to tell you exactly how the ADC works. I can not. Passed that, do you really really need more than a few adc readings a second to do what you need ? By a few, I mean anywhere from 10 to 50 a second. Now if youā€™re calculating / changing engine timing on a very high RPM engine or something . . .maybe I could understand but yeah heh.

You have coding even before I have been on this planet!! :smiley: Oh goshā€¦ :slight_smile:

and thanks for the pointers, Yeah I also have came across this book, but was putting off but not anymore!! and yes, I will stay away from how tos, I agree to you they are pretty shallow also!1 thats my experience wrt web developments and all.

With all that in mind, when it comes to programming, and Iā€™m in the zone. Iā€™m like a bulldog. Nothing stops me :slight_smile:

I can SEE that drive!! :slight_smile:

Well, once you have a decent amount of programming experience, avoiding the tutorials / howtoā€™s etc wonā€™t matter so much. Because youā€™ll know. Also with that in mind, maybe youā€™ll even think about some things differently. That is fine too. Get some experience, and then choose your own path :slight_smile:

I can SEE that drive!! :slight_smile:

Yeah well there are others here more talented and driven than me :wink: