Everything for Electronics
Nuts & Volts Magazine (May 2006)

Chip Music Composing Simplified

By G. Y. Xu    View In Digital Edition  


Theory and Practice

“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.

Chip Music Era Has Come

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.

Acoustic Basics of Music

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.

Music Tone Generation by Micro-controller

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.

Now, Let’s Compose

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.

Build Your Chip Music Library

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


References

[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.


Author Bio

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                              

Downloads

Chip Music Composing Simplified 2006-05 (2 Code Listings)



Comments