BBBCSIO

Host and PRU Data Exchange Example Code

 

About the Host and PRU Data Exchange Mechanism

Quite often PRU programs are not intended to run in isolation. On many occasions it is desirable to exchange data between the PRU and a host program (running in Linux user space) or vice versa. The example code on this page is designed to illustrate how to use the BBBCSIO PRU_Driver class to preload a data transfer buffer into the PRU and then launch a PRU binary to use it. The C# code will then subsequently interact with the PRU code by sending the running PRU program some data for processing and then receiving and displaying the output.

Since this is just an example to illustrate the data transfer process we do not really do anything too complex in terms of processing in the PRU. The example code is designed to prompt the user to input an integer value. Once received, the integer will be passed to the PRU program which is continuously checking for such input. The PRU program will then simply multiply the integer by two and pass it back to the host program. A simple semaphore mechanism is implemented to ensure the host C# program fully writes out the integer before the PRU program processes it. Access to the returned value is also coordinated by a similar semaphore.

The example below shows how to use the BBBCSIO libraries PRU_Driver class to load the PRU binary (compiled PASM Assembler) and its initial data. 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 PRUDataInOut.bin and the PASM Assembly Language source for this code can be found in the file BBBCSIOHelp_PRUDataInOutExamplePASMCode.html. You should probably read the comments in the PRUDataInOut.p source file in order to understand the operation of this program.

An Example Illustrating Data Transfer To and From an executing PRU Program

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

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// A sample which illustrates passing data to and from the PRU and
        /// a user space C# program. 
        /// 
        /// In this example, the PASM binary to load is hard coded. It is designed
        /// to monitor a flag in the PRU dataspace and when it goes non-zero 
        /// to read an UInt32 value, double it, write it back and clear the flag.
        /// 
        /// The C# user space code is designed to also observe that protocol and
        /// will set the value and the flags appropriately.
        /// 
        /// </summary>
        /// <param name="pruID">The pruID</param>
        /// <history>
        ///    05 Jun 15  Cynic - Originally written
        /// </history>
        public void PRUDataInOut(PRUEnum pruID)
        {
            uint numberToDouble=0;
            byte dataFlag = 0;
            // the number we double is stored at this offset
            const uint NUM_TO_DOUBLE_OFFSET = 0;
            // our flag is stored at this offset
            const uint DATA_FLAG_OFFSET = 4;

            // this is the array we use to pass in the data to the PRU. The
            // first 4 bytes are the integer to double, and the next byte
            // is a byte flag acting as a semaphone which indicate to the PRU
            // that data is ready (the flag !=0). The PRU code will reset
            // the flag to 0 to indicate that it has doubled the value
            // and that this program can consume it.

            // There are better ways of doing this (with structs) 
            // but seeing the data locations explicitly specified
            // is useful as a simple example.
            byte[] dataBytes = new byte[sizeof(uint)+sizeof(byte)];

            // sanity checks
            if (pruID == PRUEnum.PRU_NONE)
            {
                throw new Exception("No such PRU: "+pruID.ToString());
            }
            string binaryToRun = "/home/debian/CSharpCode/PASMCode/PRUDataInOut.bin";

            // build the driver
            PRUDriver pruDriver = new PRUDriver(pruID);

            // initialize the dataBytes array. the PRU code expects to see a 
            // zero flag byte when it starts
            dataBytes[0] = 0;
            dataBytes[1] = 0;
            dataBytes[2] = 0;
            dataBytes[3] = 0;
            dataBytes[4] = 0;

            // run the binary, pass in our initial array
            pruDriver.ExecutePRUProgram(binaryToRun, dataBytes);

            Console.WriteLine("Now doubling integers. Press 0 to quit");

            // our outer loop we operate until the user quits
            while (true)
            {
                Console.WriteLine("Enter integer: ");
                string inputStr = Console.ReadLine();
                try
                {
                    numberToDouble = Convert.ToUInt32(inputStr);
                }
                catch (Exception)
                {
                    Console.WriteLine("The value " + inputStr + " is not a positive integer");
                    continue;
                }
                // are we done?
                if (numberToDouble == 0) break;

                // set the flag
                dataFlag = 1;
                // write the data
                pruDriver.WritePRUDataUInt32(numberToDouble, NUM_TO_DOUBLE_OFFSET);
                // write the flag
                pruDriver.WritePRUDataByte(dataFlag, DATA_FLAG_OFFSET);

                // wait while the PRU processes the data. Most likely the
                // PRU has already done it
                while (true)
                {
                    // read the flag
                    dataFlag = pruDriver.ReadPRUDataByte(DATA_FLAG_OFFSET);
                    if (dataFlag == 0) break;
                }
                // read the data
                uint doubledInt = pruDriver.ReadPRUDataUInt32(NUM_TO_DOUBLE_OFFSET);
                // tell the user
                Console.WriteLine("2x"+numberToDouble.ToString() + "=" + doubledInt.ToString());
            }

            // close the driver, the code in the PRU remains running
            pruDriver.Dispose();
        }      

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

   PRUDataInOut(PRUEnum.PRU_0);