www.mikrocontroller.net

FFT.asm

FFT.asm

;-----------------------------------------------------------------------------;
; Audio Waveform Monitor R0.1                               Nov 11, 2004      ;
; (C)ChaN, 2004; http://elm-chan.org/                                         ;
;-----------------------------------------------------------------------------;
;
.include "m8def.inc"
.include "avr.inc"
.include "akiglcd.inc"

.equ  Sync  = 2    ;Port D bit definitions

.equ  FFT_N  = 128  ;Number of samples
.equ  LCD_H  = 16  ;/

.def  _0  = r15    ;Zero
.def  _Flags  = r25  ;b0:In captureing
            ;b1:In pause
            ;b2:MOSI edge detector
            ;b3:Low/High Freq
            ;b4:ADC Divider (für 5,5kHz Samplerate)
            ;b5:WDR Flag 0
            ;b6:WDR Flag 1

.equ WDRF0=5
.equ WDRF1=6

;----------------------------------------------------------;
; Data memory area

.dseg
  .org  RAMTOP
CaptBuf:.byte  FFT_N*2    ;Sampling buffer
BflyBuf:.byte  FFT_N*4    ;Butterfly operation table, Wave form buffer
LvlBuf:  .byte  FFT_N/2    ;Spectrum bar length, High Freq
LvlBuf2:.byte  FFT_N/2    ;Spectrum bar length, Low Freq
LvlBuf3:.byte  FFT_N/2    ;Spectrum bar length, Gesamt

AdPtr:  .byte  1    ;Sampling pointer
LcdDiv:  .byte  1    ;Divider for LCD drive


;----------------------------------------------------------;
; Program code area

.cseg
  rjmp  reset    ; RESET
  rjmp  0    ; INT0
  rjmp  0    ; INT1
  rjmp  0    ; TC2 COMP
  rjmp  0    ; TC2 OVF
  rjmp  0    ; TC1 CAPT
  rjmp  0    ; TC1 COMPA
  rjmp  0    ; TC1 COMPB
  rjmp  0    ; TC1 OVF
  rjmp  0    ; TC0 OVF
  rjmp  0    ; SPI
  rjmp  0    ; USART RXC
  rjmp  0    ; USART UDRE
  rjmp  0    ; USART TXC
;  rjmp  isr_adc    ; ADC
;  rjmp  0    ; EE_RDY
;  rjmp  0    ; ANA_COMP
;  rjmp  0    ; TWI
;  rjmp  0    ; SPM_RDY


;----------------------------------------------------------;
; ADC interrupt (9.6ksps)


isr_adc:
  push  AL
  in  AL, SREG
  pushw  A
  pushw  Y
  sbrs  _Flags, 0  ;Skip if in Idle
  rjmp  adc_skip  ;/
  sbrs  _Flags, 3
  rjmp nodiv
  sbrs  _Flags, 4
  rjmp setd
  cbr   _Flags, (1<<4)
  rjmp adc_skip
setd:
  sbr    _Flags, (1<<4)
nodiv:
  lds  YL, AdPtr  ;Write pointer
  clr  YH    ;
  addiw  Y, CaptBuf  ;/
  inw  A, ADC    ;Store A/D value
  stw  Y+, A    ;/
  lds  AL, AdPtr  ;Next pointer
  addi  AL, 2    ;
  sts  AdPtr, AL  ;/
  brne  PC+2    ;Terminate if 128 samples captured.
  cbr  _Flags, bit0  ;/
adc_skip:

  popw  Y
  popw  A
  out  SREG, AL
  pop  AL
  reti

;----------------------------------------------------------;
; Initialize peripherals (ATmega8 @ 18,432MHz)

reset:
  clr  _0      ;Zero reg.
  ldiw  Z, RAMTOP    ;Clear all variables
  ldiw  X, RAMEND-RAMTOP+1  ;
  st  Z+, _0      ;
  sbiw  XL, 1      ;
  brne  PC-2      ;/
  sbiw  ZL, 1      ;SP ʉŠú‰»
  outw  SP, Z      ;/

  outi  PORTB, 0b00101000  ;Port B ʉŠú‰»
  outi  DDRB,  0b00010110

  outi  DDRC,  0

  outi  PORTD, 0b00000100  ;Port D
  outi  DDRD,  0b00000100  ;Port D

  ; Start TC1 with 444kHz on OC1B pin (for MAX293 clock)
  ldiw  A, 36-1      ;TOP value
  outw  ICR1, A      ;/
  ldiw  A, 36/2      ;half value
  outw  OCR1B, A    ;/
  outi  TCCR1A, 0b00100010  ;COM1B[1:0]=10, WGM1[1:0]=10
  outi  TCCR1B, 0b00011001  ;WGM1[3:2]=11, CS1[2:0]=001

  ; Start A/D with ch0, free running, 44ksps
  outi  ADMUX, 0b00000000  ;MUX[3:0]=0000
  outi  ADCSR, 0b11101111   ;ADEN=1,ADSC=1,ADFR=1,ADIE=1,ADPS[2:0]=110

  outi   ucsrb, 8
  outi   ubrrh, 0
  outi   ubrrl, 9

  in r16, WDTCR
    ori r16, (1<<WDCE) | (1<<WDE)
    out WDTCR, r16
    ldi r16, (1<<WDE) | (1<<WDP2)
    out WDTCR, r16

  clr  _Flags
  wdr
  sei

  sbr  _Flags, bit0  ;Start wave form captureing


;----------------------------------------------------------;
; Main


main:
  sbrc  _Flags, 0  ;Wait for end of wave captureing
  rjmp  main    ;/
  
  sbrc _Flags, 3
  rjmp LoF
  outi  ADCSR, 0b11101000 + 7  ;ADEN=1,ADSC=1,ADFR=1,ADIE=1,ADPS[2:0]=111, 11kHz Samplerate
  outi  ADMUX, 0b00000001  ;MUX[3:0]=0001
  sbr _Flags, (1<<3)
  rjmp Hif
Lof:
  outi  ADCSR, 0b11101000 + 5  ;ADEN=1,ADSC=1,ADFR=1,ADIE=1,ADPS[2:0]=101, 44khz Samplerate
  outi  ADMUX, 0b00000000  ;MUX[3:0]=0000
  cbr _Flags, (1<<3)
Hif:
   rcall  do_window      ;Fill butterfly table      [340us]
      sbr  _Flags, bit0    ;Restart captureing for next
   rcall  do_fft        ;Butterfly operations      [4.4ms]
  
  sbrc _Flags, 3        ;Gesetzt: jetzt High Freq verarbeiten
  rjmp Lof2
  rcall  make_barsl      ;Get scalar values, update spectrum  [3.3ms]
  rjmp Hif2
Lof2:
  rcall  make_barsh      ;Get scalar values, update spectrum  [3.3ms]
  rcall  rfsh_bars      ;Display spectrum bars into LCD    [660us]
Hif2:

hier:
  rjmp  main        ; (approx. 30 cycles/sec)


;----------------------------------------------------------;
; Spectrum analyzer
;----------------------------------------------------------;
; 16bit fixed point FFT performance with megaAVR @16MHz
;
;  Points:  Input,    FFT, Output,   Total: Throughput
;   64pts:  .17ms,  1.9ms,  1.4ms,   3.5ms:   18.3kpps   (expected)
;  128pts:  .34ms,  4.4ms,  2.6ms,   7.3ms:   17.5kpps   (measured)
;  256pts:  .68ms, 10.1ms,  5.2ms,  16.0ms:   16.0kpps   (expected)
;  512pts:  1.4ms, 22.6ms, 10.4ms,  34.4ms:   14.8kpps   (expected)
;
; Input: Input waveform into butterfly table with applying window
; FFT: Execute butterfly operations
; Output: Descramble and output the spectrum as scalar values


do_window: ; Fill butterfly table with applying window function
  ldiw  X, CaptBuf  ;Source
  ldiw  Y, BflyBuf  ;Destination
  ldiw  Z, t_hamming128*2  ;Window table
  ldi  CL, FFT_N
ms_l1:  lpmw  A, Z+    ;Get a window attenuation ratio
  ldw  B, X+    ;Get an input value
  subiw  B, 512    ;unsigned -> signed
  FMULS16  A, B, T4, T2  ;Apply window
  stw  Y+, T4    ;Store real
  st  Y+, _0    ;Clear image
  st  Y+, _0    ;/
  dec  CL
  brne  ms_l1
  ret


do_fft:  ; Execute butterfly operations (not optimized)
  ldi  XH, 1    ;u
  ldi  EL, FFT_N/2  ;l
df_l1:  ldiw  Z, BflyBuf  ;Z = BflyBuf
  ldiw  Y, BflyBuf  ;Y = BflyBuf + l * 4
  muli  EL, 4    ;
  addw  Y, T0    ;/
  mul  AL, XH    ;T10L = u * 4
  mov  T10L, T0L  ;/
  mov  XL, XH    ;w = u
df_l2:  clr  T14L    ;p=0
df_l3:  lddw  A, Z+0    ;A = [Z+0] - [Y+0], [Z+0] += [Y+0]
  movw  CL, AL    ;B = [Z+2] - [Y+2], [Z+2] += [Y+2]
  lddw  D, Y+0    ;
  subw  A, D    ;
  addw  C, D    ;
  stw  Z+, C    ;
  lddw  B, Z+0    ;
  movw  CL, BL    ;
  lddw  D, Y+2    ;
  subw  B, D    ;
  addw  C, D    ;
  stw  Z+, C    ;/
  movw  T0L, ZL
  movw  ZL, T14L  ;C=cos(p), D=sin(p)
  addiw  Z, t_cos_sin128*2  ;
  lpmw  C, Z+    ;
  lpmw  D, Z+    ;/
  movw  ZL, T0L
  FMULS16  A, C, T4, T2  ;[Y+0] = A * C + B * D
  FMULS16  B, D, T8, T6  ;[Y+2] = B * C - A * D
  addw  T2, T6    ;
  adcw  T4, T8    ;
  stw  Y+, T4    ;
  FMULS16  B, C, T4, T2  ;
  FMULS16  A, D, T8, T6  ;
  subw  T2, T6    ;
  sbcw  T4, T8    ;
  stw  Y+, T4    ;/
  add  T14L, T10L  ;p += u
  rjne  df_l3
  muli  EL, 4    ;Y += l * 4, Z += l * 4; (skip split segment)
  addw  Y, T0    ;
  addw  Z, T0    ;/
  dec  XL    ;--w
  rjne  df_l2
  lsl  XH    ;u *= 2
  lsr  EL    ;l /= 2
  rjne  df_l1
  ret

make_barsl: ;Get scalar spectrum and update spectrum bar length
  ldiw  Z, t_desc128*2  ;Descramble table
  ldiw  Y, LvlBuf  ;Bar length buffer
  ldi  CH, FFT_N/2  ;Number of elements
ub_l1:  lpmw  A, Z+    ;Get a vector value (A = r, B = i)
  ldiw  X, BflyBuf  ;
  addw  X, A    ;
  ldw  A, X+    ;
  ldw  B, X+    ;/
  FMULS16  A, A, T4, T2  ;A = sqrt(A * A + B * B); 0..32767 (scalar)
  FMULS16  B, B, T8, T6  ;
  addw  T2, T6    ;
  adcw  T4, T8    ;
  SQRT32      ;/
  movw  T2L, AL    ; AL = sqrt(A); 0..181 (root compression)
  SQRT16      ; / * remove this for linear scale
  ldi  AH, 100    ;T0H = AL/3; (scaling)  (Orginal: 256/3)
  mul  AL, AH    ;/
  ld  AH, Y    ;Update bar length with peak holding
  subi  AH, 2    ; <-peak decay rate
  brcs  PC+3    ;
  cp  AH, T0H    ;
  brcc  PC+2    ;
  mov  AH, T0H    ;
  subi AH, 1
  brcc notzero
  clr AH
notzero:
  st  Y+, AH    ;/
  dec  CH
  rjne  ub_l1
  sbr _Flags, (1<<WDRF0)
  ret

make_barsh: ;Get scalar spectrum and update spectrum bar length
  ldiw  Z, t_desc128*2  ;Descramble table
  ldiw  Y, LvlBuf2  ;Bar length buffer
  ldi  CH, FFT_N/2  ;Number of elements
ub_l1h:  lpmw  A, Z+    ;Get a vector value (A = r, B = i)
  ldiw  X, BflyBuf  ;
  addw  X, A    ;
  ldw  A, X+    ;
  ldw  B, X+    ;/
  FMULS16  A, A, T4, T2  ;A = sqrt(A * A + B * B); 0..32767 (scalar)
  FMULS16  B, B, T8, T6  ;
  addw  T2, T6    ;
  adcw  T4, T8    ;
  SQRT32      ;/
  movw  T2L, AL    ; AL = sqrt(A); 0..181 (root compression)
  SQRT16      ; / * remove this for linear scale
  ldi  AH, 100    ;T0H = AL/3; (scaling)  (Orginal: 256/3)
  mul  AL, AH    ;/
  ld  AH, Y    ;Update bar length with peak holding
  subi  AH, 2    ; <-peak decay rate
  brcs  PC+3    ;
  cp  AH, T0H    ;
  brcc  PC+2    ;
  mov  AH, T0H    ;
  subi AH, 2    ;Eins mehr abziehen um die Störungen wegzubekommen
  brcc notzeroh
  clr AH
notzeroh:
  st  Y+, AH    ;/
  dec  CH
  rjne  ub_l1h
  sbrc _Flags, WDRF0          ;Nur wenn WRDF0 gesetzt ist, WDRF1 setzen
  sbr _Flags, (1<<WDRF1)
  cbr _Flags, (1<<WDRF0)
  ret


rfsh_bars: ;Draw spectrum bars
  cbi portd, Sync
  ldi  DL, LCD_H-7  ; (This routine refreshes LCD with bar length
  nop
  nop
  nop
  nop
  nop    ;Sync verlängern
  nop
  nop
  sbi portd, Sync      ;component, 2nd value is fundamental freq
sendl:  
   rcall  ds_hl      ;and last value is highest freq)
  subi  DL, 8
  brcc  sendl
  sbrc _Flags, WDRF1    ;Nur wenn alles durchlaufen wurde: WDR
   wdr
  cbr _Flags, (1<<WDRF0)|(1<<WDRF1)
  ret

make2p:
  ld  AH, Y+
  cp AL, AH
  brlo tausche1
rjmp weiter
tausche1:
  mov AL, AH
rjmp weiter

make3p:
  ld  AH, Y+
  cp AL, AH
  brlo tausche3
  rjmp weitert1
tausche3:
  mov AL, AH
weitert1:      ;AL=Max(1,2)
  ld  AH, Y+
  cp AL, AH
  brlo tausche4b
rjmp weiter
tausche4b:
  mov AL, AH
rjmp weiter      ;AL=Max(AL,AH)=Max(1,2,3




ds_hl:  ldiw  Y, LvlBuf+1
  ldi  CL, 16    ;24 Werte werden zu 16 zusammengefasst: 43-1333Hz
senl:  
  ld  AL, Y+    ;1-8    8

  cpi CL, 5    ;17-24    4
  brlo make3p

  cpi CL, 9    ;9-16    4
  brlo make2p
weiter:  
  sub  AL, DL
  brcc  gnull  
  ldi  AH, 0  
  rjmp  inull
gnull:
  ldi  AH, 0xFF
  cpi  AL, 8
  brcc  inull
  ldi  AH, bit7
loope:  
  subi  AL, 1
  brcs  inull
  asr  AH
  rjmp  loope
inull:
  mov  AL, AH
   rcall  lcd_put
  dec  CL
  brne  senl
  rjmp ds_hl2

make2p2:
  ld  AH, Y+
  cp AL, AH
  brlo tausche12
rjmp weiter2
tausche12:
  mov AL, AH
rjmp weiter2

make3p2:
  ld  AH, Y+
  cp AL, AH
  brlo tausche32
  rjmp weitert12
tausche32:
  mov AL, AH
weitert12:      ;AL=Max(1,2)
  ld  AH, Y+
  cp AL, AH
  brlo tausche4b2
rjmp weiter2
tausche4b2:
  mov AL, AH
rjmp weiter2      ;AL=Max(AL,AH)=Max(1,2,3)

make4p2:
  ld  AH, Y+
  cp AL, AH
  brlo tausche42
rjmp weitert22
tausche42:
  mov AL, AH
weitert22:      ;AL=Max(1,2)
  ld  BL, Y+
  ld  BH, Y+
  cp BL, BH
  brlo tausche52
rjmp weitert32
tausche52:
  mov BL, BH
weitert32:      ;BL=Max(3,4)
  cp AL, BL
  brlo tausche62
rjmp weiter2      ;AL=Max(AL,BL)=Max(1,2,3,4)
tausche62:
  mov AL, BL
rjmp weiter2      ;AL=Max(AL,BL)=Max(1,2,3,4)


ds_hl2:  ldiw  Y, LvlBuf2+5
  ldi  CL, 16    ;59 Werte werden zu 16 zusammengefasst: 1731-20021Hz
senl2:  
  ld  AL, Y+    ;1-5    5

  cpi CL, 2    ;17-31    2
  brlo make8p2      

  cpi CL, 4    ;17-31    2
  brlo make6p2

  cpi CL, 6    ;23-22    2
  brlo make4p2

  cpi CL, 9    ;17-22    3
  brlo make3p2

  cpi CL, 12    ;5-10    3
  brlo make2p2
weiter2:  
  sub  AL, DL
  brcc  gnull2  
  ldi  AH, 0  
  rjmp  inull2
gnull2:
  ldi  AH, 0xFF
  cpi  AL, 8
  brcc  inull2
  ldi  AH, bit7
loope2:  
  subi  AL, 1
  brcs  inull2
  asr  AH
  rjmp  loope2
inull2:
  mov  AL, AH
   rcall  lcd_put
  dec  CL
  brne  senl2
  ret

make6p2:
  ld  AH, Y+
  cp AL, AH
  brlo tausche72
rjmp weitert42
tausche72:
  mov AL, AH
weitert42:        ;AL=Max(1,2)
  ld  BL, Y+
  ld  BH, Y+
  cp BL, BH
  brlo tausche82
rjmp weitert52
tausche82:
  mov BL, BH
weitert52:        ;BL=Max(3,4)
  cp AL, BL
  brlo tausche92
rjmp weitert62      ;AL=Max(AL,BL)=Max(1,2,3,4)
tausche92:
  mov AL, BL
weitert62:        ;AL=Max(AL,BL)=Max(1,2,3,4)
  ld  BL, Y+
  ld  BH, Y+
  cp BL, BH
  brlo tausche102
rjmp weitert72
tausche102:
  mov BL, BH
weitert72:        ;BL=Max(5,6)
  cp AL, BL
  brlo tausche112
rjmp weiter2      ;AL=Max(AL,BL)=Max(1,2,3,4,5,6)
tausche112:
  mov AL, BL
rjmp weiter2      ;AL=Max(AL,BL)=Max(1,2,3,4,5,6)

make8p2:
  ld  AH, Y+
  cp AL, AH
  brlo tausche122
rjmp weitert82
tausche122:
  mov AL, AH
weitert82:        ;AL=Max(1,2)
  ld  BL, Y+
  ld  BH, Y+
  cp BL, BH
  brlo tausche132
rjmp weitert92
tausche132:
  mov BL, BH
weitert92:        ;BL=Max(3,4)
  cp AL, BL
  brlo tausche142
rjmp weitert102      ;AL=Max(AL,BL)=Max(1,2,3,4)
tausche142:
  mov AL, BL
weitert102:        ;AL=Max(AL,BL)=Max(1,2,3,4)
  ld  BL, Y+
  ld  BH, Y+
  cp BL, BH
  brlo tausche152
rjmp weitert112
tausche152:
  mov BL, BH
weitert112:        ;BL=Max(5,6)
  ld  AH, Y+
  ld  BH, Y+
  cp AH, BH
  brlo tausche162
rjmp weitert122      ;AH=Max(AH,BH)=Max(7,8)
tausche162:
  mov AH, BH
weitert122:        ;AH=Max(AH,BH)=Max(7,8)
  cp AH, BL
  brlo tausche172
rjmp weitert132      ;AH=Max(AH,BL)=Max(5,6,7,8)
tausche172:
  mov AH, BL
weitert132:        ;AH=Max(AH,BH)=Max(5,6,7,8)
  cp AL, AH
  brlo tausche182
rjmp weiter2      ;AL=Max(AL,BL)=Max(1,2,3,4,5,6)
tausche182:
  mov AL, BH
rjmp weiter2      ;AL=Max(AL,BL)=Max(1,2,3,4,5,6)



lcd_put:
  sbis ucsra, udre
  rjmp lcd_put
  out udr, AL
  inc  BL
  cpi  BL, FFT_N
  brne  exitm
  inc  BH
  clr  BL
exitm:
  ret


; These tables must be rebuilt when change FFT_N

t_cos_sin128:  ; {cos(x),sin(x)} table (0 <= x < pi, in 64 steps)
  .dw  32767, 0, 32727, 1607, 32609, 3211, 32412, 4807
  .dw  32137, 6392, 31785, 7961, 31356, 9511, 30851, 11038
  .dw  30272, 12539, 29621, 14009, 28897, 15446, 28105, 16845
  .dw  27244, 18204, 26318, 19519, 25329, 20787, 24278, 22004
  .dw  23169, 23169, 22004, 24278, 20787, 25329, 19519, 26318
  .dw  18204, 27244, 16845, 28105, 15446, 28897, 14009, 29621
  .dw  12539, 30272, 11038, 30851, 9511, 31356, 7961, 31785
  .dw  6392, 32137, 4807, 32412, 3211, 32609, 1607, 32727
  .dw  0, 32767, -1607, 32727, -3211, 32609, -4807, 32412
  .dw  -6392, 32137, -7961, 31785, -9511, 31356, -11038, 30851
  .dw  -12539, 30272, -14009, 29621, -15446, 28897, -16845, 28105
  .dw  -18204, 27244, -19519, 26318, -20787, 25329, -22004, 24278
  .dw  -23169, 23169, -24278, 22005, -25329, 20787, -26318, 19519
  .dw  -27244, 18204, -28105, 16845, -28897, 15446, -29620, 14009
  .dw  -30272, 12539, -30851, 11038, -31356, 9511, -31784, 7961
  .dw  -32137, 6392, -32412, 4807, -32609, 3211, -32727, 1607

t_hamming128: ; Hamming window (for 128 samples)
  .dw  2621, 2639, 2693, 2784, 2910, 3073, 3270, 3502
  .dw  3768, 4068, 4401, 4765, 5161, 5587, 6042, 6525
  .dw  7036, 7571, 8132, 8715, 9320, 9945, 10588, 11249
  .dw  11926, 12616, 13318, 14031, 14753, 15482, 16216, 16954
  .dw  17694, 18433, 19171, 19905, 20634, 21356, 22069, 22772
  .dw  23462, 24138, 24799, 25443, 26068, 26673, 27256, 27816
  .dw  28352, 28862, 29345, 29800, 30226, 30622, 30987, 31319
  .dw  31619, 31885, 32117, 32315, 32477, 32603, 32694, 32748
  .dw  32767, 32748, 32694, 32603, 32477, 32315, 32117, 31885
  .dw  31619, 31319, 30987, 30622, 30226, 29800, 29345, 28862
  .dw  28352, 27816, 27256, 26673, 26068, 25443, 24799, 24138
  .dw  23462, 22772, 22069, 21356, 20634, 19905, 19171, 18433
  .dw  17694, 16954, 16216, 15482, 14753, 14031, 13318, 12616
  .dw  11926, 11249, 10588, 9945, 9320, 8715, 8132, 7571
  .dw  7036, 6526, 6042, 5587, 5161, 4765, 4401, 4068
  .dw  3768, 3502, 3270, 3073, 2910, 2784, 2693, 2639

t_desc128:  ; Descramble table (for 128 point FFT)
  .dw  0*4, 64*4, 32*4, 96*4, 16*4, 80*4, 48*4, 112*4
  .dw  8*4, 72*4, 40*4, 104*4, 24*4, 88*4, 56*4, 120*4
  .dw  4*4, 68*4, 36*4, 100*4, 20*4, 84*4, 52*4, 116*4
  .dw  12*4, 76*4, 44*4, 108*4, 28*4, 92*4, 60*4, 124*4
  .dw  2*4, 66*4, 34*4, 98*4, 18*4, 82*4, 50*4, 114*4
  .dw  10*4, 74*4, 42*4, 106*4, 26*4, 90*4, 58*4, 122*4
  .dw  6*4, 70*4, 38*4, 102*4, 22*4, 86*4, 54*4, 118*4
  .dw  14*4, 78*4, 46*4, 110*4, 30*4, 94*4, 62*4, 126*4


webmaster@mikrocontroller.net – Impressum – Werbung auf Mikrocontroller.net