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

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!

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?

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.

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

You realize that you can read the ADC from Linux at full speed also? No need to use the PRU.

Regards,
John

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 :slight_smile: mmap() is dahmed fast . . .

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.

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

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.

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

Hi John!

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.

  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

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

/*

BTW, the was and update to BB-ADC-00A0.dts

Regards,
John

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.

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

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.

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

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

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.

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.