By Jacob Chrzanowski
LCD and LED displays have captured my attention ever since LCDSmarty, a parallel port interfacing software for simple printer lcd screens was shown to me. The moment I saw that a glass/epoxy-encapsulated LED display was up for grabs on eBay, I had to have it. I’m not sure exactly what is is about LED displays, but I think the ability to directly control tiny luminescent devices over a couple wires makes me want to develop software and in the future hardware for them. To clarify, there are LED-backlit LCD displays that use light from diodes to illuminate an image. I’m not talking about that. I’m talking about Diodes themselves producing the image. (Which is far cooler) This post will focus on the process I took in understanding how to make this PDSP188x display work, and the issues I encountered along the way. I’ll also be covering aspects of electrical engineering and microcontrollers that may not be common knowledge. I don’t want to cover a complex topic only for it to fly above someone’s head because they didn’t understand some critical piece of the puzzle.
Naturally, already preoccupied with microcontrollers and programming, I had a collection of breakout boards I could use for this display. Each display has 24 pins, so my only real option was the Mega 2560, a whopper of a board with over 40 GPIO pins. The displays having arrived in pink and black anti-static foam, in a collection of 5, I picked out the only one with a scratch on it. I figured if I killed it by accident, I wouldn’t be losing anything too irreplaceable. Slightly ironic seeing as they are no longer produced and most go on sale for upwards of $25 each, this box having been an extreme bargain at $20 for 5.
So some important things to cover about this project – microcontrollers like the Mega 2560 are basically tiny reprogrammable computers that run C code that gets compiled on your computer and uploaded via a serial connection, in this case, USB. In actuality it’s a usb-to-RS232 link (like what Cisco switches use to communicate to computers with a program like Putty) that the Arduino software uses to halt the microcontroller, wipe its memory, and program the tiny EEPROM. Ah.. more buzzwords. EEPROM stands for Electrically Programmable Read-Only Memory, which is a lie because it is possible to write to it too. It’s what stores the code Arduinos run whenever they power on.
I’ll also be talking about serial vs parallel later on so might as well cover it now. Serial communication, like USB, sends data in sets of ones and zeros one at a time on a single wire. Parallel communication, like what the display uses, sends data in groups of ones and zeros on several wires at the same time. Examples of parallel you’ve definitely seen are those giant parallel printer ports on the backs of you older computers. Older PATA hard drive and CD drive connectors are also parallel-based. RS232 (think Cisco), SATA for hard-drives, Ethernet, and USB are all serial-based communication.
Back to the fun stuff. LED displays and LEDs as a whole can be very sensitive to voltages and electro-static discharge, so before I fried one of these displays I had to check out the datasheet. You can find public datasheets for almost all electronic devices, from assembled boards like Arduino to individual components like these displays, or even individual resistors and transistors. (components that inhibit the flow of electricity and components that act as an electronic valve). Checking out this datasheet I found on Mouser.com, I see that PDSP188x is in fact a series of 5 different displays, 0-4 with different colors; each built for 5 volts. Perfect for this Arduino which also runs at 5 volts. Datasheets usually have extra information about components for PCB design and product exterior, including tolerances for, say, part height. In my case I was concerned about one thing. Pinout.
All complex electronic components have what’s called a pinout that designates a purpose for each pin. Sometimes there are repeats or unused/unattached pins, but there’ll always be a positive power and ground pin. In this case they denote it as Vcc and GNDsup and GNDlog. For whatever reason they allow you to drive the lights with one power supply and the logic with another. This just means I had to use three total wires for power. No big deal. Another fun tidbit about components like these is knowing how to count pins. Pins are counted starting at pin 1 designated as the closest counter-clockwise pin to any notch or dot on a component. From there you count up, going counter-clockwise.
Next up was figuring out how to get letters onto the display. This is an eight character display with each character comprising of a 5×7 matrix of LEDs. As such they actually show you all the hard-coded characters in the datasheet and the parallel data that needs to be sent to each pin to get that specific character to show up. Super helpful if you’re feeling lazy and don’t feel like reading the entire datasheet five times. I still did that anyway.
Ultimately this is just a big puzzle. And an even bigger puzzle for me since I worked on and off on this project since early 2017, so each time I revisited this project I had to re-learn half the things I forgot since the last time I even looked at my code, let alone what each wire did. Anyway, this Fig. 4 table is super helpful because it shows us we don’t have to worry about translating characters for the display. Comparing this to an actual ascii table shows us that everything after ‘space’ up to ‘~’ aligned perfectly, so I could use c99 chars as unsigned bytes I could send in parallel to this display. I didn’t and still don’t worry about the weird stuff like Phi or ‘Ö’. To correlate this fig.4 to fig.3’s pinout, if I wanted to display ‘a’ I would set pins 20, 21, 25-30 to 10000110. Simple, right? Haha! Boy was I wrong on my first go-around. There are about 7 more pins that need to be set before you can ‘just’ change a character on this display. There’s a pin called chip-enable that needs to go low (be set to 0) before anything can happen. Four pins labeled A0-A3 select which character you’re writing to. Another pin called write-enable allows the display to be written to. Only then will anything show up. What a mess. But a mess that is super easy to program! Fig.6 shows some of the code I wrote for this, which I had to write from scratch since any other reference was in assembly, and I’m not touching that with a fifty-foot pole. I used a bunch of bit shifting and inefficient ways of setting pins on the Arduino because of laziness. If I had incorporated even a smidgen of hardware efficiency, I could have paired pins to match registers on the Arduino. That way I could set an entire 8-bit register at a time instead of wasting cycles setting each pin individually. The overhead of calling a function with 8 variables is also probably super inefficient but that’s also probably fine. The display updates blazingly fast anyway.
In this code, (Fig. 6) labeled trans for translate, I take a string and iterate through each character. I wrote all my code to be used competently so there is absolutely zero error-checking for efficiency. Trans() is meant to be given 1-8 character long strings that will be printed to the display. So if I gave It ‘_hello’ the display would show ‘_hello__’. It forms the basis for displaying anything with my code. I also found I had to initialize the screen or else it would show garbage each time it powered up. Since the Arduino takes about 2 seconds to boot it would show garbage for about that long too. This meant I had to use built-in commands to clear the screen of any text and make sure it was readable. Fig.7 shows me setting some pins to do just that. Configuring brightness works in a similar way, simply writing to the correct display register would change the display brightness.
After tinkering with some more C I managed to get it to reliably marquee text in a circle (Fig. 8). Code like so contains 7 spaces, some text, and 7 more spaces for padding, that way the marquee enters from the right of the display and exits completely left before returning to the event loop. I’ve replaced every other space with an underscore for visibility…
circle(" _ _ _ Wowzer it goes right round in a circle. _ _ _ ", 1);
Overall, adding functionality is fairly simple. The datasheet tells you exactly what each relevant pin needs to be set to and my code is self-commented well enough that using the writeD() function can accomplish anything (Fig 9).
Just before I write any of my functions I have to prepare the Arduino. This happens in and around the setup code, which is code meant to be only run once. Fig. 7 has some confusing-looking commands; let’s take a closer look. Before you can use any pin on an Arduino, that pin needs to be initialized as either an input or an output. I initialize pins 22 through 40 and a few more for miscellaneous purposes. I also open a serial line on the USB connection; this was while experimenting to have the Arduino display things I sent to it over a serial session, but I never got that far. It involved a lot of failed memory allocation and type conversion. I plan to take another look over the winter break. Further down, you see me set something called ‘myEraser’. I’ll explain what this does later on in this post. It gets a little hairy, but keep it in mind. Then I have variable names set to pins 22-40, that way I can call upon pins by their name in the datasheet instead of the arbitrary pin I assigned them. Also, if you ever decide to work with an arudino in the future, some Arduinos have pins meant specifically for only digital communication, digital, and PWM ouput, and even some specifically designed for analog input. I talk about PWM later on and explain what it is, but this pin arbitration is the reason why I had to put my CLK pin on pin 8.
Fig.5 shows the entire project layout to get a screen working. You can see the white cable carries electrical ground to the display in the bottom right. The rest carry power, serial, and parallel data as needed. What looks like a rats-nest is in fact a very carefully crafted masterpiece of wires that allow me to both troubleshoot the display, and see what it says at the same time.
One issue I ran into with this display was its method of using its clock input. Annoyingly, the one thing this datasheet doesn’t fully explain is how the clock pin works. In short, the clock pin has threes states, high, low, and floating (disconnected, meaning the value can be changed by static or stray capacitance, but that doesn’t matter as I’ll soon explain). If you don’t connect anything to the CLK pin and pull the CLS or Clock Select pin low, the display uses an internal clock to drive its logic and display each pixel a line at a time. There are too many pixels to directly tell each one what to do at the same time, so the display uses what’s called multiplexing. This is where you flash each line of the display one at a time very quickly over and over again each second and rely on humans’ persistence of vision to combine 7 lines into one image. This is similar to how CRTs, LED alarm clocks, and LED keyboards work. If you ever want to see the strobing effect of your keyboard you can record it with your phone’s high-speed camera and then go by frame-by-frame. Or sometimes if you shake your head fast enough you’ll catch a device in mid-refresh.
The CLK pin is actually really cool, because for microcontrollers more powerful than the Arduino you can use it to manually set the pace of the display. That way if you have some sort of synchronization issue with your display, say, you’re sending it data faster than its internal clock can keep up, you can force it to stay in lock-step with your microcontroller. I don’t really remember why I thought I needed this pin, but I eventually figured out that to use it I couldn’t just hook it up to a digital pin. If I used the Arduino’s digitalWrite() function, it would not be bale to update it fast enough. The reason being that even at 16 Mhz it takes hundreds of cycles to actually change the state of a pin. So the display would actually look something like Fig. 10 Left. One line at a time, slow enough that it was very visible to the naked eye. Fig. 10 Right is with the CLK pin disconnected and CLS set to high. The reason it looks glitchy is because random static charges and stray capacitance within the display confuse the controller inside it, and cause it to freak out. Otherwise a properly performing display looks like Fig. 11.
What I did for fun one time was configure PWM on pin 8. PWM stands for pulse width modulation. How it works is there is a timer built into the microcontroller’s circuitry that outputs a signal at around 490.196Hz. Having plugged pin 8 into the CLK line caused one line to be drawn every other clock cycle, meaning the full display was drawn ~35 times a second. This is too slow for persistence of vision because the LEDs don’t glow after they’ve been powered off, so it looks like a jagged waterfall that only sometimes can be made out to look like a word. To solve this I used the TCCR4B register (Fig. 7 Bot Left) on the MEGA 2560 to change the PWM prescaler. PWM on these chips assigns a set of bits two jobs. One job to count, and another job to trigger at a certain number. By changing these bits around you can change how quickly the PWM pulses are generated. Here I changed it so instead of PWM frequencies generating at 490~Hz, they are generated at around 931kHz. That way the display looked more like it should and less like a glitchy mess. I used the Arduino forums for help with that, where they outlined specifics for each prescaler and how to setup other aspects of PWM.
A funny side effect of changing TCCRxB registers is their effect on timing as a whole. There are 3 timers total, and I changed timer#2. If I changed timer #1 I would also be changing how long the microcontroller would sleep for if I gave it a sleep() command.
The last thing I’ll talk about is the display’s 16 programmable character registers. With a few more lines of code that I at one point wrote but lost, it is possible to program 16 unique characters and call them like any other character in trans(). Their addresses are 0x80 through 0x8F in Fig. 4. With this I planned to make my display into an audio visualizer. What would be nice is the low impact on memory using these would have. If I used all 16 I would need 560 bits. Not bad.
Overall, interfacing with the PDSP188x is a lot of fun. And pairing this Arduino up with a Raspberry Pi could open up the possibility of hosting a server on your pc that would report CPU temperatures, current music playing, and email notifications to the RPi, which could then communicate to one or more Arduinos. Each Arduino daisy-chain about 4-16 of these displays depending on how much power is made available to them. And if I had enough time I would probably try to create an 80×30 terminal with these displays, since that would probably look really cool. In the future like I said before, I’ll focus on the PC to Rpi or PC to Arduino communication. The biggest hurdle being balancing time spent updating the display and bi-directional communication to the PC. I don’t know if I’ll be able to figure out asynchronous threading on an Arduino, although I know someone did at one point and showed it off at a DEF CON conference. I will probably have one Arduino act as the PC communication node which will delegate to other Arduinos over a serial bus what to display and how to display it. The main hurdle not being writing and communicating, but writing in a loop over and over, say a waveform, to the display and having time to communicate back to the PC. Another solution is taking the brunt of threading or asynchronous display rendering to python or C# on the PC. One inherent downside being if Windows has a hiccup the display will also likely halt updating.
I hope this post was insightful into how I got started on making a not-often-used display work. With just a lot of help from the datasheet and a little prior electronics knowledge, I got a really old display working. And fun fact, at my time at Cisco, I saw Sun Microsystems SPARC servers used in telecommunications using these in their front panels. They were being recycled. My one regret being I wasn’t able to take them home.
All photos of devices/code were taken by myself and written by myself)