Periodic delay reading analog inputs with C, will PRUs solve it and is it worth it?

I’m running a BBB Wireless. The OS version, version.sh output, etc. are below.

We’re sensor outputs on AIN4 and AIN0 with C using the code below. The intervals between readings is fairly constant at about 350 uS but there is periodically, about every 25 readings, a reading interval of between 4000 and 4800 uS. Our application can’t accept this.

  1. Is there some process the OS is doing periodically to cause this delay and can we eliminate it?

  2. Would the PRUs solve this problem and is it worth the effort to learn and apply them?

Thanks for your insights, advice, and help!

Code is in C…and we run it from the Cloud9 IDE.

void ReadDetectors(long TimeToRead, int CycleNo)

{

// initialize local variables

char Det1Volts_str[1024][6];

char Det2Volts_str[1024][6];

long elapsed_us;

int index;

index = 0;

struct timeval tv_start; //start time hack

struct timeval tv_now; // current time hack

long total_elapsed_time_us[1024];

long start_secs;

long last_secs;

long last_usecs;

int KeepReading = 1;

FILE* fDet1 = fopen("/sys/bus/iio/devices/iio:device0/in_voltage4_raw", “r”); // top sensor, blue wire

FILE* fDet2 = fopen("/sys/bus/iio/devices/iio:device0/in_voltage0_raw", “r”); // bottom sensor, purple wire

// get time at entry into the routine

gettimeofday(&tv_now,NULL);

start_secs = tv_now.tv_sec;

last_usecs = tv_now.tv_usec;

elapsed_us = 0;

total_elapsed_time_us[index] = 0;

while (KeepReading)

{

// update elapsed time

gettimeofday(&tv_now,NULL);

if (tv_now.tv_sec >= last_usecs)

{

elapsed_us = tv_now.tv_usec - last_usecs;

}

else

{

elapsed_us = (1000000 - last_usecs) + tv_now.tv_usec;

}

total_elapsed_time_us[index] = (tv_now.tv_sec - start_secs)*1000000 + elapsed_us;

if (total_elapsed_time_us[index] > TimeToRead) KeepReading = 0;// read detectors

fread(&Det1Volts_str[index], 5, 1, fDet1);

fread(&Det2Volts_str[index], 5, 1, fDet2);

rewind(fDet1);

rewind(fDet2);

index += 1;

if (index >1024) KeepReading = 0;

}

}

debian@beaglebone:/var/lib/cloud9$ uname --a

Linux beaglebone 4.19.94-ti-r42 #1buster SMP PREEMPT Tue Mar 31 19:38:29 UTC 2020 armv7l GNU/Linux

debian@beaglebone:/$ sudo opt/scripts/tools/version.sh

git:/opt/scripts/:[b39ec679648a6be8f25f48bd1c9784c1fc5a0c46]

eeprom:[A335BNLTBWA52027BBWG0227]

model:[TI_AM335x_BeagleBone_Black_Wireless]

dogtag:[BeagleBoard.org Debian Buster IoT Image 2020-04-06]

bootloader:[microSD-(push-button)]:[/dev/mmcblk0]:[U-Boot 2019.04-00002-g07d5700e21]:[location: dd MBR]

bootloader:[eMMC-(default)]:[/dev/mmcblk1]:[U-Boot 2018.03-00002-gac9cce7c6a]:[location: dd MBR]

UBOOT: Booted Device-Tree:[am335x-boneblack-uboot-univ.dts]

UBOOT: Loaded Overlay:[AM335X-PRU-RPROC-4-19-TI-00A0]

UBOOT: Loaded Overlay:[BB-ADC-00A0]

UBOOT: Loaded Overlay:[BB-BBBW-WL1835-00A0]

UBOOT: Loaded Overlay:[BB-BONE-eMMC1-01-00A0]

UBOOT: Loaded Overlay:[BB-HDMI-TDA998x-00A0]

UBOOT: Loaded Overlay:[BB-I2C2-RTC-DS3231]

UBOOT: Loaded Overlay:[BB-W1-P9.12-00A2]

kernel:[4.19.94-ti-r42]

nodejs:[v10.15.2]

/boot/uEnv.txt Settings:

uboot_overlay_options:[enable_uboot_overlays=1]

uboot_overlay_options:[uboot_overlay_addr4=/lib/firmware/BB-W1-P9.12-00A0.dtbo]

uboot_overlay_options:[uboot_overlay_pru=/lib/firmware/AM335X-PRU-RPROC-4-19-TI-00A0.dtbo]

uboot_overlay_options:[enable_uboot_cape_universal=1]

uboot_overlay_options:[dtb_overlay=/lib/firmware/BB-I2C2-RTC-DS3231.dtbo]

pkg check: to individually upgrade run: [sudo apt install --only-upgrade ]

pkg:[bb-cape-overlays]:[4.14.20200403.0-0rcnee0~buster+20200403]

pkg:[bb-wl18xx-firmware]:[1.20200322.0-0rcnee0~buster+20200322]

pkg:[kmod]:[26-1]

pkg:[librobotcontrol]:[1.0.4-git20190227.1-0rcnee0~buster+20190327]

pkg:[firmware-ti-connectivity]:[20190717-2rcnee1~buster+20200305]

groups:[debian : debian adm kmem dialout cdrom floppy audio dip video plugdev users systemd-journal bluetooth netdev i2c gpio pwm eqep remoteproc admin spi iio docker tisdk weston-launch xenomai cloud9ide]

cmdline:[console=ttyO0,115200n8 bone_capemgr.uboot_capemgr_enabled=1 root=/dev/mmcblk0p1 ro rootfstype=ext4 rootwait coherent_pool=1M net.ifnames=0 lpj=1990656 rng_core.default_quality=100 quiet]

dmesg | grep remote

[ 11.374832] remoteproc remoteproc0: 4a334000.pru is available

[ 11.397684] remoteproc remoteproc1: 4a338000.pru is available

[ 58.421621] remoteproc remoteproc2: wkup_m3 is available

[ 58.879676] remoteproc remoteproc2: powering up wkup_m3

[ 58.879705] remoteproc remoteproc2: Booting fw image am335x-pm-firmware.elf, size 217168

[ 58.879995] remoteproc remoteproc2: remote processor wkup_m3 is now up

dmesg | grep pru

[ 11.374832] remoteproc remoteproc0: 4a334000.pru is available

[ 11.375013] pru-rproc 4a334000.pru: PRU rproc node pru@4a334000 probed successfully

[ 11.397684] remoteproc remoteproc1: 4a338000.pru is available

[ 11.397867] pru-rproc 4a338000.pru: PRU rproc node pru@4a338000 probed successfully

dmesg | grep pinctrl-single

[ 0.929984] pinctrl-single 44e10800.pinmux: 142 pins, size 568

dmesg | grep gpio-of-helper

[ 0.943137] gpio-of-helper ocp:cape-universal: ready

lsusb

Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

END

Hi!

Check out libpruio for easy and reliable ADC measurements with accurate timing. I guess RB (ring buffer) mode is your way to go.

Regards

Thanks! I believe you are right and I’ve already started working with this. I will check out libpruio. I’m starting to believe it isn’t quite as difficult as a I thought it might be.

Maybe you can answer a quick, related question.

I’m looking at some example code and there references to ADC_TSC. I realize this is a reference to the Touchscreen controller which provides the ADC functionality. And I suspect this refers to the base memory address of the chip but I cannot find where that is defined in any header files anywhere. It would help me to follow these examples if I knew where that reference was.

Unfortunately, too, the examples are Python and I’m not a Python programmer. I work in C. So I’m having to dig through this and learn some Python at the same time. Not a bad thing but time consuming!

Walter

I’m looking at some example code and there references to ADC_TSC. I realize this is a reference to the Touchscreen controller which provides the ADC functionality. And I suspect this refers to the base memory address of the chip but I cannot find where that is defined in any header files anywhere. It would help me to follow these examples if I knew where that reference was.

Find high-level code (FreeBASIC) in files src/pruio/pruio_adc.[bas|bi] and low level code (assembler) in file src/pruio/pruio_adc.p.

Unfortunately, too, the examples are Python and I’m not a Python programmer. I work in C. So I’m having to dig through this and learn some Python at the same time. Not a bad thing but time consuming!

Python examples are in folder src/python. FreeBASIC examples are in folder src/examples. And C examples are in folder src/c_examples. Find an overview at

https://users.freebasic-portal.de/tjf/Projekte/libpruio/doc/html/ChaExamples.html

Regards

Linux is not a real-time operating system (OS) in and of itself. … “The idea is to run critical applications like the control loop on VxWorks and then run non-deterministic analytics on Linux.

Hard realtime apps like closed loop positioning used in pressing plants,automation,fighter planes etc etc don’t use Linux. Determinism required by safety critical systems certified by FAA would require you found delay measured it to calculate worst case as well as validated software.

https://www.automationworld.com/products/control/blog/13318614/clarifying-the-linux-real-time-issue#:~:text=Linux%20is%20not%20a%20real,OS)%20in%20and%20of%20itself.&text=%E2%80%9CThe%20idea%20is%20to%20run,non%2Ddeterministic%20analytics%20on%20Linux.%20in%20and%20of%20itself.&text=%E2%80%9CThe%20idea%20is%20to%20run,non%2Ddeterministic%20analytics%20on%20Linux.)

What makes the Linux scheduler seem unpredictable?
[

What makes the Linux scheduler seem unpredictable?

The question refers to the output of a multi-threaded application, where each thread merely prints its ID (user assigned number) on the standard output. Here all threads have equal priority and com…

](https://unix.stackexchange.com/questions/65969/what-makes-the-linux-scheduler-seem-unpredictable)

  1. I say no depends on how much delay is acceptable there are buses between shared memory etc it will improve over using ARM.

Ideal situation is a barebone app designed with minimal interrupt latency followed by an RTOS that’s guaranteed to meet latency specs especially one certified by FAA or FDA of course these are expensive

Understood. Our application won’t require FAA or FDA approval but it definitely needs the more deterministic performance of a bare bones app so I’m heading in the direction of the ADC being read by a PRU program and communicating back to the ARM for other processes (UI, reading other non-time-sensitive inputs, etc.).

I’d share your timing requirement needs I’ve seen people get help in here before after doing this. search group. That way you don’t spend time trying to meet a goal not attainable on PRU.

Thanks, I’ll check that out!

We are still experimenting but our preliminary calculations lead us to believe a reading every 50 microseconds will be sufficient. We can probably get by with 100 microseconds if we have to but I think the BBBw can easily sample at this rate, right?

WC

Happy to help; I have worked some with these examples, so feel free to reach out if you have any questions!

Pierrick

Hi

??but I think the BBBw can easily sample at this ???rate, right?

Asking about ARM/linux side ? or

PRU side

Polling or Interrupt?

Explain you delay details at least delay duration and what your app does with data would help.

The calculation I mentioned seeing people reply about were on the PRU.

Keep in mind there’s overhead to get Data from PRU to ARM.

The timing becomes critical when you need to react from the data you read quickly. If that’s not the case your just talking about how many sample’s you use to control right? That’s why I would expand a bit on your application.

I’ve worked on stuff that read and reacted in 10 microseconds closed loop plastic pressing for example that was easily achieved in 1988 microprocessor speeds

I’ve also worked on control loops that ran faster than 1 micro second on a similar processor OMAP L138 in that case the code ran on the DSP I forget the clock speed. In this case the motor control ran every 100 nanosecond I believe but it’s used TI RTOS.

If the delay on Linux isn’t acceptable add some details I mentioned hopefully the two guys who calculated PRU latencies will reply or I will find that post if possible.

Again it’s probably highly likely even a polled PRU app can read Data quickly dependant on conversation rates time I’m assuming you need the ARM to get Data in 100 microseconds?

I’m not sure the transfer rate between PRU and ARM us determistic if linux is used .

Hopefully this makes sense if not ask but I’d follow up with more detail

Mark

Well, the SoC reference (SPRS717J) indicates that the ADC should be
capable of 200K samples per second. If I haven't flubbed the math, that
appears to come down to one sample every 5us.

  As I recall, the PRU runs on a 200MHz clock, and PRU instructions, for
the most, run in one clock cycle. Should be enough cycles per sample to
handle processing <G>

  Of course, it may matter how you have the ADC programmed.

We are controlling fluid flow by controlling two valves. (I am being a bit obscure because there are some proprietary processes involved.)

We start the flow and then read two sensors that are ‘watching’ the fluid for an event. There is an event ‘start’ in the fluid and then a following event ‘end’ that we determine by watching the values from the sensors. I believe this ‘watch’ part will be handled by the PRU firmware. When the event is over, the valves are turned off. We believe that the ARM will handle the valve control and UI. The C code running under Linux has completely missed events because of its non-deterministic operation.
The communication between ARM and PRU programs should consist of

1 - start / stop reading the sensors - considering just flipping a bit in a memory location both can access for this. ARM will have write access, PRU will just monitor it. (I don’t really know how to do this yet. Still learning.)
2 - raw data collected about the event by the PRU doesn’t really have to be accessed by the ARM in normal operation. (For R&D, we’d probably like to have it though.) Every sensor reading has to have its corresponding timestamp but all this data can be tossed once it is consumed. The PRU will need to be able to do some statistics on it like mean, mode, std. dev.
3 - if conditions are met, the PRU needs to tell the ARM. considering just flipping a bit in a memory location the PRU can write to and the ARM can only read from for this.

The valve on/off can handle delays of 50 ms with no issue at all.

(I’m an old dog learning new tricks. Years ago my hands-on experience was with Motorola 68xx and 68xxx series processors so I was comfortable writing directly to memory locations, registers, etc. It took me a while on returning to this to get comfortable with sysfs but I definitely see the limitations in projects like this one! Thanks for your patience and help!)

Thanks Dennis. That is in sync with my research.

When I try to run this in Cloud9, I’m getting this error. I’m not sure where type __far is defined but apparently I’m missing that definition. I have to copy pru_cfg.h over to /usr/include manually to get this far. Maybe there are other include files that are missing that it hasn’t warned me about? Sorry be such as neophyte!

/usr/include/pru_cfg.h:242:10: error: unknown type name ‘__far’
volatile __far pruCfg CT_CFG attribute((cregister(“PRU_CFG”, near), peripheral));
^~~~~
/usr/include/pru_cfg.h:242:23: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘attribute’ before ‘CT_CFG’
volatile __far pruCfg CT_CFG attribute((cregister(“PRU_CFG”, near), peripheral));
^~~~~~
PRU_AnalogReadfromGT.c:15:10: fatal error: pru_intc.h: No such file or directory
#include <pru_intc.h>
^~~~~~~~~~~~
compilation terminated.
make: *** [/var/lib/cloud9/common/Makefile:215: /tmp/cloud9-examples/PRU_AnalogReadfromGT.o] Error 1

Plenty of data Walter thanks.

You could write some linux code that reads Data from from PRU ram( I’m not sure if there’s several ways to get Data beyond remote messaging and reading the shared ram directly) factor in delay for new sample’s to be updated from ADC at least you could ensure that works in your time frame.

If that seems OK

Then use examples for PRU I’m skeptical about needing to use assembler it’s not the PRU read time that’s a bottleneck.

I’d be interested in seeing that PRU to ARM transfer rate it should not take alot of time but if Linux is still interfering beyond your needed specific time period you will only have one choice but to find what’s exactly doing this delay and mitigation of that will be your only hope.

Typically a proof of concept fesigh verification like this is always wise.

If using this chip is your only option you’d have one option left but I don’t think you would like it.

And I’m already well known for suggestioning that option on ARM so I’m not going to mention it.

I do understand the value of having a feature rich OS on ARM that’s royalty free but I’ve been lucky on the 50 or so projects I’ve worked on this wasn’t the issue.

Another project was a Diesel engine controller they did a DVT phase first. Design verification Test

I wish I could help on Linux side but I can’t there’s plenty of people in here using Linux so I think you can get more ideas and help.

Not Sure how many have used Linux in actual hard realtime systems.

Your application doesn’t sound extremely hard realtime so be interested in seeing this have a happy ending.

It’s a fairly simple application really and that’s what makes it frustrating! We’ll get there! I learn something every day and that’s just fine with me.

I believe you will Walter

I just thought it prudent to mention doing some designs validation coding first. I do think the the through put coding could be done quickly on ARM . If you’re doing the other code functions on PRU you mentioned your going to face the learning curve anyway you asked about. So you might then try some tests using ADC PRU.

Otherwise I’d do the ARM part first

Usually there’s always away to shoe horn something.

Regards

Mark

For my project that used a PRU to sample the onboard ADC, I relied very heavily on the TI PRU_ADC_onChip example code, along with Mark Yoder’s most excellent PRU cookbook. With those two resources, you should be able to do what you’re hoping to do.

Good luck!
df

I would not be nearly as far along if I did not have Mark Yoder’s PRU Cookbook! I’ll take a look at the TI documentation.

Right now, I’m working with some examples from GA Tech and the compiler running in Cloud9 IDE is balking at a __far definition in pru_cfg.h. I’m searching the GCC documentation and web for some fix for this. I suspect there must be a compiler switch to cause it to accept these but I don’t know what it is or if that’s even the solution. Any help would be appreciated!

Walter