Program Zscan100C ' ' Device = 18F2420 Clock = 10 ' Config OSC = HS, LVP = OFF, DEBUG = OFF, PWRT = ON, WDT = OFF, PBADEN = OFF, 'makes port B digital XINST=OFF ' Include "usart", "EEPROM", "convert", "adc" Const CR = 13, 'carriage return LF = 10, 'line feed Tab = 9, 'tab feed WriteToMem = %0, ReadFromMem = %1, Step25Hz = %1, Step50Hz = %0, StoredFreq = %0, PotFreq = %1, LowStep = 25, 'frequency step in Hz HighStep = 50, AvgSize = 12, 'number of freq readings to boxcar average Vers1 = 2, 'Main version number Vers2 = 1 'sub version number // #Define SerialDebug 'if this is defined there are serial outputs for debugging Dim Freq As LongInt, AvgAry(AvgSize) As LongInt, 'holds individual frequency readings SumFreq As LongInt, Pass As Byte, 'for averaging pass LEDSel As Integer, CenterFreq As Integer, 'center frequency in Hz Fixed As PORTC.1, 'if 1 use current pot postion; if 0 use stored memory value SpanWidth As PORTA.5, 'if 1 step width = 25 Hz, if 0 then step width = 50 Hz SaveToMem As PORTC.0, 'if 1 recall from memory; if 0 then write to memory InPort As PORTC.5, 'input signal to this port Timer As TMR1L.AsWord, LEDBank0 As PORTC.2, 'LEDs are connected in 3 banks of 8 each LEDBank1 As PORTC.3, LEDBank2 As PORTC.4 '------------------ Sub InitializeADC() '------------------ // initialise ADC... ADC.SetConvTime(FOSC_64) ADC.SetAcqTime(5) 'PIC defaults on power up to all as inputs, but still good idea to explicit 'assign the pins to input TRISA.0 = %1 // configure AN0 as an input 'for 18F2420, follow setup in data sheet, section 19 VCFG1 = %0 'negative reference to ground VCFG0 = %0 'positive reference to Vdd PCFG3 = %1 ' The configuration nibble is %1110 PCFG2 = %1 ' - voltage reference is to Vss PCFG1 = %1 ' + voltage reference is to Vdd PCFG0 = %0 ' AN0 is analog input, all other PortA pins are DIGITAL End Sub 'turns LED p on. LEDs are numbered from 0 to 23 'for 0, the center LED is on, - goes left, + goes right '------------------------- Sub TurnOn(pID As Integer) '------------------------- Const PortVal(8) As Byte = (%11111110,%11111101,%11111011,%11110111,%11101111,%11011111,%10111111,%01111111) Dim BankNo As Byte 'to avoid ghosting, follow this order Low(LEDBank0) 'first turn off all LEDs Low(LEDBank1) 'then turn on the one that should be illuminated Low(LEDBank2) pID = pID + 12 'If pID < 0 Then pID = 0 EndIf 'bound the leds 'If pID > 23 Then pID = 23 EndIf 'stick high LED on If pID < 0 Then GoTo Bailout EndIf 'bound the leds If pID > 23 Then GoTo Bailout EndIf 'stick high LED on BankNo = pId / 8 'LEDs connected in 3 banks of 8 each PORTB = PortVal(pID Mod 8) 'to illuminate LED, Port B pin taken low to select 1 of 8 Select BankNo 'then to select bank, take one of the three PortC pins high Case 0 High(LEDBank0) Case 1 High(LEDBank1) Case 2 High(LEDBank2) EndSelect Bailout: End Sub 'Read DIP switches '------------------------- Function ReadDip() As Byte '------------------------- Result = (PORTA And %00011110) 'grab all of port A, mask off parts not wanted Result = Result >> 1 'shift right 1 to get 0...$F result, as switches are on A4...A1 End Function 'Initialization code here '--------------- Sub Initialize() '--------------- Dim I As Integer TRISB = $00 ADCON1 = %00001111 TRISA = $00 PORTB = $FF 'turn all off PORTA.4 = %1 PORTA.5 = %1 Input(InPort) 'has slicer connected to it Input(PORTA.1) 'frequency memory selection ports Input(PORTA.2) Input(PORTA.3) Input(PORTA.4) Output(LEDBank0) Output(LEDBank1) Output(LEDBank2) #IfDef SerialDebug USART.SetBaudrate(br9600) USART.Write("Good to go and ready to launch!",CR,LF) DelayMS(500) #EndIf Pass = 0 //set up for averaging SumFreq = 0 For I = 0 To AvgSize-1 AvgAry(I) = 0 Next //i End Sub 'turns all LEDs off Sub LEDoff() '-------------- Low(LEDBank0) 'turn all LEDs off Low(LEDBank1) Low(LEDBank2) End Sub 'boxcar average routine Function ComputeAvg(pFreq As LongInt) As LongInt '----------------------------------------------- Dim temp As Byte temp = Pass Mod AvgSize SumFreq = SumFreq + pFreq -AvgAry(temp) Result = SumFreq / AvgSize AvgAry(temp) = pFreq Inc(Pass) End Function 'test LEDs at startup '---------------------- Sub TestLEDs() '---------------------- Dim I As ShortInt For I = -12 To 11 TurnOn(I) DelayMS(150) Next Low(LEDBank0) 'turn all LEDs off Low(LEDBank1) Low(LEDBank2) End Sub 'One LED tone detector mode Function Goertzel() As Integer '----------------------------- Const N = 32 'number of freq readings for Goetrzel algorithm Dim x0,y0,y1,y2,sum As Integer, j As Byte, I(N) As Byte, 'data read for Goertzel analysis MicroSec As Word x0 = 0 y0 = 0 y1 = 0 y2 = 0 'MicroSec = 408 Pass = 0 '-800ns Repeat '2 us for Repeat UNTIL Pass = N I(Pass) = InPort '6.8 us for assignment DelayUS(MicroSec) ' read 1-bit adc into array Inc(Pass) Until Pass = N For j = 0 To N-1 'compute Goertzel fcn y0 = I(j) - y2 y2 = y1 y1 = y0 Next Sum = y1*y1 + y2*y2 'compute power of signal Result = Sum If Sum > 2*N Then TurnOn(0) 'apply thresholding Else LEDoff EndIf MicroSec = (83333 / ADC.Read(0)) - 9 'compute delay based on center frequency End Function 'blinks LEDs to show current software version Sub ShowVers() '-------------- Dim j As Integer DelayMS(500) For j = 1 To Vers1 TurnOn(-3) DelayMS(500) LEDoff DelayMS(500) Next DelayMS(500) For j = 1 To Vers2 TurnOn(3) DelayMS(500) LEDoff DelayMS(500) Next End Sub '================= 'Main code here '================= Initialize InitializeADC TestLEDs ShowVers Repeat #IfDef SerialDebug USART.Write(BinToStr(Fixed),CR,LF) #EndIf 'If DIP switch at $ F, then execute Goertzel tone decoder algorithm While ReadDip = $F Goertzel Wend 'option here is to read the pot If Fixed = PotFreq Then CenterFreq = 3* ADC.Read(0) 'read the pot to get the low frequency edge 'range is thus approx 200 - 3200 Hz End If 'recall stored frequency from memory If Fixed = StoredFreq Then CenterFreq = EE.ReadWord(2*ReadDip) EndIf Repeat Until InPort = %1 'following code ensures that a full half cycle is read Repeat Until InPort = %0 'delay one 0 level after the first high level 'this ensures a full half cycle is read Timer = 0 'use timer 1 to measure it PIE1.0 = 1 'counter is Fosc/4, so 400 ns/tick T1CON.0 = 1 'start the counter Repeat 'now count until the next high level Until InPort = %1 ' T1CON.0 = 0 'stop the timer #IfDef SerialDebug USART.Write(DecToStr(Timer),CR,LF) #EndIf 'counter is reading half the interval and the ticks are Freq = 1250000 / Timer '2500000/sec so for half interval, 1250000 is the number to use 'timer granularity (400ns) gives ~ 7 Hz error at 3KHz. At more normal frequencies 'error is less. At 1 KHz ~ 0.8 Hz error results from +/- 400 ns. 'Delayms(10) 'selection is 25 or 50 Hz steps If SpanWidth = Step25Hz Then LEDSel = (ComputeAvg(Freq) - CenterFreq) / LowStep Else LEDSel = (ComputeAvg(Freq) - CenterFreq) / HighStep End If TurnOn(LEDSel) 'light the appropriate LED If SaveToMem = WriteToMem Then 'in mode to read ADC and write to memory EE.WriteWord(2*ReadDip,3* ADC.Read(0)) 'read the POT and save the value to EEPROM EndIf Until False End