;***************************************************************** ; Sound Wave Generator ;***************************************************************** ; By Ariel Paling ; Based on EOD Generator by Bruce Land ;Takes 1-8 different byte-encoded sound waveforms, places them in ;a 32KByte memory, allows for any size waveform length within the ;RAM, plays the waveform from PORTB at a variable sample rate of ;(1 to 255)*0.25 microsecond waits for a variable time between ;waveforms (1 to 255)*4 mSec; produces 0-255 repeats of the waveform ; ;Memory Break-Up: ;8 4063-byte slots where a song may occupy more than one slot ;Start Addresses for slots are ;1-0x0100 5-0x4080 ;2-0x10E0 6-0x5060 ;3-0x20C0 7-0x6040 ;4-0x30A0 8-0x7020 ; ;Header format for RS-232 line: ;header bytes are transmitted as nibbles+32, 2 bytes each ; 0x00 this is the start-file flag ;samplerate (in 1.9 microSec ) ;number-of-repeats ;delay-between-waves (in 3.78 mSec intervals) ; each of the above is TWO nibbles to be combined into ONE byte ;Starting-Slot-Address-High ;Starting-Slot-Address-Low ;Final-Byte-Address-High ( Add = Slot Add + # points + 2) ;Final-Byte-Address-Low (Add will be stored in the 1st 2-bytes) ;frame-number-for-frist-slot-used ;followed data bytes emcoded as byte pairs: ; (top nibble)+32 then (bottom nibble)+32 .include "8515def.inc" .def save =r1 ;save the SREG .def preload =r2 ;preload time0 to 11.8ms .def sampleT =r11 ;Number of Ticks for sample at output .def press =r12 ;FWD button pressed register .def cntmax =r13 ;number of times to repeat a wave .def dlymax =r14 ;Number of Ticks between waves .def delay =r15 ;t0 ovfl counter for delay .def temp =r16 ;temporary register .def count =r17 ;number of waveforms to generate .def sample =r18 ;waveform sample .def itemp =r19 ;Interrupt temp variable .def byte1 =r20 ;first incoming nibble (zeroed by code) .def playbk =r21 ;playback flag .def frame =r22 ;current frame number for song .def RXchar =r23 ;a recieved character .def tempZL =r24 ;temporary for the Z-low byte register .def tempZH =r25 ;temporary for the Z-High byte register .def chrcnt =r26 ;character count for rs232 function .def datacnt =r27 ;state variable for rs232 machine ;r30 and r31 are the Z register ;r28 and r29 are the Y register ;*********** ;constants .equ PreScal1=2 ;timer1 prescale by 8 .equ ClrMatch=8 ;Set t1 to clear on Compare-Match .equ baud96 =25 ;9600 baud constant for 4Mhz crystal ;***** Initialization .cseg .org $0000 rjmp RESET ;reset entry vector reti reti reti rjmp CmpA ;compare A match reti ;T1ovfl ;timer 1 inter vector reti rjmp T0ovfl ;timer 0 interrupt vector reti rjmp RXdone ;rs232 receive reti reti reti RESET: ldi temp, 0x80 out MCUCR, temp ;Set External Memory ldi temp, 0xff ;setup stack pointer to a value in out SPL, Temp ;memory not being written ldi Temp, 0x00 out SPH, Temp ;set up timer zero to overflow every 3.78ms mSec ldi temp, 194 ;256 - 3.78/.064 mov preload, temp out TCNT0, preload ldi temp, 0 ;turn off t0 counter until t1 ovfl out TCCR0, temp ;prescale=256 one tick=64 microSec ;set up timer1 to clear on match reg A & prescale=1 ldi temp, 0 ;disable timer1 until triggered out TCCR1B, temp ;enable time0 ovfl and T1 match ldi temp, 0b01000010 ;enable the T1 match interrupt out TIMSK, temp ;and enable T0 overflow ;make port B an output port ldi temp, 0xff out DDRB, temp ldi temp, 0x00 out PORTB, temp ;make port D an input port for pushbuttons ldi temp, 0x0E out DDRD, temp ;setup UART -- enable RXdone int, and RX pins ldi temp, 0b10010000 out UCR, temp ;set baud rate to 9600 ldi temp, baud96 out UBRR, temp ;********************************************* ;Initialize from 0x0100 to 0x7fff ; This default wave is for calibration ; ldi sample, 0x00 ldi temp, 0x7f ldi itemp, 0xff clr ZL ;start triangle wave at RAM location ldi ZH,1 ;0x100 wavefm: st Z, sample cp ZH, temp brlo index cp ZL, itemp brlo index breq initl index: adiw ZL, 1 ;increment pointer brne wavefm ;************************************************ ;get ready to playback the waveform ; initializing all variables ; initl: ldi temp, 0x01 ; here we clear the song slots sts $0100, temp ; by setting last song-byte add ldi temp, 0x02 ; to point to low byte of itself sts $0101, temp ; at the second byte add of the ldi temp, 0x10 ; slot sts $10E0, temp ldi temp, 0xE2 sts $10E1, temp ldi temp, 0x20 sts $20C0, temp ldi temp, 0xC2 sts $20C1, temp ldi temp, 0x30 sts $30A0, temp ldi temp, 0xA2 sts $30A1, temp ldi temp, 0x40 sts $4080, temp ldi temp, 0x82 sts $4081, temp ldi temp, 0x50 sts $5060, temp ldi temp, 0x62 sts $5061, temp ldi temp, 0x60 sts $6040, temp ldi temp, 0x42 sts $6041, temp ldi temp, 0x70 sts $7020, temp ldi temp, 0x22 sts $7021, temp ldi frame, 1 ; initialize frame to 1 lds YH, $0100 ; set last byte pointer lds YL, $0101 ldi ZH, 0x01 ; set start-of-slot pointer ldi ZL, 0x02 ldi chrcnt, 9 ; make sure to start RS-323 ; machine to idle ldi temp, 1 mov cntmax, temp ;default total num of waves to play mov count, cntmax ;start number of times to play at max clr playbk ;start in no-play mode ldi temp, 252 mov dlymax, temp ;default time between waveforms = 1 sec clr delay ;interval between waveforms ldi temp, 254 ;default sample time to 60 microsec mov sampleT, temp sei playlp: mov temp, frame add temp,temp out PORTD, temp ; Start the loop to check if play/fwd/RS232 cpi chrcnt, 9 ; Are we downloading from RS232? brne plyblk ; If yes, block the play/Fwd sbic PIND, 4 ; Elseif the PD4 button is pushed, rjmp fwdlp ; play the song ; rjmp play ; Else Check for FWD button or return to ; playloop plyblk: clr temp ; Block the play/FWD if we are downloading out TCCR0, temp ; by stopping clocks and ignoring push buttons out TCCR1B, temp ; rjmp playlp fwdlp: sbic PIND, 5 ; If PD5 (FWD) is pressed, then test for already rjmp presst ; pressed in last clock cycle with register press tst press ; If not pressed before then increase song # breq plying ; Else come back to main play loop rjmp playlp plying: tst playbk brne playlp cmp2: ser temp ; Asserted in FWD and figure out which song now mov press,temp ; then increase by one, setting necesary pointers cpi YH, 0x10 ; Use Y to figure current song brlo song2 ; Update Y,Z,frame to new song brne cmp3 cpi YL, 0xE0 brlo song2 rjmp song3 cmp3: cpi YH,0x20 brlo song3 brne cmp4 cpi YL, 0xC0 brlo song3 rjmp song4 cmp4: cpi YH, 0x30 brlo song4 brne cmp5 cpi YL, 0xA0 brlo song4 rjmp song5 cmp5: cpi YH,0x40 brlo song5 brne cmp6 cpi YL, 0x80 brlo song5 rjmp song6 cmp6: cpi YH, 0x50 brlo song6 brne cmp7 cpi YL, 0x60 brlo song6 rjmp song7 cmp7: cpi YH,0x60 brlo song7 brne cmp8 cpi YL, 0x40 brlo song7 rjmp song8 cmp8: cpi YH,0x70 brlo song8 brne song1 cpi YL, 0x20 brlo song8 rjmp song1 song1: ldi frame, 1 lds YH, $0100 lds YL, $0101 ldi ZH, 0x01 ldi ZL, 0x20 rjmp playlp song2: ldi frame, 2 ; Update song lds YH, $10E0 lds YL, $10E1 ldi ZH, 0x10 ldi ZL, 0xE2 rjmp playlp song3: ldi frame, 3 lds YH, $20C0 lds YL, $20C1 ldi ZH, 0x20 ldi ZL, 0xC2 rjmp playlp song4: ldi frame, 4 lds YH, $30A0 lds YL, $30A1 ldi ZH, 0x30 ldi ZL, 0xA2 rjmp playlp song5: ldi frame, 5 lds YH, $4080 lds YL, $4081 ldi ZH, 0x40 ldi ZL, 0x82 rjmp playlp song6: ldi frame, 6 lds YH, $5060 lds YL, $5061 ldi ZH, 0x50 ldi ZL, 0x62 rjmp playlp song7: ldi frame, 7 lds YH, $6040 lds YL, $6041 ldi ZH, 0x60 ldi ZL, 0x42 rjmp playlp song8: ldi frame, 8 lds YH, $7020 lds YL, $7021 ldi ZH, 0x70 ldi ZL, 0x22 rjmp playlp presst: clr press ;Checked PD5 not pressed, clear press reg rjmp playlp play: cli ;critical section setup playback ser playbk ;playback active ldi count, 1 ;init # of waves played clr delay ;interval between waveforms mov tempZL, ZL ;save Z for recovery after song played mov tempZH, ZH ldi temp, 0 ;set up sample time out OCR1AH, temp ;load the A comp reg HIGH BYTE first out OCR1AL, sampleT ;load the A-comp reg LOW BYTE out TCCR0, temp ;assert t0 off out TCNT1H, temp ;start t1 count out TCNT1L, temp ;set clock rate0 ldi temp, PreScal1+ClrMatch;start timer1 out TCCR1B, temp sei ;end critical section rjmp playlp ;****************************************** ; ; t1 compare match -- ;which will be every sample of the waveform ;sends all song samples, then disables itself and starts t0 for delay ;if all repetitions have been played, stop playback, stop timers CmpA: in save, SREG ;save processor status ld sample, Z out PORTB, sample cp ZL,YL ; Check for last sample brne leave cp ZH,YH brne leave sbiw tempZL, 1 ; Offset subtracted since we add one ; to Z at exit mov ZL, tempZL ; update Z with original start of song mov ZH, tempZH cp count, cntmax ; Check for the last repetition to be played breq stopit ; and stop if enough repetitions played inc count ; if not, increase # of reps played ; and start t0 for delay again ldi itemp, 0 ;kill timer1 counter out TCCR1B, itemp out TCNT0, preload ;start timer0 counter ldi itemp, 4 out TCCR0, itemp rjmp leave stopit: clr playbk ;stop playback ldi count, 1 ;re-initialize count # ldi itemp, 0 out TCNT0, preload out TCCR1B, itemp ;kill timer1 counter out TCCR0, itemp ;kill timer0 counter leave: adiw ZL, 1 ; increase Z for next sample out SREG, save ;restore proc status reti ;back to pgm ;***************************************** ; t0 overflow sets waveform rep rate ; The waveform rep rate counter 'delay' is incremented and checked ; If the delay counter maxes out then ; the playback is started by reseting timer1, turning off timer0 ; and setting a clock rate T0ovfl: in save, SREG ;save processor status out TCNT0, preload ;set ovfl time to 3.78 mSec inc delay ;wait a while cp delay, dlymax ;enough time gone by? brne leavt0 ;if not leave ; Restart Wave play ldi itemp,0 ;init timer1 out TCCR0, itemp ;kill timer0 out TCNT1H, itemp out TCNT1L, itemp ;set clock rate ldi itemp, PreScal1+ClrMatch;start timer1 out TCCR1B, itemp clr delay ;start a new delay cycle leavt0: out SREG, save ;restore proc status reti ;back to pgm ;****************************************** ; ; UART read a character RXdone: in save, SREG ;save processor status in RXchar, UDR ;get the character cpi RXchar, 0x00 ;ascii null is data stream reset char brne RXchr0 ;if not zero read the data stream clr playbk ;turn off the waveform clr chrcnt ;reset char counter clr datacnt ;reset data counter clr byte1 ;no data recieved yet rjmp RXexit RXchr0: cpi chrcnt, 0 ;first byte is sample time brne RXchr1 ;in units of 1.9 microSec tst byte1 ;see if first byte is received brne strate ;if so, the current RXchar must be low-nibble mov byte1, RXchar ;save the high-order nibble rjmp RXexit ;and wait for low-order nibble strate: subi byte1, 32 subi RXchar, 32 andi RXchar, 0x0f ;zero the high order bits andi byte1, 0x0f ;ditto swap byte1 ;form upper nibble or RXchar, byte1 ;form byte value sample clr byte1 subi RXchar, 1 ;timer1 is ZERO based mov sampleT, RXchar inc chrcnt rjmp RXexit RXchr1: cpi chrcnt, 1 ;second byte is number of waveforms brne RXchr2 tst byte1 ;see if first byte is received brne stnum ;if so, the current RXchar must be low-nibble mov byte1, RXchar ;save the high-order nibble rjmp RXexit ;and wait for low-order nibble stnum: subi byte1, 32 subi RXchar, 32 andi RXchar, 0x0f ;zero the high order bits andi byte1, 0x0f ;ditto swap byte1 ;form upper nibble or RXchar, byte1 ;form byte value sample clr byte1 mov cntmax, RXchar inc chrcnt rjmp RXexit RXchr2: cpi chrcnt, 2 ;third byte is delay between waveforms brne RXchr3 ;in units of 3.78 mSec ticks tst byte1 ;see if first byte is received brne stdly ;if so, the current RXchar must be low-nibble mov byte1, RXchar ;save the high-order nibble rjmp RXexit ;and wait for low-order nibble stdly: subi byte1, 32 subi RXchar, 32 andi RXchar, 0x0f ;zero the high order bits andi byte1, 0x0f ;ditto swap byte1 ;form upper nibble or RXchar, byte1 ;form byte value sample clr byte1 mov dlymax, RXchar inc chrcnt rjmp RXexit RXchr3: cpi chrcnt, 3 ;4th byte is the high byte of slot pointer brne RXchr4 tst byte1 ;see if first byte is received brne sadd1h ;if so, the current RXchar must be low-nibble mov byte1, RXchar ;save the high-order nibble rjmp RXexit ;and wait for low-order nibble sadd1h: subi byte1, 32 subi RXchar, 32 andi RXchar, 0x0f ;zero the high order bits andi byte1, 0x0f ;ditto swap byte1 ;form upper nibble or RXchar, byte1 ;form byte value sample clr byte1 mov ZH, RXchar mov tempZH, ZH inc chrcnt rjmp RXexit RXchr4: cpi chrcnt, 4 ;5th byte is the low byte of slot pointer brne RXchr5 tst byte1 ;see if first byte is received brne sadd1l ;if so, the current RXchar must be low-nibble mov byte1, RXchar ;save the high-order nibble rjmp RXexit ;and wait for low-order nibble sadd1l: subi byte1, 32 subi RXchar, 32 andi RXchar, 0x0f ;zero the high order bits andi byte1, 0x0f ;ditto swap byte1 ;form upper nibble or RXchar, byte1 ;form byte value sample clr byte1 mov ZL, RXchar mov tempZL, ZL inc chrcnt rjmp RXexit RXchr5: cpi chrcnt, 5 ;6th byte is the high byte of last-byte-add brne RXchr6 tst byte1 ;see if first byte is received brne sadd2h ;if so, the current RXchar must be low-nibble mov byte1, RXchar ;save the high-order nibble rjmp RXexit ;and wait for low-order nibble sadd2h: subi byte1, 32 subi RXchar, 32 andi RXchar, 0x0f ;zero the high order bits andi byte1, 0x0f ;ditto swap byte1 ;form upper nibble or RXchar, byte1 ;form byte value sample clr byte1 mov YH, RXchar st Z+, YH inc chrcnt rjmp RXexit RXchr6: cpi chrcnt, 6 ;7th byte is the low byte of last-byte-add brne RXchr7 tst byte1 ;see if first byte is received brne sadd2l ;if so, the current RXchar must be low-nibble mov byte1, RXchar ;save the high-order nibble rjmp RXexit ;and wait for low-order nibble sadd2l: subi byte1, 32 subi RXchar, 32 andi RXchar, 0x0f ;zero the high order bits andi byte1, 0x0f ;ditto swap byte1 ;form upper nibble or RXchar, byte1 ;form byte value sample clr byte1 mov YL, RXchar st Z+, YL adiw YL, 1 ;Add one to allow for simple stop inc chrcnt ;download criterion and include last rjmp RXexit ;byte in the waveform RXchr7: cpi chrcnt, 7 ;8th byte is the frame for this song brne RXchr8 tst byte1 ;see if first byte is received brne getfrm ;if so, the current RXchar must be low-nibble mov byte1, RXchar ;save the high-order nibble rjmp RXexit ;and wait for low-order nibble getfrm: subi byte1, 32 subi RXchar, 32 andi RXchar, 0x0f ;zero the high order bits andi byte1, 0x0f ;ditto swap byte1 ;form upper nibble or RXchar, byte1 ;form byte value sample clr byte1 mov frame, RXchar inc chrcnt rjmp RXexit RXchr8: cpi chrcnt, 8 ;ready for data brne RXexit tst byte1 ;see if first byte is received brne store ;if so, the current RXchar must be low-nibble mov byte1, RXchar ;save the high-order nibble rjmp RXexit ;and wait for low-order nibble store: subi byte1, 32 subi RXchar, 32 andi RXchar, 0x0f ;zero the high order bits andi byte1, 0x0f ;ditto swap byte1 ;form upper nibble or RXchar, byte1 ;form byte value sample clr byte1 ;get ready for next char-pair st Z+, RXchar ;store the sample move the pointer cp ZH, YH ;stop download when last byte reached brne RXexit cp ZL, YL brne RXexit sbiw YL, 1 ;fix Y pointer after being added one inc chrcnt ;stop receive state machine mov ZH, tempZH ;reload the correct start of song add mov ZL, tempZL ; this section plays the waveform after downloading ldi count, 1 ;init # of waves played clr delay ;interval between waveforms ser playbk ;enable playback ldi temp, 0 ;set up sample time out OCR1AH, temp ;load the A comp reg HIGH BYTE first out OCR1AL, sampleT ;load the A-comp reg LOW BYTE out TCCR0, temp ;assert t0 off out TCNT1H, temp ;start t1 count out TCNT1L, temp ;set clock rate0 ldi temp, PreScal1+ClrMatch;start timer1 out TCCR1B, temp RXexit: out SREG, save ;restore proc status reti