;========================================================================================== ; ; 16F628 TONE DECODER ; ; This program is the intellectual copyright of Cumbria Designs and is provided free for non ; commercial applications. Cumbria Designs accept no libaility for any loss incurred through ; the use of this code or any software module from which it is comprised. ; ; The program detects a tone within an input signal applied to the input of one of the ; comparators within the 16F628. The reference voltage for the comparator is set at mid Vcc. ; The detection prcess is similar to a DFT in that the input signal is multiplied by two ; orthogonal carriers at the detection frequency. The results are summed over several full ; cycles of the carrier frequency and then compared against a threshold value to determine ; if there was asynchronous or near synchronous signal present. An LED is used to indicate ; the detector output. ; ; Key variables for experimentation are BLOCK_SIZE and DET_THRESHOLD in the definitions area. ; Reducing block size will speed up the processing time but increase detection bandwidth and ; susceptibility to noise. The current setting is quite "tight" and may need to be relaxed ; for testing purposes. Similarly reducing the DET_THRESHOLD will increase false detection ; with noise. Both of these variables have not been optimised and some experimentation may ; provide improved performance. ; ; Pin Out ; ; Port A0 Signal in via decoupling capacitor, pin connected to A3 via 10K to hold pin at mid rail ; A1 ; A2 ; A3 Comparator reference voltage, 10K +10K voltage divider Vcc-Vss to set pin at mid rail ; A4 ; ; Port B0 LED to ground via 470R ; B1 ; B2 ; B3 ; B4 ; B5 ; B6 ; B7 ; ;========================================================================================== list p=16f628A , r = dec ; list directive to define processor #include ; processor specific variable definitions errorlevel -302 ; suppress message 302 from list file ;========================================================================== ; ; Configuration Bits ; ;========================================================================== ;_BODEN_ON EQU H'3FFF' ;_BODEN_OFF EQU H'3FBF' ;_CP_ALL EQU H'03FF' ;_CP_75 EQU H'17FF' ;_CP_50 EQU H'2BFF' ;_CP_OFF EQU H'3FFF' ;_PWRTE_OFF EQU H'3FFF' ;_PWRTE_ON EQU H'3FF7' ;_WDT_ON EQU H'3FFF' ;_WDT_OFF EQU H'3FFB' ;_LVP_ON EQU H'3FFF' ;_LVP_OFF EQU H'3F7F' ;_MCLRE_ON EQU H'3FFF' ;_MCLRE_OFF EQU H'3FDF' ;_ER_OSC_CLKOUT EQU H'3FFF' ;_ER_OSC_NOCLKOUT EQU H'3FFE' ;_INTRC_OSC_CLKOUT EQU H'3FFD' ;_INTRC_OSC_NOCLKOUT EQU H'3FFC' ;_EXTCLK_OSC EQU H'3FEF' ;_LP_OSC EQU H'3FEC' ;_XT_OSC EQU H'3FED' ;_HS_OSC EQU H'3FEE' __config (_CP_ON & _PWRTE_ON & _WDT_OFF & _LVP_OFF & _MCLRE_OFF & _HS_OSC); Equates ;========================================================================== ; ; Register Definitions and Aliases ; ;========================================================================== #DEFINE LED PORTB,0 ; Test LED #DEFINE OSC_FREQ 20 ;MHz ; Processor clock frequency #DEFINE fo 800 ;Hz ; Detection frequency #DEFINE DET_THRESHOLD_L 0x00 ; Detector threshold count low byte #DEFINE DET_THRESHOLD_H 0x04 ; Detector threshold count high byte #DEFINE BLOCK_SIZE_L 0x00 ; Number of samples used in DFT #DEFINE BLOCK_SIZE_H 0x05 ; ; Calculate Sample Period constant for TMR0 based up clock frequency and detection frequency INT_TIMER= 255-(OSC_FREQ*1000000/(4*128*fo)) ;***** VARIABLE DEFINITIONS w_temp EQU 0x71 ; variable used for context saving status_temp EQU 0x72 ; variable used for context saving pclath_temp EQU 0x73 ; variable used for context saving cblock 0x020 block_l ; Block count block_h sample phase_count I_0 ; Quadrature bit pattern output registers Q_0 signal ; Signal state flag to inidcate presence or not of a tone signal_detect detect_thresholdL ; Threshold level for comparing with match_counter to dtermine if a signal is present detect_thresholdH detector I_countL I_countH Q_countL Q_countH ResultantL ResultantH endc cblock 0x70 temp_w ; Context save stores temp_status temp_fsr temp_pclath endc ;========================================================================================== ; ; MACRO AREA ; ;========================================================================================== ; Bank Select Macros to simplify memory bank changes Bank0 MACRO bcf STATUS,RP0 ; Select Bank 0 bcf STATUS,RP1 ; ENDM Bank1 MACRO bsf STATUS,RP0 ; Select Bank 1 bcf STATUS,RP1 ; ENDM Bank2 MACRO bcf STATUS,RP0 ; Select Bank 2 bsf STATUS,RP1 ; ENDM Bank3 MACRO bsf STATUS,RP0 ; Select Bank 3 bsf STATUS,RP1 ; ENDM ; Routines for saving and restoring working register data during ISR ; All associated temp registers must be defined in common user RAM 0x70 - 0x7F PUSH MACRO ; Save working register contents on entry to ISR movwf temp_w ; Save w movf STATUS,w ; Save STATUS movwf temp_status movf FSR,w ; Save FSR movwf temp_fsr movf PCLATH,w ; Save PCLATH movwf temp_pclath ENDM POP MACRO ; Restore working register contents on exit from ISR movf temp_pclath,w ; Restore PCLATH movwf PCLATH movf temp_fsr,w ; Restore FSR movwf FSR movf temp_status,w ; Restore STATUS movwf STATUS movf temp_w,w ; Restore w ENDM ;========================================================================================== ; ; Reset Entry Point ; ;========================================================================================== ORG 0x000 ; Processor reset vector clrf PCLATH ; Ensure page bits are cleared goto Start ; Go to beginning of program ;========================================================================================== ; ; ISR Entry Point ; ;========================================================================================== ORG 0x004 ; Interrupt vector location Sample: ; The input signal is compared with a mid range Vcc by a comparator and the resulting output state is sampled ; at 128x the frequency to be detected. For 800Hz this gives a sampling rate of 102.4kHz, requiring an interrupt ; period of 9.8uS. This clipping process is sometimes grandly refered to as a "1 bit A to D". ; Interrupt Rate movlw INT_TIMER ; Load TMR0 counter value to set interrupt rate (255-4x8xfs/(fclock)) movwf TMR0 ; Interrupt on overflow ; This is a simplified DFT where square waves are used instead on sine waves. The audio input is fed to a comparator ; which acts as a 1 bit D/A, 1 for +ve, 0 or -ve. The D/A output is sampled at 128x the frequency to be detected. ; ; The DFT is a repetitive product sum calculation which involves multiplying the input signal with two quadrature signals ; at the frequency to be detected, and counting "coherent"results. ; ; The incoming signal is resolved into I and Q components by XOR multiplication at the sampling rate, with two 128 bit ; quadrature patterns. The results are summed in I and Q counters as +1 and -1 values depending upon the outcome of the ; mutliplication, (+1*+1 =1, +1*-1 = -1, -1*-1=+1) The truth table for the multiplication process is; ; ; A B Action ; 0 0 Increment ; 0 1 Decrement ; 1 0 Decrement ; 1 1 Increment ; ; At the end of each block of counts the I and Q counters are tested to see if they have gone negative and if so made ; positive by complementing the registers. The I and Q results are summed and the resultant is compared against a threshold ; value. If the resultant is greater than the threshold then a signal is presumed to be present. ; ; there are two outputs from the Sample/DFT routine; "signal" which is read by the main decoder program and "decode" which ; is a flag to trigger a decode operation. Decode is set every n calls of the timed interrupt routine, this provides an ; accurate timebase for the decoder to assess tone high low timings for morse element detection. DFT clrf sample btfsc CMCON,6 ; What is the input state? (Z=1 = mark, Z=0 = space) bsf sample,0 movlw 0x01 subwf block_l,f btfss STATUS,C ; Did we go negative? subwf block_h,f btfsc STATUS,C ; Did we go negative goto DFT1 ; No, still counting down, continue ; Block complete, reload counters and process results. Depending upon the phase of the signal at each sample the ; counts may be negative. We need to establish the magnitude only, not the phase, and so negative results are made ; positive by complementing. movlw BLOCK_SIZE_L ; Re-load block size movwf block_l movlw BLOCK_SIZE_H ; movwf block_h btfss I_countH,7 ; Negative? goto $+6 ; No, use result and test Q Phase comf I_countL,f ; Yes, make positive by complementing comf I_countH,f incfsz I_countL,f ; Increment lower byte goto $+2 incf I_countH,f ; Overflow, increment higher byte btfss Q_countH,7 ; Negative? goto $+6 ; No, use result comf Q_countL,f ; Yes, make positive by complementing comf Q_countH,f incfsz Q_countL,f ; Increment lower byte goto $+2 incf Q_countH,f ; Overflow, increment higher byte ; We do not have the processing time avaialble to compute polar magnitude and so a simple comparison ; is made by summing the I and Q results and comparing against a threshold value. This has the effect ; of defining a rectangular rather than a circular threshold limit within which the polar resultant of ; the I and Q vectors would sit (or rotate through). Near the threshold, when there is a slight frequency ; difference bewteen the incoming tone and the internal I and Q streams, the output LED will flicker as ; the resultant rotates around the square threshold exceeding the triggering level on the I and Q axis ; and falling below the threshold on the longer diagonal threshold between the I and Q axis. Magnitude bcf LED ; Clear signal bit movf I_countL,w movwf ResultantL movf I_countH,w movwf ResultantH movf Q_countL,w addwf ResultantL,f btfsc STATUS,C ; Overflow? incf ResultantH,f ; Yes, increment up the higher byte movf Q_countH,w ; addwf ResultantH,f ; Sum of I and Q counts now held in Resultant movlw DET_THRESHOLD_L ; Get detection threshold count low byte subwf ResultantL,f ; Subtract from I phase count btfsc STATUS,C ; Was result positive? (C=1) goto $+4 ; Yes decf ResultantH,f ; No, decrement higher byte btfsc ResultantH,7 ; Did we go negative? goto No_signal ; No movlw DET_THRESHOLD_H ; Get detection threshold count high byte subwf ResultantH,f ; btfsc ResultantH,7 ; Was final result positive? goto No_signal ; No Signal ; Yes, set signal flag bsf LED ; Count exceeds threshold, signal present No_signal clrf I_countL ; Reset counter and start new detection cycle clrf I_countH ; Reset counter and start new detection cycle clrf Q_countL ; Reset counter and start new detection cycle clrf Q_countH ; Reset counter and start new detection cycle DFT1 ; Compare sample with I phase state movf sample,w ; Multiply sample xorwf I_0,w ; Apply I phase btfss STATUS,Z goto DFT1A ; incfsz I_countL,f ; Result is 1 - add 1 to Match Count goto DFT2 ; Any carry? (did we reach zero?) incf I_countH,f ; Yes increment higher byte goto DFT2 DFT1A movlw 0x01 ; Result is zero, decrement count subwf I_countL,f btfss STATUS,C ; Any carry? (did we reach zero?) decf I_countH,f DFT2 ; Compare sample with Q phase state movf sample,w ; Mutliply sample xorwf Q_0,w ; Apply Q phase btfss STATUS,Z goto DFT2A incfsz Q_countL,f ; Result is 1 - increment Match Count goto DFT5 ; Any carry? incf Q_countH,f ; Yes increment higher byte goto DFT5 DFT2A movlw 0x01 ; Result is zero, decrement count subwf Q_countL,f btfss STATUS,C ; Any carry? (did we reach zero?) decf Q_countH,f DFT5 ; Generate quadrature 128 bit sequence square wave patterns by counting ISR passes ; incrementing a counter and logically creating I and Q phases. Each of the 128 output ; states corresponds to a sample process of the input signal (128xfo) ; _____ ; ____| | I Phase ; __ __ ; __| |__| | 2 x I Phase ; _____ ; __| |__ XOR I Phase and 2 x I Phase = Q Phase I_pattern clrf I_0 ; Clear output registers clrf Q_0 incf phase_count,f ; Increment continuous ISR counter btfsc phase_count,6 ; b'01000000' Get state of bit 6 = ISR calls/128 bsf I_0,0 ; If one set I_0,0 to 1 Q_pattern btfsc phase_count,5 ; b'00100000' Get state of count at twice I rate = ISR calls/64 bsf Q_0,0 ; If one set Q_0,0 to 1 Q_out movf I_0,w ; Get current I output xorwf Q_0,f ; Exclusive OR to produce quadrature Q output at ISR calls/128 ISR_exit bcf INTCON,T0IF ; Clear interrupt flag retfie ;========================================================================================== ; ; Main Routine ; ;========================================================================================== Start Bank0 clrf PORTA ; Initialise Port A clrf PORTB ; Initialise Port B Bank1 ; Switch to bank 1 clrf INTCON ; No interrupts for now movlw b'00101011' ; Set Port A 0,1,3 and 5 as inputs movwf TRISA movlw 0x00 ; Set Port B as outputs movwf TRISB ; Bank0 ; Switch to bank 0 movlw b'00010100' ; Comparator configuration, inputs on RA0 and RA3, output 1 inverted movwf CMCON ; bsf PORTA,4 ; Turn audio power on ; Interrupt Rate movlw INT_TIMER ; Load TMR0 counter value to set interrupt rate (255-4x8xfs/(fclock)) movwf TMR0 ; Interrupt on overflow movlw BLOCK_SIZE_L movwf block_l movlw BLOCK_SIZE_H movwf block_h clrf phase_count ; Peripheral Configuration Bank1 movlw b'10001000' ; Set up Option Register ; No Prescaler, TMR0 Internal Clock, movwf OPTION_REG ; Prescaler assigned to TMR0, 1:8 = 1.6384mS movlw b'10100000' ; Set up Interrupt source, GIE enabled, Peripheral interrupts disabled, movwf INTCON ; TMR0 overflow interrupt enabled Bank0 ; Back to bank 0 bcf RCSTA,7 ; Disable USART Main ; When we are not in an interrupt state we wait here goto Main ; Parking loop until next Interrupt END ;