;***************************************************** ;** Cricket Call ;** Analog out to AD557 DAC ;** Songs defined in a table and stored in flash ;***************************************************** ;Makes a sinewave burst of 16 points/cycle byte-wide data from ;PORTC at a variable sample rate of (1 to 255)*0.25 microsecond ;Minimum carrier freq about 950 Hz;Max 16 KHz. ;Sinewave burst duration, spacing, repeat number and song ;length are specified to make one call. ;waits for about a variable time between calls(1 to 2^16)*64 microSec. ; or about 0 to 4 seconds ;Timer 0 runs at high speed to generate output samples ; takes care ramp up/down and sampling ; Samples ramped up/down at beginning and end of a burst ;Timer 1A compare match resets timer 1 and starts a new song ;Timer 1B compare match always holds the next event time in the song ; an event time will signal turn on/off the song. .include "d:\bruce\EEdocs\atmel\asm src\8515def.inc" .device AT90S8515 ;************************** ;the registers .def save =r1 ;save the SREG .def preload =r2 ;preload time0 for 10 Msec .def TXbusy =r3 ;rs232 transmit busy flag .def TXflash =r4 ;flag=TRUE when text is in flash mem .def bstflag =r5 ;TRUE when actually playing .def button =r6 ;button pressed .def step =r7 ;sine table step size (1 or 2) .def temp =r16 ;temporary register .def itemp =r17 ;ISR temp variable .def nwave =r18 ;number of cycles played .def RXchar =r19 ;a received character .def numbst =r20 ;the number of bursts in a song .def curbst =r21 ;the current burst .def tbstLo =r22 ;burst duration .def tbstHi =r23 ; .def tspcLo =r24 ;burst spacing .def tspcHi =r25 ; .def curTLo =r26 ;total elapsed time since song start .def curTHi =r27 .def sngTLo =r28 ;Song length .def sngTHi =r29 ;remember that r30 and r31 are the Z register ;used to index the sine table, so don't define them here! ;*************************** ;song definitions ;song 1 carolinus .equ numbursts1 =1 .equ burstduration1 =7 ;mSec .equ burstspacing1 =7 .equ songduration1 =14 .equ carrierfreq1 =6000 ;Hz ;song 2 tinnulus .equ numbursts2 =1 .equ burstduration2 =30 ;mSec .equ burstspacing2 =70 .equ songduration2 =100 .equ carrierfreq2 =9000 ;Hz ;song 3 rubens .equ numbursts3 =1 ; .equ burstduration3 =10 ;mSec .equ burstspacing3 =10 .equ songduration3 =20 .equ carrierfreq3 =4500 ;Hz ;song 4 penns. .equ numbursts4 =4 ; .equ burstduration4 =18 ;mSec .equ burstspacing4 =12 .equ songduration4 =250 .equ carrierfreq4 =5000 ;Hz ;song 5 nigricornis .equ numbursts5 =1 .equ burstduration5 =12 ;mSec .equ burstspacing5 =8 .equ songduration5 =20 .equ carrierfreq5 =3750 ;Hz ;song 6 fultoni .equ numbursts6 =3 .equ burstduration6 =15 ;mSec .equ burstspacing6 =5 .equ songduration6 =160 .equ carrierfreq6 =4500 ;Hz ;song 7 borellii .equ numbursts7 =1 ; .equ burstduration7 =10 ;mSec .equ burstspacing7 =10 .equ songduration7 =20 .equ carrierfreq7 =2700 ;Hz ;song 8 monstrosa .equ numbursts8 =50 ; .equ burstduration8 =8 ;mSec .equ burstspacing8 =12 .equ songduration8 =1500 .equ carrierfreq8 =12000 ;Hz ;*************************** ;Initialization .cseg .org $0000 rjmp RESET ;reset entry vector reti ;external intrupt not used reti reti rjmp CmpA ;timer 1 compare A match rjmp CmpB ;timer 1 compare B match reti ;T1ovfl ;timer 1 inter vector rjmp T0ovfl ;timer 0 interrupt vector reti reti ;RXdone ;rs232 receive reti ;TXempty ;UART buffer empty reti reti ;*************************** ;table of values for a sinwave with peaks of 0xff and 0x00, ;and period 16 samples sintbl: .db 0x80, 0xb1, 0xda, 0xf6, 0xff, 0xf6, 0xda, 0xb1 .db 0x80, 0x4f, 0x26, 0x0a, 0x00, 0x0a, 0x26, 0x4f ;*************************** ;Convert the song definitions to stored values in flash mem songtbl:.db numbursts1, 264-250000/(carrierfreq1/(1+(carrierfreq1>8000))) .db HIGH(burstduration1*16), LOW(burstduration1*16) ;convert to .064 mS .db HIGH(burstspacing1*16), LOW(burstspacing1*16) .db HIGH(songduration1*16), LOW(songduration1*16) .db 1 + (carrierfreq1>8000), 0x00 ;sample rate doubling .db numbursts2, 264-250000/(carrierfreq2/(1+(carrierfreq2>8000))) .db HIGH(burstduration2*16), LOW(burstduration2*16) .db HIGH(burstspacing2*16), LOW(burstspacing2*16) .db HIGH(songduration2*16), LOW(songduration2*16) .db 1 + (carrierfreq2>8000), 0x00 .db numbursts3, 264-250000/(carrierfreq3/(1+(carrierfreq3>8000))) .db HIGH(burstduration3*16), LOW(burstduration3*16) .db HIGH(burstspacing3*16), LOW(burstspacing3*16) .db HIGH(songduration3*16), LOW(songduration3*16) .db 1 + (carrierfreq3>8000), 0x00 .db numbursts4, 264-250000/(carrierfreq4/(1+(carrierfreq4>8000))) .db HIGH(burstduration4*16), LOW(burstduration4*16) .db HIGH(burstspacing4*16), LOW(burstspacing4*16) .db HIGH(songduration4*16), LOW(songduration4*16) .db 1 + (carrierfreq4>8000), 0x00 .db numbursts5, 264-250000/(carrierfreq5/(1+(carrierfreq5>8000))) .db HIGH(burstduration5*16), LOW(burstduration5*16) .db HIGH(burstspacing5*16), LOW(burstspacing5*16) .db HIGH(songduration5*16), LOW(songduration5*16) .db 1 + (carrierfreq5>8000), 0x00 .db numbursts6, 264-250000/(carrierfreq6/(1+(carrierfreq6>8000))) .db HIGH(burstduration6*16), LOW(burstduration6*16) .db HIGH(burstspacing6*16), LOW(burstspacing6*16) .db HIGH(songduration6*16), LOW(songduration6*16) .db 1 + (carrierfreq6>8000), 0x00 .db numbursts7, 264-250000/(carrierfreq7/(1+(carrierfreq7>8000))) .db HIGH(burstduration7*16), LOW(burstduration7*16) .db HIGH(burstspacing7*16), LOW(burstspacing7*16) .db HIGH(songduration7*16), LOW(songduration7*16) .db 1 + (carrierfreq7>8000), 0x00 .db numbursts8, 264-250000/(carrierfreq8/(1+(carrierfreq8>8000))) .db HIGH(burstduration8*16), LOW(burstduration8*16) .db HIGH(burstspacing8*16), LOW(burstspacing8*16) .db HIGH(songduration8*16), LOW(songduration8*16) .db 1 + (carrierfreq8>8000), 0x00 ;*************************** ;reset entry point RESET: cli ldi temp, LOW(RAMEND) ;setup stack pointer out SPL, Temp ldi Temp, HIGH(RAMEND) out SPH, Temp ;The sample clock ;set up timer zero to overflow every 64 microSec ;but preload to sample time: 256 - sampletime(microsec)/0.25 ; -- default to 20 microsec (3125 Hz sine) ldi temp, 104 ;256 - 20/0.25 + 8 cycles mov preload, temp ;stop timer 0 ldi temp, 0 out TCCR0, temp ;prescale=1 one tick=.25 microSec ;The song start clock ;want a range up to a couple of seconds so divide clock by 256 ; => one tick=64 microsec and 16 bits is four seconds ;set up timer1 to clear on match reg A & prescale=256 ldi temp, 0 ;no pin toggle, no pwm out TCCR1A, temp ldi temp, 0b00001100 ;prescalar=4 and clear on match out TCCR1B, temp ;The burst clock ;want a range similar to the previous clock ;BUT in general the B compare register will have to be changed ;for each burst in a song to reflect the next event time ;set time1b to initial VERY long timeout to avoid false trigger ;during setup ldi temp, HIGH(64000) out OCR1BH, temp ldi temp, LOW(64000) out OCR1BL, temp ;enable time0 ovfl and T1 A/B match ldi temp, 0b01100010 ;enable the T1 A&B match interrupts out TIMSK, temp ;and enable T0 overflow ;make port C an output port and set it to midrange ldi temp, 0xff out DDRC, temp ldi temp, 0x80 out PORTC, temp ;make port B an output port and turn off LEDs ldi temp, 0xff out DDRB, temp ldi temp, 0xff out PORTB, temp ;make port A an output port ldi temp, 0xff out DDRA, temp ldi temp, 0xff out PORTA, temp ;make port D an input port and turn off pullups ldi temp, 0x00 out DDRD, temp ldi temp, 0x00 out PORTD, temp ;********************************************** ;get ready to playback the call ; initializing all variables ; default is song 1 ldi ZH, HIGH(songtbl*2) ;init the song pointer ldi ZL, LOW(songtbl*2) ;number of bursts lpm mov numbst, r0 adiw ZL, 1 ;sample rate ;preload to sample time: 256 - sampletime(microsec)/0.25 ; -- default to 20 microsec (3125 Hz sine) lpm mov preload, r0 adiw ZL, 1 ;burst duration in 64 microsec units lpm mov tbstHi, r0 adiw ZL, 1 lpm mov tbstLo, r0 adiw ZL, 1 ;busrt spacing in 64 microsec units lpm mov tspcHi, r0 adiw ZL, 1 lpm mov tspcLo, r0 adiw ZL, 1 ;song duration lpm mov sngTHi, r0 adiw ZL, 1 lpm mov sngTLo, r0 adiw ZL, 1 out OCR1AH, sngTHi out OCR1AL, sngTLo ;sample rate doubler lpm mov step, r0 ;init the sine wave gen ldi ZH, HIGH(sintbl*2) ;init the waveform pointer ldi ZL, LOW(sintbl*2) ldi temp, 0xfe ;set the LED to song 1 out PORTB,temp sei ;test buttons and play forwver playlp: ;rjmp playlp in button, PIND ;wait for a button push mov temp, button cpi temp, 0xff ;button pressed? breq playlp ;no button so exit loop cpi temp, 0x7f ;check button range brlo playlp ;button out of range cli ;disable interrupts (critical section with ZL) ldi ZH, HIGH(songtbl*2) ;init the song pointer ldi ZL, LOW(songtbl*2) mov temp, button cpi temp,0xfe ;button 0 down brne but2 ;adiw ZL, 0 rjmp sginit but2: cpi temp, 0xfd ;button 1 down brne but3 ;only eight songs defined now adiw ZL,10 ;offset to next song entry out PORTB, temp rjmp sginit but3: cpi temp, 0xfb ;button 2 down brne but4 ; adiw ZL,20 ;offset to next song entry rjmp sginit but4: cpi temp, 0xf7 ;button 3 down brne but5 ; adiw ZL,30 ;offset to next song entry rjmp sginit but5: cpi temp, 0xef ;button 4 down brne but6 ; adiw ZL,40 ;offset to next song entry rjmp sginit but6: cpi temp, 0xdf ;button 5 down brne but7 ; adiw ZL,50 ;offset to next song entry rjmp sginit but7: cpi temp, 0xbf ;button 6 down brne but8 ; adiw ZL,60 ;offset to next song entry rjmp sginit but8: cpi temp, 0x7f ;button 7 down brne playlp ; adiw ZL,60 ;offset to next song entry adiw ZL,10 ;need two statements becasue of offset limit rjmp sginit sginit: out PORTB, temp ;set the LED to the song number ;number of bursts lpm mov numbst, r0 adiw ZL, 1 ;sample rate ;preload to sample time: 256 - sampletime(microsec)/0.25 ; -- default to 20 microsec (3125 Hz sine) lpm mov preload, r0 adiw ZL, 1 ;burst duration in 64 microsec units lpm mov tbstHi, r0 adiw ZL, 1 lpm mov tbstLo, r0 adiw ZL, 1 ;busrt spacing in 64 microsec units lpm mov tspcHi, r0 adiw ZL, 1 lpm mov tspcLo, r0 adiw ZL, 1 ;song duration lpm mov sngTHi, r0 adiw ZL, 1 lpm mov sngTLo, r0 adiw ZL, 1 out OCR1AH, sngTHi out OCR1AL, sngTLo ;zero timer 1 to prevent long delays when a song is changed ldi temp,0 out TCNT1H, temp out TCNT1L, temp ;sample rate doubler lpm mov step, r0 endlp: ldi ZH, HIGH(sintbl*2) ;reinit the waveform pointer ldi ZL, LOW(sintbl*2) ;ldi temp, 0b01100010 ;enable interrupts ;out TIMSK, temp sei ;enable ints rjmp playlp ;loop ;****************************************** ; t1a compare match -- if we get here, the song is starting again ;starts the song over again ;(timer1 is auto-cleared by match-clear) CmpA: in save, SREG ;save processor status ldi curbst,1 ;burst counter starts at 1 because ldi itemp,0xff mov bstflag, itemp ;sets to output=ON ldi ZH, HIGH(sintbl*2);reload sine ptr ldi ZL, LOW(sintbl*2) out TCNT0, preload ;reset timer 0 for sample interval ldi itemp, 1 ;start timer 0 out TCCR0, itemp ldi itemp, 0b01100010 ;enable the T1 A&B match interrupts out TIMSK, itemp ;and enable T0 overflow ldi itemp, 0x80 ;middle value out PORTC, itemp ;set DAC ldi itemp, 0x80 ;build a trigger out for song start out PORTA, itemp ;toggle to Vcc AND DON'T mess with itemp clr nwave ;first cycle counter ;set the current time for a new song to the end of the first timeout mov curTLo, tbstLo mov curTHi, tbstHi ;load timer1B with first timeout out OCR1BH, tbstHi out OCR1BL, tbstLo ldi itemp, 0x00 ;drop trigger pulse out PORTA, itemp ;back to gnd leaveA: out SREG, save ;restore proc status reti ;back to pgm ;****************************************** ; t1b compare match -- ;starts/ends a new burst CmpB: in save, SREG ;save processor status tstnum: cp curbst, numbst ;end of bursts? brne more ldi itemp, HIGH(64000);if end; set CmpB to max, out OCR1BH, itemp ldi itemp, LOW(64000) out OCR1BL, itemp clr bstflag ;thats all folks clr nwave rjmp leaveB ;and exit (with no more bursts) ;set up Timer 0 for burst/no-burst more: com bstflag ;toggle output state breq stop0 ;if bstflag=FALSE stop timer 0 inc curbst ; count the new burst ldi ZL, LOW(sintbl*2);else reload sine ptr ldi ZH, HIGH(sintbl*2);reload sine ptr clr nwave ;wave couter for t0 ISR ramp-up rjmp Tcalc stop0: clr nwave ;wave counter for t0 ISR ramp-down Tcalc: tst bstflag ;are we timing a burst or a space? brne Tbst ;burst if not zero add curTLo, tspcLo ;must be a space so update time for space adc curTHi, tspcHi out OCR1BH, curTHi ;and send it to the timer1b compare reg out OCR1BL, curTLo rjmp leaveB Tbst: add curTLo, tbstLo ;must be a burst so update time for burst adc curTHi, tbstHi out OCR1BH, curTHi ;and send it to the timer1b compare reg out OCR1BL, curTLo leaveB: out SREG, save ;restore proc status reti ;back to pgm ;***************************************** ; t0 overflow sends the next sinewave sample T0ovfl: in save, SREG ;save processor status out TCNT0, preload ;sample rate setting tst bstflag ;are we playing a tone? breq bstoff lpm ;must be playing, so get the next sine sample mov itemp, r0 tst nwave ;first cycle after start? brne halfamp lsr itemp ;1/4 amplitude lsr itemp subi itemp, -0x60 halfamp:cpi nwave, 1 brne fullamp ;second cycle 1/2 amp lsr itemp subi itemp, -0x40 fullamp:out PORTC, itemp ;output next sine sample add ZL,step ;get next ptr (works because Z<256) cpi ZL, sintbl*2+16 ;end of table? brne leave0 ;if not, leave ldi ZL, LOW(sintbl*2);otherwise reset ptr inc nwave ;and update number is sine cycles produced rjmp leave0 bstoff: lpm ;two cycles more, so get the next sine sample mov itemp, r0 cpi nwave, 2 breq quiet cpi nwave, 1 breq qutamp lsr itemp ;one last 1/2 amp cycle subi itemp, -0x40 rjmp fullamp qutamp: lsr itemp ; on last 1/4 amplitude lsr itemp subi itemp, -0x60 rjmp fullamp quiet: ldi itemp, 0x80 ;DC level out PORTC, itemp leave0: out SREG, save ;restore proc status reti ;back to pgm ;********************************