Right away I ran into my own ignorance about how to scale the coefficients properly, especially w.r.t. oversampling. Would appreciate anybody's feedback in that area.
Everything seems to basically work (it will oscillate at infinite Q too), but running music through the board gave me the impression that something is lacking in the high end (any reason why it would roll off in the eval board design?).
Looking at a spectrum analyzer with a noise source as the input, the bandpass output and notch outputs don't look 'right', but I am not sure what to expect.
I have attached my code if anybody would like to play with it or point out my errors.
Code: Select all
;
; Test Programme. Mono out & in on Left Channel.
;
; This implements a state-variable digital filter with frequency and Q controls and lets you select the filter output on the fly
; State-variable filter is called a biquad filter in the Spin Semi knowledge base (see 'peaking filters' in 'effects')
;
; Pot0 is frequency
; Pot1 is Q
;
; Uses Pot2 to select four possible outputs:
; Low pass
; Band pass
; High pass
; Notch filter output
;
sqrt2 equ 1.4142135623730
f1scale equ 0.05482427 * 8 ;3 times oversampled? should this be a factor of four?
;
lp_dly equ reg0
bp_dly equ reg1
f1 equ reg2 ;raw frequency coeff (needs to be scaled up appropriately)
q1 equ reg3 ;raw Q coeff (will be scaled and negative)
hp equ reg4 ;high pass
notch equ reg5 ;notch output
p0fil equ reg6
p1fil equ reg7
;
;Frequency control
rdax pot0,1 ;get freq control
rdfx p0fil,0.01 ;average with filter
wrax p0fil,1
wrax f1,0
;Q control
rdax pot1,1 ;get Q control
sof 1,-1 ;flip control to go from 1 to 0
absa
rdfx p1fil,0.01 ;average with filter
wrax p1fil,1
wrax q1,0
;Oversample (process multiple times) the filter to reach Fs/2
; LP = LP_DLY + F1 * BP_DLY
ldax bp_dly
mulx f1 ; frequency coefficient sof f1scale,0
rdax lp_dly, 1.0
wrax lp_dly, -1.0 ; low-pass delay is not referenced any more, so safe to directly write LP value here
; HP = Input - LP - Q1 * BP_DLY
; ACC has -LP in it from preceeding WRAX
rdax adcl,1 ; sample the input and add to ACC
wrax hp,1 ; store (input - lp) temporarily
ldax bp_dly
mulx q1 ; Q coefficent, should be negative and go from -2 to 0 for Q of 0.5 to infinity
sof sqrt2,0
sof -sqrt2,0 ; scale and make negative the Q1
rdax hp,1
wrax hp,1
; BP = F1 * HP + BP_DLY
; ACC has HP in it already
mulx f1
sof f1scale,0
rdax bp_dly,1
wrax bp_dly,0 ; store the BP output... bp_dly is o.k. as it is no longer referenced by the equations, clr ACC
; NOTCH = HP + LP
ldax lp_dly
rdax hp,1
wrax notch,0
;Completed pass of filter
;Oversample (process multiple times) the filter to reach Fs/2
; LP = LP_DLY + F1 * BP_DLY
ldax bp_dly
mulx f1 ; frequency coefficient 0.0 to 3.14159
sof f1scale,0
rdax lp_dly, 1.0
wrax lp_dly, -1.0 ; low-pass delay is not referenced any more, so safe to directly write LP value here
; HP = Input - LP - Q1 * BP_DLY
; ACC has -LP in it from preceeding WRAX
rdax adcl,1 ; sample the input and add to ACC
wrax hp,1 ; store (input - lp) temporarily
ldax bp_dly
mulx q1 ; Q coefficent, should be negative and go from -2 to 0 for Q of 0.5 to infinity
sof sqrt2,0
sof -sqrt2,0 ; scale and make negative the Q1
rdax hp,1
wrax hp,1
; BP = F1 * HP + BP_DLY
; ACC has HP in it already
mulx f1
sof f1scale,0
rdax bp_dly,1
wrax bp_dly,0 ; store the BP output... bp_dly is o.k. as it is no longer referenced by the equations, clr ACC
; NOTCH = HP + LP
ldax lp_dly
rdax hp,1
wrax notch,0
;Completed pass of filter
;Oversample (process multiple times) the filter to reach Fs/2
; LP = LP_DLY + F1 * BP_DLY
ldax bp_dly
mulx f1 ; frequency coefficient 0.0 to 3.14159
sof f1scale,0
rdax lp_dly, 1.0
wrax lp_dly, -1.0 ; low-pass delay is not referenced any more, so safe to directly write LP value here
; HP = Input - LP - Q1 * BP_DLY
; ACC has -LP in it from preceeding WRAX
rdax adcl,1 ; sample the input and add to ACC
wrax hp,1 ; store (input - lp) temporarily
ldax bp_dly
mulx q1 ; Q coefficent, should be negative and go from -2 to 0 for Q of 0.5 to infinity
sof sqrt2,0
sof -sqrt2,0 ; scale and make negative the Q1
rdax hp,1
wrax hp,1
; BP = F1 * HP + BP_DLY
; ACC has HP in it already
mulx f1
sof f1scale,0
rdax bp_dly,1
wrax bp_dly,0 ; store the BP output... bp_dly is o.k. as it is no longer referenced by the equations, clr ACC
; NOTCH = HP + LP
ldax lp_dly
rdax hp,1
wrax notch,0
;Completed pass of filter
;Oversample (process multiple times) the filter to reach Fs/2
; LP = LP_DLY + F1 * BP_DLY
ldax bp_dly
mulx f1 ; frequency coefficient 0.0 to 3.14159
sof f1scale,0
rdax lp_dly, 1.0
wrax lp_dly, -1.0 ; low-pass delay is not referenced any more, so safe to directly write LP value here
; HP = Input - LP - Q1 * BP_DLY
; ACC has -LP in it from preceeding WRAX
rdax adcl,1 ; sample the input and add to ACC
wrax hp,1 ; store (input - lp) temporarily
ldax bp_dly
mulx q1 ; Q coefficent, should be negative and go from -2 to 0 for Q of 0.5 to infinity
sof sqrt2,0
sof -sqrt2,0 ; scale and make negative the Q1
rdax hp,1
wrax hp,1
; BP = F1 * HP + BP_DLY
; ACC has HP in it already
mulx f1
sof f1scale,0
rdax bp_dly,1
wrax bp_dly,0 ; store the BP output... bp_dly is o.k. as it is no longer referenced by the equations, clr ACC
; NOTCH = HP + LP
ldax lp_dly
rdax hp,1
wrax notch,0
;Completed pass of filter
;Process Pot2 to decide 1 of 4 output possibilities
rdax pot2,1
and %01100000_00000000_00000000 ;mask off only 2 bits, leaving only 4 possibilities
skp zro,LowPass ;if zero, the skip over other code to Test1
sof 1,-0.25 ;subtract 1/4
skp zro,BandPass ;if zero, skip over other code to Test2
sof 1,-0.25 ;subtract 1/4
skp zro,HighPass ;if zero, skip over other code to Test3
clr ;clear the accumulator, there's 1/4 left in it!
NotchOut:
ldax notch
skp run, Pend ;skip to end of code
LowPass:
ldax lp_dly
skp run, Pend ;skip to end of code
BandPass:
ldax bp_dly
skp run, Pend ;skip to end of code
HighPass:
ldax hp
skp run, Pend ;skip to end of code
;
Pend:
wrax dacl,1 ;write both outputs
wrax dacr,0