I have a project going (home brew pinball machine) that is already using NeoPixels driven by a Beagle Bone Black via the Beagle’s PRU. I needed to add 7 segment score displays, 26 digits worth! I ended up with the solution that follows, thought you all might be interested. I designed a custom PCB using WS2811 chips that makes the displays work just like Neopixels. Now I can drive the displays and other WS2811 parts / Neopixels all in the same serial string from the Beagle. In the full system there are over 300 pixels in addition to the 26 numeric score digits all driven by one pin on the Beagle.
In this post I’ll cover everything needed to get a demo running:
Device Tree overlay
The PRU assembly driver code
C source code to generate patterns and pass to the PRU
Single pin from Beagle interface to pixel string
7-Segment driver board
Full system performance
Video of display in action
There are several great posts already on the site about device tree overlays, so I’m not going to go into a lot of detail here. An overlay is required to get access to a single pin (I have chosen P9 pin 28) from PRU #0. The compiled dtbo file (OVERLAY-PRU-00A0.dtbo) I used is included below. It is loaded with the command line:
sudo sh -c “echo OVERLAY-PRU > $SLOTS”
PRU Diver Code
PRU assembly code is include below (pixel_driver.p), along with the compiled version ready to run (pixel_driver.bin). The code can be built with the command line “pasm -b pixel_driver.p” right on the Beagle itself.
The code pulls pixel data from shared memory, one 32 bit integer for each pixel. Only the 24 low order bits are used, 8 bits each for red, green and blue intensity. The number of pixels is defined near the top of the file, I have it set to 400 for now. Connecting fewer than 400 actual pixels is not a problem, extra bits float off into never-never land without affecting the pixels you do have connected. Each bit is transmitted with a single pulse, either long or short, long being a “1” and short being “0”. The code simply uses separate branches for a 1 and 0 value bit. The idle time after a 1 bit is shorter than idle time following a 0 bit so that the total bit period remains fixed.
After all the pixel data is transmitted the code has a delay function. This delay is used to set the frame rate (or update rate) for the pixel string. When running 300-400 pixels I end up with a frame rate around 15 frames / second. The Demo code below that runs a 6 digit display is significantly higher update rate.
Here is a shot of a few bits (short pulses are 0 and two longer pulses are 1's):
The frame rate for the demo is quite high:
I use an eclipse environment on a Debian PC to cross compile the C code into an Arm executable for the Beagle Bone. The code below runs a demo pattern on a 6 digit 7-segment display (cproj.zip). The demo first counts up and then shows how digit intensity can be controlled. With the WS2811 chips you have a lot more control than just simple on and off – you can vary the intensity via software.
The code generates the test pattern and writes into shared memory where the PRU can pick it up and spool out to the hardware. At the end of the main loop, the code waits for an end of frame signal from the PRU. This is not really required (the PRU and Linux core can be asynchronous) but a really convenient way to get predictable timing in the application so that counting, fades and animation stay consistent.
Beagle hardware interface
Here is the schematic for the Beagle Bone Cape and a picture of the hand wired prototype on a nice AdaFruit proto cape. There is hardly anything required, just an HC04 inverter. But a couple really important functions are provided. First the inverter chip translates the 3.3v signal from the Beagle to full 5v logic. Second the inverter chip is powered not by the beagle but from the external supply used to power the pixels. This prevents the pixels from getting a bit stream when not powered – that is a condition that can damaged the WS2811 chips.
Here is the prototype:
7-segment driver board
As stated above, I used WS2811 chips to drive monochromatic 7-segment displays. To keep everything even I only used 2 (out of 3) outputs from each WS2811 to drive segments. This means each Digit is driven by 4 chips (including the decimal point makes 8 segments per digit). The schematic and layout are included below (Display_pcb.zip). This project was my first time ordering from JLCPCB and I was quite happy with the results. The boards worked out with no modifications required.
Here is one of the finished boards, front and back view:
I used 1” tall Blue display modules purchased on eBay, if you use something different be sure to check the layout and adjust accordingly.
I set up the boards with 4 -pin input and output connectors so you can daisy chain a bunch of them together. I also tested with Neopixels before, after and between display modules – no problems!
Check out the videos. As I said, I tested with longer strings of pixels this is just a single display unit for clarity. Note at the end of the video where the display is not counting show how segment intensity can be controlled for all kinds of cool effects.
For the second video I place a piece of smoked plexi glass on top of the display. This really makes it look nice, I highly recommend trying it out.