07/25/2019 at 14:36 •
Quick update, files are uploaded so feel free to make your own or modify to your heart's content. I'll add the 3d printed frame stl/solidworks files shortly, but everything else should be good to go. I originally wanted to make a kit but I just don't see that being possible given my current work load. The only restrictions I have are that any derivatives of my board design or firmware must also be released as open source and the original work be attributed to me (Shawn Maxwell, sjm4306). Other than that have at it!
03/25/2019 at 12:54 •
So to start, here's a quick little demo of the current state of the software I described in the last log.
A main concern when I first started this project was that there would be too much light bleed between nearby letters, thus making the display difficult to read for lack of contrast. To help on the pcb side, I opted for black soldermask, and additionally used the copper layer to try and block as much light as possible, but none of these would help with light scattered internally in the pcb's FR4 material which is a yellowish semi-opaque hue that works pretty well as a diffuser. I've gotten tons of suggestions involving using vias around each letter to try and contain as much light as possible within each letter, but I really liked how clean the top surface looked without having to riddle it with vias.
So the main method I could fall back on was the age old method of using a physical separator between each LED to try and contain as much of the light as possible. Luckily I have a 3D printer and was able to very quickly print out a few test prints to iterate a light box design which also doubles as a stand for the clock, holding it up at a slight angle.
In the end this is how this iteration of the design looks:
Definitely not perfect, but at least for me it is good enough considering the trade-off between inter-LED contrast and cost/simplicity of design (I intend to sell a kit of this project so the less complex the design the better).
03/15/2019 at 19:25 •
This may be a little dry but let's go through how/why I designed the software for the clock in the way I did. Keep in mind I'm definitely not a pro software developer so the way I ended up doing things are very likely not optimized or conventional. With that disclaimer out of the way let's get into it.
For starters I needed a way to autonomously scan the 12x10 led matrix so my main portion of code could talk with the RTC chip, generate animations, etc. To do this I've opted to use an interrupt that triggers at something like 4kHz. Each time the interrupt executes it draws the current column of LEDs (stored as a global array) and then increments the column counter so the next time it can draw the next one until it reaches the last column and starts all over again. So you can see how with this method I can update the display image by writing to the display array (acting as a crude display buffer) while the interrupt worries about how to draw it. Additionally by choosing to skip cycles or not it can even make the display look brighter or dimmer!
One small complication though, in order to make the board easier to wire I ended up assigning all the rows and columns to seemingly random I/O (and not neatly/organized in pin order like I normally would). This means that I cant just do a simple quick port write and update all row/column values correctly. To get around this I created a pin map and associated function that takes the desired row and column states and toggles all the pins correctly despite them being out of order. This function basically ended up being a big switch statement.
Now how is the display actually multiplexed/scanned? Well to start an entire column is lit at once. To do this the vertical strip of LEDs in the column have their anodes driven in the pattern we want them to light up. To actually select the correct column we then set the pin attached to all of the cathodes of the LEDs in that column as an output and pull it low, while at the same time setting every other column pin to an input (high impedance so no current can flow). Simple right? There are tons of examples and schematics that explain it much better than me if you google something like "how a led matrix works".
So from the perspective of the programmer, all you have to do to write to the display is set the pixels stored in a display buffer array and they seemingly magically light up the corresponding LED! Cool, right!
So next step is handling button inputs. Well I just used another interrupt that gets triggered when a button input pin changes states (known as pin change interrupt, a feature the PIC supports on certain pins). When my interrupt sees a button pressed it sets a global flag that any part of my program can read, act on, and then clear to let every other program know that the button press has been serviced.
The reason I used an interrupt for the display scanning was to save me from having to manually do it within my main loop and for an asynchronous button input because sitting there and repeatedly reading a button waiting for it to be pressed is a waste of clock cycles. So interrupts handle both of these tedious tasks and leave the main part of my program enough computational freedom to actually do something interesting like telling the time or displaying neat animations.
So how does it keep track of time then? Well I took the easy road and used a DS1302 serial real time clock chip to do it for me. It can even charge a supercap to keep time for awhile when power is removed. I talk to the RTC over a three wire serial interface that is basically SPI, using a soft serial library I wrote so I can use any GPIO pins I want.
The final thing I had to contend with was adding animation modes. So far it has seven modes: chase, random, twinkle, pong, rain, bouncing ball, and ripple. Chase lights up an LED that runs left to right and top to bottom. Random hypnotically fills the screen with randomly lit pixels. Twinkle randomly turns on and off random pixels all over the screen. Pong is basically a one paddle game of pong where a ball bounces around and hits a paddle at the bottom that follows it. Rain is a randomly generated storm of pixels falling down (almost reminiscent of the infamous Matrix falling letters animation). Bouncing ball is basically Pong but without the paddle and added randomly changing speed each time the ball hits a wall. Ripple is a randomly generated spreading wave (not unlike drops of water disturbing the surface of a puddle of water).
You may have caught on that many of the descriptions for these animations use the word random. I wanted to display many of the animations differently each time they are displayed but how to do that with a microcontroller. Random for computer systems is pretty tough to pull off without reading some kind of random input. Well once again I cheated a little. I used rand(), a standard C function to generate a pseudo-random number.
I say pseudo-random because it is actually deterministic (by merit of it being a linear shift feedback computation), but for short enough runs a normal person wont be able to notice that it isn't really very random. The issue is if you just use rand() then every time the micro turns on it will generate the same exact series of "random" numbers ... this random function is starting to really sound not very random!
Well to fix that we can "seed" it with different starting numbers using the function srand (srand = seed+rand ... very creative ...). But to make sure the seed is then not always the same we take advantage of the fact that this device is a clock and when the user starts an animation is not likely to be at exactly the same time as the last time. So we set our seed to a function of the current time (I basically concatenated the hours, minutes, and seconds into a large number).
So that is pretty much an overview of all the semi-interesting bits of the software. I still have a few tiny bugs to work out related to one or two of the animations getting stuck randomly (I'd place a bet it is something related to the random aspect of them) but I'm happy to say that time display and setting modes work 100%. In the next project update I'll show the design and results for the 3D printed light box, but for now I'll get back to bug hunting till everything works perfectly.
03/15/2019 at 19:24 •
Huge thanks to JLCPCB (who sponsor my various projects and videos as well as provide the pcbs). $2 for 10 PCBs (in 48hours)!!!: jlcpcb.com/m
So a quick intro to the main design philosophy for this project. I've seen tons of word clocks using everything from ws2812s, off the shelf led arrays, transparency masks, etc and I've always wanted to try my hand and making my own. I am sure I'm not the first person to think this, but after making a few projects with the help of JLCPCB, my youtube sponsor, I wanted to push both what I am comfortable with in terms of pcb design and JLCPCB's manufacture process. Here's an overview video of the first prototype (with the firmware partially working):
Basically I wanted to make a word clock that would be small (akin to a small alarm clock) to sit inconspicuously on my desk, run happily off 5V via USB, and be fairly easy/cheap to make. So to achieve this I've designed the front lettering mask as a pcb that snaps away from the rear pcb that contains all the leds and micro as shown below.
To achieve this placed the letters on the top stop and restrict layers to keep both the soldermask and copper from obstructing the light. On the bottom then all I had to do is do the same in a square shape that overlaps the letter. I expect the light will be diffused pretty well through the FR4. And that is really all I had to do to achieve this sharp looking effect.
Now one worry I have will be how to effectively block light between adjacent letters. Well here's hoping a 3D printed light box/midframe will work to box in each led and limit light bleed ...
So one final note, I wanted accuracy down to the minute, but having separate words for all possible minutes would make the array of letters larger than I wanted so I compromised by having words describe the time down to 5 minute intervals and then adding asterisks at the bottom to come up with the exact time. For example: "It is ten fourty five ***" would mean it is 10:48. Works for me!
In the next update I'll explain the software that runs on the PIC16F887 (and hopefully have it fully working and progress on the 3D printed midframe).