; ****************************************************************** ; * Music on an STK200, using a R/2R network for playing tunes * ; * PortD has eight keys (active low), PortB generates the * ; * output for the R/2R-network, plays notes when key is activated * ; * ATMEL AT90S8515 at 4 Mcs/s * ; * (C)2005 by http://www.avr-asm-tutorial.net * ; ****************************************************************** ; .NOLIST .INCLUDE "8515def.inc" .LIST ; ; Constants ; .EQU clock = 4000000 ; processor clock .EQU cNSine = 32 ; Table length of sine wave ; .DEF rLen = R1 ; length of delay for the sine wave .DEF rCnt = R2 ; Counter for length delay .DEF rmp = R16 ; multi purpose register .DEF rTab = R17 ; counter for table length ; ldi rmp,0xFF ; all bits of port B = output => R/2R network out DDRB,rmp ; set data direction register wtloop: in rmp,PIND ; read the keys cpi rmp,0xFF ; all keys inactive? breq wtloop ; yes, wait until active ldi ZH,HIGH(2*MusicTable) ; Point Z to music table ldi ZL,LOW(2*MusicTable) tabloop: rol rmp ; rotate next lowest bit into carry brcc tabfound ; found that key pressed adiw ZL,1 ; point Z to next table value rjmp tabloop ; check next bit tabfound: lpm ; read music table value to R0 mov rlen,R0 ; copy to delay, R0 used otherwise ; ; Play a tone until all keys are inactive ; startsine: ldi ZH,HIGH(2*SineTable) ; Set Z to sine wave table ldi ZL,LOW(2*SineTable) ldi rTab,cNSine ; Length of SineWave table ; ; The following code is optimized for reaching equal runtimes ; for all loop runs, clocks cycles needed are calculated ; for all instructions after their execution, ; clock cycles during table read / clock cycles at table end ; loopsine: in rmp,PIND ; 1/- Check if input still active cpi rmp,0xFF ; 2/- breq wtloop ; 3/- key not active any more, restart nop ; 4/- delay to synchronize loopnext: lpm ; 7/3 read sinewave value to R0 out PORTB,R0 ; 8/4 copy to R/2R network mov rCnt,rLen ; 9/5 Set delay counter value ; delay loop, requires 3*rLen-1 clock cycles loopdelay: dec rCnt ; (1) next counter value brne loopdelay ; (2/1) not yet zero ; 3*rLen+8/3*rLen+4 at end of delay loop adiw ZL,1 ; 3*rLen+10/3*rLen+6 next value in table dec rTab ; 3*rLen+11/3*rLen+7 table counter brne loopsine ; 3*rLen+13/3*rLen+8 play next value in table ldi ZH,HIGH(2*SineTable) ; -/3*rLen+9 reload first table value ldi ZL,LOW(2*SineTable) ; -/3*rLen+10 ldi rTab,cNSine ; -/3*rLen+11 Length of sine wave table rjmp loopnext ; -/3*rLen+13 restart loop ; ; Table for delays to generate the 8 different frequencies ; ; frequency = clock / TableLength / ( 3 * rLen + 13 ) ; rLen = ( clock /TableLength / frequency - 13 ) / 3 ; MusicTable: ; f=261.6 293.7 329.6 349.2 392.0 440.0 493.9 523.2 (should be) .DB 155, 138, 122, 115, 102, 90, 80, 75 ; f=261.5 292.7 329.8 349.2 391.9 441.7 494.1 525.2 (is really) ; differences caused by rounding and limited resolution) ; ; Sinewave table for 8 bit D/A ; Table length = 32 values ; VCC=5.000V, uLow=0.000V, uHigh=2.500V ; (generated by sinewave.pas) ; Sinetable: .DB 64,76,88,99,109,117,123,126 .DB 128,126,123,117,109,99,88,76 .DB 64,51,39,28,19,11,5,1 .DB 0,1,5,11,19,28,39,51 ; ; End of program ;