In a previous tutorial I mentioned a thing called a Character LED Display, which I said was 'another story.' This is the other story.
This project is about controlling LED matrix displays. It's about doing the hard work with a microcontroller so that the work left to do on a Raspberry Pi is minimised.
The following diagram represents the system architecture. A Raspberry Pi, on the left, sends text or image data to the display over an SPI interface. The LED controller, in the middle, transforms the display data and generates the control signals required to control the LED matrix display board. The controller then sends the data and control signals over a HUB08 or HUB75 interface to a display on the right. This project is about making the controller, the bit in the middle.
The goal of this project is to make a Pi HAT for LED Matrix displays that supports the HUB08 and HUB75 interface and can drive displays in character or graphical modes. These modes are used to classify the display. Character mode is the simplest. In this mode the Raspberry Pi calls printf style functions with strings of text to write to a display. In graphical mode the Raspberry Pi provides an array of RGB data. Graphical mode will allow you to create static or moving images on the display.
I've been using character LCD displays in projects for a long time. They are simple to use, easy to find and great for a simple, small UI. When you need something big and bright and bold then character LCD displays are not a good choice. Big LED matrix displays are a much better choice for a display with real impact.
LED matrix displays are hard to control. There's not a lot of documentation available about how to drive them. They require constant refreshing and this takes a lot of processing power which you might want for other things like pinging a server and retrieving some data to show.
What I want is a display controller that I can set and forget or draw 'images' to, without having to worry about real time signal generation or clocks or strobes or addresses or PWM or BCM or any of the other things that need to be handled to control one of these displays.
I know there are such things as LED matrix display controllers. The manufacturers of displays usually recommend or supply a controller you can buy that does almost what I want, almost. The documentation for the controllers is as hard to find as documentation for the display. This introduces a risk I can't accept: that the night before shipping a huge order to a giant customer I find out the controller doesn't do what I want, and there's nothing I can do to fix it.
That's the official story. That's what I would claim as the reason I decided to make my own LED matrix controller. The real story, is that I just want to. I don't know how these LED matrix displays work and I want to figure it out. I have never made a Pi hat and I want to figure out how to. It's been a few years since I've designed a piece of hardware and I want to learn how to use KiCAD. I haven't used an STM32 before and I want to learn how to use them.
There are a whole mess of new things to learn, and what better way to learn than to just jump right in a figure it out. I learn best when I need to use something in anger, i.e., I have to produce some actual result, not just a flashing LED or Hello World!
The concept is to make a Pi Hat for controlling LED matrix displays that does all the heavy lifting, The software running on the Pi should be as simple as possible, something like printf or sending a bitmap over SPI.
These are my high level goals for the project:
- Make a Pi Hat. Where practical, follow the Pi foundation so I have the option certify it as a 'Pi Hat'
- It must drive the display in 'character mode' (first iteration)
- It should drive the display in 'graphical mode' (second iteration)
- It must support both HUB08 and HUB75 interfaces
I started with an STM32 Blue Pill, a simple, cheap microcontroller board. I wrote about it on my blog, you can check out the full story there. What follows is a short summary of that post.
Here's a diagram of the proof of concept. The display is represented by the 16 pin header on the right. This display is a single colour, HUB08 type.
This proof of concept would drive the display pins at 3.3V which I thought would probably work, though 5V might be better. Here's how it looked.
The result of this experiment with the proof of concept was a working LED Matrix display. I developed it for a bus stop sign project. Here's what that looked like.
My prototype firmware was based on this library. I added a simple command interpreter and devised a protocol to control the display over SPI. The main concept here is that this was a character display, meaning that you send strings of text to the display. The display firmware figured out what LEDs to illuminate based on the characters in the text strings it received. The display had its own font that turns characters into patterns of illuminated and non-illuminated LEDs.
From the prototype I learned that it was doable. I also learned how to control the HUB08 type of display. The library I used didn't do exactly what I needed so I learned a lot by modifying the library. I decided that I should drive the LED control signals at 5V, this would require level shifters to convert the Blue Pill's 3.3V IO levels to 5V. I thought about trying to cram it all onto a Raspberry Pi prototype Hat, but decided that it would probably be easier to design my own board based on the Blue Pill.
I also learned that the pinout of the development system I was using did not comply with the Raspberry Pi pinout. SPI pins MOSI and MISO were swapped. When I made a board I would need to support both the 'correct' and 'incorrect' pinouts.
After proving that "it's doable, " the next step was to design a PCB. This is what I came up with for the design requirements:
- Be a Pi Hat
- use the stm32
- support HUB08 and HUB75 connection
- drive the LED display signals at 5V
- support the correct and incorrect SPI pinout
I decided to get the board manufactured and populate it myself. After a little research I settled on 0805 size components with hand-solder pad footprints. I started with the schematic for the Blue Pill and datasheets for the stm32 and level shifters. I created a project in KiCAD.
I watched the Getting to Blinky 4.0 series by Chris Gammell of Contextual Electronics, then I watched it again. I went back to selected episodes to help with setting up the libraries and creating my own parts library. The series covered everything I needed to get started.
The hardest part about laying out the board was battling OCD. I'm sure it's mostly science for professionals, but for me it was all art and ignorance. I moved stuff around until it looked right then started laying tracks. I went back to the schematic and changed the pin mapping a few times so that I could get the level shifters, stm32 and connectors arranged with a minimum amount of vias.
A tricky part was getting the dimensions of the Pi hat right. I had to redo it a few times to get it close enough to the specification.
I ordered the boards from Seeed Studio. They make 10 boards for $4.90. I planned to only make 1 or 2 of this version. I adopted the convention of making development boards in red. The idea is to make development boards red and production boards green. This way if anyone accidentally went to ship a dev board it would be easy to spot.
The disadvantage of low cost board fabs is that it usually takes a lot longer to get the boards than if you used a premium local service. The trade off is cost. I was in no hurry and had no budget so I could justify the wait.
With the schematic finished and most of the board designed I could set about finding and ordering the parts I needed to make the board. To do this you need a list of parts, this is the Bill of Materials (BOM). KiCAD generates a BOM for you. What you need to do is find all the parts from the BOM for an acceptable price and with lead times that suit. I got everything from Digikey.
I ordered enough of the expensive components to make three boards. I bought a 100 of all the passives, resistors and capacitors etc.
I used to do soldering for a living, but it's been a while since I had done anything substantial, and don't have access to specialist tools. I watched a few tutorials on YouTube to see what's new and what I'd forgotten. Building the board wasn't difficult, there are a few fine pitch surface mount components, but with plenty of flux and a little technique the board went together nicely. I soldered up one board. I had ordered enough parts to make three but knew that there'd likely be problems with the first board so saved the parts for the next iteration.
bringing up a board is always a fun experience. You're not quite sure if it's going to work or if smoke is going to come out, or worse. I went for a conservative approach, starting with a good probe around with a multimeter. I was looking for a high resistance between 5V, 3.3V and GND. Then I connected the 3.3V rail to a power supply and switched it on, no smoke, but no LEDs either. Then I tried the digit probe. The digit probe is where you stick your finger on stuff to see if it's hot. Not recommended for high voltage circuits, but for low voltage things it's usually OK. (I accept no responsibility for lost fingerprints, some things can get real hot even at low voltage).
After a little probing and measuring I found a mistake with an unconnected net. I missed a connection on the GND net, so part on part of the board GND wasn't connected to GND. I also realised the diodes I had placed as protection diodes on the 5V and 3.3V lines to protect the Pi against over-voltage were dropping too much voltage. I'd used shotkey diodes which drop about 0.3V, but that was too much.
I used a short bit of wire make the missing connection, and replaced the diodes with 0R resistors. I then loaded the firmware from the prototype. After I was confident that there were no mistakes on the Pi connector, nothing connected together, or to voltage or GND that shouldn't be I connected it to a development board.
The display didn't work and after some more probing around with a logic probe I could see that the switch I had put in the SPI lines to correct the development board mistake was delaying the signal enough to put it MISO and MOSI out of phase with the clock. I decided to remove the switch, I'd need to find an alternative method to swap the lines. For the next iteration I decided to use jumpers instead. Not automatic, but acceptable. Here's what the board looked like after the mods.
I got the display working with this first version of the board. I'd learned enough to start the second version.
I made a few mistakes in the first board. Firstly, I missed a connection on the GND net, so part on part of the board GND wasn't connected to GND. My second mistake was to use diodes to 'protect' 5V and 3.3V on the board. The diodes dropped too much voltage. The third mistake was to use a switch in the MISO and MOSI lines. I had planned on detecting which development kit the board was connected to and auto-magically set the right MISO, MOSI mapping for both the correct and incorrect boards. This didn't work. The switch introduced a latency in the SPI lines so that the clock ended up out of phase with MISO and MOSI.
A couple of other improvements could be made for the next iteration. I decided to ditch the SPI switch and use 0R resistors. No fancy board detection, just fit the correct resistors to get the SPI lines the right way around. I would run the Design Rule Checker (DRC) before shipping the files to the fab house this time. This will make sure that I had connected all the nets correctly. I ditched the protection diodes from the 5V and 3.3V rails. I would also add some test points to the board to make the next bring up easier. The last thing I changed was to add labels to the display connectors.
I had enough parts to make three boards. I decided to make the changes and get a new set of boards made.
On the second board I'd made the following changes:
- removed the protection diodes
- added test points
- added labels for the display connections
- removed the SPI line switch
- made the mounting holes bigger
I'd also got a bit better at soldering the surface mount components by hand. I think it looks a little neater than the first board. I populated 2 of the second boards. I made one for the Technexion 6UL pico pi dev kit and one for the Raspberry Pi.
About this time Google announced that the 6UL dev kit would no longer be supported for Android Things. This was a blessing and a curse. It meant that I no longer needed to worry about correcting the SPI line swap on my board, but it also meant that I needed to buy another dev kit, the imx7D pico pi. Google also announced that there would be no shippable products based on the Raspberry Pi format. You could use the imx7D and Raspberry Pi for development, but not for production.
The second board worked well enough for me to consider making a final version. This would be the third board. I decided to get the third board assembled professionally which meant that I needed to make a few changes to the BOM to support automated production.
I made a few more changes for the third board (V1.2). I removed the jumpers to set which way around the SPI lines were connected. I added a jumper so that I could select CS0 or CS1 from the Raspberry Pi connector as Chip Select for the display. The biggest change was to change the processor. I wanted to get the board assembled so I used components from Seeed Studio's Open Parts Library (OPL). The only STM32 available in the OPL was the STM32F401. I changed the schematic and PCB for the new microcontroller. I used the schematic from the ST discovery kit for reference, there were a few extra things I needed to do for this new controller, a few extra pins to connect.
I also switched to smaller resistors and capacitors as these would be machine assembled I didn't need them to be as large. Another change was to add a 5V connector to power the board and Pi from the same power supply as the display. To do this I added the recommended protection circuit, another diode. This time an ideal diode that drops much less voltage than the real diode I'd used previously.
For the third board I made the following changes:
- changed the processor to a STM32F401
- expanded the programming header to include SWD
- added a jumper for SPI chip enable CE0-CE1
- added ideal diode and connector for 5V in
This is a WIP project, i.e., it's not finished. I have the third board up and running with the code ported to the STM32F401 which meant using the Mbed platform.
I'm working on the firmware for the HUB075 display, but haven't devoted much time to it lately. I'll update this project when I find the time to work on it.