-
Notifications
You must be signed in to change notification settings - Fork 0
Home
Beaglebone boards have two main subsystems namely ARM and PRU. The ARM allows us to run a linux OS and utilize the plethora of already existing code and libraries in out implementation, however running a linux also has its downsides.As it is a non premtive OS so it is not suitable for situations where we need to perform tasks in real time. This is where PRU comes in picture, using the two 200MHz PRU on a beaglebone one can offload the time critical tasks to the PRU and can fetch the final results on the ARM side. Thus creating a solution which has best of both the worlds.
As the name suggests project which is done under under GSOC2020 is aimed at providing a solution where a bidirectional communication can be achieved between PRU and ARM and firmware is implmented to interface a Universal shift register 74hc299 which helps extend the no of i/o which do not have the OS induced overhead. This might be used in situations for example when interfaceing a LED matrix or when connecting alot of inputs like in case of Keypads and joystics. So all the processing can be done inside the PRU and the results can be sent to the ARM where there are alot of possibilities for processing this Data, may be creating a web based UI to disply states etc.
The blow diagram below shows the overview of the process involved in sharing data between the PRU and ARM

As can be seen in the diagram on the left the userspace application accesses the GPIOchip to send the data. The GPIOchip created by the driver has 9 lines. In the ARM to PRU communication scenario, the userspace application grabs the lines and set them as output. As mentioned earlier there are lines 0 to 8. The value on line no 0 determines the mode of the shift register. The rest of the 8 lines are used to create 8 bit of data. Once all the 9 lines are assigned a value driver creates a RPMSG payload/message and sends it over to the PRU side. The PRU firmware continuously looking for mailbox interrupt, when a new message is received interrupt is triggered. Then PRU reads the buffer which has the data. The data sent on line 0 is analyzed and accordingly the PRU configures the shift register in SIPO and PISO mode. If in the SIPO mode the rest of the 8 bits are grabed one by one from the payload/message and shifted in the shift register.This process is discussed in detail in the later sections. If the data sent on line 0 is 1 then the rest of the 8 bits are discarded and the PRU configures the shift register in PISO mode and after that reads in the 8 inputs, creates a rpmsg payload and sends it back to the ARM side from where the userspace code can read the value by setting these lines as input and then reading the state.
To access the code, dependencies, ReadMe file, description of folder contents and wiring diagrams. Visit the link below
The technique/protocol used to implement the communication is called rpmsg,The RPMsg protocol was created as part of the Open Asymmetric Multi Processing (OpenAMP) framework project to provide a standard interface for communication between multiple cores in a heterogeneous multi-core system. There was already a character driver in the linux source for this implementation. But here a gpiochip driver is implemented which is a new implementation to deal with the rpmsg based communication. To understand this better, we need to understand the architecture at a little more deeper level. The TRM tells us that there are 4 general purpose i/o modules and each module provides 32 gpio pins. So the AM335X provides 32 X 4 gpio. This fact can also be corroborated by looking inside the /dev and running the gpiodetect command.

However not all of these gpios are accessible on the headers available on the beaglebone board. Now the interesting part is that after we insmod our GPIO driver the linux system thinks it has another gpio module which consists of 9 gpio lines. However these lines are virtual and there is no actual hardware pins corresponding to these lines inside the linux subsystem.

The data that is sent using the chardev interface is packaged in a char array and then send over to the PRU where this data is pushed to the i/o of the shift register.Similarly a gpio line can also be used to read input. So this data can also be read using the sysfs interface. This is a more intuitive interface as compared to using a char driver when any project involves controlling multiple i/o on the PRU side. Also this comes with another advantage that now one can use the libgpiod library to send and receive i/o state as if these are a part of gpio modules on the ARM.
The code from this repository can be used partially or completely as it is, the probable usage senarios can be:
- Just use the linux kernel module to pass on the messages using sysfs interface and libgpiod
- Just want to interface the 74HC299 or 74HC595 to the PRUs in C and then want to use the usual char driver.
- Use the project as it is.
sysfs is a pseudo file system provided by the Linux kernel that exports information about various kernel subsystems, hardware devices, and associated device drivers from the kernel's device model to user space through virtual files.[1] In addition to providing information about various devices and kernel subsystems, exported virtual files are also used for their configuration. Our field of interest was gpio class which can be acesses at /sys/class/gpio.User-mode GPIO has historically been performed via the legacy “integer-based”sysfs pseudo file system. For example our gpiochip driver has a interface in the sysfs and can be accessed as following:

In the image above we can see the gpiochip interface exported by our driver is 'gpiochip593'. When we cat the label and ngpio we see that the label is "channel 30" as we have set in the PRU firmware and the ngpio number is 9 as we have set in out gpiochip driver.Now to use the lines we need to export them and then use them as we use any other gpio line available on the beaglebone header using the sysfs.The figure below shows how we can export lines from the gpiochip503. Here as an example two lines namely gpio503 and gpio504 are exported. You would notice that there are two folders gpio503 and gpio504 which have interfaces to control these lines. This gpiochip503 have 9 lines which go from gpio503 to gpio513.

Further the screenshot below shows the process of setting the direction of the pin and then assigining a value to it.

It is also worth mentioning that the userspace examples in the repo uses the new recomended way of using GPIO via the “descriptor-based” character device ABI (Application Binary Interface). The interface is exposed at /dev/gpiochipN as shown in the previous section. But for the sake of complete understanding this document has also explained the sysfs interface.
To use the chardev gpio interface there is a library called libgpiod which provide us with APIs using which we can easily access the gpio lines. There are userspace examples that you can find in the repository but you can follow thislink to understand the API better and write your own userspace code. The library is in C but there are bindings for other languages like C++ and Python. Also if one wants to use it through the terminal one can follow as shown below.
gpioinfo: It prints information about the GPIO lines of a specific gpiocontroller. Here in the example wr are printing for our controller 4.
gpioset: It sets a gpio line as HIGH or LOW. Ex gpioset <line no,>=<1 or 0>
gpioget: It is used to read value of a line. Ex gpioget
Shift Registers are sequential logic circuits, capable of storage and transfer of data. They are made up of Flip Flops which are connected in such a way that the output of one flip flop could serve as the input of the other flip-flop, depending on the type of shift registers being created. In the project here two different ICs are used to demonstrate bidirectional communication and provide additional i/o. First IC is 74HC595 which is can work in SIPO (Serial Input and Parallel Output Mode) and the second IC is 74HC299 which can work in SIPO, PISO (Parallel Input Serial Output) , Shift left and shift Right modes.
The image below shows block diagram of a shift register in SIPO mode. As you can see to shift in n bits of data n flipflops are required and n number of clock pulses are required. Once the serial data is loaded the parallel output can be obtained without any further clock. Further the time required to input the data will be n x time period of clock.
Further in the PISO mode the data is loaded in the shift register in 1 clock pulse and the data is read out serially using n-1 pulses. The value of shift/load as shown in the figure decides if shifting will happen or loading of new parallel data will take place.
In this section i will go through the process of using the actual 74HC595 and 74HC299 IC. I started with 74HC595 because it is very widely available and i didn't had access to 74HC299 due to the corona pandemic.However it can be a blessing in disguise as now my repo has code for 74hc595 which is way more popular among hobbyists so somebody can use it out of the box. However for bidirectional communication you would need 74HC299. Now i will go through the overview and you can look for the finer details in the respective data sheets.
OE1 and OE2 need to be pulled LOW and
As shown in the image above there are two main blocks inside the 74hc595 one is the shift register which shifts the data on every low to high clock transition and the other one is Latch block which receives the parallel input . A low−to−high transition on the latch clock pin input latches the shift register data. Now if the OE pin is LOW it allows the data from the latch to be presented on the output. When this pin is HIGH the output pins are in high impedance state and can be driven by some external circuit.
However in case of 74HC299 we have an extra step, first we need to configure the IC to be used as SIPO or a PISO. To configure in SIPO mode both OE1 and OE2 are pulled LOW and SO is set HIGH and S1 as LOW.Once configured value is loaded as in case of other IC and you are good to go.
To configure in PISO mode OE1 , OE2 are kept LOW S0 and S1 all are set HIGH. Then a left shift or right shift configuration is made . Q0 gives data with least significant first and Q7 gives serial data with most significant first.other details are evident in the datasheet.

Before you could understand the code that i wrote, another very important bit of information need to be understood. There are 2 32 bit resistors in the PRU namely R30 and R31. The R30 register controls the PRU pins output and R31 controls the input. In simple words it means if you have a pin which is set as output. You need to figure out which is the bit number that it corresponds to in the R30 register and then use bit wise operation to set it low or High. Similarly if you have set a pin as input, in order to read this pin you need to read the corresponding bit in R31. You can read about it in detail in my Blogpost.
- At one point of time the Shift register pins can only act as input or output. There is no way to setup some of them as input and others as output. So this is half duplex communication.
- The PRU is operating at 200MHz and can switch at a much higher rate then the 74HC299 (or 74HC595) can register.It has a propagation delay of around 60 to 65nS and typical frequency of 15MHz at operating voltage of 2v and ambient temperature of 25 degree. Hence it is a bottleneck to the overall performance of this setup. However the same communication can be done faster if we can implement the shift register on a FPGA like lattice ICE40 which also has a opensource tool chain.