Intro: SN76489 Synthesizer
This instructable is intended to provide accommodation for anyone interested in interfacing a TI SN76489 sound chip with a microcontroller, particularly Microchip PIC18F series microcontrollers.
There are ancillary features in my synthesizer design which will not be discussed here. Once you get the critical parts of the system working you will presumably be capable of designing and implementing further features as you like. You will also find other literature online of similar projects with different microcontrollers from which you can adopt the theory. Personally I found the early stages of development most challenging, so that is what I will focus on.
Step 1: Outlining Your Design Requirements
How exactly you approach this project is going to be based largely on your intentions for the finished product (i.e. do you want MIDI control? USB interfacing? Analog effects?) and what parts you have available. This is your time to do the trades and preliminary research to determine what is feasible for you. Read all available datasheets like a madman and compile as much reference material as you can in your project notes.
Most parts can be ordered online through suppliers such as Mouser or Digi-Key, but the keyboard is somewhat unique and I suggest you source one from a relatively cheap electronic keyboard and then design everything around that.
I recommend that you order more parts than you need and be able to reuse them later, rather than find yourself short one and unable to continue. Whenever possible order enough parts to make two complete projects in parallel; it makes the debugging stage go much quicker. Also be aware of bulk discounts. You may only need six capacitors at 30 cents each, but if you get ten the unit price could drop to 15 cents each.
Step 2: Understanding the Keyboard
There are two major types of keyboards you can expect to find (polyphonic and monophonic), depending on how much expense was spent in manufacturing, but they will be interfaced in roughly the same way.
The vast majority of keyboards use what is referred to as a "switching matrix". In short, the keys are mapped out on a two-dimensional plane where each key has its own (row, column) coordinate.
What I mean by "polyphonic" and "monophonic" in this context is whether or not the columns are isolated sufficiently at the hardware level to where you can press more than one key without "ghosting". This gets beyond the scope of the instructable, but please do look it up if you are interested.
You may also encounter "velocity sensitive" keyboards; these will have two coordinates per key and you can either wire one or wire both if you want to support the velocity sensitive functionality. The accompanying code on your microcontroller will have to account for this of course. When you find a keyboard to source for your project, look for the flat ribbon cable connecting it to its original control circuitry. Those conductors are the rows and columns which you will interface with your controller board. In order to map out their location, you will need to either follow the traces by eye or press a key and find a short between rows and columns with a multimeter. Make a table in your notes for this project which maps these coordinates to the musical notes. Usually we center the keyboard at octaves 0 and 1.
Once you settle down on a donor keyboard, your enclosure should be designed around it.
Step 3: Designing the Enclosure
If you intend to make your own original enclosure, start by measuring the dimensions of the keyboard and looking for ways to interface it with the rest of your enclosure. This is most often done by adapting the original mounting brackets, and you may even be able to reuse mounting hardware.
In my design I specified two brackets which the keyboard could be screwed into. (See picture)
Depending on what kind of ancillary features you plan to include, you want to consider different kinds of control panel layouts. I recommend that you make enough room for the most complicated control panel you could possibly populate, but not drill any holes for mounting components to the control panel until the entire panel is finalized. This usually cannot be easily reworked, so make sure you know what you want beforehand. Revisit the control panel after your circuit has been designed and your working board is ready to be installed.
You may find that modeling your design in CAD software such as Autodesk Inventor or SolidWorks is beneficial, but for something this simple I did not. I feel that there are too many variables which you can't account for and interfaces you can't imagine if you are not handling and working with the parts in person. I did model this project and ended up redoing every interface at least twice, not to mention several complete revisions which did not pan out due to various circumstances.
When designing the enclosure it's better to be aware of what you have available, so think of what you want to make and then go to the hardware store and see what they have to make it happen. Perhaps you want a 4x12" control panel, but you can find a 6x18" sheet at the store; adapt your design to reduce the amount of custom parts you need. Once again, it's also better to buy more than you need and return it or end up one short, so keep that in mind.
If you don't have any immediate ideas for designing a keyboard enclosure this may not be the right project for you, but you can reference plenty of historical keyboard and synth designs and adapt them as you like. Most people just making a quick hobby project will likely opt to reuse the enclosure they got their keyboard from.
Step 4: Understanding the Microcontroller
When you are choosing a microcontroller, the first thing you need to do is determine what kind of hardware support it has and if it is sufficient for your project. If you want to include MIDI support, for instance, you will appreciate the microcontroller having a UART module. Likewise for USB. I chose a PIC18F2450 for my project for these two reasons; it has UART and USB hardware on board.
If you are interested in using a PIC microcontroller, see this reference which contains a tabulated representation of different microcontrollers based on feature set:
Less importantly, make sure you have enough I/O on your microcontroller. "Less important" because there are many ways to break out pins and effectively multiply them based on what glue logic you choose to implement. ICs such as the 4051 or 4016 can be used as analog multiplexers to break out a single analog or digital pin. I have used two of these to consolidate the rows and columns of the scanning matrix on this project. Another option is to use a PISO (Parallel in, serial out) shift register (e.g. 74*165), but these can only be used to break out digital pins.
Breaking out I/O is useful to isolate potentially sensitive signals and to reduce clutter in wiring, so keep it in mind.
There's a chance you will choose a microcontroller in good faith and it will end up being insufficient for your endgame; it's a good opportunity to practice designing around a problem and a reality check to force you to limit your scope (at least for revision 1.0), so embrace it.
Step 5: Designing the Circuit and Flow of Development
Design and test the circuit in sections for minimal time spent debugging.
This is the order I recommend going with, as it will most easily allow you to build on your progress.
The output amplifier:
Once the output section is done you will more easily be able to see if the sound chip output is also functional and so forth. No code is required to test the output amplifier; you can wire it as a headphone amplifier and test it with your phone as input, then simply wire to the sound chip after it is found to work.
The SN76489 interface:
This is the first part of the project which requires code to interface, and you can work out some basic timing issues here. To test, I would recommend programming the microcontroller to have the sound chip play back a sequence of notes rather than one or two, it makes a more distinctive sound and emphasizes problems with insufficient signal levels or timing.
The keyboard interface:
Once your sound chip has been integrated in code and hardware, making the jump to integrating a keyboard will be largely trivial. Instead of playing a test tone, each key will control its respective tone and you will most likely have to wrestle with debouncing.
The MIDI/USB interface:
Finally you can make accommodations for external control to get the most out of your sound chip. If you design with this in mind from the onset, you can calculate key presses and handle them the same way as if they were fed in as MIDI so that methods can be shared between the two approaches.
In the steps to follow I will break down the aforementioned stages and how to approach them specifically.
Step 6: Designing an Output Amplifier
Our SN76489 sound chip and many other sound generating sources have limited output power; they can't effectively drive a speaker or what we call "line levels" in audio engineering. For this reason we want to include an output amplifier in our design to pick up the slack from out sound chip.
There are many ways to approach this, but for the sake of simplicity and because I found the series of chips intriguing, I decided to use the LM386 power amplifier. "Chipamps" such as the LM386 are convenient because they require little external circuitry and are consistent from device to device, unlike some discrete transistor designs which require matching (you may be tempted to go that route coming out of a circuits class). By reading the datasheet two or three times and reading about amplifier wiring methodology you should be able to breadboard an ideal LM386 headphone amplifier for testing and try implementing features such as gain control and "bass boost" to see what works for before finalizing it in your design.
Step 7: The SN76489 Interface
Our SN76489 sound chip uses an 8-bit parallel bus (D0-D7) to interface with a microcontroller or microprocessor. As you see in the datasheet, updates to frequency can require two byte transmissions while updates to attenuation/volume require one byte. Once you determine which I/O pins you want to use on the microcontroller, configure them appropriately as output, and then write a method to appropriately package any desired tone into two bytes for a frequency update.
Going from a key press in (row, column) format can be achieved in a variety of ways. Conditional statements, switch cases, look up tables, etc. Choose the method that makes the most sense to you.
There is some minor calculation that needs to be done in converting (r,c) to a control word for the sound chip. Here is my recommended flow:
-Translate from (r,c) to MIDI note number (e.g. middle C is decimal 60)
-Translate from frequency to a 10-bit control word as defined in the SN76489 datasheet
-Arrange the control word into a two-byte sequence to update the correct voice
Going from (r,c) to MIDI note number lets you pipeline MIDI info more easily and could help down the road; you have established the framework you just have to use it. You can see my personal reference table for octave 0, your coordinates will be different.
Attenuation control is easy and you can address it at any point. Getting down a two-byte transfer proves that you have a handle on timing so any further manipulation on this end should be trivial.
To test this part of the system try writing a loop to play a sequence of notes. As previously mentioned it is a good metric for troubleshooting.
Step 8: The Keyboard Interface
When considering the most suitable way to wire your keyboard to the microcontroller, I advise that you focus on handling the issue of contact bouncing.
In my solution I am scanning the rows and columns with 4051 ICs. When a column is "scanned" it is pulled high and when a row is "scanned" it is being read from. Columns effectively are given input and the rows are used as output.
The order of my scanning routine is to start with the first row, scan all the columns on it, and then move to the next row until the end is reached and everything wraps around. In my case the keyboard has 32 keys, so I have 4 rows and 8 columns. When a button is pressed one of the rows will short to one of the columns and if that column is high the row it is shorted to will also become high.
R1C1, R1C2, R1C3, R1C4, R1C5, R1C6, R1C7, R1C8, R2C1,..., R4C8, R1C1,...
This pattern is scanned sufficiently fast as to not be outrun by a human. In other words, it would not be possible for one to press a button and release it before the scan completes one cycle and that press is detected. This is a traditional method of taking input and is called "polling", as opposed to interrupt-based methods (most commonly) where the polling is still in place but is handled by a coprocessor that sends an interrupt to the CPU on a key press. In my case I had enough overhead on the microcontroller to handle this. The polling is effectively our mcu's idle state.
Considering that the only input to our mcu in my design is the return from the 4051 scanning rows, I have the somewhat easy option to feed it into a schmitt trigger input (which can help to handle contact bounce). For this design I opted to disable MCLR (the master reset) and use RE3 for its schmitt trigger input.
Additionally it would be advisable to add a delay when checking for input. When input is sensed, give it a short time to settle out that is faster than a human can press and release but not as fast as any noise we might pick up. This will help us to deal with bounce as well.
Step 9: The MIDI/USB Interface
Once everything else has been implemented and the system is functionally operational while still self-contained, we can open it up to external control (either by a computer or as a keyboard to control other sound modules) and even operation as a MIDI USB interface.
If you have configured your input scheme as previously recommended, decoding MIDI data taken from the UART and manipulating it is trivial. Start with basic NOTE ON/OFF then work your way up to attenuation control and more complex features such as arpeggio, portamento, and legato. Once you have these features working on a single voice, develop a scheme to take advantage of all three voices of the SN76489 with something like fingered chords or just simply supporting three key presses at any given time. You can also make use of the SN76489's white noise channel for percussive sounds.
With MIDI and USB control the limit is your imagination, until of course you imagine songs requiring more than three notes at a time! In your next design consider using multiple sound chips and more complex or different ones as well. You might find one works better for bass tones and the other for treble, or you might have a specific style that you are after. Regardless, you now have the skill set to pursue that so go for it!