256 LED SMD “Practice” Board
I can’t resist a ridiculously cheap LED matrix (as might be apparent by now), so when this popped up in the usual enticing “deals” section of a popular overseas electronics store, I must admit I was weak 🙂
It is described as some combination of the following:
- Electronic LED Display Kit 64LED/256LED Red LED Dot Matrix Display Kit SMD Components Soldering Practice Board DIY Kit
- DIY Electronic Kit SMD LED Advertising Screen 256 Display Units Soldering Project Practice Suite Component Welding Training
And variations thereof.
It is typically available for around £3-4 for the 256 LED version and £2-3 for the 64 LED version. One thing I thought was quite interesting is that the LEDs go right to two edges of the PCB so that does make me wonder if a couple of these could be tiled together.
It also includes a breakout header hinting at the possibility of customisation.
https://makertube.net/w/dRN6hyoHKTXez2PwyKs1A4
The Circuit Design
There is a bit of information online, but not as much as I’d like. Essentially it is a microcontroller, some 595 shift registers, driver transistors, LEDs and a few ancillary components in what would probably be a fairly standard arrangement. There is a low-res schematic I’ve found:
The bill of materials names the 8-pin microcontroller as a DX156, but I can find no information about it online. Interestingly though, all six pins that are used in the circuit are broken out to a header, so if the microcontroller is left off the board, I don’t see why it wouldn’t be possible to drive the shift registers directly via the header.
The full BOM is as follows:
- 1x 220uF Electrolytic capacitor (listed as 220-1000uF in the BOM, shown as 220uF in the schematic)
- 6x 0603 100nf (104) capacitors
- 16x 0603 5K6 resistors (1K is shown in the schematic, 2K7 is listed in the official BOM!)
- 16x 8550 transistors (PNP apparently)
- 256x 0805 red LED
- 4x 74HC595 shift registers (SOP-16)
- 1x DX156 microcontroller (SOP-8)
There is a comment in the description of the kit about changing the display:
” If you want to change the display content, you can connect the control signal from interface JP1 without soldering the included microcontroller, and any content can be displayed. (You can refer to the relevant information of our store’s 16 * 16 dual color dot matrix, which is consistent with the interface of the microcontroller. This interface needs to be provided by oneself and is not included in the kit.)”
But I’ve not found who the “our” is in “our store” or which kit/module is being referred to, so I’ll come back to that in a bit. Again this confirms that for now, I’ll leave the MCU off until I’ve decided what to do.
Other things I note from the schematic:
- Naturally the four 595s are chained together. I can just about make out the chain.
- The last 595 OUT is connected to J4 so further chaining should be possible.
- Other jumpers include J1, J2 for POWER and GND; J3, J5, J6 appear to be connected to the matrix.
- As already mentioned, all used microcontroller pins are broken out to the 6-way header.
An LED will be on when the Hn signal is LOW and Rn signal is LOW. This is because Hn going LOW will allow the PNP transistor to conduct, thus making In HIGH. when In is HIGH and Rn is LOW the LED will light up.
Building the Kit
The approach to take for building should be relatively obvious. It will just take a fair bit of patience! One initial consideration – do I test each LED prior to soldering, or just go for it and rework anything that might not work…
Everything apart from the 100nF capacitors and resistors has a polarity to watch out for.
I’ve decided to fix the non-LED components first, thinking that I can then test each row of LEDs as I fix them to ensure they all work before moving on. So I built it in the following order:
- Transistors
- Resistors
- Shift registers
- Ceramic capacitors
- Header
I’ll save the electrolytic until last, as it is presumably just for stability of the power supply, which can be external for now. And I think I’ll leave the MCU off for the time being too. I might use a SOP-8 to DIP breakout to allow me to use it to drive the board via the header.
Note: after starting on the LEDs, with hindsight, soldering on the header was a mistake. It allows me to test the board, but it means the board no longer sits perfectly flat on the desk whilst working on it. Something to consider for any similar activity in the future.
Determining the polarity of the LEDs is slightly challenging, but there is an arrow on the back, and if one looks carefully a dot on the front, that both indicate the cathode (line). When assembled in the orientation shown below, the dot is on the right-hand side (excuse my dodgy soldering – this is meant to be a soldering practice kit after all).
After one row I took a break to get some code running (see below). Then added three more rows and am now taking a longer break! Its slow, but steady progress 🙂
Initial Testing
The initial plan, once built and shown to be working, was to reprogram the microcontroller, but seeing as there is next to nothing published online about the DX156, it would be a lot easier to replace it with a SOP-8 footprint microcontroller that I already know how to use.
Unfortunately the pinout of the footprint doesn’t quite match with something like an ATtiny85… it’s close, so I don’t know yet if that would be an option.
Anyway, I’ve left off the provided microcontroller and for initial testing am driving the board via the 6-way header.
I anticipated just using the provided microcontroller on a SOP-8 to DIP-8 breakout but couldn’t find one to hand (I’ve definitely got one somewhere), but that will have to come later, as I now have one on order.
Instead, after soldering on one row of LEDs, I jumped into hooking it up to an Arduino which meant I had to figure out how to drive it myself.
Programming
The 74HC595 has the following pinout
The basic use in a circuit requires the following:
74HC595 pinFunctionHeader pinPCB or ArduinoVCCVCCVCC+5VSER (or DS)Serial / data inIND11/OEEnableOEGNDRCLKLatch (Storage register clock)STBD8SRCLKSerial clockCLKD10/SRCLR (or /MR)Clear / master re-clearN/C+5VQH’Data outN/CNext 595 in the chainGNDGNDGNDGNDQA-QHIndividual outputsN/CThe LEDs
Given how everything is connected according to the schematic, the decoding works as follows:
- There are four 8-bit shift registers, giving a total of 32 bits to drive the 16 ROWS and 16 COLUMNS.
- Confusingly, the ROWS are labelled H1-H16, which become I1-I16 after the transistors; and the COLUMNS are labelled R1-R16.
- QA to QH are bits 0 through 7 for each shift register, so are H1/I1/R1 to H8/I8/R8 or H9/I9/R9 to H16/I16/R16.
- Bits are streamed to the shift registers most significant bit first.
- For an LED to light up, the Hx and Rx must both be LOW.
This all means that a 32-bit value encodes the ROW/COLUMN information as follows:
Bit: 31...24 23...16 15...08 07...00
Row/Col: C16..C9 C8...C1 R16..R9 R9...R1
Schematic: R16..R9 R8...R1 H16..H9 H8...H1
The algorithm to push data out to the shift registers is thus as follows:
void shiftWrite32 (uint32_t data) {
digitalWrite(SHIFT_LATCH, LOW);
digitalWrite(SHIFT_CLOCK, LOW);
digitalWrite(SHIFT_DATA, LOW);
// Shift data MSB first
for (int i=31; i>=0; i--) {
digitalWrite(SHIFT_CLOCK, LOW);
if ((data & (1UL<<i)) == 0) {
digitalWrite(SHIFT_DATA, LOW);
} else {
digitalWrite(SHIFT_DATA, HIGH);
}
digitalWrite(SHIFT_CLOCK, HIGH);
digitalWrite(SHIFT_DATA, LOW);
}
digitalWrite(SHIFT_CLOCK, LOW);
digitalWrite(SHIFT_LATCH, HIGH);
}
Note the use of “1UL”. Without the “UL” this does not appear to get extended to a full 32-bit value. This gave me quite a bit of grief, until I hooked up an oscilloscope to CLK and DATA and could only see half the data getting written out!
At some point this would be worth re-implementing using PORT IO, but the use of digitalWrite will do for now.
This means I can cycle through each column of LEDs with the following code:
uint32_t dataval;
for (int i=0; i<16; i++) {
dataval = (~(1UL<<i))<<16UL;
shiftWrite32(dataval);
delay(50);
}
This will continually set one of the top 16 bits LOW in turn, whilst keeping all lower 16-bits LOW, thus illuminating each column in sequence. This will light up all completed rows as I’m doing nothing to select the row yet.
Note again the use of “UL” to force 32-bit arithmetic.
Here is a more complete version that now includes selecting the row too.
uint32_t dataval;
for (int r=0; r<16; r++) {
for (int i=0; i<16; i++) {
dataval = (~(1UL<<i)) << 16UL; // Column
dataval |= (~(1UL<<r) & 0xFFFF); // Row
shiftWrite32(dataval);
delay(50);
}
}
In both cases the default “off” state is a bit high (so 0xFFFF for each of the 16-bit chunks). To select a row and column, the corresponding bit has to be set to 0, hence using ~(1<<bit) which is NOT (bit).
Scanning the Display
The above is all fine for some simple tests, but really I need a simple way to scan the display independently of any running code and for that, the best way is to use a timer interrupt to trigger the updating of the display.
#include <TimerOne.h>
uint16_t disp[16];
void shiftUpdate() {
uint32_t dataval;
for (int r=0; r<16; r++) {
dataval = (~((unsigned long)disp[r])) << 16UL; // Column
dataval |= (~(1UL<<r) & 0xFFFF); // Row
shiftWrite32(dataval);
}
shiftWrite32(-1);
}
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
pinMode(SHIFT_LATCH, OUTPUT);
pinMode(SHIFT_DATA, OUTPUT);
pinMode(SHIFT_CLOCK, OUTPUT);
shiftWrite32(-1);
Timer1.initialize(20000);
Timer1.attachInterrupt(shiftUpdate);
}
void loop (void) {
// update each row of the display using disp[row]
}
This uses the same shiftWrite32() function, but now it is called for all rows every 20 ms using the TimerOne library.
The use of shiftWrite32(-1) is a simple way of clearing the display as it will set all bits HIGH. I have to do this at the end of the shiftUpdate() function to clear the display after the last row update, otherwise the last row will remain lit until the next shiftUpdate scan. This makes the last row appear brighter than all the rest as it is on for slightly longer.
I’ve used 20,000 in the call to Timer1.Initialize() as 20mS appears to give a good balance between a flicker free scan of the display whilst allowing some spare CPU time to actually run the loop. I’m still using the relatively slow digitalWrite() function calls in the shiftWrite32() function, so this is one area for obvious performance improvements if I need to do something better.
There is no buffering between writing to the disp[] “screen” array and actually updating the display, so it would be quite possible to get a screen update partway through calculating a new display. If this becomes an issue then it would be possible, memory permitting, to use a double-buffering arrangement and ensure the screen buffer that is written to the display is never the one being written to by the main loop, but I’ve not bothered about that right now.
Conclusion
After realising that reprogramming the original MCU wouldn’t be an easy thing to do, I did wonder about the utility of this board, but actually driving it from an Arduino turned out to be relatively straight forward.
In the end, my total SMD parts count was:
- SMD parts soldered: 256 + 16 + 16 + 6 + 4 + 1 = 299 (I think)
- SMD parts lost = 1 (one LED pinged off into the great unknown)
- SMD LEDs tested as FAIL after soldering = 6 (thankfully there were plenty spare!)
- SMD parts unused = 1 (the original microcontroller)
- Hours spent squinting at small components = 4 or 5 (approx, in shifts)
- Completed and working 256 LED matrix board = 1
I’d say that was a success for me.
Kevin
#arduino #gameOfLife #ledMatrix