PRU direct to McSPI?

Hi all.

I’m trying to cram a lot of realtime work onto a single BBB, and things are getting tight.

I’ve fully consumed PRU1 and I’ve got a lot of realtime work left to do.

One of the things that I need to do is to get a bunch of ADC readings. Specifically I need to get 5 12-bit readings on a 31250Hz sample rate. (31250Hz is not strictly necessary. 31250Hz is in the 20kHz to 60kHz range and is easy to get to off the 200MHz PRU clock, but really anything between 20kHz and 60kHz will do fine)

I’ve implemented this on PRU0 by bit banging SPI to a couple ADCs with 300ksps rates. It works just fine, and I even have a few spare cycles on PRU0 to mess with…but not really enough.

The other thing that I need to do is implement my control algorithm which takes the values from the ADC, does its thing, and then feeds values to the program running on PRU1. This also has to happen in realtime and requires some floating point (or quasi-floating-point) calculations. If I’m bit banging my ADCs, then PRU0 just doesn’t have enough spare cycles to get the job done. So, I’m looking for options.

Based on my reading it seems as though I ought to be able to drive the am335x hardware SPI modules directly from the PRU by reading and writing to the McSPI registers via the constants table. The fact that these are pointed to by the constants table indicates to me that TI was figuring that people would do this all the time! The hope is that this would take a big load off my PRU0 and very likely free it up enough to run my calculations. Nice trick, but I have not been able to dig up any examples of this PRU->McSPI online.

I’m also not sure what kind of determinism I could hope for with such a scheme. It looks like the way from the PRU to the MCSPI is over the L4 buss and so read latencies are reported to be 34 PRU cycles… best case… no promises. This is perfectly fine if I can run the McSPI continuously (aka regularly), which appears to be possible, but not clearly straightforward.

The not straightforward bit seems to be channelling the interrupts that are generated by the McSPI to the PRU so that the PRU can know when to go and pick up the new data. It appears that this signaling would have to be done through the interrupts because if you have to burn 34 cycles for every register poll you make, then you would again consume a big portion of your PRU cycles and things don’t look much better than for bit banging.

Anyone out there tried this sort of thing? Is this a good/bad idea?

The other option I have is to install the PREEMPT_RT patch and run some of the realtime stuff in the host program, and leave PRU0 bit banging the ADCs. That would likley get me more horsepower in the end, but I am way (way!) more familiar with the PRU’s than the linux kernel, and so I am rather intimidated by that path… and I haven’t been able to any docs that make it look straightforward. I have found some stuff that make PREEMPT_RT sound possible, but with a big old learning curve to climb. I should say that if I’m bit banging my ADCs and doing the calculations on the host, then I don’t need to turn my calculations around on 20kHz. Turning a batch of calculations every 1kHz would be just fine.

Suggestions much appreciated.



Suggestion. Get another external embedded chip, board, or whatever, and let it handle some of the work. Then just communicate samples, or data via CANBUS, UART, or which ever communication protocol works best for you.

Why not use edma to capture your ADC samples? Look at Starterware examples on how to do this. The same ARM code can be compiled with the PRU C compiler and you only need to make a few changes to make this work. Look on github for Starterware code that has been modified to run on the PRU.


Hi Bill,

I’m thinking of trying something similar and was wondering how successful you were or decided to try something else?


I’m currently traveling so I don’t have access to my desktop, but the Starterware examples have everything you need. Start with the ADC example, which is an interrupt based solution. Use the mcspi-edma example to learn how to use EDMA and then pull the EDMA portions and add those to the ADC example. Disable ADC interrupts and enable dma events. I also pulled from the cache example (I don’t remember the project name), but this included MMU setup and also enabled the data and instruction cache. If you test the GPIO example, and reduce the delay to 1, the GPIO toggle at a few KHz, but if you enable MMU, data and instruction cache, the speed increases to 12MHz, so MMU and Cache are important.

That is all I can remember. Without the MMU and Cache enabled, you will get FIFO overflows.