June
2000, Issue 119
The
Chips are Alive with the Sound of Music
Imitatiing
the Dead Melody IC
TOS
In
the synthesizer industry, TOS means top octave synthesizer,
which was developed in the 70s for electronic
organs. A 1- or 2-MHz input would be internally divided
to produce 12 equally spaced frequency outputs. The
12 semitones comprise the musical scale, an octave.
An octave is the interval between two notes where one
frequency is twice or half of the other. When an octave
is produced, dividing a notes frequency by two
derives the remaining notes in lower octaves. Hence,
notes are based on the single clocks input frequency.
Because
TOSs are hardware devices, no overhead was necessary.
Doing this with a processor requires hardware PWMs or
an execution speed that supports software PWM. Hardware
PWMs are not available on these low-end micros. Execution
speed of both of these is 1 µs using their internal
4-MHz oscillators. Software divide-by-N counters can
be coded using an interrupt routine. Look at Figure
2 to see how this is accomplished.
|
|
| Figure 2The melody
generator flowchart shows frequency and duration
based on a single timer. |
At least two
divide-by-N counters are part of the timer overflow
interrupt, there is an accurate time base tick. Upon
N counts or ticks, an action is performed. Here, two
actions are necessary. The first, frequency creation,
is done using an 8-bit count and by complementing an
output bit each time the divide-by-N reaches zero. Two
transitions of this bit equal one cycle of output frequency
(period = two divide-by-N counts).
The second
action concerns duration, which requires a 16-bit count.
This is the duration of the fastest note, in this case
Im using the eighth note (one beat in x/8 time).
The longest path through this routine must include reloading
the "N" each time a divide-by-N counter reaches
zero for a single note (frequency counter) and a 23-µs
duration (beat counter). Although I could reload timer0
with any number greater than the maximum interrupt time,
I used 64 µs to give the rest of the code enough time
to execute between interrupts.