I have never seen a clock (or anything else for that matter) with 3x5 matrix displays. So when I gave away the analog clock in my radio room I and needed a new clock I decided to tackle that challenge.
First I made a quick study, to see what I needed:
This still shows the original numbering which I abandoned quickly while developing the code. I counted the LEDs I would need, which was 94. I then ordered 100 of these orange 5x5mm square LEDs. At first I thought that the PIC would be able to drive all rows easily but that idea was quickly abandoned. I then realized that I still had a LED driver IC with 16 outputs. Since I needed 19 columns (3x6 plus one for the colons) I would have needed a second chip. Being the frugal developer I decided to use three ports on the PIC instead:
The PIC first drives a row while its data is shifted into the STP16CP05 LED driver. This is repeated for each row. Columns 10-12 (hex) are driven separately. The data for the shift register in the LED driver is taken from five lookup tables. The colon is not looked up because it's either steady (sync'ed) or flashing (unlocked). The original idea was to make a nice light show but that idea was abandoned because it was deemed too complex to implement (with more lookup tables) and because it almost never shows.
The rows are driven from the PIC via P-MOSFETs because the PIC is unable to furnish the needed current. This is set by a resistor on the STP16CP05. The maximum current needed is 19x15 mA=285 mA with is way too much. The electrolytic C is 1000 µF and is needed to cope with changes in current as LEDs are switched on or off. The NDS0605 SOT-23 SMD transistor was in my MOSFET tray and luckily enough I happened to have exactly five. The 2N7002 N-MOSFET (also SMD) I had many more of...
Once the LEDs arrived I had to mount them on the prototyping board. This proved to be a challenge because they tend to fall out of the PCB during assembly. Eventually I resorted to lash the board to a piece of wood:
The code (asm file) for PIC16F628A posed some headaches. As I had had issues with lookup tables before that was easily solved (preload PCLATH). The lookup tables themselves were created quickly, taking in account the shapes of each number. I especially like the form of the numeral 1, which is something you cannot do with 7-segment displays. Another catch was the use of STATUS,Z instead of carry. This caused some weird behavior when the test would pass when it should have failed (test for 59th second). The funniest bug was the mask for the parity check for the hours. It passed until 20:00 after which the time was stuck at 19:59 because the DCFOK flag was kept set. The mask was wrong because there are only six hour bits (01, 02, 04, 08, 10 and 20) and seven minute bits. The txtempus DCF transmitter for Raspberry Pi helped enormously to debug this. Great name too, "transmit time" (in latin). Also the parity check routine that I found here was a great find. My routine would have been much more inefficient.
The code runs solely on interrupts. There are three: DCFHandler, Timehandler and Multiplex. DCFHandler gets triggered from Timer0 and runs every 50 ms. It looks if the DCF input is high or low and decided what to do with the outcome. If it's low it will test if there are pulses at all (DCFLOSS) and then if the preceding HIGH pulse was 1, 2 (DCF databit zero) or 3, 4 or 5 (DCF databit one). If it's longer the DCF data is deemed invalid. After that, the databits are rotated into the minutes- or hours buffer, depending on the count of received bits. If it's high it will test for the 59th second. If this test is OK it will decode the DCF data in the buffers, else it will again deem the received data invalid.
Once the DCF data in the buffers has been rotated into their respective registers (minutes, 10 minutes, hours and 10 hours) the parity is checked. If the test fails again the data is discarded. If the tests pass DCF data is valid and the registers are diplayed. If DCF data remains invalid the clock runs in unsynchronized mode. And runs slow...
The timehandler runs on Timer1 and is triggered every second. It increments the second and, if DCF data is invalid (SOLID is 0, flashing colon) it also handles the hours and minutes like the regular clock the code is copied and pasted from. Of course not without its bugs (STATUS,Z!).
Finally Multiplex. This runs every 4114 µs and is triggered by an unreloaded TMR2 because that saves two clock cycles. First it switches to the next row (0-4). Then it looks up the pattern for each number from a lookup table associated with that row:
Then it outputs the column data into the shift register of the STP16CP05 (columns 0x00-0x0F) and columns 0x10 and 0x11. The colon (0x12) is output last, flashing or not. Finally, the STP16CP05 output is latched (LE) and re-enabled (OE) and the interrupt handler exited.
The results are great!
At the bottom left my two test LEDs are still there. The DCF LED is still functional in the code, the yellow TETS (sic) LED is not but could be used for DST indication or something. The light patterns are just lovely:
The colon is flashing during syncing and is solid when synced to DCF time.
Future developments? I discovered that 5x7 LED matrix displays also can be used when rotated horizontally:
Only the last column is not used, and of course the columns between the numbers. Then I could design a nice PCB layout with SMD PIC and STP because the DIL version of the latter is not made anymore.
Back to the homepage
Date: 25 September 2019
This software is licensed under the CC-GNU GPL.