8 ADC input @20 KHz

524 views
Skip to first unread message

Rathin Dholakia

unread,
Sep 29, 2015, 4:16:06 PM9/29/15
to BeagleBoard
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. :)

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

William Hermans

unread,
Sep 29, 2015, 4:29:05 PM9/29/15
to beagl...@googlegroups.com
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.

--
For more options, visit http://beagleboard.org/discuss
---
You received this message because you are subscribed to the Google Groups "BeagleBoard" group.
To unsubscribe from this group and stop receiving emails from it, send an email to beagleboard...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Rathin Dholakia

unread,
Sep 29, 2015, 4:38:23 PM9/29/15
to BeagleBoard
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
<iframe src="http://pastebin.com/embed_iframe.php?i=yBHdN548" style="border:none;width:100%"></iframe>

William Hermans

unread,
Sep 29, 2015, 7:08:50 PM9/29/15
to beagl...@googlegroups.com
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.

William Hermans

unread,
Sep 29, 2015, 7:26:14 PM9/29/15
to beagl...@googlegroups.com
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. 

William Hermans

unread,
Sep 29, 2015, 7:32:22 PM9/29/15
to beagl...@googlegroups.com
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.

William Hermans

unread,
Sep 30, 2015, 1:41:34 AM9/30/15
to beagl...@googlegroups.com
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


That's actually not too bad for opening, and closing the file 1000 iterations, then using printf() to display the data to stdout. So, looks like 328ms for 1000 iterations on the same adc channel.

debian@beaglebone:~$ uname -a
Linux beaglebone 4.1.5-ti-rt-r10 #1 SMP PREEMPT RT Fri Aug 14 00:02:42 UTC 2015 armv7l GNU/Linux

debian@beaglebone:~$ cat /etc/dogtag
BeagleBoard.org Debian Image 2015-03-01

Meaning Wheezy 7.8, and systemd disabled - if the later maters at all.

William Hermans

unread,
Sep 30, 2015, 1:48:20 AM9/30/15
to beagl...@googlegroups.com
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() ).

Rathin Dholakia

unread,
Sep 30, 2015, 1:53:20 AM9/30/15
to BeagleBoard
Dear William,

Amazing, i m awed by your rigorous treatment of the topic :-) 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!! :-)

Rathin Dholakia

unread,
Sep 30, 2015, 2:00:33 AM9/30/15
to BeagleBoard
Hi,

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

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?

William Hermans

unread,
Sep 30, 2015, 2:01:21 AM9/30/15
to beagl...@googlegroups.com
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.



William Hermans

unread,
Sep 30, 2015, 2:03:46 AM9/30/15
to beagl...@googlegroups.com
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.

William Hermans

unread,
Sep 30, 2015, 2:06:09 AM9/30/15
to beagl...@googlegroups.com

William Hermans

unread,
Sep 30, 2015, 2:16:38 AM9/30/15
to beagl...@googlegroups.com
Amazing, i m awed by your rigorous treatment of the topic :-) 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 . . .

Rathin Dholakia

unread,
Sep 30, 2015, 2:32:07 AM9/30/15
to BeagleBoard
Yeah, I saw those posts of yours :-) 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 :-)

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!! :-)

Rathin

Rathin Dholakia

unread,
Sep 30, 2015, 2:38:08 AM9/30/15
to BeagleBoard

 
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!!  :-) your explanation is scaring me :-p
 
 

William Hermans

unread,
Sep 30, 2015, 2:44:39 AM9/30/15
to beagl...@googlegroups.com
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 :)

William Hermans

unread,
Sep 30, 2015, 2:52:13 AM9/30/15
to beagl...@googlegroups.com
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!!  :-) 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.

Rathin Dholakia

unread,
Sep 30, 2015, 2:59:23 AM9/30/15
to BeagleBoard
You have coding even before I have been on this planet!! :-D  Oh gosh... :-)

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 :)

I can SEE that drive!! :-)

William Hermans

unread,
Sep 30, 2015, 3:05:51 AM9/30/15
to beagl...@googlegroups.com
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 :)


I can SEE that drive!! :-)

Yeah well there are others here more talented and driven than me ;)

William Hermans

unread,
Sep 30, 2015, 3:07:55 AM9/30/15
to beagl...@googlegroups.com
Oh, I probably forgot the most important part. Never stop learning. For me I love learning, maybe even more than actually getting anything done heh.

Rathin Dholakia

unread,
Sep 30, 2015, 3:09:44 AM9/30/15
to BeagleBoard
No in my case 50 - 60 samples are not sufficient in my case.why? let me explain my objective in a brief:

" I want to sample power supplied, So my basic waveform is of 50 /60 Hz but supply is coming from inverter( DC to AC converter) and as its coming from converter it will have high frequency harmonics / noise; bcoz inverter switching freq can be from 4 KHz to 6 KHz ".
Hence as per Nyquist criteria,  to reconstruct or analyze, I require samples at higher freq than the that. 

Hope it clears the things :-)

Once again my sincerest gratitude for guidance.

Rathin Dholakia

unread,
Sep 30, 2015, 3:16:55 AM9/30/15
to BeagleBoard


On Wednesday, September 30, 2015 at 12:37:55 PM UTC+5:30, William Hermans wrote:
Oh, I probably forgot the most important part. Never stop learning. For me I love learning, maybe even more than actually getting anything done heh.


Oh yes, I also LOVE learning.. Thats why I have taken this project. Actually I am from electrical background, Power system to be precise, but thought of taking a walk on wild side ;) though feeling the heat but, yeah....

I liked your casual approach, Thank you for the insight..!!

William Hermans

unread,
Sep 30, 2015, 3:30:50 AM9/30/15
to beagl...@googlegroups.com
Modified sine or true sine ?

--

William Hermans

unread,
Sep 30, 2015, 4:00:03 AM9/30/15
to beagl...@googlegroups.com
One thing that did cross my mind earlier. I seemingly forgot to post.

First, try running time ./test with sudo so...

$ sudo time ./test

If this works, then it's a permission issue. I really do not think this is the problem, but it possibly could be.

Second, change:

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

To
void read_adc(int fd)
{
        char adc[5] = {0};
        int len = read(fd, adc, sizeof(adc - 1));
        if(len == -1){

            printf("error: %s\n", strerror(errno));
                exit(1);
        }
        else if(len == 0){
            printf("%s\n", "buffer is empty");
        }
        else{

            adc[len] ='\0';
            printf("%s ", adc);
        }
}

What this does is give us more error control, and possibly a clue as to the problem with this executable on your system. If there is an error the program will exit, and give you the reason why. If the buffer in empty, this possibly means you're running in continuous mode, and these pseudo files are empty( unused ). I'm thinking one of these two should be the output you'll get. If not, then I'd have to do more reading to find out exactly why, but.

If you have a spare sdcard, and install the same image you have now on it. Well flash from the standalone command line image for 3.8.x. Then follow the guide I put up on updating device tree files, including upgrading the kernel to the one I've shown. That would be great. As it would allow you to keep your older image too just in case this does not work out for you. Or if you have a Linux system, you could use dd or tar to backup the rootfs, save it, and then wipe the sdcard you do have clean. And start fresh. It could be that libpruio is somehow involved in that code not working properly.

Rathin Dholakia

unread,
Sep 30, 2015, 4:36:30 AM9/30/15
to BeagleBoard
Hi, Its supposed to be true sine but due to switching harmonics & other non-linearity it will be MODIFIED SINE.

And OKK, I do have a spare SDcard, I will put an new image and do your steps and see if it works or not.

Though It will take some time for me, coz I have not done kernel patching & compilation.

Will  get back to you as soon as I have a card ready & results.

And no, I dont think libpruio is a problem cause, I didnt liked it while compiling your code & to my (limited) knowledge, libpruio dose not make any permanent changes to the system.

TJF

unread,
Sep 30, 2015, 2:05:25 PM9/30/15
to BeagleBoard
Hi Rathin!

I didn't read all the stuff in the posts of this thread. But most of it seams irrelevant. Forget it.

You're using libpruio. That's the only way to reach your target --> That's OK.

You cannot reach 20 kHz sampling rate due to two reasons:
  • you're using default step configuration. This is 183 cycles open delay and avaraging 4. That means 183 + 1 + 4 x 14 cycles for each sample (@ 24 MHz) = 10 kHz per channel. Therefor maximum sampling rate for 8 channels is 1.25 kHz.
  • you're using IO mode. The ARM CPU determines the sampling speed and your printf statement slows down the program execution, so you don't get the previous mentioned 1.25 kHz.

In order to reach 20 kHz sampling rate you have to do

  1. configure customized steps for fast sampling (ie. no open delay and no avaraging)
  2. use either MM or RB mode to get accurate timing

Either have a look at the triggers example (to use MM mode) or check out the following code (comming from an example named rb_file for next libpruio versions)


/** \file rb_file.c
\brief Example: fetch ADC samples in a ring buffer and save to file.
 
This file contains an example on how to use the ring buffer mode of
libpruio. A fixed step mask of AIN-0, AIN-1 and AIN-2 get configured
for maximum speed, sampled in to the ring buffer and from there saved
as raw data to some files.
 
Licence: GPLv3
 
Copyright 2014-2015 by Thomas{ dOt ]Freiherr[ At ]gmx[ DoT }net
 
Thanks for C code translation: Nils Kohrs <nils[ dot ]kohrs{ AT }gmail[ dOt ]
com>
 
Compile by: `gcc -Wall -o rb_file rb_file.c -lpruio -lprussdrv`
 
\since 0.2.0.2
*/

 
#include "unistd.h"
#include "time.h"
#include "stdio.h"
#include "../c_include/pruio.h"
 
//! The main function.
int main(int argc, char **argv)
{
 
const uint32 tSamp = 123401;  //!< The number of samples in the files (per step).
 
const uint32 tmr = 5000;      //!< The sampling rate in ns (5000 -> 200 kHz).
 
const uint32 NoStep = 3;      //!< The number of active steps (must match setStep calls and mask).
 
const uint32 NoFile = 2;      //!< The number of files to write.
 
const char *NamFil = "output.%u"; //!< The output file names.
 
struct timespec mSec;
  mSec
.tv_nsec=1000000;
  pruIo
*io = pruio_new(PRUIO_DEF_ACTIVE, 0x98, 0, 1); //! create new driver
 
if (io->Errr){
               printf
("constructor failed (%s)\n", io->Errr); return 1;}
 
 
do {
   
if (pruio_adc_setStep(io, 9, 0, 0, 0, 0)){ //          step 9, AIN-0
        printf
("step 9 configuration failed: (%s)\n", io->Errr); break;}
   
if (pruio_adc_setStep(io,10, 1, 0, 0, 0)){ //         step 10, AIN-1
       printf
("step 10 configuration failed: (%s)\n", io->Errr); break;}
   
if (pruio_adc_setStep(io,11, 2, 0, 0, 0)){ //         step 11, AIN-2
       printf
("step 11 configuration failed: (%s)\n", io->Errr); break;}
 
    uint32 mask
= 7 << 9;         //!< The active steps (9 to 11).
    uint32 tInd
= tSamp * NoStep; //!< The maximum total index.
    uint32 half
= ((io->ESize >> 2) / NoStep) * NoStep; //!< The maximum index of the half ring buffer.
 
   
if (half > tInd){ half = tInd;}  //       adapt size for small files
    uint32 samp
= (half << 1) / NoStep; //!< The number of samples (per step).
 
   
if (pruio_config(io, samp, mask, tmr, 0)){ //       configure driver
                       printf
("config failed (%s)\n", io->Errr); break;}
 
   
if (pruio_rb_start(io)){
                     printf
("rb_start failed (%s)\n", io->Errr); break;}
 
    uint16
*p0 = io->Adc->Value;  //!< A pointer to the start of the ring buffer.
    uint16
*p1 = p0 + half;       //!< A pointer to the middle of the ring buffer.
    uint32 n
;  //!< File counter.
   
char fName[20];
   
for(n = 0; n < NoFile; n++){
      sprintf
(fName, NamFil, n);
      printf
("Creating file %s\n", fName);
      FILE
*oFile = fopen(fName, "wb");
      uint32 i
= 0;               //!< Start index.
     
while(i < tInd){
        i
+= half;
       
if(i > tInd){             // fetch the rest(no complete chunk)
          uint32 rest
= tInd + half - i;
          uint32 iEnd
= p1 >= p0 ? rest : rest + half;
         
while(io->DRam[0] < iEnd) nanosleep(&mSec, NULL);
          printf
("  writing samples %u-%u\n", tInd -rest, tInd-1);
          fwrite
(p0, sizeof(uint16), rest, oFile);
          uint16
*swap = p0;
          p0
= p1;
          p1
= swap;
       
}
       
if(p1 > p0) while(io->DRam[0] < half) nanosleep(&mSec, NULL);
       
else        while(io->DRam[0] > half) nanosleep(&mSec, NULL);
        printf
("  writing samples %u-%u\n", i-half, i-1);
        fwrite
(p0, sizeof(uint16), half, oFile);
        uint16
*swap = p0;
        p0
= p1;
        p1
= swap;
     
}
      fclose
(oFile);
      printf
("Finished file %s\n", fName);
   
}
 
} while(0);
  pruio_destroy
(io);
 
return 0;
}
Desription

rb_file {#SubSecExaRbFile}
-------

\Item{Description}

 
This file contains an example on how to use the ring buffer mode of
  libpruio
. A fixed step mask of AIN-0, AIN-1 and AIN-2 get configured
 
for maximum speed, sampled in to the ring buffer and from there saved
 
as raw data to some files.

\Item{Preparation}

 
No preparation is required. Optionaly you can customize the number of
  samples
, the sampling rate or the number of samples in the source
  code
and recompile your version.

\Item{Operation}

 
Start the program by `./rb_file` and you'll see console output like
~~~{.txt}
Creating file output.0
  writing samples 0-65534
  writing samples 65535-131069
  writing samples 131070-196604
  writing samples 196605-262139
  writing samples 262140-327674
  writing samples 327675-370202
Finished file output.0
Creating file output.1
  writing samples 0-65534
  writing samples 65535-131069
  writing samples 131070-196604
  writing samples 196605-262139
  writing samples 262140-327674
  writing samples 327675-370202
Finished file output.1
~~~

  The program created two new files in the current folder, named
  output.0 and output.1. The files contain the raw data from the three
  ADC channels AIN-0 to AIN-2.

\Item{Source Code}

  src/examples/rb_file.bas

  src/c_examples/rb_file.c


BR

Rathin Dholakia

unread,
Sep 30, 2015, 3:26:11 PM9/30/15
to BeagleBoard
Dear Thomas,

First of all, Thanks a lot for coming by and answering, I respect your efforts in developing in libpruio!! I cant imaging how much work you must have put in..! :-)

Well, second things is you mean to say if I use 8 ADCs I cant achieve 1.25Khz+ sample rate, Than should I use external ADC? would it be easier?

and to be frank I have plenty of doubts in the pruio construct and pruio_config, I which are as follows:

  • You said I have 183 cycle open delay - but where is it? I have made everything 0 ( zero!!) you can have a look at option setting in line bellow!!
    • pruio_new(PRUIO_DEF_ACTIVE, 0, 0, 0)

  • And in my pruio_config, I have made averaging to 0 (you can see that as well). delay 0,
    • pruio_config(io, 1, PRUIO_DEF_STPMSK , 0, 4)

So, is my configuration is wrong or the approach?


few queries with respect to your new example
  • This is also for only 3 ADC channels, so if I extend it for 8, will this one also become slow?
  • I am going to "monitor" the inputs continuously, so I require continuous stream of input, will this approach work?
  • Its later part of my project but I saw u using system timer so asking that I also want to time stamp the data with system time, so can it be done here or should I do it later?

Sorry if my queries sound novice but I am new to this & I am felling overwhelmed by the whole thing..!! :-)


I have been reading the processor TRM and PRU reference manual and I petrified, its kind of ocean of information. ( it remind me of "The old man & the sea"!! )


Sincerely,

Rathin


PS:

evilwulfie

unread,
Sep 30, 2015, 3:48:18 PM9/30/15
to beagl...@googlegroups.com
There is only one ADC in the sitara processor.
There is a 8 to 1 mux to provide you with 8 inputs.
If you want really fast multi channel ADC conversions you may want 8 separate ADC Chips external
to the processor.
--

William Hermans

unread,
Sep 30, 2015, 4:51:44 PM9/30/15
to beagl...@googlegroups.com
So, let me say what is not irrelevant. Standardization, documentation, and usability.  It is my personal opinion that the documentation for libpruio could use *A LOT* of work. Which leads to the third thing I mentioned above. I also do not feel that the whole library is open enough. Last I looked, one had to download a pre-compiled binary in order to make it work. This is not open source, which is big on many peoples mind.

So, if I seem like I have not a clue as to how libpruio works. That would be correct. Because if the documentation is not good enough for me to read, and understand what is going on, I'm not going to bother with the whole situation.

Why ? Because there are other methods to do the same thing, that *are* open sourced, are documented very well, and are standardized. I would rather invest my time into understanding these things. Instead of attempting to reinvent the wheel - As it were.

Does this mean I think libpruio is garbage. No! This means I think there are several things concerning the library that can use some improvement. Which personally I do not care to invest my time into.
 . . .

Rathin Dholakia

unread,
Sep 30, 2015, 5:09:46 PM9/30/15
to BeagleBoard
Dear Wulf man,
Thanks for the input. So do you recommend that external ADC and communicate through PRU with SPI, GPIO?

and do you have some good ADC IC in mind?
...

William Hermans

unread,
Sep 30, 2015, 6:39:38 PM9/30/15
to beagl...@googlegroups.com
Dear Wulf man,
Thanks for the input. So do you recommend that external ADC and communicate through PRU with SPI, GPIO?

and do you have some good ADC IC in mind?

He's left to go out of town, and possibly wont be able to reply for a few days.

So here is one of my concerns with the general path you're on Rathin. You're using the PRU's right now to interface with the on board / die ADC. It only has 7 channels, and it seemed like you said this is not enough in the end, but is possibly good enough right now to experiment with. I'm seeing a problem with that.

Let's just assume for a minute that you intend on using the PRU's to interface with these external ADC(s) later. How are you going to do that ? So we have potentially, I2C, SPI, and *maybe* UART to work with as a communications medium. Assuming the Beaglebone is still an option at this point.

The first problem I'm seeing is: how do you intend to communicate with the external ADC in question ? There is no PRU interface or protocol to interface external devices that I'm aware of. This means you'll have to implement the bus interface / protocol in software through the PRU. e.g. bit bang. Which is completely possible, but it is a hurdle.

The second potential problem I'm seeing here is that let us assume for a minute ( again ), that you intend to now stop using the PRU because of the above. So now your bus communications are directly through the peripherals, and through Linux it's self. Using Linux( or any OS ) will have some latency overhead. Passed that, now that you're no longer using the on board ADC, you're going to have that bus to contend with as well. SPI, I do believe can handle this - bandwidth wise, but this is where I get in a little over my head. I2C, I'm not too sure about, and UART has a maximum theoretical ceiling of 3Mbit/s. Plus, since I have not looked into it. I'm not even sure if there is such a thing as an ADC with a UART bus interface.

Anyway, I only mention this because I know you have a time constraint. Not to make you worry about. But you should be mindful of these things, *and* perhaps others I personally have not thought of yet.

TJF

unread,
Oct 1, 2015, 4:33:46 AM10/1/15
to BeagleBoard
Hi Rathin!


Am Mittwoch, 30. September 2015 21:26:11 UTC+2 schrieb Rathin Dholakia:
Well, second things is you mean to say if I use 8 ADCs I cant achieve 1.25Khz+ sample rate, Than should I use external ADC? would it be easier?

The internal ADC can achieve up to 25 kHz sampling rate for 8 channels. Using an external ADC wouldn't be easier, but you may have reasons for that (which you didn't specify yet): ie. like more than 12 bit resolution, lower impedancy, ...

and to be frank I have plenty of doubts in the pruio construct and pruio_config, I which are as follows:

  • You said I have 183 cycle open delay - but where is it? I have made everything 0 ( zero!!) you can have a look at option setting in line bellow!!
    • pruio_new(PRUIO_DEF_ACTIVE, 0, 0, 0)

 I missed that line, sorry. (I'm not used to C syntax.) This is one way to override the default step configuration. So your code already solves point 1 in my above post.

  • And in my pruio_config, I have made averaging to 0 (you can see that as well). delay 0,
    • pruio_config(io, 1, PRUIO_DEF_STPMSK , 0, 4)

The zero in that line means Tmr = 0. Since you set Samp = 1, you're operating in IO mode. In that case parameter Tmr has no function. Tmr is used in MM or RB mode. It specifies the sampling rate (for all channels).

So, is my configuration is wrong or the approach?


Your configuration is OK, but your main loop is too slow. The fprintf statement is too slow to get the desired sampling rate. libpruio serves the samples, but the ARM CPU doesn't get them all (some get skipped).

Instead of using fprintf(), you could try to write binary numbers by function fwrite() and convert to ASCII numbers later.


few queries with respect to your new example
  • This is also for only 3 ADC channels, so if I extend it for 8, will this one also become slow?

As Wulf Man said:
 
There is only one ADC in the sitara processor.
There is a 8 to 1 mux to provide you with 8 inputs.

Each additional channels slows down the overall sampling rate, which is 200 kHz for one step, 100 kHz for two steps, ..., 25 kHz for 8 steps
  • I am going to "monitor" the inputs continuously, so I require continuous stream of input, will this approach work?

It isn't easy to monitor 8 channels @ 20 kHz by the ARM CPU. The CPU load generated by the OS like interrupts or multitasking may get too big.

I'd do the measurements by libpruio running on PRU-1 in RB mode and do the monitoring by customized real-time ASM code running on PRU-0.

  • Its later part of my project but I saw u using system timer so asking that I also want to time stamp the data with system time, so can it be done here or should I do it later?

Sorry, I've not enough infromation to give any statement here.

BR

TJF

unread,
Oct 1, 2015, 4:40:40 AM10/1/15
to BeagleBoard
Hey William, thanks for your statements. (In bad mood again?)


Am Mittwoch, 30. September 2015 22:51:44 UTC+2 schrieb William Hermans:
I also do not feel that the whole library is open enough. Last I looked, one had to download a pre-compiled binary in order to make it work. This is not open source, which is big on many peoples mind.

Where did you find that? My packages include the source code and the pre-compiled binary. Each user is free to either install the binary (for convenience reasons) or compile from the open source.

 
It is my personal opinion that the documentation for libpruio could use *A LOT* of work.

Aren't you the same William Hermans who wrote this and the following posts, ten month ago? Didn't I generate a PDF version of the documentation and sent it to you, in order to get your help on improvements? Nothing came back, yet. Are you now passing criticism on the progress of your own work?

BR

William Hermans

unread,
Oct 1, 2015, 12:04:59 PM10/1/15
to beagl...@googlegroups.com
So why does this have to be about me ? Did you ever stop to think that *maybe* I tool one look at the documentation, and decided Idid not want to have anything to do with it ? I never even saw a PDF . . .

--

Rathin Dholakia

unread,
Oct 1, 2015, 12:33:23 PM10/1/15
to BeagleBoard
Gentleman,
I know I am in no capacity to say anything to both of you coz you both are great in own field. So I just want to request you that, please calm down.

and

Sorry I was busy whole day so couldn't experiment what you suggested, TJF . But I will do it now and will post my response or queries.

but please Calm down..
 
On Wednesday, September 30, 2015 at 1:46:06 AM UTC+5:30, Rathin Dholakia wrote:
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. :)

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

William Hermans

unread,
Oct 1, 2015, 12:55:07 PM10/1/15
to beagl...@googlegroups.com
Agreed.

--

William Hermans

unread,
Oct 1, 2015, 2:57:40 PM10/1/15
to beagl...@googlegroups.com
Here you go Rathin, something to experiment with in the future. http://www.embeddedhobbyist.com/2015/10/beaglebone-black-adc-getting-started/

I need to edit the post and add the packages needed in order to compile the demonstration executable with gcc, but that is not too much of a hurdle. sudo apt-get install build-essential should take care of that.

Rathin Dholakia

unread,
Oct 1, 2015, 3:08:02 PM10/1/15
to BeagleBoard
Cool, Thanks a lot, William..!!


And one good news, first of all for me (of course) but for you and TJF as well. Its that I talked with my professor and we have agreed that its ok *for now* if our sampling rate is low around 1.2 KHz or so no hassle of external ICs and stuff at least for now... later on I can refine + Update my implementation as I gain momentum.. :-)

So yeah, I will try out your steps and let you know, but I have one assignment hence bit stuck with it.. please give me a day to try out...

Sincerely,
Rathin

William Hermans

unread,
Oct 1, 2015, 3:36:23 PM10/1/15
to beagl...@googlegroups.com
Well, using the ADC from userspace as I've shown in that post should be able achieve ~3k samples a second. Using Continuous mode, I've read up to around 10k.

Also, I happened upon DR Molloy's companion site for the "exploring the Beaglebone" book
, and noticed he demonstrates using a couple of different external ADC's through the PRU's. One @100k, the other at ( I think he said ) just above 1msps. Where then the PRU's were approaching their max speed I guess. Anyway, it's neat to read about, but for me, I think userspace one-shot mode will be plenty. Even when experimenting with MPPT charge controlling on our solar system here.

William Hermans

unread,
Oct 1, 2015, 4:01:34 PM10/1/15
to beagl...@googlegroups.com
So yeah, I will try out your steps and let you know, but I have one assignment hence bit stuck with it.. please give me a day to try out...

No big hurry. I was just putting that out there in case you wanted to experiment with it. I think for your end goal, needing 20Khz, what I propose is a dead end. However I still think it is a good learning experience, and perhaps useful for other things. 2.5k-3k samples a second is still pretty good. Just not for your end goal.

TJF

unread,
Oct 2, 2015, 4:46:46 AM10/2/15
to BeagleBoard
@William


Am Donnerstag, 1. Oktober 2015 18:04:59 UTC+2 schrieb William Hermans:
I never even saw a PDF . . .

This is how your email program quoted my mail in which I sent the PDF

On Tue, Nov 11, 2014 at 1:09 PM, ... <...> wrote:

It's OK when you like to confuse yourself by ignoring facts. Here in public, you're confusing others as well. You steal their time.
 

@Rathin

What does the new agreement with your professor mean?

Do you have to start again, from scratch?

Or are you close to finish, just replace the file output (fprintf() call) by your monitoring code and you're done?


BR

William Hermans

unread,
Oct 2, 2015, 5:07:42 AM10/2/15
to beagl...@googlegroups.com
TJF, knock it off already. So far, I've been really nice to you, when I really did not have to. If you want to continue this discussion feel free to mail me directly, and stop sidetracking this post.


--

Rathin Dholakia

unread,
Oct 2, 2015, 10:33:28 AM10/2/15
to BeagleBoard
Hi, TJF,

"New agreement" in a sense that  I discussed and we are OK (for now) to study low frequency transients, hence it would be OK if we sample at low rate.

I can not say that I have to start from scratch coz I am at "scratch" level right now :( . I am in the beginning of this whole thing and I have sort of 20 days to finish it.

No I dont have my "monitor" code ready because I was depending upon the samples which I would get from the system, That would decide my monitoring/analysis method- like do I require software filter to remove noise? Do I need a batch mode operation or a sample by sample operation ( depending upon the way I receive the ADC input , say your io_input example program used to give me separate samples where as your new RB programs gives me a whole chunk of data!!)

So,yeah I am still starting off, though I have very less time I am still at the "start" (I wasted (or invested) almost 30-35 days just to study different things -what is PRU- PRU assembly coding, - BeaglelLogic, libpruio etc.. )  

Rathin Dholakia

unread,
Oct 2, 2015, 12:01:54 PM10/2/15
to BeagleBoard
Hi TJF,

I had a small gap so I tried your new code, and Its giving me "config fault: sample rate too big".

I tried changing tSamp, to 2000 but still it doesn't work So what changes should I make? and one another query how to extend the same code of 8 ADCs? just to change NoStep??

Regards,
Rathin

TJF

unread,
Oct 2, 2015, 1:25:41 PM10/2/15
to BeagleBoard
Hi Rathin!


Am Freitag, 2. Oktober 2015 18:01:54 UTC+2 schrieb Rathin Dholakia:
Hi TJF,

I had a small gap so I tried your new code, and Its giving me "config fault: sample rate too big".

I tried changing tSamp, to 2000 but still it doesn't work So what changes should I make?
 
tSamp is the number of samples in the file. The final file size will be tSamp * NoStep * 2.

Your error message is about the sampling rate, it reaches a certain limit.

There's a miss-configuration in version-0.2 in file pruio_adc.bas. The limits for the sampling rate get computed to rigid. You can either use much slower sampling rates, as a workaround for now to get started) like

  const uint32 tmr = 50000;      //!< The sampling rate in ns (50000 -> 20 kHz).

or fix the code and recompile. Therefor install the FreeBASIC compiler and adapt file pruio_adc.bas in function AdcUdt.configure() around line 133 it should be

    IF Samp < 2 THEN ' IO mode
      Samples = 1
      TimerVal = 0
    ELSE
      IF r < 1 THEN             .Errr = @"no step active" : RETURN .Errr
      Samples = Samp * ChAz
      IF (Samples SHL 1) > .ESize THEN _
                                 .Errr = @"out of memory" : RETURN .Errr
      d = (d * (Conf->ADC_CLKDIV + 1) * 1000) \ 24
      IF Tmr <= d ORELSE Tmr < 5000 THEN _
                           .Errr = @"sample rate too big" : RETURN .Errr
      Value = .ERam
      TimerVal = Tmr
    END IF

Then compile and install the new version.


and one another query how to extend the same code of 8 ADCs? just to change NoStep??

As mentioned in the code:


  const uint32 NoStep = 3;      //!< The number of active steps (must match setStep calls and mask).

 You have also to adapt the mask variable and the calls to pruio_adc_setSteps()

  const uint32 NoStep = 8;      //!< The number of active steps (must match setStep calls and mask).

...


   
if (pruio_adc_setStep(io, 9, 0, 0, 0, 0)){ //          step 9, AIN-0
        printf
("step 9 configuration failed: (%s)\n", io->Errr); break;}
   
if (pruio_adc_setStep(io,10, 1, 0, 0, 0)){ //         step 10, AIN-1
       printf
("step 10 configuration failed: (%s)\n", io->Errr); break;}
   
if (pruio_adc_setStep(io,11, 2, 0, 0, 0)){ //         step 11, AIN-2
       printf
("step 11 configuration failed: (%s)\n", io->Errr); break;}

   
if (pruio_adc_setStep(io,12, 3, 0, 0, 0)){ //         step 12, AIN-3
       printf
("step 12 configuration failed: (%s)\n", io->Errr); break;}
   
if (pruio_adc_setStep(io,13, 4, 0, 0, 0)){ //         step 13, AIN-4
       printf
("step 13 configuration failed: (%s)\n", io->Errr); break;}
   
if (pruio_adc_setStep(io,14, 5, 0, 0, 0)){ //         step 14, AIN-5
       printf
("step 14 configuration failed: (%s)\n", io->Errr); break;}
   
if (pruio_adc_setStep(io,15, 6, 0, 0, 0)){ //         step 15, AIN-6
       printf
("step 15 configuration failed: (%s)\n", io->Errr); break;}
   
if (pruio_adc_setStep(io,16, 7, 0, 0, 0)){ //         step 16, AIN-7
       printf
("step 16 configuration failed: (%s)\n", io->Errr); break;}

    uint32 mask
= 255 << 9;         //!< The active steps (9 to 16).
...

BR

Rathin Dholakia

unread,
Oct 3, 2015, 10:43:00 AM10/3/15
to BeagleBoard
Hi TJF,

Finally I tried your code, with reduced sample rate ( i preferred not to recompile) I got file output.0 & ouput.1 but they are binaries!! I am not able to read them with any format or in any text-editor. Can you suggest how to read them?

Once again thanks a lot for tips!! I

Lenny

unread,
Oct 3, 2015, 11:01:26 AM10/3/15
to BeagleBoard
Hi Rathin,
just to let you know: The BBB ADC's can sample at 1.6 MHz. That is, if you multiplex the ADC on 8 inputs, each one of them can sample at 200kHz. I am not familiar with user-friendly libraries like libpruio, so I cannot tell you where they lose the time. But technically (e.g. by writing the assembler instructions), 200 kHz is definitely possible, so you could upgrade your project at a later moment without changing the hardware. The price to pay (at the moment) is a little more customized code. But seeing that people struggle already at 20kHz just hurts my eyes ;)
Lenny

Lenny

unread,
Oct 3, 2015, 11:06:13 AM10/3/15
to BeagleBoard
here is a thread talking about it, in which you can also find my code example among other info.

https://groups.google.com/forum/#!searchin/beagleboard/fast$20ADC/beagleboard/3AFiCNtxGis/RiWxHDiOWnMJ

Micka

unread,
Oct 3, 2015, 11:12:39 AM10/3/15
to BeagleBoard
In my case, I'm going to bitbang the I2C to work with the ad7997 to sample 8 analog input. I don't have the choice the 8 input of the processor is already used for something else.

This will not be that difficult because the code to bitbang the I2C is available there : 

My only problem is that for the moment the pru is not working with the kernel 4.1 . I will have to test that on the kernel 3.8 :( .

Micka,

Rathin Dholakia

unread,
Oct 3, 2015, 11:21:05 AM10/3/15
to BeagleBoard
Hi William,

Finally I got times to try out your post and have to admit, very precise instructions!! :-) coming to technical point, I have following queries.

1) This program samples only 1 ADC correct? involtage0_raw To extend it for all 7 channels woudl I have to create 7 such pointers, correct?
2) this is Ti rt kernel, is it same as TI-RTOS? and if not, where can I find it's APIs?
3) This kernel has completely new structure,  so now my PRUs are completely unusable :-) any suggestions? initially it was accessible by  "/sys/devices/bone_capemgr.*/slots" which now doesn't exist!!
4) Is your existing code able to reach 3K sample speed? if not can you suggest changes for that?

Sorry for such a long list of queries..

Sincerely,
Rathin :-)

Rathin Dholakia

unread,
Oct 3, 2015, 11:25:45 AM10/3/15
to BeagleBoard
Lenny,

Thanks for dropping by!! yes I am aware of the thing you are saying, actually your post (& code ) was the post because of which I decicded to use BBB and PRU for my purpose :-)

but to be frank, Assembly coding is bit intimidating for me right now, I invested almost 20 days to study PRU reference guide but couldnt start a bit!!
another bummer was how to transfer data from PRU to userspace,
If you can guide me precisely, I will be more than happy to walk on that path..!! :-)

William Hermans

unread,
Oct 3, 2015, 12:42:58 PM10/3/15
to beagl...@googlegroups.com
1) This program samples only 1 ADC correct? involtage0_raw To extend it for all 7 channels woudl I have to create 7 such pointers, correct?

Technically, yes. The pointer is actually a string pointer for the given file pah. However, you could use a for loop, and generalize the string, using the for loop to "inject" 0-7. Look into the function sprintf(), and while at it google "sprintf() dangers" or you could just red this post: http://stackoverflow.com/questions/3662899/understanding-the-dangers-of-sprintf


2) this is Ti rt kernel, is it same as TI-RTOS? and if not, where can I find it's APIs?

No. For the Linux API's, you just need to become familiar with std libc / POSIX functions. Then google. Each libc POSIX function when you need more information, etc. I actually goolge A LOT when it comes to API calls, as I do not always remember exact details. Anyway, TI-RTOS is potentially problematic for this hardware. First I'm not even sure it would run on this hardware. Second, you would have to write much functionality yourself. Besides you could use just plain Linux to do what you want, using the PRU's. I've read at least one blog post where a person was achieving 14MB /s + data using the PRU's + a non rt kernel. Yes, that's 14 Megabytes a second.

3) This kernel has completely new structure,  so now my PRUs are completely unusable :-) any suggestions? initially it was accessible by  "/sys/devices/bone_capemgr.*/slots" which now doesn't exist!!
Yes, the PRU's do not work on this kernel. I believe I mentioned this before, but in case I did not this is why I recommended if you experimented with this to keep your old sdcard as well. However, and with with that said. Currently I am working on a similar non TI kernel that does support the PRU. That, and I WILL be using the PRU to access the on chip 200k  SAR module. Not sure how long this will take me to get working though. Anyway, I have interest in this as well. Remember one of my electronics interests is power electronics ;) So using the ADC, and a PWM could be used as a control for a SEPIC( or similar ) DC-DC power converter. If not, well then, it's still useful for testing.

4) Is your existing code able to reach 3K sample speed? if not can you suggest changes for that?

Reload my blog post and read the update. ~2960 samples a second seems to be max for a single channel, in one-shot mode. This also uses ~77% CPU, so there is not much room for improvement. One thing that I did consider, was using fork(), with some form of timing, and multiple channels. Also note that my code has changed a little. Specifically . . .

if(len == -1){
                        close(fd);
                        continue;             
}

This enables the code to run continuously without problems related to the device being busy. So in effect you could remove the while loop check, and just run it in an infinite loop. If you want to know how it works. I briefly describe that on my blog post in the update part at the bottom.

So . . . I've not done this yet, but I think one might be able to achieve between 3-4k a second samples using multiple processes, on multiple channels, and with tight timing. I've done some reading, and this seems to be inline what others have achieved in the past. The biggest hindrance will be the CPU in this case however.

Using continuous mode on the ADC should be able to improve the situation somewhat. But according to what I've read. ~10k samples a second while using sysfs will be the maximum. I've read that mmap() can improve this considerably. But honestly, and for the moment this is over my head. I know mmap() fairly well, but in this case, I do not know what address should be mapped through /dev/mem/, or how I would get the data out of the ADC running in continuous mode. I do think that using mmap() + /dev/mem/ *could* come close to the maximum theoretical limit of the 200k SAR module though . . . But this is just a hypothesis based on various things I've read.



TJF

unread,
Oct 3, 2015, 1:03:50 PM10/3/15
to BeagleBoard
Hi Rathin!


Am Samstag, 3. Oktober 2015 16:43:00 UTC+2 schrieb Rathin Dholakia:
Hi TJF,

... but they are binaries!! I am not able to read them with any format or in any text-editor. Can you suggest how to read them?

Yes, I can. But I wont!

I'm not your personal trainer and it's up to you to lean the basic skills to reach your target specified in the subject of this thread.

BR

William Hermans

unread,
Oct 3, 2015, 1:20:23 PM10/3/15
to beagl...@googlegroups.com
wow, my math was *way* off.

My first experiment was actually 4997.334754797441 samples a second. Using this new kernel I'm using now, with all the printf()'s commented out is:

debian@beaglebone:~$ time ./test

real    0m50.929s
user    0m0.340s
sys     0m50.450s

For 300,0000 iterations. So . . .5890.55351567869 samples a second.

With the printf()'s back in place:

. . .

4015 4009 4009 4013 4007 4011 4015 4004 4004 4013
real    0m57.302s
user    0m0.940s
sys     0m52.710s
 
So . . .  5235.419357090503 samples a second.

--

Rathin Dholakia

unread,
Oct 3, 2015, 4:30:41 PM10/3/15
to BeagleBoard
Dear William,

Thanks for the detailed reply..!

1) Thanks for the sprintf tip, I will work on it and put a code here..!!

2) 14 MB/S data rate, yes, I have read that post :-) that was one of the reason I got impressed by BBB & choose..!! but thing is over there an external ADC + buffer is used..!!

3) Regarding kernel question , sorry I felt silly after asking it. I asked it because I wanted to use system time & Ethernet socket so I thought there would be some separate API. (just like Xenomai, it has its own command)
  - Oh you are in to Power Electronics.. wonderful..! yeah this can be used in converters but interesting application would be sensor-less BLDC motor controller.. or HIL recorder-player!!
 
4) I tried with while(1) and it ran perfectly. Now I will try changing to continuous mode as well as the no of ADC in to the program and will report back to you..!! 

Thanks for the kick start, It is of immense help and yes I have understood since the beginning that  Google is our BEST friend when working on something new...!! though my results are not as accurate as ur's but working on it.. :-)

Once again thanks.. will post back my new code, soon.

Rathin

Rathin Dholakia

unread,
Oct 3, 2015, 4:43:04 PM10/3/15
to BeagleBoard
Dear TJF,

Sorry to bug you with a silly question, I thought being a binary it must have some specific encoding rel. to library hence I asked it. I didnt knew it was lack of knowledge. I am sorry...!!

I Have figured out the way to read binary (code pasted bellow for others;) but I have a doubt in it that I have lot of Zeros & negative readings as well
Is it because of my decoding error or is it the samples themselves missing.

Thanks a ton..!!

#include<stdio.h>
int main()
{
    FILE
*fp = NULL;

   
short result[100];
   
int i;

    fp
=fopen("output.0", "w+");
        rewind
(fp);
   
if(fp != NULL)
   
{
        fread
(result, sizeof(short), 100 /*# of samples*/, fp);
   
}
   
else
       
return 1;

    printf
("Result\n");
   
for (i = 0; i < 100; i++)
        printf
("%d = %d\n", i, (int)result[i]);

    fclose
(fp);
   
return 0;
}



William Hermans

unread,
Oct 3, 2015, 8:49:58 PM10/3/15
to beagl...@googlegroups.com
4) I tried with while(1) and it ran perfectly. Now I will try changing to continuous mode as well as the no of ADC in to the program and will report back to you..!! 

Thanks for the kick start, It is of immense help and yes I have understood since the beginning that  Google is our BEST friend when working on something new...!! though my results are not as accurate as ur's but working on it.. :-)

Once again thanks.. will post back my new code, soon.

If you get continuous mode working I'd be interested in seeing the code. Might even be able to optimize it some for you ;) But I know someone on another forum who is interested in continuous mode. I told him that I am more interested in getting the PRU's involved, but might get around to it someday. I know compared to PRU + ADC it will be slow. *But* if you can get continuous mode working from sysfs, you only need one open(), and close() call for the lifetime of the application for each file path. Which is to say that open(), and close() will no longer have an impact on the application run speed. Then you only need worry about OS latency, and the efficiency of your code . . .

You know, a couple friends from different countries, and I have proven that google results vary from country to country. So maybe your results are skewed based on your locale some ? There is a way you can specify which server google uses when you search, but I forget the details. Well, aside from using a web proxy ;) Have fun !

William Hermans

unread,
Oct 3, 2015, 9:06:35 PM10/3/15
to beagl...@googlegroups.com
Rathin, on a side note, I did get the pru's working on the kernel Im using now which is:

debian@beaglebone:~$ uname -a
Linux beaglebone 4.1.9-bone-rt-r16 #1 Thu Oct 1 06:19:41 UTC 2015 armv7l GNU/Linux

The pru example code seems to work fine too . . .
debian@beaglebone:~/am335x_pru_package/pru_sw/example_apps/bin$ sudo ./PRU_memAcc_DDR_sharedRAM

INFO: Starting PRU_memAcc_DDR_sharedRAM example.
        INFO: Initializing example.
        INFO: Executing example.
        INFO: Waiting for HALT command.
        INFO: PRU completed transfer.
Example executed succesfully.

But man, lot's of jumping through hoops to get to here. I had to piece together from all over the web, so you can bet once I'm ready I will make a blog post on it. If for no one else, for myself. I still want to write a simplified "hello world" example. Blinking USR0 using the PRU . . . After that, then onto using the on chip ADC.


William Hermans

unread,
Oct 3, 2015, 9:35:46 PM10/3/15
to beagl...@googlegroups.com
Ah!

ADC Driver Limitations

This driver is based on the IIO (Industrial I/O subsystem), however this is the first release of this driver and it has limited functionality:

  1. No HW trigger Support. Currently only supporting software trigger.
  2. Limited number of samples in continuous capture mode. (Only 1528 samples per capture)
  3. Limited maximum sample rate in continuous mode: 8K samples / second.
  4. Simultaneous capture on multiple ADC channels is not supported. Currently only supports continuous capture on a single ADC input channel at a time.
  5. "Out of Range" not supported by ADC driver.

http://processors.wiki.ti.com/index.php/AM335x_ADC_Driver's_Guide



Przemek Klosowski

unread,
Oct 3, 2015, 11:21:35 PM10/3/15
to beagl...@googlegroups.com


On Sat, Oct 3, 2015 at 9:35 PM, William Hermans <yyr...@gmail.com> wrote:

http://processors.wiki.ti.com/index.php/AM335x_ADC_Driver's_Guide

[listing severe limitations of the ADC driver]

But that page claims it's obsolete and directs to the new page:
 

which seems to list fewer limitations.
Message has been deleted
Message has been deleted

TJF

unread,
Oct 4, 2015, 4:33:02 AM10/4/15
to BeagleBoard
Hi!


Am Samstag, 3. Oktober 2015 22:43:04 UTC+2 schrieb Rathin Dholakia:
Dear TJF,

Sorry to bug you with a silly question, I thought being a binary it must have some specific encoding rel. to library hence I asked it. I didnt knew it was lack of knowledge. I am sorry...!!

Never mind!
 
I Have figured out the way to read binary (code pasted bellow for others;) but I have a doubt in it that I have lot of Zeros & negative readings as well
Is it because of my decoding error or is it the samples themselves missing.

As I said, I'm no C expert. Here're my comments:
  • I'd fopen() for read.
  • I'd check the return value from fread() in order to avoid missreadings (= interpreting memory garbage).
  • Most important: since Adc->Value is an Uint16*, the result array has to get declared as
    unsigned short result[100];

Here's code I use (FB syntax):

VAR fnam = "output.0", fnr = FREEFILE
IF OPEN(fnam FOR INPUT AS fnr) THEN
  ?"Cannot open " & fnam
ELSE
  DIM AS USHORT v
  VAR cc = 8, c = 0, i = 0
  WHILE NOT EOF(fnr)
    c -= 1 : IF c <= 0 THEN i += 1 : c = cc : ? : ? i & ": ";
    GET #fnr, , v
    ? v,
  WEND
  CLOSE #fnr
END IF


It reads the complete file and formats the output in 8 columns (cc = 8).


BR

Rathin Dholakia

unread,
Oct 5, 2015, 2:58:48 AM10/5/15
to BeagleBoard
Dear William,

As Prezemek suggested its an older version of driver user guide!! though the new version doesn't enlist the limitation at all except 1!! it's "Limitless".. :-)
I am currently studying continuous mode. stuck with few hurdles, I will let you know once I get some lead.

Sincerely,
Rathin

PS: Yes, there are lot of "jumping through hoops", yet you were able to manage it, I couldnt :-(  I got so confused and clueless.. though I feel assembly language would be the most easiest way to deal with PRUs even after considering the efforts in writing the assembly  code. what do you say?

William Hermans

unread,
Oct 5, 2015, 3:52:43 AM10/5/15
to beagl...@googlegroups.com
mmap() should max out the ADC's, but I'm going to do it just because I can. So, ADC, to PRU, to DDR, plus userland C to mmap() the region of DDR is very similar. What is not similar and in favor of the PRU is less CPU load, and determinism.

William Hermans

unread,
Oct 5, 2015, 6:39:21 PM10/5/15
to beagl...@googlegroups.com
Rathin,

Anyways, This morning I decided go ahead and test continuous mode using the ti user space driver. After much testing and fooling around I've come to the conclusion, and perhaps incorrectly. That this userspace driver is dog slow. How I've come to this conclusion is as follows.

With slightly modified code from my one-shot example, I was able to achieve ~15k samples a second right from the get go. Thinking I could improve on this somewhat I was experimenting with something ( sorry do not know exactly what ), and got a figure of 35k samples a second. After this, I started to attempt to control read by using a simple timing mechanism. Both usleep(), and nanosleep() introduce a large amount of latency into the application. So neither of those will work. I have not tried using ualarm() *yet*, and I'm kind of hopeful, but . . . I'm thinking it depends on syscalls so . . .will likely not work either.

So after experimenting with  couple timing functions, I decided to check what len = read() was collecting. To my surprise, it was returning an awful lot of -1's. SO I figure hey, I'll just check len, and continue like with the old app. Well it worked, except sample rate dropped into the toilette. A "whopping" 300-400 samples a second. This is why I think the ADC userspace driver is dog slow.

Anyway, I did full blast testing, no timing, just 100% loop, read, printf() every 100,000 iterations - For 2 million total iterations. I get somewhere between 800k-900k samples a second. Of course, much of that data is redundant, and I did a rough guess count, and figured it was roughly 35k / second unique values. CPU is pretty much pegged at 95-99% CPU like this though . . .

Another thing to keep in mind with my testing data. What I'm testing could actually be faster. I do not have a voltage applied to the ADC. I'm just reading the floating pin values off channel 0 . . . Maybe I can hook up an LED to AIN0 to AIN_ground, and aim it at the blinking USER LEDs to get some variation ? heh.

Anyway I need to take a little break, then I'm back at it.

Rathin Dholakia

unread,
Oct 6, 2015, 6:18:37 AM10/6/15
to BeagleBoard
Hello William,

So you were able to run the continuous mode driver? I am on to it since 1.5 day.. You are talking about the driver program that is "generic_buffer.c" am I correct? and your 800k - 900k samples are for single channel correct, so in that case for 8 channels it would be 100K which is really exciting!! :-)

btw you are using RT kernel have you tried giving priority to the program while running? it might improve the results..

Your results are exciting me.. please I will make my program work somehow and paste it here. Please dont paste your code yet.. let me figure out my way and then if i face problem, you can guide 0:-).

Rathin

William Hermans

unread,
Oct 6, 2015, 11:34:34 AM10/6/15
to beagl...@googlegroups.com
921234.4541685859 Samples a second exactly. You need to understand though. Many, many samples are the same value. At this time I was sampling just one channel. Late last night before bed, I was sampling all 7 available channels, and was noting that some channels were putting values into the buffer more often than others. It was nothing concrete, and almost seemed random. So I implemented reading the channel ID for each channel, and started sampling by ID. Samples / second went way down
 as a result, but now I have accurate 1-7 samples in order. Anyway, I think this has to do with ADC data averaging, but there is no way that I know of through userspace to set averaging. I think if I disable averaging, the buffer should be populated more consistently. But again . . . this is a guess. Also, I've read that one can change the clock multiplier of the ADC too. Default is 0x7, and it can be set to 0x0. From a 24Mhz source. But maybe this is a mmap() or PRU thing ?

With mmap() I should be able to manipulate the ADC registers just like how the PRU's do things.

Anyway, I think I've shown that Linux can keep up with more than 200k / second samples.
 Now, to find a way to make the ADC keep up ;)

So Rathin, let me give you a hint:

http://processors.wiki.ti.com/index.php/Linux_Core_ADC_User's_Guide#Beaglebone.2FBeaglebone_Black
and
http://elinux.org/images/6/65/Spruh73c.pdf#page=1573&zoom=auto,0,719

Is all the information I needed to get all this working. So, in fact should be all you need as well. However, I think you might be struggling more with the C aspect . . .

Rathin Dholakia

unread,
Oct 6, 2015, 5:14:27 PM10/6/15
to BeagleBoard
Hi,
Oh, magnificent..!!

Yes you are correct I am facing problem in C..!! actually this link depicts exactly my state..
http://c.learncodethehardway.org/book/introduction.html
specially the last paragraph in "Introduction" section and 1st in "Core competence" section :-)

and btw two small queries,
1) I have gone through your links, so, did you recompile the kernel? with those modules ON...? 
2) your this result is for single shot mode right?

Once again thanks for a spell of motivation.

Rathin

William Hermans

unread,
Oct 6, 2015, 5:43:13 PM10/6/15
to beagl...@googlegroups.com
#1 No, the module is already compiled for the image.

#2 No, this is continuous mode with all 7 configurable channels running( 8 including AIN7 ), and just blasting the data to screen. No ordering, not checking anything.  There are many *MANY* redundant reads.

Right now I am experimenting with mmap(). Using mmap() I am able to configure, and read from the ADC without loading that module, or configuring the ADC channels using the iio userspace driver. What this nets me is not having to run an interrupt related to the driver ( I'm guessing ). Which *could* slow things down tremendously. Also, just now I'm toying with sample averaging turned off.

Anyway, this is all new to me, so much experimentation will be needed on my part to figure this all out. With that said, I'm about ready for a break for a little while. Maybe and hour or two, or maybe a day or two . . . heh Helps keep me from getting burnt out. . .

William Hermans

unread,
Oct 6, 2015, 5:54:33 PM10/6/15
to beagl...@googlegroups.com
By the way, one thing I did note. Remember me saying printf() was not slowing anything down( much ) using sysfs, and the iio driver ? Well, using mmap() and then printf() to display the data. Slows the sample rate by 50%+. Specifically newlines -> "\n" seem to be the biggest culprit.

Anyway, that just goes to show how much faster using mmap() through /dev/mem *is*. Now . . . I need to figure out a good way to display, or otherwise view the output data without slowing down the whole process . . . Maybe one huge array that get's blasted to screen, or a file all at once ? *Shrug*

Rathin Dholakia

unread,
Oct 6, 2015, 6:25:38 PM10/6/15
to BeagleBoard
Okk..!!
#1 So I will keep using the image which you have shown in your blog post(for now) for my further development
#2 Ok, for that I am not worried much for now! as for now I WANT data, and I will be implementing a filter maybe in second stage once I get this thing working.

Yes, you should take a break, I will post here once I have something to show!! Though I wont go in mmap for now, I will stick to my singleshot & continuous mode implementation.

Thanks a lot  for help..

Rathin 

William Hermans

unread,
Oct 6, 2015, 6:41:29 PM10/6/15
to beagl...@googlegroups.com
Couldn't step away until I did this test but . . .

debian@beaglebone:~$ time sudo ./test > output.txt

real    0m1.059s
user    0m0.370s
sys     0m0.030s

Data is formated as such:  channel:data That's 200k iterations( nearing 200k samples / second !!!) with A LOT of data that looks like this:

 0:3731 2:3157 0:2115 6:3248 1:0520 0:3619 2:1464 4:0796 3:0872 6:2609
 1:0831 6:3612 2:0141 1:3921 3:1488 0:0243 5:2981 3:1685 2:1118 5:0523
 1:3068 5:2318 4:0123 3:0532 3:3336 1:1599 1:2541 0:3731 2:3157 0:2115
 6:3248 1:0520 0:3619 2:1464 4:0796 3:0872 6:2609 1:0831 6:3612 2:0141
 1:3921 3:1488 0:0243 5:2981 3:1685 2:1118 5:0523 1:3068 5:2318 4:0123
 3:0532 3:3336 1:1599 1:2541 0:3731 2:3157 0:2115 6:3248 1:0520 0:3619
 2:1464 4:0796 3:0872 6:2609 1:0831 6:3612 2:0141 1:3921 3:1488 0:0243
 5:2981 3:1685 2:1118 5:0523 1:3068 5:2318 4:0123 3:0532 3:3336 1:1599
 1:2541 0:3731 2:3157 0:2115 6:3248 1:0520 0:3619 2:1464 4:0796 3:0872
 6:2609 1:0831 6:3612 2:0141 1:3921 3:1488 0:0243 5:2981 3:1685 2:1118
 5:0523 1:3068 5:2318 4:0123 3:0532 3:3336 1:1599 1:2541 0:3731 2:3157
 0:2115 6:3248 1:0520 0:3619 2:1464 4:0796 3:0872 6:2609 1:0831 6:3612
 2:0141 1:3921 3:1488 0:0243 5:2981 3:1685 2:1118 5:0523 1:3068 5:2318
 4:0123 3:0532 3:3336 1:1599 1:2541 0:3731 2:3157 0:2115 6:3248 1:0520
 0:3619 2:1464 4:0796 3:0872 6:2609 1:0831 6:3612 2:0141 1:3921 3:1488
 0:0243 5:2981 3:1685 2:1118 5:0523 1:3068 5:2318 4:0123 3:0532 3:3336
 1:1599 1:2541 0:3731 2:3157 0:2115 6:3248 1:0520 0:3619 2:1464 4:0796
 3:0872 6:2609 1:0831 6:3612 2:0141 1:3921 3:1488 0:0243 5:2981 3:1685
 2:1118 5:0523 1:3068 5:2318 4:0123 3:0532 3:3336 1:1599 1:2541 0:3731
 2:3157 0:2115 6:3248 1:0520 0:3619 2:1464 4:0796 3:0872 6:2609 1:0831
 6:3612 2:0141 1:3921 3:1488 0:0243 5:2981 3:1685 2:1118 5:0523 1:3068
 5:2318 4:0123 3:0532 3:3336 1:1599 1:2541 0:3731 2:3157 0:2115 6:3248
 1:0520 0:3619 2:1464 4:0796 3:0872 6:2609 1:0831 6:3612 2:0141 1:3921
 3:1488 0:0243 5:2981 3:1685 2:1118 5:0523 1:3068 5:2318 4:0123 3:0532
 3:3336 1:1599 1:2541 0:3731 2:3157 0:2115 6:3248 1:0520 0:3619 2:1464
 4:0796 3:0872 6:2609 1:0831 6:3612 2:0141 1:3921 3:1488 0:0243 5:2981
 3:1685 2:1118 5:0523 1:3068 5:2318 4:0123 3:0532 3:3336 1:1599 1:2541
 0:3731 2:3157 0:2115 6:3248 1:0520 0:3619 2:1464 4:0796 3:0872 6:2609
 1:0831 6:3612 2:0141 1:3921 3:1488 0:0243 5:2981 3:1685 2:1118 5:0523
 1:3068 5:2318 4:0123 3:0532 3:3336 1:1599 1:2541 0:3731 2:3157 0:2115
 6:3248 1:0520 0:3619 2:1464 4:0796 3:0872 6:2609 1:0831 6:3612 2:0141
 1:3921 3:1488 0:0243 5:2981 3:1685 2:1118 5:0523 1:3068 5:2318 4:0123
 3:0532 3:3336 1:1599 1:2541 0:3731 2:3157 0:2115 6:3248 1:0520 0:3619
 2:1464 4:0796 3:0872 6:2609 1:0831 6:3612 2:0141 1:3921 3:1488 0:0243
 5:2981 3:1685 2:1118 5:0523 1:3068 5:2318 4:0123 3:0532 3:3336 1:1599
 . . .
 
Now I *am* off for a break.

William Hermans

unread,
Oct 6, 2015, 10:02:32 PM10/6/15
to beagl...@googlegroups.com
To screen:
 2000000 total iterations
 channel 0 samples: 296296
 channel 1 samples: 444444
 channel 2 samples: 296296
 channel 3 samples: 370371
 channel 4 samples: 148149
 channel 5 samples: 222222
 channel 6 samples: 222222
real    0m16.247s
user    0m3.850s
sys     0m3.140s

To file:
 2000000 total iterations
 channel 0 samples: 296296
 channel 1 samples: 444445
 channel 2 samples: 296296
 channel 3 samples: 370370
 channel 4 samples: 148148
 channel 5 samples: 222222
 channel 6 samples: 222223
real    0m5.768s
user    0m3.650s
sys     0m0.090s

How does ~25684 Samples a second worst case, per channel sound ? Channel 4 there is looking a bit weak. I need to investigate that. Who said 20k samples / second per channel from the Linux side was not possible ? Also, since ssh is eating half the CPU while using printf(), those numbers can be improved. 13.5M in 5 seconds though . . . not too shabby.



William Hermans

unread,
Oct 6, 2015, 10:10:46 PM10/6/15
to beagl...@googlegroups.com
By the way, there are 2 FIFO buffers for the ADC. I'm only reading from one. So this is something else I need to look into. Actually there is a lot I need to read up now that I'm able to directly twiddle the ADC's registers.

Rathin Dholakia

unread,
Oct 7, 2015, 2:47:51 AM10/7/15
to BeagleBoard
Hi,

So during this implementation you are using FIFO buffer that you pointed out?? should I as well use that?? or should I simply read from "/sys/bus/iio/devices/iio\:device0/in_voltage4_raw" ..?

William Hermans

unread,
Oct 7, 2015, 2:42:56 PM10/7/15
to beagl...@googlegroups.com
File path is mentioned here. Towards the end. Read everything, not just the command line examples.

William Hermans

unread,
Oct 7, 2015, 5:16:59 PM10/7/15
to beagl...@googlegroups.com
OK, so now, I seem to be having issues with the FIFO. Not sure what it is, but I have to tone down my experimentation a bit as I've got other things that are of a higher priority. Such is life, but I'm by no means *done* with this.

Rathin Dholakia

unread,
Oct 7, 2015, 5:48:01 PM10/7/15
to BeagleBoard
Hi William,

Oh, I had already seen that and experimented with it..!!but had forgotten, after watching your link I recollected. I am really sorry for silly question.

Have  you experimented with buffer size? is there any optimal value calculation? Would it have any impact on the result, Like if we keep a larger buffer and than directly take that buffer that way it would be faster? I have currently kept 1k.

And yes, Priority is a priority!! I though you were on break from BBB,...!! :-)

Sincerely,
Rathin

William Hermans

unread,
Oct 7, 2015, 7:35:19 PM10/7/15
to beagl...@googlegroups.com
Well, the buffer I'm talking about is the ADC buffer. I've been looking through others code for PRU -> ADC, and have been attempting to translate that. I'm afraid my ASM skills are very lacking for this task( I have not written ASM code in years ). However the constants used in much of the code out there, are the same. So while I do not yet know what LBBO, and stuff liek r0-r31 mean for program flow, I can figure out the addressing very quickly. Not to mention that the TRM has this information too, but the TRM is very terse reading for many things. It's great for "cherry picking" offsets, but much of the information is not presented in an order that makes the most sense to me. ie, you have to bounce around too much form one place to another in this *huge* manual . . .

So, I may have to take a break, and get to know the PRU assembly language well before proceeding much further. Which is something I intended on doing anyhow, just not right at this moment. One thing that has me excited here is an idea that came to me last night. Concerning using the PRU's in a way I've not seen anyone else do - yet. Well, I've seen mention of others touching on the subject I suppose, but . . . yeah I do not want to let my "secrete" out just yet.

--

William Hermans

unread,
Oct 7, 2015, 7:44:48 PM10/7/15
to beagl...@googlegroups.com
Have  you experimented with buffer size? is there any optimal value calculation? Would it have any impact on the result, Like if we keep a larger buffer and than directly take that buffer that way it would be faster? I have currently kept 1k.

Yeah sorry, I'm kind of in my own world here at the moment. Anyway, like I mentioned above I was speaking of the ADC FIFO. As for buffering into system RAM, this is certainly possible, and very likely preferable. This can also be done, very easily, using POSIX shared memory. Potentially, this is a problem, as once the data is in RAM, how do you get it back out for transport. Without using additional CPU cycles, or using the PRU's ? Not using the PRU's for this by the way, is a constraint I've placed on myself. Just to see if it is *reasonably* possible. Indeed, I do believe it is possible, but not quite sure  how reasonable that possibility *is*. - Yet.

William Hermans

unread,
Oct 7, 2015, 8:11:17 PM10/7/15
to beagl...@googlegroups.com
More info on the issues I'm having with the FIFO. The data seems to repeat, and never changes between system reboots. I'm not sure if this is my fault, or the fault of something to do with this Linux kernel, the iio user space drivers, or something else. For now, I'm assuming it is my fault. Things that I am noticing:

When reading the values out of the ADC via mmap() versus using iio, the values read out are not in the same range. Using sysfs, the floating voltage values are around ~4000. But with mmap() these values vary starting from as low as in the hundreds, or up close to, but not passing 4000. The ID field for the ADC's *always* stay in the correct range though. Which is why I think I'm not flushing / clearing the FIFO correctly - More on this later.

It does not matter how I configure the ADC( sysfs or mmap() ) in this case. What I've been experimenting with is a header file originally written for the Beaglebone white, but I checked the base address / offset constants( against the TRM ), and they seem to be exactly the same. Here, my problem lies in not completely understanding the hardware, and how various things interact inside of, or with Linux. Writing the software for all this once understood. For me, will be trivial.

What does make sense to me with this problem is that I do not understand how to flush the buffer, and then tell the ADC "hey send more samples". But I am not exactly sure this is what my problem is. This is just a guess on my behalf, that makes the most sense.

Another thing that did occur to me is that I'm reading from the FIFO too fast. But there are many factors here, including but not limited to: Averaging, stepping, clock divider, and ADC clock cycles needed to read out a correct value. These are the things that are foremost on my mind right now, of which I have limited understanding of - so far.

William Hermans

unread,
Oct 7, 2015, 8:20:42 PM10/7/15
to beagl...@googlegroups.com
Oh and dahm, one more thing heh. Sorry people I'm kind of remembering this as I think about it. Normally I keep notes, but was up late attempting to track this issue down. So, I'm reasonably sure the FIFO is not refreshing, as if I flush the data value manually ( value address &= ~0xFFFF ), it never gets repopulated.

Once more, if I read out the sample in order based on channel id, the values stay the same form one iteration to the next. But If I burst read in whatever comes from the FIFO next, the values do change, but repeat after many read. Which let us just assume, for now, it's the length of the buffer I set through iio:device0.

*Perhaps* I just need to enable / disable the ADC once the buffer fills - via iio ? I'm not sure, as I've only been working with the ADC for about what ? A week now ? With no prior experience . . .

Harvey White

unread,
Oct 7, 2015, 8:44:49 PM10/7/15
to beagl...@googlegroups.com
On Wed, 7 Oct 2015 17:10:49 -0700, you wrote:

>More info on the issues I'm having with the FIFO. The data seems to repeat,
>and never changes between system reboots. I'm not sure if this is my fault,
>or the fault of something to do with this Linux kernel, the iio user space
>drivers, or something else. For now, I'm assuming it is my fault. Things
>that I am noticing:

What that *sounds* like is when you allocate memory for the FIFO, it
is never cleared. The variables that read from the FIFO *ought* to be
initialized appropriately, however. So this is puzzling.


>
>When reading the values out of the ADC via mmap() versus using iio, the
>values read out are not in the same range. Using sysfs, the floating
>voltage values are around ~4000. But with mmap() these values vary starting
>from as low as in the hundreds, or up close to, but not passing 4000. The
>ID field for the ADC's *always* stay in the correct range though. Which is
>why I think I'm not flushing / clearing the FIFO correctly - More on this
>later.

I'd clear the FIFO memory (or queue memory) just because. Look for
calloc rather than malloc if you're dealing with bare metal (I do....)

>
>It does not matter how I configure the ADC( sysfs or mmap() ) in this case.
>What I've been experimenting with is a header file originally written for
>the Beaglebone white, but I checked the base address / offset constants(
>against the TRM ), and they seem to be exactly the same. Here, my problem
>lies in not completely understanding the hardware, and how various things
>interact inside of, or with Linux. Writing the software for all this once
>understood. For me, will be trivial.

Why do I think pointers are used here?

>
>What does make sense to me with this problem is that I do not understand
>how to flush the buffer, and then tell the ADC "hey send more samples". But
>I am not exactly sure this is what my problem is. This is just a guess on
>my behalf, that makes the most sense.

The ADC in most processors needs to be "started", and depending on the
interrupt handler (which may feed the FIFO), is automatic from then on
until a specified number of reads is done (or not...).

Since the FIFO pointers are supposedly initialized to
(beginning:beginning) for read/write (head/tail)... and since data is
supposedly written into the FIFO and the pointers are adjusted then
(which means clean data)....

Either the FIFO is not being set up properly, or the code is wrong....

>
>Another thing that did occur to me is that I'm reading from the FIFO too
>fast. But there are many factors here, including but not limited to:

Reading from the FIFO too fast is not really possible on a well
designed FIFO. You either have data available or you do not. Data
available should be set when the complete message is written. FIFO
ought to be interlocked that read does not interfere with write....


>Averaging, stepping, clock divider, and ADC clock cycles needed to read out
>a correct value. These are the things that are foremost on my mind right
>now, of which I have limited understanding of - so far.

Averaging generally handles noise, and averages n samples for a single
reading (and reports it). Stepping I'm not sure of. clock divider
simply means that the ADC is not reading at an optimum rate *for the
ADC hardware* which has nothing to do with sampling or anything else.
ADC clock cycles to read out simply means I say read, how soon (clock
cycles) do I get data?

So I don't know what's going on.

However, I'd try disabling the FIFO for the moment, slowing down the
read process to where a software loop could check it, and look for
data integrity. *then* I'd go looking for things to blame....

Just me, though.

Harvey

>
>On Wed, Oct 7, 2015 at 4:44 PM, William Hermans <yyr...@gmail.com> wrote:
>
>> *Have you experimented with buffer size? is there any optimal value
>>> calculation? Would it have any impact on the result, Like if we keep a
>>> larger buffer and than directly take that buffer that way it would be
>>> faster? I have currently kept 1k.*

Harvey White

unread,
Oct 7, 2015, 8:46:16 PM10/7/15
to beagl...@googlegroups.com
On Wed, 7 Oct 2015 17:20:26 -0700, you wrote:

>Oh and dahm, one more thing heh. Sorry people I'm kind of remembering this
>as I think about it. Normally I keep notes, but was up late attempting to
>track this issue down. So, I'm reasonably sure the FIFO is not refreshing,
>as if I flush the data value manually ( value address &= ~0xFFFF ), it
>never gets repopulated.
>
>Once more, if I read out the sample in order based on channel id, the
>values stay the same form one iteration to the next. But If I burst read in
>whatever comes from the FIFO next, the values do change, but repeat after
>many read. Which let us just assume, for now, it's the length of the buffer
>I set through iio:device0.
>
>*Perhaps* I just need to enable / disable the ADC once the buffer fills -
>via iio ? I'm not sure, as I've only been working with the ADC for about
>what ? A week now ? With no prior experience . . .

You're working with two things, FIFO and ADC.

What does the ADC do when the FIFO is full?

What does the FIFO do when it is full?

How do you know?

Do you record it?

Harvey
>>> *Have you experimented with buffer size? is there any optimal value
>>>> calculation? Would it have any impact on the result, Like if we keep a
>>>> larger buffer and than directly take that buffer that way it would be
>>>> faster? I have currently kept 1k.*

William Hermans

unread,
Oct 7, 2015, 9:36:27 PM10/7/15
to beagl...@googlegroups.com
You're working with two things, FIFO and ADC.

What does the ADC do when the FIFO is full?

What does the FIFO do when it is full?

How do you know?

Do you record it?

Hey Harvey,

There is nothing wrong with my code per se. What is wrong however, and possibly indirectly related to the code in question. Is that I'm still learning the hardware, and some very obscure details as to how Linux plays a part in that.

So, the way I understand it is that stepping is related to averaging. In this context, a step is a single sample in a set of samples contained in an average. Here is what I think I understand. Once enabled, the first step reads from the pin, stores the value, decrements the stepping value, then checks if step > 0 - to possibly restart the sampling. Once all steps are finished ( 1, 2, 4, 8, or 16 possible steps ), the step enable bit is toggled. So this last part here, I'm not clear on, but I think I have the rest correct. But assuming this last part is correct, what could be happening is that once I have a full buffer, the step enable bit is enabled, and the ADC then "goes to sleep" until one, or more channels have their step enable bit set.

The FIFO, in my case FIFO0DATA is only a single 32bit register. 12bit data, 4bit reserved, 3bit channel id, the rest reserved. So, technically, it is always full. I never clear the whole register, only the data field 12 bits. When I do this with devmem2, the value resets, and the whole field refreshes with new values.

With the above said, I suppose you are right in that my code might be wrong, but still I think it is more of a hardware misunderstanding. Not that I think that I am the greatest C programmer to ever live, but usually, I can code my way out of a wet paper bag.

At any rate. Believe it or not prior to answering your post. I did hit on the idea that I could either manually toggle step enable ( was just reading a bit of code on this ), or I could manually clear the lower 12bits on the FIFO register, and see what happens.
 

Harvey White

unread,
Oct 7, 2015, 9:59:13 PM10/7/15
to beagl...@googlegroups.com
On Wed, 7 Oct 2015 18:36:00 -0700, you wrote:

>>
>> *You're working with two things, FIFO and ADC.*
>>
>> * What does the ADC do when the FIFO is full?*
>>
>> * What does the FIFO do when it is full?*
>>
>> * How do you know?*
>>
>> * Do you record it?*
>
>
>Hey Harvey,
>
>There is nothing wrong with my code per se. What is wrong however, and
>possibly indirectly related to the code in question. Is that I'm still
>learning the hardware, and some very obscure details as to how Linux plays
>a part in that.

Oh, I'm not suggesting that there is something wrong with your code,
but I am wondering if there is something wrong with the process
involved.

Just off the top of my head (and without offence, since I'd apply the
same list to myself... and have....

1) do we know what the OS is doing?
2) do we know what the hardware is doing?
3) do we know what the firmware/driver is doing?
4) have we made a mistake?
5) is someone else's code not doing what's designed?
6) have we run into a boundary condition of some sort that is not
handled?
7) how do we know the above answers?

Has nothing to do with programmer (your) competence. I'd decent at
this, and I've graduated to more complex and obscure mistakes,
although I will occasionally make simple ones just to keep myself in
practice (for what, I don't know....)

>
>So, the way I understand it is that stepping is related to averaging. In
>this context, a step is a single sample in a set of samples contained in an
>average. Here is what I think I understand. Once enabled, the first step
>reads from the pin, stores the value, decrements the stepping value, then
>checks if step > 0 - to possibly restart the sampling. Once all steps are
>finished ( 1, 2, 4, 8, or 16 possible steps ), the step enable bit is
>toggled. So this last part here, I'm not clear on, but I think I have the
>rest correct. But assuming this last part is correct, what could be
>happening is that once I have a full buffer, the step enable bit is
>enabled, and the ADC then "goes to sleep" until one, or more channels have
>their step enable bit set.

Sounds more like a sequence where the "stepping" is used to indicate
which samples the multiplexed input of the ADC is actually sampling
and measuring.

The AVR megas and Xmegas do this.

Analog multiplexer hooked to ADC.

>
>The FIFO, in my case FIFO0DATA is only a single 32bit register. 12bit data,
>4bit reserved, 3bit channel id, the rest reserved. So, technically, it is
>always full. I never clear the whole register, only the data field 12 bits.
>When I do this with devmem2, the value resets, and the whole field
>refreshes with new values.

Now *that* is not what I'd call a FIFO. It's a single buffer, not a
First In First Out multibyte buffer with lots of storage, but a single
byte buffer for one reading. The most you can ever get behind is one
reading cycle.
>
>With the above said, I suppose you are right in that my code might be
>wrong, but still I think it is more of a hardware misunderstanding. Not
>that I think that I am the greatest C programmer to ever live, but usually,
>I can code my way out of a wet paper bag.

Oh, and a few dry ones as well.... That, as I mentioned, is not the
issue.

Often times I find it useful to go back and check the fundamental
assumptions just because.

<yoda voice>
Is no fault, is only program.
<end yoda voice>


>
>At any rate. Believe it or not prior to answering your post. I did hit on
>the idea that I could either manually toggle step enable ( was just reading
>a bit of code on this ), or I could manually clear the lower 12bits on the
>FIFO register, and see what happens.

Not sure what that'll do, but give it a try.

Not sure about the ARM hardware, but I know how other processor do
this, and there are some little gotchas.

William Hermans

unread,
Oct 7, 2015, 10:18:14 PM10/7/15
to beagl...@googlegroups.com
Harvey,

Actually, you're right. the previously mentioned FIFO is actually 100h in size, my bad. Too much on my brain here. What I described above is actually what each 32bit field *in* that buffer *is*. hah !

Anyway, I make it a habit to jump into things like this because they're hard to do. So I often find myself struggling with details like this. But in this case, not only am I "fighting" the hardware. I'm also fighting my ignorance of how Linux stores this information in memory.

What I hope to take away from this is something like: "God, that was a pain in the butt, but man was it worth it!" Meaning: hopefully I'll learn something worth knowing ;)

William Hermans

unread,
Oct 8, 2015, 12:34:45 AM10/8/15
to beagl...@googlegroups.com
Ok, I got it sorted. I was not setting ADC STEPENABLE bit for each pin.

offset 54h STEPENABLE Step Enable Section 12.5.16 Shown here http://elinux.org/images/6/65/Spruh73c.pdf#page=1484&zoom=auto,0,650.3

Now, I'm back on par.

To screen:

debian@beaglebone:~$ time sudo ./test
. . .
 0:3883 1:3851 2:3969 3:3659 4:3680 5:3756 6:3731 7:3876
Read: 8 channels - 200000 samples per channel for a total of 1600000 samples read.

real    0m13.371s
user    0m3.510s
sys     0m2.230s

Piped to file:

debian@beaglebone:~$ time sudo ./test > output.txt

real    0m4.343s
user    0m3.160s
sys     0m0.070s
debian@beaglebone:~$ tail output.txt
 0:3870 1:3831 2:3755 3:3766 4:3769 5:3784 6:3775 7:3873
 0:3844 1:3846 2:3859 3:3804 4:3798 5:3795 6:3788 7:3871
 0:3800 1:3787 2:3828 3:3779 4:3779 5:3780 6:3808 7:3871
 0:3879 1:3829 2:3849 3:3800 4:3788 5:3761 6:3771 7:3871
 0:3832 1:3799 2:3831 3:3817 4:3806 5:3787 6:3791 7:3871
 0:3859 1:3780 2:3818 3:3781 4:3785 5:3779 6:3777 7:3883
 0:3900 1:3835 2:3851 3:3806 4:3802 5:3793 6:3760 7:3875
 0:3821 1:3795 2:3832 3:3785 4:3789 5:3811 6:3795 7:3875
 0:3869 1:3828 2:3840 3:3768 4:3772 5:3782 6:3771 7:3875
Read: 8 channels - 200000 samples per channel for a total of 1600000 samples read.

So do keep in mind that this is a "lazy" profile of the code. Meaning if I actually profile the code properly. That would work out into my favor. Anyway, using my basic math skills . . . I get 46051.11673958093 samples a second - per channel *OR* 368408.9339166475 samples a second total.

Also there are a couple things worth mentioning. Since I am only piping stdout to a file, there is room for improvement here. I'm betting it net me a HUGE performance gain, if I used mmap() to put that data into memory, or a POSIX shared memory file. Both would achieve the same end goal, and I do not believe either would be faster than the other.

Second, this is roughly 2M a second worth of data. So, one could simply setup an NFS share, and very conveniently pipe that data remotely to another system without much fuss. That is to say I have 99 lines of code in a header file, and 39 lines in my main C code file. No need to fuss with the PRU's, Linux sockets API, or any of that. Or if you wanted to get fancy, you could pipe that data out over netcat.

One additional thing to note: I have not validated the samples. I'll leave that up to someone else, but mark this horse beat into another incarnation.

William Hermans

unread,
Oct 8, 2015, 12:42:06 AM10/8/15
to beagl...@googlegroups.com
Also before someone says: "Hey, you're sampling 8 channels, but I can only physically connect to 7 . . ." or some such. Keep in mind that will only increase the performance of the code . . .

Harvey White

unread,
Oct 8, 2015, 3:19:24 PM10/8/15
to beagl...@googlegroups.com
On Wed, 7 Oct 2015 19:18:02 -0700, you wrote:

>Harvey,
>
>Actually, you're right. the previously mentioned FIFO is actually 100h in
>size, my bad. Too much on my brain here. What I described above is actually
>what each 32bit field *in* that buffer *is*. hah !

Good, that means that there *is* a FIFO.
>
>Anyway, I make it a habit to jump into things like this because they're
>hard to do. So I often find myself struggling with details like this. But
>in this case, not only am I "fighting" the hardware. I'm also fighting my
>ignorance of how Linux stores this information in memory.
>

Well, I'd guess something like this: the FIFO data is stored on the
heap, which is malloc'd. You need the pointers as needed to figure
out where it is. Standard pointers to head and tail of the ring
buffer will be stored locally, unless someone did a pointer to the
data structure of the FIFO (but not the data itself). I'm doing that
in an OS that I'm writing. The only advantage here is that heap
memory (or managed memory) is managed by the OS, and should not belong
to any one task. (you could argue that, but it's more convenient this
way for me.)


>What I hope to take away from this is something like: "God, that was a pain
>in the butt, but man was it worth it!" Meaning: hopefully I'll learn
>something worth knowing ;)

I suspect you will, so go for it.

Best of luck

William Hermans

unread,
Oct 8, 2015, 5:00:57 PM10/8/15
to beagl...@googlegroups.com
Well, I'd guess something like this:  the FIFO data is stored on the
heap, which is malloc'd.  You need the pointers as needed to figure
out where it is.   Standard pointers to head and tail of the ring
buffer will be stored locally, unless someone did a pointer to the
data structure of the FIFO (but not the data itself).  I'm doing that
in an OS that I'm writing.  The only advantage here is that heap
memory (or managed memory) is managed by the OS, and should not belong
to any one task.  (you could argue that, but it's more convenient this
way for me.)

I can not say it any better than this: http://linux.about.com/library/cmd/blcmdl4_mem.htm So basically, when you mmap() /dev/mem/ you get a pointer to any system address you point it to. In this case, for me, the physical address space assigned by the kernel, for the peripherals. This is what I *think* is happening. I'm still learning how this works, by starting off with examples by others.

So what I've figured out already is that the code I'm using, and have partly written myself. Gives me a pointer to the ADC registers - and more. I know it works, as I can enable the ADC, take readings with my executable, or devmem2, the values are very likely accurate for reason mentioned previously. Then I've written code myself based on information I've read from the TRM to disable the ADC clock when done. After which devemem2 no longer returns a value from this area of memory - Which is the memory location for the first FIFO ( FIFO0 ) Of the ADC.

What I would really like to find is a good comprehensive guide, document, or something about this whole process. I have not really found this yet, and maybe I've been looking in the wrong places ? Stuff like POSIX shared memory, etc, may be considered advanced topics in programming. But I'd personally consider the topic of /dev/mem/ far more advanced. Certainly, it can be "dangerous" to start poking around in random memory locations on a live system. Which is why in this case, I started off experimenting with code written by others.

Being able to write code from scratch to disable the ADC when done with it, has boosted my confidence *some*. But I know I have a lot to learn about this topic, and at least the Linux kernel memory addressing schema.

Harvey White

unread,
Oct 8, 2015, 5:30:35 PM10/8/15
to beagl...@googlegroups.com
On Thu, 8 Oct 2015 14:00:39 -0700, you wrote:

>>
>> *Well, I'd guess something like this: the FIFO data is stored on the*
>> * heap, which is malloc'd. You need the pointers as needed to figure*
>> * out where it is. Standard pointers to head and tail of the ring*
>> * buffer will be stored locally, unless someone did a pointer to the*
>> * data structure of the FIFO (but not the data itself). I'm doing that*
>> * in an OS that I'm writing. The only advantage here is that heap*
>> * memory (or managed memory) is managed by the OS, and should not belong*
>> * to any one task. (you could argue that, but it's more convenient this*
>> * way for me.)*
>>
>
>I can not say it any better than this:
>http://linux.about.com/library/cmd/blcmdl4_mem.htm So basically, when you
>mmap() /dev/mem/ you get a pointer to any system address you point it to.
>In this case, for me, the physical address space assigned by the kernel,
>for the peripherals. This is what I *think* is happening. I'm still
>learning how this works, by starting off with examples by others.

mem is the whole system memory, protected or otherwise. It doesn't
seem to include ports;

port is the I/O memory.

I suspect that you're right (not a linux type, but it seems to be
what's going on).

>
>So what I've figured out already is that the code I'm using, and have
>partly written myself. Gives me a pointer to the ADC registers - and more.


>I know it works, as I can enable the ADC, take readings with my executable,
>or devmem2, the values are very likely accurate for reason mentioned
>previously. Then I've written code myself based on information I've read
>from the TRM to disable the ADC clock when done. After which devemem2 no
>longer returns a value from this area of memory - Which is the memory
>location for the first FIFO ( FIFO0 ) Of the ADC.

no longer returns any value or doesn't return a value you expect? You
should be able to read the memory regardless. What it says is another
matter.

>
>What I would really like to find is a good comprehensive guide, document,
>or something about this whole process. I have not really found this yet,
>and maybe I've been looking in the wrong places ? Stuff like POSIX shared
>memory, etc, may be considered advanced topics in programming. But I'd
>personally consider the topic of /dev/mem/ far more advanced. Certainly, it
>can be "dangerous" to start poking around in random memory locations on a
>live system. Which is why in this case, I started off experimenting with
>code written by others.

Just offhand, I'd suggest not messing with the hardware directly, but
using the system drivers unless you want to write system level
drivers.

>
>Being able to write code from scratch to disable the ADC when done with it,
>has boosted my confidence *some*. But I know I have a lot to learn about
>this topic, and at least the Linux kernel memory addressing schema.

Guess there isn't a linux ADC driver for this chip. I'm used to
writing bare metal drivers myself, but not for linux at all.

The heap memory is effectively the *remaining* memory that the
operating system doesn't use, it should include all of the data
segments, the ram data, and the current stack. From this, malloc
pulls memory, (in the GCC-AVR) from the bottom up.

So it doesn't at all seem to be the same as the "mem" array, or the
"port" array...

Harvey

Rathin Dholakia

unread,
Oct 8, 2015, 5:31:04 PM10/8/15
to beagl...@googlegroups.com
I m just sitting here astonished & awed by the discussion!! Congratulations on success..! :-)

Take away from this discussion for me is that, I have a long way to go!

Rathin
You received this message because you are subscribed to a topic in the Google Groups "BeagleBoard" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/beagleboard/OXe88RyHqX8/unsubscribe.
To unsubscribe from this group and all its topics, send an email to beagleboard...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.


--
Rathin A. Dholakia
"Dont GO through life, GROW through life"


William Hermans

unread,
Oct 8, 2015, 5:57:45 PM10/8/15
to beagl...@googlegroups.com
no longer returns any value or doesn't return a value you expect?  You
should be able to read the memory regardless.  What it says is another
matter.

devmem2 returns a NULL value. Meaning, the completes the procedure, but returns a NULL value. Which is because the ADC module is in effect - Disabled. I'm convinced it works.


Just offhand, I'd suggest not messing with the hardware directly, but
using the system drivers unless you want to write system level
drivers.

The whole idea, at least for me( and many others ) is to use the hardware without drivers that can unnecessarily add application execution overhead. Which is exactly how the PRU's control the ADC hardware in many examples I've seen. Some might consider this "the non Linux "way" ", But I'd argue that doing things the way *you*(generic term) want, is the true Linux way. i.e. Flexibility.

Guess there isn't a linux ADC driver for this chip.  I'm used to
writing bare metal drivers myself, but not for linux at all.

There is a Linux driver for the on chip ADC, but it is "slow". Well, to be 100% accurate I suppose I should say that technically, there is a iio driver, or capability for a iio driver to have better performance. It also uses mmap(), but there may not be such a driver for this specific ADC. I honestly have no looked into that *yet*. To be honest( again ), I'm not sure I care if there is such a driver or not - Either. But what I've read, is that iio drivers can come in two varieties. Slow, and fast. Sometimes both exist, sometimes not.


The heap memory is effectively the *remaining* memory that the
operating system doesn't use, it should include all of the data
segments, the ram data, and the current stack.  From this, malloc
pulls memory, (in the GCC-AVR) from the bottom up.

So it doesn't at all seem to be the same as the "mem" array, or the
"port" array...

I have no hands on with the "port" device. But what I took away from that link I pasted, and others, is that it is related, but not the same thing.

So thinking on this a little, you're probably right. As far as kernel space, heap etc Because we have /dev/kmem , /dev/mem/, and /dev/port/. In this context it makes sense that they're all separate. In the end however, I'm doing a lot of speculation on stuff I *think* I understand. Which is to say, I understand a small portion of it all, but still need more understanding. Which is why I want to find a good read on the subject.

In the end it's all about the learning - For me. Which it does indeed seem I have a lot to keep me busy for a good long while.

William Hermans

unread,
Oct 8, 2015, 6:25:00 PM10/8/15
to beagl...@googlegroups.com
I m just sitting here astonished & awed by the discussion!! Congratulations on success..! :-)

Take away from this discussion for me is that, I have a long way to go!

Rathin
Just know that no one is born knowing any of this, or anything for that matter. We all have to start from square one - as it were. As far as learning C, or any programming language, that just comes with lots of code writing. The more you write, the more you fail, and hopefully learn from. I do think it is important to emphasize the key point: That failure in this context is not a bad thing. So long as you eventually learn *why* something failed. Hopefully in many cases, sooner rather than later.

Harvey White

unread,
Oct 8, 2015, 8:57:33 PM10/8/15
to beagl...@googlegroups.com
On Thu, 8 Oct 2015 14:57:28 -0700, you wrote:

>>
>> *no longer returns any value or doesn't return a value you expect? You*
>> * should be able to read the memory regardless. What it says is another*
>> * matter.*
>
>
>devmem2 returns a NULL value. Meaning, the completes the procedure, but
>returns a NULL value. Which is because the ADC module is in effect -
>Disabled. I'm convinced it works.

Ok, I'm puzzled why you'd get a null value. To me, says that an
allocation failed, or memory is not there.

If I were writing this, it would mean that the driver could not be
found, or that the memory address is invalid...

I probably don't understand that at all, just guessing how it might
work.


>
>*Just offhand, I'd suggest not messing with the hardware directly, but*
>> * using the system drivers unless you want to write system level*
>> * drivers.*
>>
>
>The whole idea, at least for me( and many others ) is to use the hardware
>without drivers that can unnecessarily add application execution overhead.
>Which is exactly how the PRU's control the ADC hardware in many examples
>I've seen. Some might consider this "the non Linux "way" ", But I'd argue
>that doing things the way *you*(generic term) want, is the true Linux way.
>i.e. Flexibility.

OK, better system drivers, I can see that.

Generally you get lots of different levels in an operating system

Hardware -> driver -> medium level interface -> system level interface
-> application

Hardware -> driver -> medium level interface -> high level interface->
system level interface -> application

You get either. Idea is that the driver messes with the hardware, and
the medium level interface does sequences, data fetches from the
driver (knows how to make the driver work) and provides resources to
the system level interface...

System level interface is optional, but allows you to treat the ADC
(for instance) as a subsystem, while the medium level interface has a
bunch of device independent primitives (set resolution, make
measurement, set reference, etc....)

System stuff would be (make list of measurements using port/adc,
return scaled values, make averaged measurements, etc....) Depends on
how you treat a device.

Graphics GUI commands can go to a system driver which commands remote
displays or on board hardware. On board hardware driver selects
graphics hardware. Driver makes dots appear....

That kind of thing.

I think what you are doing is wanting to roll the hardware driver and
the medium level together, and then let the application do the work.

I'm thinking that this has more to do with directly writing the
hardware driver, providing hardware agnostic routines to the
application program and letting it go at that.

In a sense, this has nothing to do with linux, except the mechanism
for getting at the memory/hardware and such system things as fifos,
queues, semaphores and the like.





>
>*Guess there isn't a linux ADC driver for this chip. I'm used to*
>
>* writing bare metal drivers myself, but not for linux at all.*
>
>
>There is a Linux driver for the on chip ADC, but it is "slow". Well, to be
>100% accurate I suppose I should say that technically, there is a iio
>driver, or capability for a iio driver to have better performance. It also
>uses mmap(), but there may not be such a driver for this specific ADC. I
>honestly have no looked into that *yet*. To be honest( again ), I'm not
>sure I care if there is such a driver or not - Either. But what I've read,
>is that iio drivers can come in two varieties. Slow, and fast. Sometimes
>both exist, sometimes not.

Let alone how well written an existing driver is... been there.....

>
>*The heap memory is effectively the *remaining* memory that the*
>> * operating system doesn't use, it should include all of the data*
>> * segments, the ram data, and the current stack. From this, malloc*
>> * pulls memory, (in the GCC-AVR) from the bottom up.*
>>
>> * So it doesn't at all seem to be the same as the "mem" array, or the*
>> * "port" array...*
>>
>
>I have no hands on with the "port" device. But what I took away from that
>link I pasted, and others, is that it is related, but not the same thing.

Yep. I suspect that if the I/O devices are in a separate memory space
(x86) then you get the "port" array. Else you get the "mem" array,
and nothing but.

>
>So thinking on this a little, you're probably right. As far as kernel
>space, heap etc Because we have /dev/kmem , /dev/mem/, and /dev/port/. In
>this context it makes sense that they're all separate. In the end however,
>I'm doing a lot of speculation on stuff I *think* I understand. Which is to
>say, I understand a small portion of it all, but still need more
>understanding. Which is why I want to find a good read on the subject.

I'd be happy to help if I knew a good linux reference that was slanted
towards the ARM architecture. Don't, though.

>
>In the end it's all about the learning - For me. Which it does indeed seem
>I have a lot to keep me busy for a good long while.

Know that feeling.

Harvey

>
>On Thu, Oct 8, 2015 at 2:30 PM, Rathin Dholakia <rathind...@gmail.com>
>wrote:
>
>> I m just sitting here astonished & awed by the discussion!!
>> Congratulations on success..! :-)
>>
>> Take away from this discussion for me is that, I have a long way to go!
>>
>> Rathin
>>
>> On Friday, October 9, 2015, William Hermans <yyr...@gmail.com> wrote:
>>
>>> *Well, I'd guess something like this: the FIFO data is stored on the*
>>>> * heap, which is malloc'd. You need the pointers as needed to figure*
>>>> * out where it is. Standard pointers to head and tail of the ring*
>>>> * buffer will be stored locally, unless someone did a pointer to the*
>>>> * data structure of the FIFO (but not the data itself). I'm doing that*
>>>> * in an OS that I'm writing. The only advantage here is that heap*
>>>> * memory (or managed memory) is managed by the OS, and should not belong*
>>>> * to any one task. (you could argue that, but it's more convenient this*
>>>> * way for me.)*
>>>>
>>>

William Hermans

unread,
Oct 8, 2015, 10:42:02 PM10/8/15
to beagl...@googlegroups.com
@ Harvey,

So, buy a RevC, and join in on the fun :)

Harvey White

unread,
Oct 8, 2015, 10:55:06 PM10/8/15
to beagl...@googlegroups.com
On Thu, 8 Oct 2015 19:41:33 -0700, you wrote:

>@ Harvey,
>
>So, buy a RevC, and join in on the fun :)

I have one already....

It's on my list of things to do.

It has to run either linux or android, and I need the whole spectrum
of I2C available on one interface, it's existing hardware.

Still need to get that stuff up and running completely, but the BBB
(or equivalent) will be a high level controller (over I2C) running
custom data acquisition hardware.
It is loading more messages.
0 new messages