C#: Checking when ADC is ready - or- speeding up sample rate? (kernel 4.1 debian)

100 views
Skip to first unread message

PatM001

unread,
Mar 2, 2016, 7:11:27 AM3/2/16
to BeagleBoard
I'm reading sensors using the analog inputs on my BBG and for my real application the SFS system is plenty fast enough but I do occasionally get share violation exceptions. I'm assuming that I'm attempting to read the file exactly when it is being written and therefore I get the exception. A try/catch handles that but I'd much rather just check to see if the conversion/write has finished and not generate exceptions.

Problem is I can't seem to find any information on what to check or even if there is anything I can check. Anyone have any information on this?

The second part of the question is about speeding up the conversions. Since I'm reading sensors and this BBG is mucho fast I made a simple oscilloscope using Zedgraph to see the effects of my low pass filter combinations (Its MUCH easier than using my real oscilloscope!). Problem is that if I just read the ADC values and stuff them into an array I get tons of exceptions. I have to slow down the reading of the adc file by doing other operations in the loop (I tried thread.sleep() but that only goes down to 1ms and cuts the sample rate in half.

I did run across mentions of changing the ADC conversion rate but only with assembler or C running on a PRU. I'm nowhere near skilled enough to do PRU programming and there doesn't seem to be any library for C# for accessing the registers (I see there is for Python and C/C++).

Any clues on how to change the conversion rate from C# (maybe a PyPRUSS like library for C#?)

Thanks!

William Hermans

unread,
Mar 2, 2016, 8:12:54 AM3/2/16
to beagl...@googlegroups.com
I'm reading sensors using the analog inputs on my BBG and for my real application the SFS system is plenty fast enough but I do occasionally get share violation exceptions. I'm assuming that I'm attempting to read the file exactly when it is being written and therefore I get the exception. A try/catch handles that but I'd much rather just check to see if the conversion/write has finished and not generate exceptions.

Problem is I can't seem to find any information on what to check or even if there is anything I can check. Anyone have any information on this?


So, try / catch blocks add a performance hit when used, and should be avoided on "fast moving" code. You've shown us no code, so there is no way really we can say what you need to  / need not do. It's been a long while since I've written any C# code, however look as this C example I wrote on my blog site, and how I deal with the problem in that code. http://www.embeddedhobbyist.com/2015/10/beaglebone-black-adc/

Do you see how it's dealt with ?

                                  len = read(fd, adc, sizeof(adc - 1));
 
                if(len == -1){
                        close(fd);
                        continue;              
                }

The major key to the above code snippet is understanding how the API call read() works. The rest is academic. But in short. If the length of read() is -1, close the file descriptor, and *continue*. There are probably better definitions of what continue does in code on the internet. But basically, continue jumps execution to the top of the code block ( { } ) the code is executing in. Which in fact, starts over by attempting to reopen the file descriptor.

--
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.

TJF

unread,
Mar 2, 2016, 3:43:29 PM3/2/16
to BeagleBoard
Hello PatM001!

There's libpruio, which provides ADC sampling at full speed (200 kHz). You'll get rid of the exeptions (and the miss readings of the sysfs driver in case of sampling multiple channels).

The downside: no C# binding yet. It's written in FreeBASIC/PASM and gets shipped with a C header. You may try SWIG on the C header in order to generate a binding for your prefered language.

If you try, please share your results, or at least report.

BR

John Syne

unread,
Mar 2, 2016, 4:32:16 PM3/2/16
to beagl...@googlegroups.com
You realize that you can read the ADC from Linux at full speed also? No need to use the PRU. 

Regards,
John




William Hermans

unread,
Mar 2, 2016, 5:20:06 PM3/2/16
to beagl...@googlegroups.com
You realize that you can read the ADC from Linux at full speed also? No need to use the PRU. 
Regards,
John
I do, because I've proven just that :) mmap() is dahmed fast . . .

William Hermans

unread,
Mar 2, 2016, 5:27:14 PM3/2/16
to beagl...@googlegroups.com
errr oops, I sent too soon. mmap() is fast, and can actually read from the ADC faster than the ADC can update values. But, it's using the main processor to do so, and if you need to do more than just read the ADC. Additional processes would have to compete for processor time. So, if one does want / need to read at maximum speed, it might be wise to offload the main processor, by using a PRU.

It would not matter if this were done in userspace, or kernel space. It'll definitely put a load strain on the ARM processor.

John Syne

unread,
Mar 2, 2016, 6:07:02 PM3/2/16
to beagl...@googlegroups.com
First, that isn’t going to work because the ADC uses a scan loop and unless you can respond to interrupts, you cannot determine when the ADC conversion has completed. There is a much simpler way to do this. Simply use the IIO driver and then read /dev/iio:device0

For example, do:

dd if=/dev/iio:device0 of=~/test

Enable the iio buffer and your file will receive samples at the configured speed.

Regards,
John



William Hermans

unread,
Mar 3, 2016, 1:12:51 AM3/3/16
to beagl...@googlegroups.com
First, that isn’t going to work because the ADC uses a scan loop and unless you can respond to interrupts, you cannot determine when the ADC conversion has completed. There is a much simpler way to do this. Simply use the IIO driver and then

FIrst of all, it *will* work. I've done it, and it works. Second of all, in continuous mode, values are put out as 32bit values. Only the first 12bits is the actual ADC value. The next 4 bits is the channel ID( 0 - 7 ), and the last 16bits reserved / unused. Thirdly, using interrupts in fast moving code is about as bad of an idea as using try / catch blocks in fast moving code. It adds code latency, and also introduces non deterministic behavior. This is why iio does not work fast for short data sets.


dd if=/dev/iio:device0 of=~/test

Maybe, but it's a terrible idea if the target is flash media. The ADC's can, and will use up a lot of storage space, very quickly. Just using 7 channel in one shot mode, one channel after the next. In a loop of 300k iterations. I was using up ~3MB/s disk space. Maxed out, and all channel used. The ADC's should use up ~9MB/s or more.

TJF

unread,
Mar 3, 2016, 1:49:51 AM3/3/16
to BeagleBoard


Am Donnerstag, 3. März 2016 07:12:51 UTC+1 schrieb William Hermans:
First, that isn’t going to work because the ADC uses a scan loop and unless you can respond to interrupts, you cannot determine when the ADC conversion has completed. There is a much simpler way to do this. Simply use the IIO driver and then

FIrst of all, it *will* work. I've done it, and it works. Second of all, in continuous mode, values are put out as 32bit values. Only the first 12bits is the actual ADC value. The next 4 bits is the channel ID( 0 - 7 ), and the last 16bits reserved / unused. Thirdly, using interrupts in fast moving code is about as bad of an idea as using try / catch blocks in fast moving code. It adds code latency, and also introduces non deterministic behavior. This is why iio does not work fast for short data sets.

It works when polling the related FIFODATA register. This puts additional load on the ARM CPU.

BTW:
bits 00-11: ADC DATA
bits 12-15: reserved
bits 16-19: channel ID (optional)
bits 20-31: reserved

TJF

unread,
Mar 3, 2016, 1:52:17 AM3/3/16
to BeagleBoard
Hi John!


Am Donnerstag, 3. März 2016 00:07:02 UTC+1 schrieb john3909:
For example, do:

dd if=/dev/iio:device0 of=~/test

Enable the iio buffer and your file will receive samples at the configured speed.

Thanks for your statement. An interesting solution, I didn't find that yet.

From my point of view this is a big issue for BB[WBG?]. Most of the IO kernel features are badly documented. For a kernel novice, features are
  1. hard to find and
  2. hard to understand. (Especially for non-native speakers.)
One or more examples in each documentation would help a lot, like the one you posted here (How to configure speed? How to set up step configurations? Does it solve the iio miss-sampling issue, appearing at multi channel sampling in single mode?).

Anyway, my prefered solution is still libpruio since it
  • doesn't load the ARM CPU
  • offers a lot of further features (ie. scaling for 13 to 16 bit or triggering the measurement start by up to four events)
  • can act in concert with the other PRU (important for hard real-time requirements > 10 kHz).
BR

John Syne

unread,
Mar 3, 2016, 1:52:34 AM3/3/16
to beagl...@googlegroups.com
When the ADC is in continuous mode, you shouldn't read the data until it has been updated. Simply reading the data over and over again to get the same value that hasn’t been updated is just dumb. The interrupt tells you when the conversion has been updated and then you read it. The point I was making originally was that there was no need to use PRU to sample the ADC at full speed. You can do the same from the IIO driver. Running at full speed consumes less than 10% of the CPU. If the IIO driver was updated to use DMA, then there would be no CPU utilization. From what I remember, the solution you proposed was using 90% of the CPU. 

Regards,
John



John Syne

unread,
Mar 3, 2016, 1:56:21 AM3/3/16
to beagl...@googlegroups.com
/*
 * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */
/dts-v1/;
/plugin/;

/ {
compatible = "ti,beaglebone", "ti,beaglebone-black", "ti,beaglebone-green";

/* identification */
part-number = "BB-ADC";
version = "00A0";

/* state the resources this cape uses */
exclusive-use =
/* the pin header uses */
"P9.31", /* AIN0 */
"P9.40", /* AIN1 */
"P9.37", /* AIN2 */
"P9.38", /* AIN3 */
"P9.33", /* AIN4 */
"P9.36", /* AIN5 */
"P9.35", /* AIN6 */
/* the hardware ip uses */
"tscadc";

fragment@0 {
target = <&tscadc>;
__overlay__ {

status = "okay";
adc {
ti,adc-channels = <0 1 2 3 4 5 6>;
ti,chan-step-avg = <0x1 0x1 0x1 0x1 0x1 0x1 0x1>;
ti,chan-step-opendelay = <0x98 0x98 0x98 0x98 0x98 0x98 0x98>;
ti,chan-step-sampledelay = <0x0 0x0 0x0 0x0 0x0 0x0 0x0>;
};
};
};
};

So the speed is selected by changing the number of samples in averaging and the open delay and sample delay. 

Regards,
John




John Syne

unread,
Mar 3, 2016, 2:03:30 AM3/3/16
to beagl...@googlegroups.com, Robert Nelson
BTW, the was and update to BB-ADC-00A0.dts

Regards,
John



William Hermans

unread,
Mar 3, 2016, 2:04:49 AM3/3/16
to beagl...@googlegroups.com
From what I remember, the solution you proposed was using 90% of the CPU.

93% CPU load when using one shot mode, and continuously opening / closing a file descriptor to the ADC module. There is no such load when using mmap(), as mmap() is light years faster.

John Syne

unread,
Mar 3, 2016, 2:06:31 AM3/3/16
to beagl...@googlegroups.com
Oh, and one more thing; the IIO driver uses a top half and bottom half for handling interrupts, so you won’t see and added latency. All the real work is done in a workqueue.

Regards,
John



William Hermans

unread,
Mar 3, 2016, 2:08:27 AM3/3/16
to beagl...@googlegroups.com
Oh, and one more thing; the IIO driver uses a top half and bottom half for handling interrupts, so you won’t see and added latency. All the real work is done in a workqueue.

That's not true. The developer of the software cautions not to use the interrupts too frequently. As it will do exactly as I stated. His words, not mine.

John Syne

unread,
Mar 3, 2016, 2:13:01 AM3/3/16
to beagl...@googlegroups.com
mmap isn't faster than a kernel driver (kernel code has priority over user space code) and you still cannot handle interrupts from user space. Anyway, you won’t find any drivers in the kernel implemented your way (/dev/mem, mmap). However, mmap is used in drivers to eliminate mem to mem copy when transferring data between user space and kernel space. 

Regards,
John



John Syne

unread,
Mar 3, 2016, 2:15:51 AM3/3/16
to beagl...@googlegroups.com
Well, as I said, it would have been better to have implemented the driver with DMA, but it does still work with interrupts. And what isn’t true? IIO does use top half and bottom half interrupt handling. Look at the code.

Regards,
John



William Hermans

unread,
Mar 3, 2016, 2:22:14 AM3/3/16
to beagl...@googlegroups.com
mmap isn't faster than a kernel driver (kernel code has priority over user space code) and you still cannot handle interrupts from user space. Anyway, you won’t find any drivers in the kernel implemented your way (/dev/mem, mmap). However, mmap is used in drivers to eliminate mem to mem copy when transferring data between user space and kernel space. 
Regards,
John
Now. not only are you wrong, but you're making stuff up. You can handle interrupts from userspace, as much as iio can. But it's not my job to tell you how. I will mention that perhaps you should look into "userspace drivers". As far as whats faster ? who f***ing cares. mmap() is a lot faster than the ADCs . . . and still not the point.

The point is, if you need fast ADC you should be using the PRU, and then you may want to seriously consider using an external module. That is, for anything serious. It does not matter how much CPU mmap() or iio uses. As any % can preempt other code that needs to run *now* thus creating potential non determinism.

John Syne

unread,
Mar 3, 2016, 2:26:23 AM3/3/16
to beagl...@googlegroups.com
Wrong again, UIO attempts to handle interrupts as events, but the concept is slow (typically ms, not us)

Regards,
John



William Hermans

unread,
Mar 3, 2016, 2:28:03 AM3/3/16
to beagl...@googlegroups.com
Wrong again, UIO attempts to handle interrupts as events, but the concept is slow (typically ms, not us)

You're full of it if you're trying to purport that any interrupt in Linux works in the uS range.

Micka

unread,
Mar 3, 2016, 2:32:51 AM3/3/16
to beagl...@googlegroups.com
?? John said that UIO drivers work in ms not in us range .....Looks like that both of you agree on that !

William Hermans

unread,
Mar 3, 2016, 2:36:02 AM3/3/16
to beagl...@googlegroups.com
So, let me put it this way, and no that's not what John was trying to infer. *Any* time you cross over from user space to kernel space, and back. There is *always* a performance hit. Which technically is not 100% accurate, but is accurate for anything that matters . . .

John Syne

unread,
Mar 3, 2016, 2:36:12 AM3/3/16
to beagl...@googlegroups.com
Well, it does, but not consistently, hence the interrupt latency issue. For the most part, linux can respond to interrupts in the uS range, especially when the CPU load is low. When the CPU load is high, the latency does get worse. However, UIO event handling is about a 1,000 slower than interrupts in kernel code. 

Regards,
John



William Hermans

unread,
Mar 3, 2016, 2:39:45 AM3/3/16
to beagl...@googlegroups.com
And thats the problem. It's not consistent, and nothing is of this sort.

TJF

unread,
Mar 3, 2016, 3:41:16 AM3/3/16
to BeagleBoard
Hi John!

Thanks for the dts examples.

Lets say I want to develop a simple osziloscop application and the user should be able to choose the sampling rate or the number of channels. Therefor changing the device tree overlay is necessary, right? And since unloading an overlay is unsave in some kernel versions, the application has to reboot?

That's anoying. One spends a lot of time to find and test a kernel feature, just to find out that it's unuseable for most practical purposes.

BR

John Syne

unread,
Mar 3, 2016, 12:57:36 PM3/3/16
to beagl...@googlegroups.com
Why not use IIOScope?

Regards,
John



John Syne

unread,
Mar 3, 2016, 1:11:53 PM3/3/16
to beagl...@googlegroups.com
Thinking a little about what your requirement, it would by simple to modify these parameters in BB-ADC driver via sysfs. Think of the DT as default parameters when the driver first starts.

Regards,
John



TJF

unread,
Mar 3, 2016, 5:59:28 PM3/3/16
to BeagleBoard

Am Donnerstag, 3. März 2016 19:11:53 UTC+1 schrieb john3909:
Thinking a little about what your requirement, it would by simple to modify these parameters in BB-ADC driver via sysfs. Think of the DT as default parameters when the driver first starts.

Sure, that'd be a more practicable solution. But rather than for ADC, I'd like to see such a solution for pinmuxing. See this post.

BR
 

John Syne

unread,
Mar 3, 2016, 6:08:30 PM3/3/16
to beagl...@googlegroups.com

John Syne

unread,
Mar 3, 2016, 8:23:17 PM3/3/16
to beagl...@googlegroups.com
All you want to do is a a mode feature, correct?

Regards,
John



John Syne

unread,
Mar 3, 2016, 8:28:28 PM3/3/16
to beagl...@googlegroups.com
That should read add mode feature

Regards,
John



TJF

unread,
Mar 4, 2016, 1:25:52 AM3/4/16
to BeagleBoard
Hi John!

Thanks for that link. Another issue first:

We're poluting this thread by interesting, but off-topic stuff. I hope you don't mind that I switch our discussion to the new thread and answer there.

BR

PatM001

unread,
Mar 8, 2016, 3:38:57 PM3/8/16
to BeagleBoard
Wow, didn't mean to start a war!

Libpruio looks great but as mentioned, no C# version. I'll have a look at that utility mentioned above for converting to other languages.

I don't know anything about mmap, I'll have to google that.

As it is the app I'm working on only needs to read the ADCs once per second and the low passes are working so well I just take the average of 10 samples in a row (something like 16ms for three ADC 10x each). Even once per second is overkill (the process change is quite slow - like 5-30 minutes from one extreme to the other).

Anyway, faster sampling was so that I could make a rudimentary oscilloscope for playing around with low pass filter configurations. Just using the default BB-ADC overlay I get some 800+ samples per second (which I'm sure a lot are probably reading the same value multiple times) and it does give me all the info I need for tuning up the input filters.

William Hermans

unread,
Mar 8, 2016, 5:09:29 PM3/8/16
to beagl...@googlegroups.com
Well, one shot mode is good for close to 6k reads / second *if* one needs / wants to. Well, realistically, because of CPU usage. I'd probably limit oneshot mode to around 3-4k samples a second. Once a second is definitely no problem.

http://www.embeddedhobbyist.com/2015/10/beaglebone-black-adc/

The example code is C, but perhaps you can use it to write an interface for C#.

--

William Hermans

unread,
Mar 8, 2016, 5:13:41 PM3/8/16
to beagl...@googlegroups.com
By the way, don't worry about mmap(). It's an C/C++ API call meant interact directly with a systems memory. e.g. you can poke / prod any memory location for good, and even bad purposes. Usually using mmap() in this manner is frowned on, as it can be insecure, or can lead to system instability if one does not know what they're doing. Essentially, you bypass the kernel completely when doing this, and is one reason why it can be so fast.
Reply all
Reply to author
Forward
0 new messages