By G. Y. Xu View In Digital Edition
“Chip Music” may sound like a new terminology to you, but its meaning is really self-explanatory and it has been around us for a long time.
We've all heard the Christmas or birthday songs coming out from various greeting cards. But do you know how to create such music in a tiny chip? Honestly, I didn’t — until recently.
Beginning this year, the prices for some eight-bit micro-controllers have dropped to an unprecedented new low. For example, only 38 cents each for the Atmel’s eight-pin ATtiny11 at the quantity of 100 is now available (www.digikey.com). I have been able to purchase Atmel’s 8051-like 4KB Flash microcontroller AT89C4051 for only $1.50 each at a quantity of 150 (www.jameco.com).
This is a great phenomenon for us as chip users. Lots of new opportunities are now open to us. What can we do with these opportunities in order to take advantage?
I can’t live without music. I can’t pass July 4th without singing and hearing The Star-Spangled Banner. So, I thought it was about time for me to program some of my favorite music into the chips. Even though I have been accustomed to those songs coming from greeting cards, I never knew how they were programmed.
I decided to try my own way by first learning some basics on music, then starting to write musical tone subroutines emulating piano keys’ frequencies. The results were very rewarding and exciting. By comparing the standard “A” (440 Hz) tone frequency generated by my “A” Tone Generator program to my piano’s A4 key, I noticed for the first time that my piano was a little out-of-tune.
This article is a recap of my recent work. And I hope it will help encourage more people to program their favorite songs into chips. I expect very soon there will be a flurry of chip music booming all around.
I play piano almost every day, so the natural starting point of music topic is piano. As we know, there are 88 keys on a piano ranging more than seven octaves. The keys within an octave are named by the letters C, D, E, F, G, A, and B. In order to designate a specific key on the piano, we put a subscript number after the letter. Figure 1 shows part of the piano keys and the white keys’ frequencies.
FIGURE 1. This diagram is showing a section of the piano keys and the white keys’ frequencies. |
The frequency for Middle C (C4 key) is 261.626 Hz, but we can round it up to 262 Hz with no problem, because the human ear can’t distinguish tones if the frequency difference is less than 3 Hz.
The standard frequency for musical tuning is 440.000 Hz at the A4 key. This frequency has been adopted as the International Frequency Standard for musical instruments; any other key’s frequency can be determined from it. For instance, its higher one octave key A5 frequency is 880 Hz, its higher two octave key A6 frequency is 1760 Hz, etc. And its lower one octave key A3 frequency is 220 Hz, but such low frequency will not be used in our chip music program since most speakers or buzzers won’t have good response that low.
Now, let’s see how to use a microcontroller to generate the 440 Hz tone. Even though I actually used an eight-pin AVR micro to do it first here, I would like to utilize Atmel’s 8051-like micro AT89C1051/2051/4051 for explanation because most people are more familiar with it than the other micros, and writing the assembly code for it is just the same as writing 8051 assembly language.
FIGURE 2. Hardware Configuration. |
Figure 2 is the hardware configuration for this purpose. In addition to the micro, a reset capacitor, a 12 MHz crystal or oscillator, and a speaker are all we need to form the circuit.
Why do we choose 12 MHz? Because in the 8051, a machine cycle consists of 12 clock cycles, so each machine cycle takes one microsecond (µS) and most 8051 instructions take either one or two machine cycles, so these instructions take either one or two µS. Therefore, calculation becomes very convenient.
The main idea in creating this “A” tone is very simple. A half period for 440 Hz is T/2 = 1/(440*2) = 1,136 µS. As shown in Figure 3, if we apply high/low voltage to the speaker at T/2 alternately, it will generate the required frequency square wave tone.
FIGURE 3. This diagram shows how to generate the required frequency square wave tone. |
The entire assembly language program is shown in Listing 1. As we can see, to get very high accuracy, we create subroutine DL1132 µS; and because setting up a port pin or calling subroutine takes two µS each, the total time for a half period comes to 1,136 µS exactly.
<strong>LISTING 1. Program for Generating the Standard 440 Hz Tone</strong>
;Chip Music Example for the 8051-like Microcontroller<br />
;Atone.ASM: Generate 440 HZ "A" tone on AT89C1051/2051/4051<br />
;use 12 MHz XTAL or Oscillator, 1 Cycle = 1 us
;Theory: 440 Hz corresponds T/2 = 1136 us. Use DL1132us delay routine for that.<br />
;And because Calling the routine and Setting up High/Low take 2us respectively, ;so the total High/Low time is 1136us.
SPKR BIT P1.7 ; Speaker connected to P1.7 (pin 19)
TICK DATA 10H ; Timer0 Tick Counter
;-------------------------------------------------------------------------
ORG 0000H
MOV A, #30H<br />
MOV SP, A ; set up stack pointer
;<br />
;Generate 440 Hz "A" tone ________ ___<br />
; ____| |________|
AGAIN:<br />
CLR SPKR ; SPKR=LOW<br />
call DL1132us ; call = 2us
SETB SPKR ; SPKR=HIGH<br />
call DL1132us ; call = 2us<br />
jmp AGAIN
;------*--------*---------*-------*----------<br />
; 1132us DELAY Routine<br />
;--------------------------------------------<br />
DL1132us:<br />
MOV A, #0 ; 1us<br />
LOOP1:<br />
DJNZ Acc, LOOP1 ; 2uS X 256 = 512us<br />
LOOP2:<br />
DJNZ Acc, LOOP2 ; 2us X 256 = 512us<br />
MOV A, #52 ; 1us<br />
LOOP3:<br />
DJNZ Acc, LOOP3 ; 2 X 52us = 104us<br />
RET ; 2us<br />
;--------------------------------------------<br />
; Total = 1024 + 104 + 4 = 1132 us
END
With a good 40 ohm two-inch speaker, and a programmed AT89C1051/2051/4051 microcontroller using my 8x51 programmer [3], I’ve found that its tone matches many fine-tuned Steinway and Yamaha pianos. The frequency meter or oscilloscope measurement shows 440.0 Hz. This circuit can be built and mounted in a small box and act like a “Tuning fork” for musicians.
Just as the words saying “When you know the notes to sing, you can sing (al)most anything.” Chip music composing is no difference. Simply put, we need to create the subroutines for each note, then call these routines to make a song. Listing 2 is an example showing how to compose the beginning melody of The Star-Spangled Banner.
It utilizes only four different notes, but we’ve created eight note routines for your convenience for future use. Each note routine deals with two parameters: the frequency and the duration of the note.
Using 8051’s Timer0 interrupt is the main reason for creating each note routine. As we see, the 8051 works in mode 3, where Timer0 acts as two separate eight-bit counters TL0 and TH0. If the register TL0 is loaded with number 0 to start, it will count up one each microsecond, and overflow after 256 µS.
The necessary steps to enable Timer0 interrupt and start it are shown at the beginning part of the main program. After that, an infinite loop is entered to generate the beginning melody of The Star-Spangled Banner.
The principle of frequency generation is the same as on “A” Tone, but the technique is different. Here we deal with a number of different frequencies, not just one like 440 Hz or T/2 = 1,136 µS, and we need to keep the Timer0 interrupt service routine the same for all these frequencies.
A simple solution is to set up the Timer0 so that it always overflows every 8 µS, then calculate how many timer Ticks are needed for T/2 of any frequency we are dealing with.
When counting elapsed time between timer ticks, the time it takes to execute the interrupt service routine, that is, the Interrupt Execution Time (IET) must be taken into account. As calculated in Listing 2, IET=7 µS/INT, so the elapsed time between two ticks is fixed 8+7=15 µS.
Under this scheme the number of ticks for some frequencies may not be an integer and need to be rounded, in such case the calculation can only be approximate. But 8 µS is very small compared to any T/2 we can have, so the created note would still be satisfactory.
Now, let’s look at an example from the note subroutine: How many ticks are needed to generate the 523 Hz (T/2=956 µS) tone. Since 956/15 = 63.73, we round it up to 64. But in the note subroutine the tick starts from 0, so it should take 64-1 = 63 as the required ticks. By the way, we have used the note name “Doe” in parallel with “C5” for it; this is helpful in composing.
As for the second parameter, the duration of the note is decided by the number of repeat times Rp for a square wave. Roughly speaking, we can simply assign a fixed number such as Rp = 250 to every note routine. It works, and I did it in my beginning compositions. But this way can’t achieve equal duration for every note. The result is the lower the frequency (with larger T/2), the longer the note duration.
A better way to achieve equal note duration is to start from the highest frequency (shortest T/2), assign the largest Rp=255, then calculate the note duration. For instance, in the “C6” note subroutine, the highest frequency is 1,047 Hz, T/2 = 478 µS, if Rp=255 is assigned to it, then the note duration will be
Rp * T = 255 * 478 * 2 = 243780 µS, or roughly 1/4 second.
After that, we use this formula to get the required Rp for other lower frequencies. For instance, in the “C5” note subroutine we get
Rp = 243780 / T = 243780 / (2*956) = 127.5 => 128
By doing so for all other note subroutines, we keep each note duration almost equal to 1/4 second. And we can think of each subroutine call as a “quarter note.” This is very helpful when composing; you can estimate the required number of calls for the notes you are going to play.
Last, but not least, we need some delay routines for REST note composing. For example, we already provide 10 milliseconds (ms) and 100 ms delay routines. From there, you can create the “quarter rest” note routine, if needed. Just remember: “half time of all music is silence.”
Now that we’ve created note subroutines, composing The Star-Spangled Banner is just a matter of calling the required notes into the main program to construct the melody, as shown in Listing 2. Of course, in order to make a good song, we need to do it for several iterations, not just once. We need to listen, try, and listen again.
The hardware for playing this song is still the circuit shown in Figure 2, but it is more flexible. For example, you can use 11.0592 MHz instead of 12 MHz, and won’t get any unpleasing result. You may also use a buzzer to replace the speaker if space is limited and sound quality can be tolerated.
So far, we‘ve discussed chip music composing only on the 8051, but the principles outlined here can be easily modified and applied to other micros such as AVRs or PICs, as they all have a timer and a similar interrupt scheme.
Once you’ve created your music files, you need a device programmer to “burn” it on to a micro. For 8051-like micros, there are numerous programmers available on the market, including my 8x51 Flash/EPROM programmer.
With the information presented here, not only you can complete the composing of the remaining portion of The Star-Spangled Banner, but also do much more. For example, you can compose Beethoven’s Ode to Joy. With chip prices dropping so low, it’s much easier and cheaper than ever for chip music composing. So don’t miss this chance to build your own chip music library as I did.
So, happy chip music composing. NV
[1] John Backus: The Acoustical Foundations of Music, 1977.
[2] Scientific American’s Reading Series: The Physics of Music, 1978.
[3] G.Y. Xu: 8x51 Flash/EPROM Microcontroller Programmer, Circuit Cellar Magazine, April 1998.
[4] G.Y. Xu: Play the AVR HyperTerm, Nuts & Volts Magazine, February 2005.
G.Y. Xu is an Electrical Designer specializing in microprocessor/microcontroller systems design and development, both in hardware and software. He can be reached by email at [email protected]
<strong><a id="Listing 2" name="Listing 2"></a>LISTING 2. Program the Beginning Melody of The Star-Spangled Banner</strong>
;Chip Music Composing Example for the 8051-like Microcontroller<br />
;Anthem.ASM: American National Anthem program for AT89C1051/2051/4051<br />
;use 12 MHz XTAL or Oscillator, 1 Cycle = 1 us
SPKR BIT P1.7 ; Speaker connected to P1.7 (pin 19)<br />
TICK DATA 10H ; Timer0 Tick Counter<br />
;-------------------------------------------------------------------------
ORG 0000H
AJMP INIT
ORG 000BH ; Timer0 Overflow Interrupt Vector
AJMP TIMOVF ; Interrupt Service routine; 2us
INIT:
MOV A, #30H<br />
MOV SP, A ; set up stack pointer
MOV IE, #82H ; Enable Timer0 Interrupt<br />
MOV TMOD, #03H ; Timer0 work in mode 3: 8-bit Timer<br />
SETB TR0 ; turn ON Timer0
REPET:<br />
call Sew<br />
call Sew
call Me
call Doe<br />
call Doe
call Me<br />
call Me
call Sew<br />
call Sew
call Doo<br />
call Doo<br />
call Doo<br />
call Doo<br />
call Doo<br />
call Doo<br />
call Doo<br />
call Doo<br />
call DLhalfS<br />
call DLhalfS<br />
call DLhalfS<br />
call DLhalfS
ajmp REPET
;------------------------------------
;This routine delays 10 ms at 12 MHz
DLY10ms:<br />
PUSH ACC<br />
PUSH B<br />
MOV A, #227<br />
LOOPN1:<br />
MOV B, #20<br />
LOOPN2:<br />
DJNZ B, LOOPN2<br />
DEC A<br />
JNZ LOOPN1<br />
POP B<br />
POP ACC<br />
RET<br />
;--------------------------<br />
DL100ms:<br />
ACALL DLY10ms<br />
ACALL DLY10ms<br />
ACALL DLY10ms<br />
ACALL DLY10ms<br />
ACALL DLY10ms<br />
ACALL DLY10ms<br />
ACALL DLY10ms<br />
ACALL DLY10ms<br />
ACALL DLY10ms<br />
ACALL DLY10ms<br />
RET<br />
;---------------------------<br />
;delay .5 sec at 12 MHz
DLhalfS:<br />
ACALL DL100ms<br />
ACALL DL100ms<br />
ACALL DL100ms<br />
ACALL DL100ms<br />
ACALL DL100ms<br />
RET<br />
;------------------------------------------------<br />
; Music Note Subroutines<br />
;------------------------------------------------<br />
; 523 Hz "C5" tone, T/2 = 956 us
Doe:<br />
C5:<br />
MOV R7, #0<br />
REPETCh:<br />
MOV TICK, #0 ; clear Tick Counter<br />
loopCh1:<br />
CLR SPKR ; SPKR=LOW<br />
MOV A, TICK<br />
CJNE A, #63, loopCh1
MOV TICK, #0 ; clear Tick Counter<br />
loopCh2:<br />
SETB SPKR ; SPKR=HIGH<br />
MOV A, TICK<br />
CJNE A, #63, loopCh2<br />
INC R7<br />
MOV A, R7<br />
CJNE A, #128, REPETCh<br />
RET<br />
;--------------------------------------------------<br />
; 587 Hz "D5" tone, T/2 = 852 us
Ray:<br />
D5:<br />
MOV R7, #0<br />
REPETDh:<br />
MOV TICK, #0 ; clear Tick Counter<br />
loopDh1:<br />
CLR SPKR ; SPKR=LOW<br />
MOV A, TICK<br />
CJNE A, #56, loopDh1
MOV TICK, #0 ; clear Tick Counter<br />
loopDh2:<br />
SETB SPKR ; SPKR=HIGH<br />
MOV A, TICK<br />
CJNE A, #56, loopDh2<br />
INC R7<br />
MOV A, R7<br />
CJNE A, #143, REPETDh<br />
RET<br />
;--------------------------------------------------<br />
; 659 Hz "E5" tone, T/2 = 759 us
Me:<br />
E5:<br />
MOV R7, #0<br />
REPETEh:<br />
MOV TICK, #0 ; clear Tick Counter<br />
loopEh1:<br />
CLR SPKR ; SPKR=LOW<br />
MOV A, TICK<br />
CJNE A, #50, loopEh1
MOV TICK, #0 ; clear Tick Counter<br />
loopEh2:<br />
SETB SPKR ; SPKR=HIGH<br />
MOV A, TICK<br />
CJNE A, #50, loopEh2<br />
INC R7<br />
MOV A, R7<br />
CJNE A, #161, REPETEh<br />
RET<br />
;--------------------------------------------------<br />
; 698 Hz "F5" tone, T/2 = 716 us
Far:<br />
F5:<br />
MOV R7, #0<br />
REPETFh:<br />
MOV TICK, #0 ; clear Tick Counter<br />
loopFh1:<br />
CLR SPKR ; SPKR=LOW<br />
MOV A, TICK<br />
CJNE A, #47, loopFh1
MOV TICK, #0 ; clear Tick Counter
loopFh2:<br />
SETB SPKR ; SPKR=HIGH<br />
MOV A, TICK<br />
CJNE A, #47, loopFh2<br />
INC R7<br />
MOV A, R7<br />
CJNE A, #170, REPETFh<br />
RET<br />
;--------------------------------------------------<br />
; 784 Hz "G5" tone, T/2 = 638 us
Sew:<br />
G5:<br />
MOV R7, #0<br />
REPETGh:<br />
MOV TICK, #0 ; clear Tick Counter<br />
loopGh1:<br />
CLR SPKR ; SPKR=LOW<br />
MOV A, TICK<br />
CJNE A, #42, loopGh1
MOV TICK, #0 ; clear Tick Counter<br />
loopGh2:<br />
SETB SPKR ; SPKR=HIGH<br />
MOV A, TICK<br />
CJNE A, #41, loopGh2<br />
INC R7<br />
MOV A, R7<br />
CJNE A, #191, REPETGh <br />
RET<br />
;--------------------------------------------------<br />
; 880 Hz "A5" tone, T/2 = 568 us
La:<br />
A5:<br />
MOV R7, #0<br />
REPETAh:<br />
MOV TICK, #0 ; clear Tick Counter<br />
loopAh1:<br />
CLR SPKR ; SPKR=LOW<br />
MOV A, TICK<br />
CJNE A, #37, loopAh1
MOV TICK, #0 ; clear Tick Counter<br />
loopAh2:<br />
SETB SPKR ; SPKR=HIGH<br />
MOV A, TICK<br />
CJNE A, #37, loopAh2<br />
INC R7<br />
MOV A, R7<br />
CJNE A, #214, REPETAh<br />
RET<br />
;---------------------------------------------------<br />
; 988 Hz "B5" tone, T/2 = 506 us
Tea:<br />
B5:<br />
MOV R7, #0<br />
REPETBh:<br />
MOV TICK, #0 ; clear Tick Counter<br />
loopBh1:<br />
CLR SPKR ; SPKR=LOW<br />
MOV A, TICK<br />
CJNE A, #33, loopBh1
MOV TICK, #0 ; clear Tick Counter<br />
loopBh2:<br />
SETB SPKR ; SPKR=HIGH<br />
MOV A, TICK<br />
CJNE A, #33, loopBh2<br />
INC R7<br />
MOV A, R7<br />
CJNE A, #241, REPETBh<br />
RET<br />
---------------------------------------------------<br />
; 1047 Hz "C6" tone, T/2 = 478 us
Doo:<br />
C6:<br />
MOV R7, #0<br />
REPETCs:<br />
MOV TICK, #0 ; clear Tick Counter
loopCs1:<br />
CLR SPKR ; SPKR=LOW<br />
MOV A, TICK<br />
CJNE A, #31, loopCs1
MOV TICK, #0 ; clear Tick Counter<br />
loopCs2:<br />
SETB SPKR ; SPKR=HIGH<br />
MOV A, TICK<br />
CJNE A, #31, loopCs2<br />
INC R7<br />
MOV A, R7<br />
CJNE A, #255, REPETCs<br />
RET<br />
;------*--------*---------*-------*-------*----------------------<br />
;Timer0 Overflow Interrupt Service Routine<br />
;----------------------------------------------------------------<br />
TIMOVF:<br />
MOV TL0, #248 ; load Timer0 with 256-8=248; 2us<br />
inc TICK ; inc Tick Count by 1 ; 1us<br />
RETI ; 2us<br />
;--------------------------------------------------------------<br />
; Total IET = 7 us/Interrupt
END