Despite looking like a complicated instrument, the synth was actually one of the easiest to manufacture and to code the firmware for. In fact, it only took a few hours this afternoon to get a "working" miniature synthesizer!
As ever, the first thing to do was create a PCB with a working PIC microcontroller for the USB/HID interface
The acrylic keys sit over a large, single-piece, PCB.
There are 17 pushbuttons, arranged so that each one sits under the front-most edge of each individual key.
These pushbuttons are routed to the digital input pins on our old friend the 18F2455 PIC microcontroller.
After all our fancy routing and re-naming buttons, pins and ports, we forgot to connect the ground pin of the USB socket to the ground trace on the board! D'oh.
Since we discovered hot air soldering, and how surface mount components can be just as quick and easy as (and sometimes quicker and easier than) through hole components (no pesky drilling) we've decided to stick with the surface mount version of this chip, rather than the earlier favoured through-hole version.
That's all there is to this synth really - we've allowed for a rotary dial/potentiometer on one of the analogue pins, and perhaps a separate pushbutton to switch sustain on and off. But really, it's just a simple PCB with a load of buttons.
We decided to use the PIC's internal pull-up resistors on PORTB, which means that all input pins are high and when a button is pressed, the input goes low. Likewise, we wired the remaining buttons up the same way (high with no input, low when the button is pressed) which means that for 17 keys, we only used 9 pull-up resistors instead of needing one for each button.
The remaining 9 inputs were spread across PORTA and PORTC.
This makes reading the data back a little difficult but not impossible. We use three different byte-sized buffers, to report back the status of every button over USB. PortA is bit-masked by reading the value off the port, and OR-ing with the input pins not used (in our case, only RA1-RA5 are used, so we OR the value on PORTA with 1+64+128 = 193) This is because RA0 is the analogue input (2^0=1) and RA6(2^6=64) and RA7(2^7=128) are not used, so we will always report these bits as high (not pressed) irrspective of their state.
By doing this, we should get a unique combination of numbers on the three buffer bytes, for every combination of key presses. This allows up to 17 keys to be pressed and recognised - a truly polyphonic synthesizer!
To find out which buttons returned which values, we knocked up a simple VB app which reads the first three bytes back from the USB device and displays them on screen.
This photo shows how pressing the first "key" on the keyboard results in byte3 taking the value 191. We went through every single key, pressing each one in turn, and wrote down each of the three byte buffer values. This map of values will form the basis of our playback code.
Because the input pins are high when inactive and low when active (when the button is pressed) we can invert the key buffer values (subtract from 255) to work out which key (or combination of keys) is pressed. When two or more keys are pressed, their values are simply OR-ed together.
For example, if a key with the inverse value 64 is pressed together with one having an inverse value of 4, together with one having the inverse value 1 (when playing a chord or triad, for example), the keyboard input buffer value will simply be 64+4+1=69
By taking the values from each of the three byte buffers, and using simple logic queries (AND/OR) we can easily identify exactly which combination of keys has been pressed at any one time.