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

Hi Walter!

A further “old dog” here. Sometimes I’m still working on my old Hades computer with 68060 CPU (loving that box).

In my house I’m using a BBB for a solar system running 24/7. It also controlls two valves (on/off, and four AC pumps in 16 power levels), connected to WLAN by an external USB-Stick. Most temperatures are comming from 1-wire sensors, but ADC is used to fetch samples from a high-temperature sensor on the roof/collector.

You should know that the onboard TSC_ADC_SS sometimes hangs, due to electromagnetical noice. In that case it allways measures/serves the same voltage, regardless of the changing input. There’s a way to unblock the subsystem by software. But the better solution is to spend some effort in a decoupled input circruitry.

In a new project I start the controller development on ARM, doing measurements by libpruio. Once the prove of concept is done, I migrate the controller loop to the other PRU for hard real-time capability. libpruio is perfect for that concept, since the measurements are available from both sides, ARM and PRU. All setup is coded only once (on ARM), and only the inner controller loop needs adaption (from ARM to PRU). In that adaption the controller usually gets much better, since you won’t repeat the bugs and pitfalls from the POC phase.

The most important initial decision is concerning the kernel driver to use. Drop rproc, and use uio_pruss driver instead. Then data exchange is pretty easy. Ie use DRam[0,1] for PRU-writing and SRam for ARM-writing. A simple and effective concept to avoid writing collisions (and pretty fast as well). uio_pruss driver provides pointers to that memory, while using rproc you’ve to find a solution by yourself.

Regards

That looks more like a compiler directive than a type in its own right
-- something forcing a pointer type to be a full address, and not a short
relative address. You may need to ask the source of the code what compiler
they were using?

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

  Well, since the PRU doesn't have asynchronous interrupts, using a
polling loop on the interrupt bits, that's probably reasonable -- though
I'd hope that RPMsg (since TI pushes it these days) should be usable
without a polling loop itself.

  Especially if you can test for an incoming message between actually
reading an ADC sample.

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.

  Which probably need to be transferred to the Linux side -- another
RPMsg message block? <G> Using the same channel as #3 below, just different
message contents (or same message for both?)

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.

  Ugh... Polling loop sucking up CPU cycles on an OS that already adds
potential process swapping when the loop eats all cycles of a quantum...

  Just feels like attempting a (blocking) read on an RPMsg channel would
be cleaner. Linux side waits for PRU to send some message without eating
cycles. However ...

https://www.kernel.org/doc/html/latest/staging/rpmsg.html

... seems to rely upon a callback function for return data on the channel.
Example is too brief to really understand the API -- which does not seem to
have explicit RECV (to complement the SEND calls). Makes it a touch
confusing to me... Is the callback "active' from the moment the channel is
created, or only when a SEND operation was performed (since the channel is
considered bidrectional, I'd expect the callback to be active from the
start -- so either side can act upon a message sent by the other).

Hello TJF

Drop rproc, and use uio_pruss driver instead. Then data exchange is pretty easy. Ie use DRam[0,1] for PRU-writing and SRam for ARM-writing. A simple and effective concept to avoid writing collisions (and pretty fast as well). uio_pruss driver provides pointers to that memory, while using rproc you’ve to find a solution by yourself.

Thanks, TJF! This sounds like good advice but tough tricks to learn! The more I think through the architecture of my solution, the more I realize the PRU code needs to do. In our lab evaluation setup, I need the voltages measured by the ADC for analysis to develop base algorithms for detecting the events from the sensors properly. But once we have that developed, in a production system, the PRU code will apply the logic to the data it is reading and probably should control the valves directly through with the GPIOs. Otherwise, if we pass the data to the ARM side, Linux could go off and service something else and leave the valves running when they shouldn’t be or not running when they should be. But startup parameters will be selected by the user, so I’m thinking the UI is on ARM and once it has the start-up information, it just passes key info to the PRU code and lets it go do its thing.

So what is the long-term support for the different options? I thought remoteproc was the way of the future and uio_pruss drivers were phasing out. We are on the front end of a new product/solution design and need to pay attention to ‘end of life’ type issues. (Of course, the solution must work too!)

Good to know about the EM interference. Theoretically, we should be in a low external EM noise environment, however, we are using the BBB Wireless! I had painful experiences with RF wreaking havoc on a system in graduate school.

I’ll look at that. I thought remoteproc was the way of the future so I was heading down that path. And if in production I don’t need to do a lot of data transfer, does it make sense to use uio_pruss/libpruio ( I don’t know much about these, it’s probably evident that I don’t know much about remoteproc either) ?

I’m working through this and learning a lot. But also realizing how much I have either forgotten or just never knew. So, can I get a quick primer on what this line of C code is doing?

HWREG(SOC_CM_WKUP_REGS + CM_WKUP_ADC_TSC_CLKCTRL) = 0x02;

Thanks!

Look for a registrer similar name to ADC clk Ctrl in TRM under the ADC section.

That’s looks like a C macro and it’s writing 0x02 to that register. Macro Probably defined in a header file.

the registers will have different offsets depending on ARM or PRU access

Perhaps revisit init code on ARM you had working and document every bit that’s important in ADC set-up and compare that to this PRU code.

Remember getting the Data out of PRU to ARM timings are important. I see you asked me about rproc that I don’t use Linux that was TJ comments.

I’m afraid the PRU was marketed to you as the answer by people that don’t understand your timing requirement. Lot’s of script kiddies and cookbook reader’s in this group few system engineer that actually read your intial post

Good luck

What’s really throwing me is the + between what looks like two macro values. Normally, we see the + on the right sign of the equals, right? Or am I forgetting something I used to know!?

I’m not a C guy (but prefering FreeBASIC for ligh-level and ASM for PRU code). Just guessing:

As lazarman mentioned HWREG is a preprocessor macro (perhaps using ARM in supervisor mode?)
SOC_CM_WKUP_REGS is the base adress of the wakeup registers in the control module
CM_WKUP_ADC_TSC_CLKCTRL is the offset for the TSC_ADC_SS clock control register

In oder to enable the TSC_ADC_SS the value 0x02 needs to be writen to that register. Afterwards you can read or write the subsystems registers. (libpruio additionally checks read acces to the ID register in order to validate that the subsystem is ready.)

After posting this, I realized this was a macro and that it was passing the sum of the two values. That seemed weird to me.
Thanks for the confirmations! It is coming back slowly!

Why? Take into account the ()s.

  From what I can tell, this is adding the ADC register offset to the
base address of the (?) wakeup register block, which is passed as parameter
to HWREG (no doubt some macro that sets up actual access to the SoC
registers and returns a pointer or some such), and then assigns 0x02 into
the register so indicated.

Walter

Your best bet.

Run your whole control loop on the PRU that’s as realtime as you get. Use a foreground background loop. Use the ARM like a PC with Linux to access the system via ethernet.

You could also run control on ARM without linux but this way you have all the resources of Linux to access the system.

This assumes your output’s from control loop are accessable from PRU.

The point is Linux can only run slow control loops and this way you don’t have to debug the delay.

This wasn’t obvious to me before as all the hard realtime systems I work on run an RTOS on ARM it has all the resources of Linux but cost $$$$$.

In our system we did that on the DSP the PID did the math on a fast DSP.

ARM is just a gateway to outside world.

Myself I’d debug the PRU with JTAG and CCS you can see exactly what’s going on and dump these registers from CCS.

Some people like printf but with a PRU based system you are essentially doing barebones.

There’s videos on PRU development doing this online.

Loading code via rproc and using printf is like burning and erasing an eeprom to test your changes. You wait 45 minutes for it to erase try your code and do it again.

Not for me.

Mark

Yeah, I woke up realizing what a dunce I was reading it the way I was. HWREG equates to (*((volatile unsigned int *) (x)))

We’re designing it the way you suggested. The nice thing is that basically the control logic has already been written in C on the ARM side. Now, I just need to get it ported to the PRUs and create the communications between the PRUs and a new ARM app that supervises everything from the user’s point of view. Meaning, taking inputs like start, stop and giving feedback by turning LEDs on and off. And it needs to take in some basic system configuration data from the cloud periodically that the PRU will consume to adjust it’s operation.

I am making progress. Part of my problem was using Cloud9 for development. That’s where all my ARM development has been so far. I have CCS installed on my Windows 10 machine and started working through TI’s PRU Hands On Labs. Unfortunately, the very first one doesn’t work because their document is written for CCS running on Linux. Step 6 says to delete the linker file and add it back with Project->Add Files but that’s grayed out. I’ve asked TI about that. But I got it to compile by writing on the Beagle using nano and compiling it there. I’ve ready a lot of TI documentation today and learned a lot.

Here’s one more thing I am struggling with though. It’s a mental block I think. I’m used to controlling GPIOs on the ARM side using sysfs. It appears that on the PRU, we use __R30 instead but I don’t understand how that works. I read through it this morning and it still isn’t sinking in. If anyone can help make this clearer, I’d appreciate it.

Nano isn’t best choice for polyglot applications. I’m using Geany (on PC exchanging source files via virtual file system), while I compile and test under LINUX on the BB.

My setup is a Windows 10 PC with a Black connected via USB or a Black wireless connected via wi-fi. So far I have done all the C development on the ARM side with Cloud9 and only occasionally use Nano. It’s met my needs just fine so far but this is getting more complex. I installed TI’s CCS but it (or TI) prefers to be on a Linux box apparently.

The TSC_ADC only has 8 channels. So why are there 16 STEP registers?

So I looked over the libpruio page and it looks great. My head’s spinning a bit between remoteproc, uio, and libpruio options but I’d like to try libpruio.
I don’t want to break remoteproc if I set up to use libpruio. Will that happen?

Also, I’m running Buster (version.sh) at the bottom of this post

The instructions refer to Jessie. Are the Debian packages referred to compatible with Buster? Here’s what I am referring to.

The easy way to benefit from libpruio is to install the Debian packages. They’re not in mainline, yet. So you have to add a PPA (Personal Package Archive) to your package management sources. On the default Debian operating system, edit the file sudo nano /etc/apt/sources.list and add the lines:

deb http://beagle.tuks.nl/debian jessie/ deb-src http://beagle.tuks.nl/debian jessie/

Then grep the keyring by (mind the ‘-’ character at the end)

wget -qO - http://beagle.tuks.nl/debian/pubring.gpg | sudo apt-key add -

Once prepared, you can update your package manager database

sudo apt-get update

debian@beaglebone:/$ sudo opt/scripts/tools/version.sh
git:/opt/scripts/:[b39ec679648a6be8f25f48bd1c9784c1fc5a0c46]
eeprom:[A335BNLT00C04417BBBK1847]
model:[TI_AM335x_BeagleBone_Black]
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-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-r61]
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.20210401.0-0~buster+20210401]
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
[ 66.835497] remoteproc remoteproc0: wkup_m3 is available
[ 67.240120] remoteproc remoteproc0: powering up wkup_m3
[ 67.240151] remoteproc remoteproc0: Booting fw image am335x-pm-firmware.elf, size 217148
[ 67.240404] remoteproc remoteproc0: remote processor wkup_m3 is now up
[ 69.894313] remoteproc remoteproc1: 4a334000.pru is available
[ 69.907897] remoteproc remoteproc2: 4a338000.pru is available
[15549.657580] remoteproc remoteproc1: powering up 4a334000.pru
[15549.665009] remoteproc remoteproc1: Booting fw image am335x-pru0-fw, size 30880
[15549.665035] remoteproc remoteproc1: header-less resource table
[15549.675909] remoteproc remoteproc1: Boot failed: -22
[15602.811891] remoteproc remoteproc1: powering up 4a334000.pru
[15602.812184] remoteproc remoteproc1: Booting fw image am335x-pru0-fw, size 30880
[15602.812202] remoteproc remoteproc1: header-less resource table
[15602.823804] remoteproc remoteproc1: Boot failed: -22
[15801.464252] remoteproc remoteproc1: powering up 4a334000.pru
[15801.464540] remoteproc remoteproc1: Booting fw image am335x-pru0-fw, size 30880
[15801.464559] remoteproc remoteproc1: header-less resource table
[15801.475947] remoteproc remoteproc1: Boot failed: -22
[15835.561165] remoteproc remoteproc1: powering up 4a334000.pru
[15835.561459] remoteproc remoteproc1: Booting fw image am335x-pru0-fw, size 30880
[15835.561478] remoteproc remoteproc1: header-less resource table
[15835.575362] remoteproc remoteproc1: Boot failed: -22
[15973.384568] remoteproc remoteproc1: powering up 4a334000.pru
[15973.384866] remoteproc remoteproc1: Booting fw image am335x-pru0-fw, size 30880
[15973.384884] remoteproc remoteproc1: header-less resource table
[15973.395805] remoteproc remoteproc1: Boot failed: -22
[15996.157221] remoteproc remoteproc1: powering up 4a334000.pru
[15996.157504] remoteproc remoteproc1: Booting fw image am335x-pru0-fw, size 30880
[15996.157523] remoteproc remoteproc1: header-less resource table
[15996.171335] remoteproc remoteproc1: Boot failed: -22
[16031.348941] remoteproc remoteproc1: powering up 4a334000.pru
[16031.349226] remoteproc remoteproc1: Booting fw image am335x-pru0-fw, size 30880
[16031.349244] remoteproc remoteproc1: header-less resource table
[16031.359886] remoteproc remoteproc1: Boot failed: -22
[26382.806806] remoteproc remoteproc1: powering up 4a334000.pru
[26382.807380] remoteproc remoteproc1: Booting fw image am335x-pru0-fw, size 94376
[26382.820350] remoteproc remoteproc1: registered virtio0 (type 7)
[26382.820370] remoteproc remoteproc1: remote processor 4a334000.pru is now up
dmesg | grep pru
[ 69.894313] remoteproc remoteproc1: 4a334000.pru is available
[ 69.894508] pru-rproc 4a334000.pru: PRU rproc node pru@4a334000 probed successfully
[ 69.907897] remoteproc remoteproc2: 4a338000.pru is available
[ 69.908098] pru-rproc 4a338000.pru: PRU rproc node pru@4a338000 probed successfully
[15549.657580] remoteproc remoteproc1: powering up 4a334000.pru
[15549.665009] remoteproc remoteproc1: Booting fw image am335x-pru0-fw, size 30880
[15602.811891] remoteproc remoteproc1: powering up 4a334000.pru
[15602.812184] remoteproc remoteproc1: Booting fw image am335x-pru0-fw, size 30880
[15801.464252] remoteproc remoteproc1: powering up 4a334000.pru
[15801.464540] remoteproc remoteproc1: Booting fw image am335x-pru0-fw, size 30880
[15835.561165] remoteproc remoteproc1: powering up 4a334000.pru
[15835.561459] remoteproc remoteproc1: Booting fw image am335x-pru0-fw, size 30880
[15973.384568] remoteproc remoteproc1: powering up 4a334000.pru
[15973.384866] remoteproc remoteproc1: Booting fw image am335x-pru0-fw, size 30880
[15996.157221] remoteproc remoteproc1: powering up 4a334000.pru
[15996.157504] remoteproc remoteproc1: Booting fw image am335x-pru0-fw, size 30880
[16031.348941] remoteproc remoteproc1: powering up 4a334000.pru
[16031.349226] remoteproc remoteproc1: Booting fw image am335x-pru0-fw, size 30880
[26382.806806] remoteproc remoteproc1: powering up 4a334000.pru
[26382.807380] remoteproc remoteproc1: Booting fw image am335x-pru0-fw, size 94376
[26382.814125] pruss 4a300000.pruss: configured system_events[63-0] = 00000000,00030000
[26382.814148] pruss 4a300000.pruss: configured intr_channels = 0x00000005 host_intr = 0x00000005
[26382.820370] remoteproc remoteproc1: remote processor 4a334000.pru is now up
[26382.891327] virtio_rpmsg_bus virtio0: creating channel rpmsg-pru addr 0x1e
[26382.946821] rpmsg_pru virtio0.rpmsg-pru.-1.30: new rpmsg_pru device: /dev/rpmsg_pru30
dmesg | grep pinctrl-single
[ 0.942975] pinctrl-single 44e10800.pinmux: 142 pins, size 568
dmesg | grep gpio-of-helper
[ 0.956726] gpio-of-helper ocp:cape-universal: ready
lsusb
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
END

Walter

Just trying to be a guiding light. Why not get the control loop working on PRU first.?

Your being pulled into to many directions. I know how that feels I’ve been there.

Once the ADC and output works worry about getting Data over to ARM.

Too many new things will kill you. Master the PRU coding first and on second thought forget the CCS JTAG suggestions I gave.

I’m getting dizzy reading all the suggestions you received.

What’s working what’s the architecture?

Too many chef’s the soup will boil away.

Thanks :+1:

Mark