Andrew B. Wright, S. M. ’88, Ph. D.
June 9, 2018
In “Hello, world!” the first major debugging tool for the PRU was developed. Having communication through the remoteproc driver allows the host and the PRU to pass information back and forth. This is useful to provide debugging information and basic flow control of the PRU.
In embedded applications, the next debugging tool is to toggle a digital output. The presence of a toggled bit allows the user to see timing information, especially when using real-time loops.
The 4.1+ kernel has seen significant changes in how the cape manager system works. A universal cape device tree (cape-universaln) is loaded on boot time in preference to individually designed devices trees. It uses the config-pin utility to choose amongst the options defined in that overlay.
Determine what the pin can do with
config-pin -l P8_12
There should be these lines:
Function if cape loaded: default gpio gpio_pu gpio_pd pruout qep
Function information: gpio1_12 default gpio1_12 gpio1_12 gpio1_12 pr1_pru0_pru_r30_14 eQEP2A_in
If you want to test the pin with the arm, you should configure it to use the function gpio
config-pin P8.12 gpio
The gpio ID is computable from the gpio1_12 line. Take the 1, multiply by 32 and add to 12. The correct gpio will be 44.
Determine specific information for the pin with
config-pin -q P8.12
You should see the response:
P8_12 Mode: gpio Direction: in Value: 0
Look for the entry in the file system:
There should be a directory gpio44.
Change the direction by typing
echo “out” > /sys/class/gpio/gpio4/direction
and check the direction with
config-pin -q P8.12
You should see the response:
P8_12 Mode: gpio Direction: out Value: 0
Plug a voltmeter into GND and into P8-12 on the Beaglebone with a range around 5 v.
echo “1” > /sys/class/gpio/gpio44/value
and the voltmeter value should change to about 3.3 v.
echo “0” > /sys/class/gpio/gpio44/value
and the voltmeter should return to 0 V.
To gain PRU access to P8.12, type
config-pin P8.12 pruout
and check with
config-pin -q P8.12
The lower 16 bits of __R30 are connected to general purpose output (gpo). Toggling bit 14 of __R30 will toggle P8-12 on the Beaglebone connector.
The code to perform communications with the remoteproc system has been split into a separate file, pru_comm.c. This file will be used for every project after this. It’s good practice to put the function prototypes in a header file, which will be done in a subsequent project.
The remoteproc communication flow was redesigned to work with a real-time loop (see the next blog for real time loop). In the previous example, there are a number of steps where the program waits for the system to do something. These kinds of steps can lead to an infinite wait if something is not quite right. Should your PRU be connected to a motor, the motor might be configured in an undesirable state, and this wait could lead to consequences.
Aside: When I was finishing my doctoral work, another graduate student in the lab was working on a slip-ring to allow his project (a rotating beam to simulate a gun turret) to make 360 degrees of rotation. The 100 pound apparatus was set up on one end of a table. My computer was at the other end of the table. On the first test, the control loop was unstable, and the gun turret started to rotate in an increasingly rapid circle. Due to rotating imbalance, the whole apparatus started to walk across the table towards me! Fortunately, the wires weren’t long enough, and the connection to the apparatus was severed before it could finish its march towards my computer, me, and, most importantly, the only copy of my dissertation draft. This all happened so fast that the other student could not intervene by punching the stop button before the machine reached me.
The redesigned logic uses a state machine (rpmsg_init) to step through the various tasks in order. Once the final task has been accomplished, the state (rpmsg_state) is used to bypass the initialization routine. This logic is placed in both the PRU-side code (main.c, pru_comm.c) and the ARM-side code (toggle.c, arm_comm.c).
A second state machine is used to parse the commands from the remoteproc driver (parse_message). By using a communication format of required characters ‘a’ followed by ‘f’ followed by the actual command, spurious inputs from the system can be rejected. As an aside, the reason for ‘a’ and ‘f’ are a throwback to the use of the hexidecimal number 0xa and 0xf as the markers. Going with ASCII commands allows the PRU to be managed from the command line using the echo/cat interface.
In this simple state machine, “afS” starts PRU0 and “afs” stops PRU0. In later exercises, additional commands will be coded. The ARM-side code sends “afS” and waits for the PRU to respond with “afs”. The ARM-side code then sends “afs” and awaits the response “afS”. This allows an ARM-side loop to send a series of these commands, which will produce a square-ish wave from the P8.12 pin on the beaglebone. If you use a voltmeter instead (placed in AC mode rather than DC), you will get a brief increase in voltage (the rms value between 0V and 3.3V) before the system returns to zero.
You should create a subdirectory under your $(HOME) called toggle (e.g., /root/toggle) and put your code files and the Makefile in that directory. You will have to create subdirectories pru0 and pru1 in your code directory for this example to work.
You should set the variable, CODE_ROOT, in the Makefile to the directory containing the code. The archive (below) is set for /root/code/toggle/toggle, which may not be desirable in all cases.
Download the archive to a directory (/root/code if you want the code to work out of the box) and unpack it using the command “tar -xzf ‘archive name’ “. This will create an extra directory /root/code/toggle in which the files will be located. The archive should create the pru0 and pru1 subdirectories as well. If you go to the directory, typing “make toggle” and “make install” will create the executable files. Typing “./toggle” should give you the output (10 iterations of P8.12 toggling and “aft” back from PRU0).
If you connect a voltmeter across pins P8.1 (ground) and P8.12 (signal), you should get a brief value above zero. If you connect an oscilloscope, you will see a burst of low->high-> low (10 cycles).
In the next blog, a real-time loop will replace the while loop. This will allow precise real-time control, along with control over the sample time of the loop.
For this code, I genericized the routines update_command_state and act_on_command. These routines interact with the code add_command_state and add_action_state. In your code (both ARM and PRU), you use the command add_command_state to send a code and a pointer to a function in your file. Likewise for add_action_state. The code in arm_comm and pru_comm loops through the codes stored when you added them and executes the function that you added when the codes match.
This allows you to easily create a flexible set of codes without changing the library (arm_comm or pru_comm).
For this code, I modified arm_comm.c to allow either PRU0 or PRU1 to be assigned at the command line. Since the files used to start and stop the PRU are different, sending the desired PRU at compile time allows you to choose without modifying arm_comm.c.