I've started learning electronics recently, following Chris Gammell's Contextual Electronics course, which is really good. One of the first exercises there is called "Getting to Blinky", and it's mostly about getting used to using the KiCad EDA suite, and getting over the initial barrier to getting PCBs made.
I worked through the tutorial, but didn't really feel like sending boards out just for a blinky. I wanted to do something more entertaining. It seemed like it might be fun to make blinkies that blinked messages. In Morse code? Yeah, why not?
The silly idea
So, how to do it? Four approaches came to mind:
Do it using 555 timers and discrete components only. That seemed like it might be hard.
Use some sort of serial EEPROM and just clock the Morse on/off bit sequence out of it. That seemed like it might be boring.
Use a microcontroller (maybe one of these 3-cent ones?!). That seemed like it might be cheating!
Use a 555 timer plus some 74xx logic. That seemed like it might be the sweet spot for learning some (very simple) electronics.
So I set myself some ground rules: no programmable components, just a 555 timer, some logic gates, counters, discrete components and LEDs. I often find these kinds of "artificial" restrictions are really useful for learning, even if they lead you to do things differently from what you would do if you were doing something "for real".
I started off by working out some circuits by hand, including some simulations using Logisim:
After doing a few by hand, I had the slightly silly idea to automate it all. Thus was born the idea of Morse Blinkies as a Service! You can go to mbaas.skybluetrades.net, type in some text, set some rules, and get back a ZIP file containing a KiCad project with a netlist that implements a blinky blinking out your message in Morse.
It's all quite useless, but it was a pretty good learning exercise, and it's easy to imagine how the techniques I used could be applied to more useful applications.
How it works
The back end is written in Python and does the following:
Takes your text and converts it into a bit sequence representing the Morse code version of your message.
Generates some logic expressions to say when a blinky LED should be on, depending on the binary value of a counter that counts the number of bits in the bit sequence (rounded up to the next power of two to make resetting the counter simple).
Simplifies the logic expressions (which are in disjunctive normal form (DNF), so represented as a bunch of ANDs feeding into a big OR), using the Espresso logic simplifier. This results in a smaller DNF expression, which will be represented by 74xx logic gates.
Some custom Python code places the logic expressions on realisable gates (2-input ORs and 2-, 3- or 4-input ANDs, just to target the most common 74xx chips), then assigns those gates to real chips. (This is the most complicated part of what I wrote, and definitely the part with the most fatal flaws!)
Generates a KiCad netlist for a 555 timer, some counters (74HC193), the necessary 74xx logic chips and some LEDs (and maybe some driver transistors). This is done using SKIDL, which is a tool that I really like. Learning about SKIDL was probably the most useful thing that I got from this whole project.
The netlist is zipped up with some supporting material to make a KiCad project and you can download it. You need to do the layout yourself, pulling the netlist into KiCad's PCB editor.
Using it, and unexpected difficulties
So, does it work? Sort of. It definitely generates valid netlists, and I'm pretty sure that those netlists really do represent circuits with the functionality that I claim (blinking an LED...). But, but, but...
It's actually pretty useless.
I'd originally planned to make a "KICAD" blinky, with one group of LEDs per letter, each blinking out one letter in turn. All the MBaaS stuff to that seems to work fine, and the results look reasonably convincing.
However, the circuits are really hard to lay out. The "KICAD" blinky circuit has 1 7404 hex inverter chip, 3 7408 quad 2-input AND gate chips, 7 7421 dual 4-input AND gate chips and 10 7432 quad 2-input OR gate chips, for a grand total of 21 logic chips (plus 2 four-bit counters, plus the 555 timer and its associated discretes, plus LEDs, transistors and associated resistors). It's all a bit much, and the combinatorial logic is just a huge tangle of connections going every which way, which makes it almost impossible to route (at least for me: someone with real experience with KiCad might do a better job!).
So, faced with ignominious defeat, I retreated. First I thought I'd do an "IAN" blinky. No dice. Still too hard to lay out. Then I thought I'd do a "CE" blinky (for Contextual Electronics). After a couple of hours of routing and muttering, I gave up on that one too.
The grand outcome of this whole exercise is this:
What's it do? It blinks "K" (-.-) again and again and again... A single letter (which in this case gives a bit sequence of length 16) was about all I could manage. I'll get a PCB made and populate it, just to demonstrate that it can be done, but it's not the most impressive outcome!
This was kind of a silly project, but I did learn a lot of different things, so I don't consider it too much of a failure.
In terms of electronics, I learnt a little bit about 555 astables, 74xx logic chips, LEDs, transistors (a small dose of each of those things);
I learnt a fair bit about KiCad, including rummaging around in some of the file formats, and I got to spend a bit of time trying to do PCB layout for some "unfriendly" circuits;
I learnt about SKIDL, which has a lot of potential for "real" things.
Actually doing the blinky generation as a programmatic thing was interesting, and it forced me to be much more explicit about things (which is usually the case when you program something, instead of fudging it by hand).
A couple of questions came out of this:
First, why was the PCB layout for these things so hard? I have an idea about this, and an idea for how to fix it. Basically, the Python code that I wrote to turn logic expressions into assignments of gates to real 74xx logic chips doesn't know at all about routeability: the chips are abstract, the gates are abstract, the connections between them are abstract. So everything ends up being a bit of a tangle.
To fix this, one approach would be to add a routeability optimisation step after assigning gates to chips. You'd set up some sort of semi-concrete model of placement of chips on the PCB (lay them out on some sort of grid, controlling their orientation, assigning them to one side or the other of the board), along with a heuristic metric for routeability. This could be some sort of weighted sum of ratsnest lengths once the chips are placed, plus some terms counting the number of "crossings" in the ratsnest, maybe using something topologically a bit more clever for that. (This placement and routeability model doesn't need to be exact, since it's not going to be doing the routing itself, just trying to place gates on chips so that the circuit is easier for a human to route.)
Then, starting from a feasible assignment of gates to chips (like the assignment generated by MBaaS now), you would follow a standard optimisation approach, making "moves" in your placement (swap pins, swap gates within a chip, swap gates between chips, change physical placement of chips, etc.) and seeing which arrangements give the best routeability score.
That's obviously a non-trivial amount of work, and wouldn't teach me any electronics, so I'm not going to do it, but I think it would lead to more routeable assignments of gates to chips.
The second question was about SKIDL. It's pretty neat on its own, but it would be even neater if it was integrated into KiCad. Imagine if you could define SKIDL blocks in KiCad schematics, which would basically be hierarchical sheets with Python code inside them. That would give you the best of both worlds of code and schematics. At the moment, as far as I can tell, SKIDL is an all-or-nothing proposition: you need to generate all of your netlist using it. What would be nicer would be if you could drop bits of SKIDL into a schematic, taking advantage of code when you need to do tedious or repetitive things, but keeping the visual representation of a schematic when that makes more sense.
Again, that seems like it would be a lot of work, but I have a project I want to do soon that might benefit from it, so I might look into it.
Anyway, enough of Morse blinkies. I'm going to do some more "normal" electronics this week!