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

Introduction

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. Code Composer Studio (CCS) oder Eclipse are not needed 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 and it uses libraries provided by ti. Other ones for different 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_pru_disable(0);
  prussdrv_exit ();

  return 0;
}
int main (void)
{
  // Initialition 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);
  prussdrv_pruintc_init(&pruss_intc_initdata);

  // 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 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. Compile the code with:

gcc pwm.c -lprussdrv -lpthread -o pwm

This will create an executable output file called pwm.

The PRU Application

Now let’s discuss 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 it’s an easy to use and light-weight tool for compiling, so I’ll use 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 take 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)
  DELAY:
    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.
.endm

// 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
.mparam LOWTIME, HIGHTIME
  CLR    R30.t5
  DELAY_NS LOWTIME
  SET    R30.t5
  DELAY_NS HIGHTIME
.endm

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

// Entrypoint
START:
  // Store 100000 to register 0
  // This value will be used for a loop
  MOV    r0, 100000

  // Jump to the label called 'RUN'
  QBA    RUN

// This section sends one pulse
// then subtracts 1 from the loop counter (r0)
// and repeats this until the loop counter is 0
// the program terminates afterwards
RUN:
  // 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
  // equal 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!)
END:
  MOV     R31.b0, PRU0_R31_VEC_VALID | PRU_EVTOUT_0 HALT 

At first, this looks like a lot of code but I tried my best commenting it properly. 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 set 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 assembler 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
.endm

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 take a look at the results:

./pwm

You can make the output visible with an oscilloscope:

pru_wavegen_output

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!

Where to go from here?

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)

comment-banner

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

  1. Really nice article! You earned a new follower. Really looking forward to seeing more great tutorials and especially projects/builds!

    Liked by 3 people

      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

    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:

      http://processors.wiki.ti.com/index.php/PRU_Assembly_Advanced_Topics#Using_Macros

      Liked by 1 person

Leave your two cents, comment here!

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

WordPress.com Logo

You are commenting using your WordPress.com 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.