BBBCSIO

Host and PRU Pin Input/Output Example Code

 

About the PRU Pin Input/Output Mechanism

There are two ways a PRU program can read or write to the pins on the Beaglebone Black's P8 and P9 header. The first method is to enable the OCP master port and then manipulate the pins via the GPIO subsystem. In other words, the PRU accesses addresses in the main Beaglebone Blacks memory and can read or write the bits which are related to the GPIO OCP device. This is exactly the way the Linux GPIO device driver operates and also the method used in other examples in this series such as the PRU Blink USR3 LED Example.

The second method of reading or writing to the pins on the Beaglebone Black's P8 and P9 header is to use the PRUs own internal registers (rather than the Beaglebone Blacks main memory space) for the purpose of reading or writing to the inputs or outputs. This is considerably faster (by about a factor of 10 or 20). However there is a drawback in that the pins involved are split among, and dedicated to, a specific PRU. In other words, if you write some PRU code to directly read or write to its I/O pins then that code must necessarily be specific to either PRU0 or PRU1. Fortunately there are quite a number of input pins and output pins per PRU. Also note that the some of the pins cannot be configured both an input and an output like the GPIO's can. The pins the PRU accesses are hard coded to the PRU and are also often restricted to being either an input or and output.

The upshot is that the PRU direct input and outputs are much faster but are somewhat restricted in number, are specific to the PRU and are often not bi-directional. The GPIO I/O method is more versatile, slower, but is able to reference pins that cannot be accessed from either PRU. A discussion of the PinMux and how it controls access to the I/O lines on the P8 and P9 Header can be found on this page and a discussion on how to configure the PRU I/O pins can be found here.

This sample code will illustrate how to use the R31 and R30 registers of PRU0 to read the high/low state of an input and to set the state of an output to match that input. For the purposes of demonstration, the off/on state of the USR3 LED will also be set to follow the state of the input. The setting of the USR3 LED state is via the slower GPIO mechanism. There is no direct PRU output (on either PRU) which can adjust the state of the USR3 LED. As mentioned previously, not all I/O is available via direct PRU access.

The example below shows how to use the PRU_Driver class of the BBBCSIO library to load the PRU binary (compiled PASM Assembler). For the purposes of illustration, the example binary being loaded and run is hard coded in the sample source and you will need to modify the path prior to executing it yourself.

NOTE: The PRU code the example below will run is called PRUPinInOut.bin and the PASM Assembly Language source for this code can be found in the file BBBCSIOHelp_PRUPinInOutExamplePASMCode.html. You should probably read the comments in the PRUPinInOut.p source file in order to understand the operation of this program.

An Example Illustrating Pin Input and Output using a PRU Program

The code below illustrates how to use the launch, and interact with, a compiled PRU binary using the PRU_Driver class of the BBBCSIO library.

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// A sample to launch the PRU code which demonstrates the use of 
        /// the P8 Header Pins the PRU can access natively via its R30 and R31 
        /// registers.
        /// 
        /// In this example, the ASM binary to load is hard coded. It is designed
        /// to monitor a PRU GPIO associated with the pin on Header8 Pin 16 and
        /// set the USR3 LED and the PRU GPIO associated with the pin on Header 8
        /// pin 11 to match that high/low state.
        /// 
        /// </summary>
        /// <param name="pruID">The pruID</param>
        /// <history>
        ///    05 Jun 15  Cynic - Originally written
        /// </history>
        public void PRUPinInOut(PRUEnum pruID)
        {
             // sanity checks
            if (pruID == PRUEnum.PRU_NONE)
            {
                throw new Exception("No such PRU: "+pruID.ToString());
            }
            string binaryToRun = "/home/debian/CSharpCode/PASMCode/PRUGPIOInOut.bin";

            // build the driver
            PRUDriver pruDriver = new PRUDriver(pruID);
           
            // run the binary, pass in our initial array
            pruDriver.ExecutePRUProgram(binaryToRun);

            Console.WriteLine("Now processing. Press any key to quit");
            Console.ReadKey().ToString();

            // we can leave here and even close and dispose of the driver
            // and the PRU code will remain running unless we explicitly 
            // stop it
            pruDriver.PRUStop();
 
            // close the driver
            pruDriver.Dispose();
        }   

The PRUPinInOut call would be made via a line which looks like:

   PRUPinInOut(PRUEnum.PRU_0);