Composing Happy Birthday song

Composing Happy Birthday song

As a composing example program we present Listing 1, the familiar Happy Birthday song composition. Figure 1 is the corresponding circuit for it. To decorate the birthday atmosphere, two LEDs are turned ON/OFF alternately in program.

Figure 1.  Song Example Circuit

Just as for any song composing, we need to write the music note subroutines for each note that will be used in song. The basic idea on note creation is the same as described for 440 Hz music tone: find the note's frequency, then calculate its half period T/2; then drive the speaker High or Low in such half period.

For example, in the 784 Hz "G5" tone subroutine, T/2 = 638 us, so the speaker needs to be driven High for 638us, then Low for 638us, and so on. To count this time, we utilize the ATtiny11's timer interrupt. This 8-bit timer is an up counter, it increases its count every clock cycle (that is 1 microsecond), when reaching count=256, it overflows to zero and generates an interrupt, and then increases one count per us again. Each interrupt causes the MCU to execute its interrupt service routine, in this case (see Listing 3) it is TIMOVF.

Instead of letting the timer count from zero, we set it start at 240, so the timer will overflow each 16 us. This is equivalent to every 16 us the timer has a "tick." Therefore for T/2=638 us, the needed Ticks# = 638/16=40.

Each music note takes some time to play. So the above half period High/Low must be repeated a number of times. We appropriately choose this number as 150. This number was proved appropriate by experiment. You can change it if you found it too short or too long.

After writing all note subroutines, the last step is to call these notes to form a song, as shown in the main program of Listing 3.  Of course, you need to listen to it, then change it by your feeling, and do this for several iterations until you get the satisfactory music.

LISTING 1. Birthday Song program

;BDay.ASM(.bin 466 bytes)   BirthDay Song with Flashing LEDs

.include "TN11def.inc"      ; Port definitions here

.cseg

.org 0

rjmp    RESET

RETI

RETI

rjmp    TIMOVF

RESET:

ldi     r16, \$80

out     SREG, r16   ; Enable any Int

ldi     r16, \$02

out     TIMSK, r16  ; Enable Timer0 Int

ldi     r16, \$01

out     TCCR0, r16  ; TimerControl:  CK=>trig

sbi     DDRB, 4     ; config DDRB bit-4 as output

sbi     DDRB, 1     ; config DDRB bit-1 as output

sbi     DDRB, 0     ; config DDRB bit-0 as output

AGAIN:

;----------------------------------*

cbi     PORTB, 0    ; RLED=ON

Sbi     PORTB, 4    ; GLED=OFF

in      R4, PORTB

COM     R4

out     PORTB, R4   ; complement PortB bits

rcall   Sew

rcall   DLms

rcall   Sew

rcall   DLms

rcall   La

rcall   La

rcall   Sew

rcall   Sew

rcall   Dos

rcall   Dos

rcall   Tea

rcall   Tea

rcall   Tea

rcall   Tea

rcall   DLhalfS

rcall   DLhalfS

rcall   DLhalfS

rcall   DLhalfS

;----------------------------------*

in      R4, PORTB

COM     R4

out     PORTB, R4   ; complement PortB bits

rcall   Sew

rcall   DLms

rcall   Sew

rcall   DLms

rcall   La

rcall   La

rcall   Sew

rcall   Sew

rcall   Rays

rcall   Rays

rcall   Rays

rcall   Dos

rcall   Dos

rcall   Dos

rcall   Dos

rcall   DLhalfS

rcall   DLhalfS

rcall   DLhalfS

rcall   DLhalfS

;----------------------------------*

in      R4, PORTB

COM     R4

out     PORTB, R4   ; complement PortB bits

rcall   Sew

rcall   DLms

rcall   Sew

rcall   DLms

rcall   Sews

rcall   Sews

rcall   Sews

rcall   Sews

rcall   Mes

rcall   Mes

rcall   Mes

rcall   Dos

rcall   Dos

rcall   Dos

rcall   Tea

rcall   Tea

rcall   Tea

rcall   La

rcall   La

rcall   La

rcall   La

rcall   La

rcall   DLhalfS

rcall   DLhalfS

rcall   DLhalfS

rcall   DLhalfS

;----------------------------------*

in      R4, PORTB

COM     R4

out     PORTB, R4   ; complement PortB bits

rcall   Fars

rcall   Fars

rcall   DLms

rcall   Fars

rcall   Mes

rcall   Mes

rcall   Mes

rcall   Dos

rcall   Dos

rcall   Rays

rcall   Rays

rcall   Rays

rcall   Rays

rcall   Dos

rcall   Dos

rcall   Dos

rcall   Dos

rcall   Dos

rcall   Dos

rcall   DLhalfS

rcall   DLhalfS

rcall   DLhalfS

rcall   DLhalfS

rcall   DLhalfS

rcall   DLhalfS

rcall   DLhalfS

rjmp    AGAIN

;---------------------------------------------------------

;the delay = .? sec at 1 MHz

DLhalfS:

LDI     R20, 2

calop:

LDI     R19, 235

malop:

LDI     R21, 235

LOP1:

DEC     R21

brne    LOP1

DEC     R19

brne    malop

DEC     R20

brne    calop

RET

;---------------------------------------------------------

;delay = .? msec at 1 MHz

DLms:

LDI     R20, 155

LOPms:

DEC     R20

brne    LOPms

RET

;-----------------------------------------------------------

TIMOVF:

LDI     R16, 240    ;\ initialize TCNT0=240

OUT     TCNT0, R16  ;/           256-16=240

inc     r18         ; inc TickCount by 1

RETI

;-----------------------------------------------------------

; 784 Hz "G5" tone; T/2 = 638 us, Ticks# = 638/16=39.875=>40

Sew:

Gh:

CLR     R17

REPETGh:

CLR     r18         ; clear TickCounter

loopGh1:

cbi     PORTB, 1    ; PB1=LOW

cpi     r18, 40     ; if reach 40 Ticks, then jmp

brlo    loopGh1

CLR     r18         ; clear TickCounter

loopGh2:

sbi     PORTB, 1    ; PB1=HIGH

cpi     r18, 40     ; if reach 40 Ticks, then jmp

brlo    loopGh2

INC     R17

cpi     R17, 150

BRLO    REPETGh

RET

;-----------------------------------------------------------

; 880 Hz "A5" tone; T/2 = 568 us, Ticks# = 568/16=35.5

La:

Ah:

CLR     R17

REPETAh:

CLR     r18         ; clear TickCounter

loopAh1:

cbi     PORTB, 1    ; PB1=LOW

cpi     r18, 36     ; if reach 36 Ticks, then jmp

brlo    loopAh1

CLR     r18         ; clear TickCounter

loopAh2:

sbi     PORTB, 1    ; PB1=HIGH

cpi     r18, 35     ; if reach 35 Ticks, then jmp

brlo    loopAh2

INC     R17

cpi     R17, 150

BRLO    REPETAh

RET

;-----------------------------------------------------------

; 988 Hz "B5" tone; T/2 = 506 us, Ticks# = 506/16=31.625

Tea:

Bh:

CLR     R17

REPETBh:

CLR     r18         ; clear TickCounter

loopBh1:

cbi     PORTB, 1    ; PB1=LOW

cpi     r18, 32     ; if reach 37 Ticks, then jmp

brlo    loopBh1

CLR     r18         ; clear TickCounter

loopBh2:

sbi     PORTB, 1    ; PB1=HIGH

cpi     r18, 31     ; if reach 36 Ticks, then jmp

brlo    loopBh2

INC     R17

cpi     R17, 150

BRLO    REPETBh

RET

;------------------------------------------------------------

; 1047 Hz "C6" tone; T/2 = 478 us, Ticks# = 478/16=29.875=>30

Dos:

Cs:

CLR     R17

REPETCs:

CLR     r18         ; clear TickCounter

loopCs1:

cbi     PORTB, 1    ; PB1=LOW

cpi     r18, 30     ; if reach 30 Ticks, then jmp

brlo    loopCs1

CLR     r18         ; clear TickCounter

loopCs2:

sbi     PORTB, 1    ; PB1=HIGH

cpi     r18, 30     ; if reach 30 Ticks, then jmp

brlo    loopCs2

INC     R17

cpi     R17, 150

BRLO    REPETCs

RET

;-----------------------------------------------------------

; 1175 Hz "D6" tone; T/2 = 426 us, Ticks# = 426/16=26.625

Rays:

Ds:

CLR     R17

REPETDs:

CLR     r18         ; clear TickCounter

loopDs1:

cbi     PORTB, 1    ; PB1=LOW

cpi     r18, 27     ; if reach 27 Ticks, then jmp

brlo    loopDs1

CLR     r18         ; clear TickCounter

loopDs2:

sbi     PORTB, 1    ; PB1=HIGH

cpi     r18, 26     ; if reach 26 Ticks, then jmp

brlo    loopDs2

INC     R17

cpi     R17, 150

BRLO    REPETDs

RET

;-----------------------------------------------------------

; 1319 Hz "E6" tone; T/2 = 379 us, Ticks# = 379/16=23.6875

Mes:

Es:

CLR     R17

REPETEs:

CLR     r18         ; clear TickCounter

loopEs1:

cbi     PORTB, 1    ; PB1=LOW

cpi     r18, 24     ; if reach 24 Ticks, then jmp

brlo    loopEs1

CLR     r18         ; clear TickCounter

loopEs2:

sbi     PORTB, 1    ; PB1=HIGH

cpi     r18, 23     ; if reach 23 Ticks, then jmp

brlo    loopEs2

INC     R17

cpi     R17, 150

BRLO    REPETEs

RET

;-----------------------------------------------------------

; 1397 Hz "F6" tone; T/2 = 358 us, Ticks# = 358/16=22.375

Fars:

Fs:

CLR     R17

REPETFs:

CLR     r18         ; clear TickCounter

loopFs1:

cbi     PORTB, 1    ; PB1=LOW

cpi     r18, 23     ; if reach 23 Ticks, then jmp

brlo    loopFs1

CLR     r18         ; clear TickCounter

loopFs2:

sbi     PORTB, 1    ; PB1=HIGH

cpi     r18, 22     ; if reach 22 Ticks, then jmp

brlo    loopFs2

INC     R17

cpi     R17, 150

BRLO    REPETFs

RET

;------------------------------------------------------------

; 1568 Hz "G6" tone; T/2 = 319 us, Ticks# = 319/16=19.93=> 20

Sews:

Gs:

CLR     R17

REPETGs:

CLR     r18         ; clear TickCounter

loopGs1:

cbi     PORTB, 1    ; PB1=LOW

cpi     r18, 20     ; if reach 20 Ticks, then jmp

brlo    loopGs1

CLR     r18         ; clear TickCounter

loopGs2:

sbi     PORTB, 1    ; PB1=HIGH

cpi     r18, 20     ; if reach 20 Ticks, then jmp

brlo    loopGs2

INC     R17

cpi     R17, 150

BRLO    REPETGs

RET

;-----------------------------------------------------------

So far we only present three example program for the ATtiny11 application. But the list can go endless. It's only limited by everybody's capability and need.

Perhaps the most widely use for ATtiny11 is in chip music composing. Now that its price is so low, we won't have to care about how many chips we need. In this sense you can use as many ATtiny11 you need to build a "chip music library."