Help: "Fast" ADC sampling on the beagleboneblack

Hi!

I am currently using my Beaglebone Black with the following KERNEL: 3.8.13xenomai-bone28.1 (including the ti_am335_adc driver).

Part of my Project is reading signals from an ADC and then processing them. I would love to use the onboard ADC for that, but I require to sample 5 channels @ 2 kHz each. (I have installed the Xenomai Kernel mainly for processing purposes but it might be useful for sampling as well :wink: )

My first simplistic try was to just read the voltage values from the files provided in “/sys/bus/iio/devices/iio:device0/in_voltage0_raw” .
This works fine so far, but is just not fast enough. ( I get about 500 sps on ONE channel)

Here is that part of my CODE:

`

for(;:wink: {

rt_task_wait_period(NULL); // Xenomai

now=rt_timer_read(); // Xenomai

// Work for the current period //

FILE *read = fopen ( “/sys/bus/iio/devices/iio:device0/in_voltage0_raw”, “r”);

fscanf (read, “%f”, &value);
float voltage = value * (1800.0/4095.0);
fprintf (write, “Value: %f Voltage: %f\n”, value, voltage);

printf(“Time since last turn: %ld,%06ld ms Value:%f Voltage: %f\n”,
(long)(now - previous) / 1000000,
(long)(now - previous) % 1000000, value, voltage);
previous = now;

fclose ( read );
fclose ( write );

}

`
(I know that leaving out the printf part and the voltage calculation increases performance, but still not enough)

Is there any other way reading the values, directly from the memory location or a register? Perhaps using mmap?
Or is there a Xenomai function for reading from an iio device? (I couldn’t find any…)

I have seen two interesting links about continuous sampling:

But i can’t get the generic_buffer.c from those sites to work correctly (as the trigger handling seems to be outdated), plus i might need to know a kind of “oneshot” read function anyway to mux the 5 channels i want to sample. Or is it possible to continuous sample multiple channels? The Driver guide says ‘no’, but the Author from the other link seems to have worked a lot on the adc Driver since.

I am currently trying to figure out the code in generic_buffer.c and the used iio_utils.h and the adc driver, but it is a lot of code and drivers are not my strong suit.
So far I think I need to setup the ADC to my needs (somewhat similar to generic_buffer.c) and then read from it somehow, but I am really struggling with that!

So now you know the state of my project :slight_smile: but to sum it up:

Are my demands (5 channels @ 2 kHz) even possible using only the on-board ADC?
And if yes how do i read the values fast enough? Any tips?

Thank you in advance!!

Hi,

I’m using the same kernel (with xenomai) as you, and I’m facing a similar issue with the encoder interface

If you use xenomai and then access via sysfs to the driver you0re causing a domain switch to linux OS over xenomai.
Moreover this way in any cycle you’re issuing to many syscalls to reach high frequencies.

Just this morning I’ve tried to sample the encoder not using the sysfs interface but mmaping the device counter to the xenomai task and i experienced a big performance bump.

BR
Giuseppe

Note that in addition to the domain switch overhead, if you ever make a
Linux syscall from a xenomai task, you have lost any real-time
performance guarantees. The Linux kernel can hold your task off for as
long as it wants, potentially hundreds of milliseconds or more.

I suggest all to subscribe to xenomai mailing list as this issue is more xenomai related than beagle...

Thank you for all your answers!

I do know why my sampling is to slow and I do know I am not really executing the code in the Xenomai domain. I was just struggling with finding alternative ways.

Ok, i will look into Xenomai data aquisition and mmap a little closer and only ask more Questions here if they are Beaglebone related.

Thank you for all your tips!

I have already subscribed to the mailing list, but I am not really struggeling with the Xenomai part ( at least at the moment :wink: ).
I know why my first try is too slow, but I just didn’t really know any alternatives.

I have tried the mmap approach today, but I am failing to even map the device correctly.
Do I map /dev/iio:device0 directly or /sys/bus/iio/devices/iio:device0/in_voltageX_raw ?
Or something else entirely?

I know I will have to read some tutorials and tryout some code for a while, since this is new to me.
But could you share that part of your code with me? (Or send me an Email: nflames92@googlemail.com)
That would be awsome!

Thanks again.

Felix

I might as well jump in here regarding the hardware side of things. Abd Halimeh mentioned, that you will be limited by "that ADC sub-system sample time is about 15 ADC clock cycles (at 24MHz (typ) it will be 41.6nSec per cycle) results into about 625nSec.

See Technical Reference for AM335x page 1152 (REV.F)"

So there you go. Your requirements should be quite feasible, not considering latent channel switching costs.

http://beaglebone.cameon.net/home/reading-the-analog-inputs-adc

Hi!

I am currently using my Beaglebone Black with the following KERNEL: 3.8.13xenomai-bone28.1 (including the ti_am335_adc driver).

Part of my Project is reading signals from an ADC and then processing them. I would love to use the onboard ADC for that, but I require to sample 5 channels @ 2 kHz each. (I have installed the Xenomai Kernel mainly for processing purposes but it might be useful for sampling as well :wink: )

My first simplistic try was to just read the voltage values from the files provided in “/sys/bus/iio/devices/iio:device0/in_voltage0_raw” .
This works fine so far, but is just not fast enough. ( I get about 500 sps on ONE channel)

Here is that part of my CODE:

`

for(;:wink: {

rt_task_wait_period(NULL); // Xenomai

now=rt_timer_read(); // Xenomai

// Work for the current period //

FILE *read = fopen ( “/sys/bus/iio/devices/iio:device0/in_voltage0_raw”, “r”);

fscanf (read, “%f”, &value);
float voltage = value * (1800.0/4095.0);
fprintf (write, “Value: %f Voltage: %f\n”, value, voltage);

printf(“Time since last turn: %ld,%06ld ms Value:%f Voltage: %f\n”,
(long)(now - previous) / 1000000,
(long)(now - previous) % 1000000, value, voltage);
previous = now;

fclose ( read );
fclose ( write );

}

`
(I know that leaving out the printf part and the voltage calculation increases performance, but still not enough)

Is there any other way reading the values, directly from the memory location or a register? Perhaps using mmap?
Or is there a Xenomai function for reading from an iio device? (I couldn’t find any…)

I have seen two interesting links about continuous sampling:

But i can’t get the generic_buffer.c from those sites to work correctly (as the trigger handling seems to be outdated), plus i might need to know a kind of “oneshot” read function anyway to mux the 5 channels i want to sample. Or is it possible to continuous sample multiple channels? The Driver guide says ‘no’, but the Author from the other link seems to have worked a lot on the adc Driver since.

I am currently trying to figure out the code in generic_buffer.c and the used iio_utils.h and the adc driver, but it is a lot of code and drivers are not my strong suit.
So far I think I need to setup the ADC to my needs (somewhat similar to generic_buffer.c) and then read from it somehow, but I am really struggling with that!

So now you know the state of my project :slight_smile: but to sum it up:

Are my demands (5 channels @ 2 kHz) even possible using only the on-board ADC?
And if yes how do i read the values fast enough? Any tips?

Reading the ADC values via sysfs isn’t going to be fast. Also, it doesn’t help to have a Xenomai kernel if you don’t use it. You have to write a Xenomai kernel driver and then create a user space app that uses the Xenomai API to communicate with the Xenomai kernel driver. Xenomai comes with several examples to help you get started. You could also look at Charles Steinkuehler’s LinuxCNC work where he uses nmap to control GPIO with the Xenomai kernel.

Regards,
John

@Fe wi

John is totally right, I’d like to add that you don’t have to mmap linux devices like those in /dev or via /sys but you have to map directly register,

e.g. in my case (the eqep)

... constexpr uint32_t eQEP1phAdd=0x48302180; // if you look at am335x TRM this is the base address of the eqep map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, (eQEP1phAdd`) & ~MAP_MASK );

`
Please note that I’m continuing to use the linux drivber to the initial configuration of the device…

Hi,

Which distribution are you using with Xenomai?

I’m trying to use Xenomai with Angstrom version download from beagleboard page (link: https://s3.amazonaws.com/angstrom/demo/beaglebone/Angstrom-Cloud9-IDE-GNOME-eglibc-ipk-v2012.12-beaglebone-2013.06.20.img.xz)

But when trying to install the kernel in the SD, I get some messages saying that there isn’t enough space in the SD…

ton@ubuntu:~/linux-dev$ sudo ./tools/install_kernel.sh
[sudo] password for ton:

I see…
fdisk -l:
Disk /dev/sda: 21.5 GB, 21474836480 bytes
Disk /dev/sdb: 3951 MB, 3951034368 bytes

lsblk:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 0 20G 0 disk
├─sda1 8:1 0 19G 0 part /
├─sda2 8:2 0 1K 0 part
└─sda5 8:5 0 1022M 0 part [SWAP]
sdb 8:16 1 3.7G 0 disk
├─sdb1 8:17 1 70.6M 0 part /media/BEAGLE_BONE
└─sdb2 8:18 1 3.3G 0 part /media/Angstrom
sr1 11:1 1 1024M 0 rom

Hi,

Which distribution are you using with Xenomai?

I'm trying to use Xenomai with Angstrom version download from beagleboard
page (link:
https://s3.amazonaws.com/angstrom/demo/beaglebone/Angstrom-Cloud9-IDE-GNOME-eglibc-ipk-v2012.12-beaglebone-2013.06.20.img.xz)

But when trying to install the kernel in the SD, I get some messages saying
that there isn't enough space in the SD...

ton@ubuntu:~/linux-dev$ sudo ./tools/install_kernel.sh

Don't use that script on an Angstrom install... Copy the boot files by hand...

-----------------------------
Trying: [/dev/sdb1]
Partition: [/dev/sdb1] trying: [vfat], [ext4]
Partition: [vfat]
Installing 3.11.0-rc1-armv7-d1 to /dev/sdb1

That kernel isn't going to boot on the beaglebone anyways..

Regards,

Hello Robert,

First of all, thankyou for your help and your quick response.

As you said, I was compiling the wrong kernel version, but now, I’m working with this kernel for BBB: https://github.com/cdsteinkuehler/linux-dev/tree/3.8.13-bone28-xenomai

I’ve tried with both Angstrom distributions for BBB (SD and eMMc version), whithout success.

Please, could you provide a little bit more detailed guide (Angstrom Image, SD size, kernel installation steps…) for getting Xenomai kernel working on BBB that runs Angstrom distribution?

Thanks for your help!

Ton

Hi,

try it with ubuntu image. It works for me.

Hi we fi

So how did it end up for you? I am having similar problem, would you care posting your solution?

Regards
Hernando

Hi we fi

So how did it end up for you? I am having similar problem, would you care posting your solution?

Regards
Hernando

Hi!

I am currently using my Beaglebone Black with the following KERNEL: 3.8.13xenomai-bone28.1 (including the ti_am335_adc driver).

Part of my Project is reading signals from an ADC and then processing them. I would love to use the onboard ADC for that, but I require to sample 5 channels @ 2 kHz each. (I have installed the Xenomai Kernel mainly for processing purposes but it might be useful for sampling as well :wink: )

My first simplistic try was to just read the voltage values from the files provided in “/sys/bus/iio/devices/iio:device0/in_voltage0_raw” .
This works fine so far, but is just not fast enough. ( I get about 500 sps on ONE channel)

Here is that part of my CODE:

`

for(;:wink: {

rt_task_wait_period(NULL); // Xenomai

now=rt_timer_read(); // Xenomai

// Work for the current period //

FILE *read = fopen ( “/sys/bus/iio/devices/iio:device0/in_voltage0_raw”, “r”);

fscanf (read, “%f”, &value);
float voltage = value * (1800.0/4095.0);
fprintf (write, “Value: %f Voltage: %f\n”, value, voltage);

printf(“Time since last turn: %ld,%06ld ms Value:%f Voltage: %f\n”,
(long)(now - previous) / 1000000,
(long)(now - previous) % 1000000, value, voltage);
previous = now;

fclose ( read );
fclose ( write );

}

`
(I know that leaving out the printf part and the voltage calculation increases performance, but still not enough)

Is there any other way reading the values, directly from the memory location or a register? Perhaps using mmap?
Or is there a Xenomai function for reading from an iio device? (I couldn’t find any…)

I have seen two interesting links about continuous sampling:

Read the latest updates:

http://beagleboard-gsoc13.blogspot.com/2013/09/success-at-last.html

Regards,
John

Try using an mmap() pointer to read the registers themselves: It is much, much faster. Of course, then you will be responsible for setting the right register bits to start conversions, wait until they are done, etc. You still may want to open the device driver so the power and clock is turned on for the ADCs. You may also need to disable the ADC interrupts so the device driver does not interfere with your user-space code.

But you still have a basic problem with doing this from user-space: Any system interrupt can take execution time and even run other threads when you need to be capturing your ADC samples. Linux is not suitable for real-time aps that need to run below several milliseconds latency, even if you write your own device drivers. This is why TI included the PRUs. Get your code running in user-space, and then move the critical part to one of the PRUs. They can run without interrupts to give you perfectly deterministic ADC sampling, so you could even run an FFT on the samples and get decent results.

Try using an mmap() pointer to read the registers themselves: It is much, much faster. Of course, then you will be responsible for setting the right register bits to start conversions, wait until they are done, etc. You still may want to open the device driver so the power and clock is turned on for the ADCs. You may also need to disable the ADC interrupts so the device driver does not interfere with your user-space code.

But you still have a basic problem with doing this from user-space: Any system interrupt can take execution time and even run other threads when you need to be capturing your ADC samples. Linux is not suitable for real-time aps that need to run below several milliseconds latency, even if you write your own device drivers. This is why TI included the PRUs. Get your code running in user-space, and then move the critical part to one of the PRUs. They can run without interrupts to give you perfectly deterministic ADC sampling, so you could even run an FFT on the samples and get decent results.

You can also use IIO with buffering. The linux-iio mailing list discussed several examples recently of ADC sampling in the mega samples per second.

Regards,
John

The best work I’ve seen so far on this is here:
https://github.com/VegetableAvenger/BBBIOlib
His adc is very fast (uses /dev/mem) - and it works.

Best work on this is here:

https://github.com/VegetableAvenger/BBBIOlib

Very fast adc, and it works.