BeagleBone Black programmable realtime unit (PRU) Hello World – Part 2


In this part of the series I’m going to show you how to write a very basic C Host-Application that will run a program on the PRU of the BBB. This part of the series assumes, that you have basic C skills. I’ll also include some useful links to the official ti API. Everything can be done with the tools, that come with the BBB, no Code Composer Studio (CCS) oder Eclipse needed, but you can use them, if you want to.

New kernel versions

Newer versions of the linux kernel that runs on the BBB seem to use a different driver for accessing the PRU and the memory. If you are running into issues, for example when you can’t compile or run PRU applications, consider downgrading to an older version (3.8 seems to work fine) or take a look at this great guide, which will show you, how to disable the newer drivers and use the older, simpler ones, instead.

The Host-Application

As I described above, we will need two seperate applications for this example. The host application will be written in C, using libraries provided by ti. Other libraries for other languages are available (like PyPRUSS for python).

In this hello world example the host-application does just the minimum that is needed to load a program on the PRU and execute it:

#include <stdio.h>
#include <prussdrv.h>
#include <pruss_intc_mapping.h>

static int cleanUp(void)
        // Disable PRU 0
	prussdrv_exit ();

	return 0;

int main (void)
	// Initialize structure used by prussdrv_pruintc_intc
	// PRUSS_INTC_INITDATA is found in pruss_intc_mapping.h
	tpruss_intc_initdata pruss_intc_initdata = PRUSS_INTC_INITDATA;
        // Initialize the PRU Interrupt System
	prussdrv_init ();
	prussdrv_open (PRU_EVTOUT_0);
	// Load and execute the PRU program on the PRU (PRU 0)
        // For PRU 1 write a 1 instead of the 0 as the first
        // parameter
	prussdrv_exec_program (0, "./pwm.bin");

        // Now wait here until the PRU messages the host
        // That it finished executing the program
	int n = prussdrv_pru_wait_event (PRU_EVTOUT_0);
	printf("Pru ended. Event: %i\n", n);
	// Disable PRU
	return cleanUp();

The code is pretty simple and can also be found in ti’s API.
Now compile the above code with:

gcc pwm.c -lprussdrv -lpthread -o pwm

This will create an executable output file called pwm.

The PRU Application

Now let’s look at the program, that will run on the PRU. It is written in ARM assembler and I will compile it with pasm. I know, that pasm will not longer be maintained by ti, but it works and is an easy to use and light-weight tool for compiling, so I’ll stick to it as long as possible.

// Origin 0 defines the start of the program in the
// PRU's instruction RAM, entrypoint is for debugging
.origin 0
.entrypoint START

// The following macro defines a very simple delay function
// Because most (but not all!) instructions in the PRU takes 5ns,
// we can wait quite precisely, but you could
// adapt this DELAY_NS function, if you needed even more
// accuracy.
.macro DELAY_NS
.mparam	wait_number
    MOV r10, ((wait_number/10)-5)
        SUB r10, r10, 1
        QBNE DELAY, r10, 0
        // This is obviously not the nice way,
        // because you can easily miss r10 reaching 0,
        // but it will work for this simple example.

// This macro sets an output pin low,
// then waits for the LOWTIME (in ns)
// and then sets the pin high and waits
// again for HIGHTIME nanoseconds.
.macro SEND_R30_5_PULSE
	CLR    R30.t5
	SET    R30.t5

// Define constants for host notification
#define PRU0_R31_VEC_VALID    32
#define PRU_EVTOUT_0          3

// Entrypoint
        // Store 100000 to register 0
        // This value will be used for a loop
	MOV    r0, 100000
        // Jump to the label called 'RUN'

// This section sends out one pulse,
// then subtracts 1 from the loop counter (r0)
// and repeats this until the loop counter is 0
// the program is ended afterwards
        // Send out one pulse
	SEND_R30_5_PULSE    18450, 26540
        // Subtract 1 from the loop counter
	SUB     r0, r0, 1
        // Jump to 'RUN' if the loop counter is not
        // equals to 0
	QBNE	RUN, r0, 0
        // Jump to 'END' if the loop counter is 0
	QBEQ	END, r0, 0

// Notifies the Host-Program that the PRU-Application
// has finished and halts the PRU (Don't forget to
// halt the PRU at the end of your code, otherwise
// it could stall and you'll need to restart your BBB
// to use it again!)
	MOV     R31.b0, PRU0_R31_VEC_VALID | PRU_EVTOUT_0

This looks like a lot of code on the first sight, but I’ve tried my best commenting it properly, so the most of it are actually comments. Just one thing: By setting the bit 5 of register 30 high or low (CLR R30.t5 and SET R30.t5), the output pin (P9_27), we defined in the device tree overlay, gets high or low. This is possible because of the PRU’s so called magic registers, where R30 is for outputs and R31 is for inputs (Take a look at the pinmux table I showed you in the last part. What name does the pin have in mode5?). You can get more information about these in the TRM.

If you are completely new to assembly programming, the instructions might be new for you, but don’t worry, the PRU has a rather small instruction set. You can find a link with all available instructions at the end of this article!

Unfortunately the default instruction set doesn’t include a NOP, which could be used to wait 5ns. However you can easily create a macro yourself:

 .macro NOP
    ADD r21, r21, 1

However the NOP is not really needed for this example.
Now you just have to compile the code from above:

pasm -b pwm.p

This will create a little endian encoded file, that the host-program can send to the PRU and execute it.

The output

So let’s run the program and look at the results:


You can make the output visible with an oscilloscope:


Or by using an LED on a breadboard, or by adapting the above example, so that the output goes to one of the USR-LEDs on the BBB, instead of a GPIO-Pin!

Further steps

You could change the above code to output the signal directly to one of the BBB User-LEDs. You could also build your own wave-form generator with variable frequencies by using the BBB analog inputs. Be creative!

Hopefully I got you up and running with the PRU, so you can start using it in your own projects! Post your PRU project idea in the comment section and share it with other readers!

Further readings

ti Linux Application Loader API
ti PRU-Assembler instructions (API)
A good article about ARM-Assembler
Official ARM assembly instructions page (API)
Another PRU tutorial

Table of contents

Part 0 – Introduction
Part 1 – About device trees and overlays
Part 2 – Programming (You are here)



19 thoughts on “BeagleBone Black programmable realtime unit (PRU) Hello World – Part 2

      1. I really like your articles. I created an account on wordpress just to follow you lol, so you better keep up the good work! 😀

        Liked by 2 people

      2. Wow that’s very kind! You can also follow us just by entering your email address, you’ll then get email updates without having a wp account. Cheers!


    1. A macro is a series of instructions you can easily place in different lines of your code. They help you to organize your code. So they are sort of like methods (the can have methods too, but no return values), but as far as I know when you invoke the macro, the macro’s code is simply substituted to the position you called it at. However they help keeping your code organized.
      Read more about macros here:

      Liked by 1 person

Leave your two cents, comment here!

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.