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