; Fayaz Onn, Lisa Ong ; EE 476 Final Project: Spectrum Analyzer ; Sp 1999 ;SAMPLING AT 9000Hz ;FIR BANDPASS FILTERS WITH 100 POLES ; yn = b0*xn + b1*xn1 + b2*xn2 + ... + b1000*x100 .nolist .include "8535def.inc" .device AT90S8535 .list .def save = r15 ;SREG temp reg .def temp = r23 ;temporary register .def temp2 = r24 .def scount = r25 ;sample count for recording and playback .def count = r26 ;general purpose counter .def levelcnt = r27 ;level counter .def freqpresent = r16 .def op1L = r16 .def op1H = r17 .def op2L = r18 .def op2H = r19 ;***** Sampled and calculated values .def xnL =r2 ; holds x variables .def xnH =r3 .def ynL =r4 ; y[n] .def ynH =r5 .def filtercnt = r6 .def leveladdrH = r7 .def leveladdrL = r8 .def thresaddrH = r9 .def thresaddrL = r10 .def thresvalueH = r11 .def thresvalueL = r12 ;***** mpy16u Register Variables .def mc16uL =r16 ;multiplicand low byte .def mc16uH =r17 ;multiplicand high byte .def mp16uL =r18 ;multiplier low byte .def mp16uH =r19 ;multiplier high byte .def m16u0 =r18 ;result byte 0 (LSB) .def m16u1 =r19 ;result byte 1 .def m16u2 =r20 ;result byte 2 .def m16u3 =r21 ;result byte 3 (MSB) .def mcnt16u =r22 ;loop counter ;******** UART registers .def TXbusy =r20 ;transmit busy flag .def RXchar =r21 ;a received character .def TXflash =r22 ;text to be sent is in flash if <>0 .equ baud96 =25 ;9600 baud constant for 4Mhz crystal .equ azero ='0' ;0x30 ascii '0' .equ NUMSAM = 238 ;number of samples .equ FILTERS = 8 ;number of filters .equ POLES = 101 ; poles+1 ;**** Thresholds of level counts ****** .nolist .include "firmac.asm" ;macros file .list .dseg samples: .byte 476 levels: .byte 16 ;define variable strings to be tranmitted from RAM cntstr: .byte 4 ;a three digit count + a zero terminate fp: .byte 16 ;***** Initialization .cseg .org $0000 rjmp RESET ;reset entry vector reti reti reti reti reti reti reti reti reti reti reti rjmp TXempty ;UART buffer empty reti rjmp adcdone reti reti ;define fixed strings to be tranmitted from flash- zero terminated text: .db "Frequency Analyzer",0x00 crlf: .db 0x0d, 0x0a, 0x00 ;carrage return/line feed limit: .db 01, 01, 01, 01 .db 01, 01, 01, 01 .db 0xff, 0xff, 0xff, 0xff .db 0xff, 0xff, 0xff, 0xff f200: .db "0180-0220Hz:",0x00 f400: .db "0380-0420Hz:",0x00 f600: .db "0580-0620Hz:",0x00 f800: .db "0780-0820Hz:",0x00 f1000: .db "0980-1020Hz:",0x00 f1200: .db "1180-1220Hz:",0x00 f1400: .db "1380-1420Hz:",0x00 f1600: .db "1600-1700Hz:",0x00 f0100: .db 0x00 yes: .db "yes", 0x00 no: .db "no",0x00 .include "square.asm" .nolist .include "coeffs.asm" .list RESET: ldi temp, LOW(RAMEND) ;setup stack pointer out SPL, temp ldi temp, HIGH(RAMEND) out SPH, temp ldi scount, NUMSAM ;load number of samples clr xnH ;clear working registers clr xnL clr ynH clr ynL clr levelCnt ldi ZL, low(levels) ldi ZH, high(levels) mov leveladdrL, r30 mov leveladdrH, r31 ldi ZL, low(threslevel<<1) ldi ZH, high(threslevel<<1) mov thresaddrL, r30 mov thresaddrH, r31 ;set up analog converter to read channel zero Pin A0 ldi temp, 0 out ADMUX, temp ; ADC freerun mode, prescale 32 (for 4MHz), interrupt on sample done ldi temp, 0b11101101 out ADCSR, temp ldi ZL, low(samples) ldi ZH, high(samples) sei ;enable all interrupts ; ********** Freerun Sampling loop *************** swait: tst scount breq output rjmp swait ;************************************************ ; After NUMSAM samples output: ;disable ADC clr temp out ADCSR, temp ldi YL, low(coeff<<1) ;load filter coefficients ldi YH, high(coeff<<1) ldi temp, FILTERS ;# of filters mov filtercnt, temp ;t0 interrupt to play back y samples ;ldi temp, exp2(TOIE0) ;out TIMSK, temp ;ldi temp,0b00000101 ;out TCCR0, temp ; loops changes bandpass filters outerloop: mov temp, filtercnt ; compare with count of 10 filters tst temp breq outerdone dec temp ;else decrement filtercnt mov filtercnt, temp mov r31, thresaddrH mov r30, thresaddrL ; <********************************************** lpm mov thresvalueH, r0 adiw ZL, 1 lpm mov thresvalueL, r0 adiw ZL, 1 mov thresaddrH, r31 mov thresaddrL, r30 ldi ZL, low(samples) ldi ZH, high(samples) ldi scount, (NUMSAM-POLES + 1) ;scount = number of processing samples = 250 push r29 ;store initial Y for each different filter push r28 clr levelcnt filterlp: tst scount breq done dec scount pop r28 ;restores r28, 29 pop r29 push r29 ;store back on stack for next diffeq push r28 rcall diffeq ; yn <- difference eqn mov temp, ynH ; negative values?? cpi temp, 0b10000000 brsh zeroit ;threshold cont1: mov temp2, ynH mov temp, ynL cp temp2, thresvalueH brlo cont2 breq checklow brsh increaseCnt checklow: cp temp, thresvalueL brlo cont2 brsh increaseCnt ; display y's or x's cont2: mov temp2, ynH mov temp, ynL rjmp filterlp ; after 240 samples processed done: mov r30, leveladdrL mov r31, leveladdrH st Z+, levelcnt mov leveladdrL, r30 mov leveladdrH, r31 ; indicate time for next filter pop temp ;dump old initializing pop temp ;value of Y rjmp outerloop zeroit: clr temp mov ynH, temp mov ynL, temp rjmp cont1 increaseCnt: inc levelCnt rjmp cont2 outerdone: ; the end of it all (Uart Code) ;************************************************************* ; limit maximum # filters to 16 for the meantime clr freqpresent ; overrides r16 = op1L clr count ldi ZL, low(levels) ldi ZH, high(levels) mov leveladdrH, r31 mov leveladdrL, r30 ldi YL, low(fp) ; addr for freq present ldi YH, high(fp) ldi ZL, low(limit<<1) ldi ZH, high(limit<<1) mov r18, r31 ;store limit addr mov r17, r30 compare: mov r31, leveladdrH ;addr for levels mov r30, leveladdrL ld levelcnt, Z+ mov leveladdrH, r31 mov leveladdrL, r30 mov r31, r18 mov r30, r17 lpm adiw ZL, 1 mov r18, r31 mov r17, r30 cp levelcnt, r0 brsh set_yes set_no: clr freqpresent rjmp jump set_yes: ser freqpresent jump: ;store byte for freq present st Y+, freqpresent inc count cpi count, 16 breq endcmp rjmp compare ;************************** Transmit to UART ***************** endcmp: clr TXbusy ;start out not busy on TX ;intialize text pointer BEFORE turning on interrupts ;because RESET causes the TX empty flag to be SET ldi ZL, LOW(text<<1) ldi ZH, HIGH(text<<1) ;setup UART -- enable TXempty and TX pin ldi temp, 0b00001000 out UCR, temp ;set baud rate to 9600 ldi temp, baud96 out UBRR, temp ldi ZL, LOW(text<<1) ldi ZH, HIGH(text<<1) lpm out UDR, r0 ;trigger the UART TX ser TXflash ;text string in flash memory ser TXbusy ;and set the TX busy flag sbi UCR, UDRIE ;enable the TXempty interrupt rcall TXwait ldi ZL, LOW(crlf<<1) ;shifted becuase pgm memory is words ldi ZH, HIGH(crlf<<1) lpm out UDR, r0 ;trigger the UART TX ser TXflash ;text string in flash memory ser TXbusy ;and set the TX busy flag sbi UCR, UDRIE ;enable the TXempty interrupt rcall TXwait ldi temp, FILTERS mov filtercnt, temp ldi YL, low(levels) ;initialize to stored threshold level data ldi YH, high(levels) ldi ZL, LOW(f200<<1) ldi ZH, HIGH(f200<<1) mov thresaddrL, r30 mov thresaddrH, r31 clr scount mov filtercnt, temp ldi YL, low(levels) ;initialize to stored threshold level data ldi YH, high(levels) ;Loop and Print threshold level counts to RS232 serial port via UART format: mov temp, filtercnt tst temp breq finallydone dec temp mov filtercnt, temp mov r30, thresaddrL mov r31, thresaddrH push r30 push r31 adiw ZL, 14 mov thresaddrL, r30 mov thresaddrH, r31 pop r31 pop r30 lpm ;put the char in r0 out UDR, r0 ;put the character in the UART buffer ser TXflash ;string is in flash memory ser TXbusy ;and set the TX busy flag sbi UCR, UDRIE ;enable the TXempty interrupt rcall TXwait ;chill until done ;load the above threshold counts from memory ld levelcnt, Y+ ldi temp2, azero ;load ascii zero ;format the number into an ascii string hundreds: cpi levelcnt, 100 brlo storehun subi levelcnt, 100 inc temp2 rjmp hundreds storehun: ldi ZL, LOW(cntstr) ;storage space for formatted ascii digits ldi ZH, HIGH(cntstr) st Z+, temp2 ;store hundreds digit ldi temp2, azero tens: cpi levelcnt, 10 brlo storeten subi levelcnt, 10 inc temp2 rjmp tens storeten: st Z+, temp2 ;store tens digit subi levelcnt, -azero st Z+, levelcnt ;store ones digit clr temp ;terminating zero st Z, temp ldi ZL, LOW(cntstr) ;ptr to RAM ldi ZH, HIGH(cntstr) ld r0,Z out UDR, r0 ;fire off the UART transmit clr TXflash ;the string is in RAM ser TXbusy ;and set the TX busy flag sbi UCR, UDRIE ;enable the TXempty interrupt rcall TXwait ;setup ptr for string ldi ZL, LOW(crlf<<1) ;shifted becuase pgm memory is words ldi ZH, HIGH(crlf<<1) lpm out UDR, r0 ;trigger the UART TX ser TXflash ;text string in flash memory ser TXbusy ;and set the TX busy flag sbi UCR, UDRIE ;enable the TXempty interrupt rcall TXwait rjmp format finallydone: rjmp finallydone ;***************************** ISRS ************************** adcdone: in save, SREG in temp, ADCL ;read the voltage in temp2, ADCH ; Take 240 samples of x[n] and store in SRAM st Z+, temp ;store low byte first st Z+, temp2 dec scount out SREG,save reti t0overflow: in save, SREG dec count out SREG, save reti TXempty:in save, SREG ;save processor status tst TXflash ;Is the string in flash memory? breq TXram ;If not, it is in RAM inc ZL ;get the next char from flash lpm ;and put it in r0 rjmp TXfls TXram: inc ZL ;get the next char from RAM ld r0,Z TXfls: tst r0 ;if char is zero then exit breq TXend out UDR, r0 ;otherwise transmit it rjmp TXexit ;exit until next char TXend: clr TXbusy ;no more chars cbi UCR, UDRIE ;clear the TXempty interrupt TXexit: out SREG, save ;restore proc status reti ;back to pgm TXwait: tst TXbusy ;now wait for the tranmission to finish brne TXwait ret ;****************************** SUBS ************************** ; Difference Equation ; yn = b0*xn + b1*xn1 + b2*xn2 + b3*xn3 + b4*xn4 +.....+ b100*xn100 diffeq: push r31 ; store values of ZL and ZH push r30 ; to point to correct address next time clr temp2 ; clear yn mov ynH, temp2 mov ynL, temp2 ldi count, POLES ; loop POLES times add running sum ; yn = yn + sum of bi*x[n-i] for i=0 to 100 runninglp: ld xnL, Z+ ; load x[n-6] first ld xnH, Z+ push r30 push r31 mov r30, r28 mov r31, r29 ; lpm wants Z register lpm ; get upper byte of coeff mov r1, r0 ; store it in r1 adiw ZL, 1 lpm ; get lower byte of coeff in r0 pop r31 ; restore Z value pop r30 adiw YL, 2 ; point to next coeff loadop1 r1, r0 loadop2 xnH, xnL cpi op1H, 0b10000000 brsh subit rcall multiply loadop1 ynH, ynL rcall add16 back: storeop2 ynH, ynL ; <--------- running sum dec count brne runninglp rjmp exitrunning subit: ;take 2's complement com op1L com op1H subi op1L, -1 tst op1L brne noovfl ;if op1L = 0 after adding 1 then overflow occurred subi op1H, -1 noovfl: rcall multiply ;result is in op2 movop1 ;move the thing we want to subtract to op1 loadop2 ynH, ynL rcall sub16 rjmp back exitrunning: ; make Z point to next x[n-6] pop r30 pop r31 adiw ZL, 2 ret ;********** computational subroutines add16: ;op1+op2 -> op2. add op2L, op1L adc op2H, op1H ret sub16: ;op2-op1 -> op2 sub op2L, op1L sbc op2H, op1H ret multiply: ;op2 * op1 -> op2 rcall mpy16u cpi r18, 0x80 ;lowest byte>125, add one to answer (round up 0.5) brlo skipadd ; Note: brcc doesn't seem to work here. Seems like carry is ALWAYS set. subi r19, -1 ; the ONLY time overflow actually occurs is if r19= 0xff cpi r19, 0x00 ; Hence, if r19+1 = 0x00 then add one to r20 brne skipadd subi r20, -1 skipadd:mov r18, r19 ;result is only 10 bits, hopefully....... mov r19, r20 mov r20, r21 lsr r20 ror r19 ;triple precision shift ror r18 ;right 4 times lsr r20 ror r19 ror r18 lsr r20 ror r19 ror r18 lsr r20 ror r19 ;answer is in r19, r18. y*65536/256/16 ror r18 ret ;*************************************************************************** ;* ;* "mpy16u" - 16x16 Bit Unsigned Multiplication ;* ;*************************************************************************** mpy16u: clr m16u3 ;clear 2 highest bytes of result clr m16u2 ldi mcnt16u,16 ;init loop counter lsr mp16uH ror mp16uL m16u_1: brcc noad8 ;if bit 0 of multiplier set add m16u2,mc16uL ;add multiplicand Low to byte 2 of res adc m16u3,mc16uH ;add multiplicand high to byte 3 of res noad8: ror m16u3 ;shift right result byte 3 ror m16u2 ;rotate right result byte 2 ror m16u1 ;rotate result byte 1 and multiplier High ror m16u0 ;rotate result byte 0 and multiplier Low dec mcnt16u ;decrement loop counter brne m16u_1 ;if not done, loop more ret