This is DTMF decoding program, provided by:
Stephen James Hardy (T/A SW Tekno) Canberra, Australia
hardy@sweng.stortek.com

======== Source code ========

; Caveat: Requires tone duration of 160ms.  DTMF standard only
; requires 50ms tone.

; This source code is copyright (C) 1996 to Stephen James Hardy.
; Permission is hereby granted to reproduce, assemble and run this code
; for non-commercial purposes providing this notice is not removed
; from source code copies.
;
; This source code is provided as-is with no warranty expressed or
; implied.  Do not use where damage to persons or property may result
; from source code failure.
;
; Written for the 16C84, but should run on most any 16Cxx...
;
; Target configuration:
;   PORTB: Sample input as unsigned binary.  With a 16C7x, change the
;          code to read the ADC instead.
;   PORTA: Connnected to 5 LEDs:
;          RA0-3 : indicate decoded tone (0-F)
;          RA4   : 0 if tone OK, 1 if tone not recognised.
;   Osc: 10MHz xtal.
;
; Assemble using MPASM /C- flag.
;
;
        list    p=pic16c84
        include "p16c84.inc"    ; Mchip standard include
        radix   dec

; NB: If the following is changed, check assembly to ensure timer
; constants (TONEL0 etc) fit in 1 byte.
CLKSPD  equ     10              ; Target clock speed (MHz)

; Register mapping:
RBASE   equ     0Ch             ; First general register.

;------------------
; Bank 0 registers.  Rather profligate use made thereof.  Could be
; reduced with a bit of twiddling.
;------------------

        cblock  RBASE


        dtmf_code1              ; Result of first trial
        dtmf_code2              ; Result of 2nd trial
        dtmf_r                  ; Result of high- or low-group trial
        dtmf_rr                 ; Temp result

        dtmf_t1l                ; Regs for correlation accumulators.
        dtmf_t1h
        dtmf_t2l
        dtmf_t2h

        dtmf_tc                 ; Tick counter for timing
        dtmf_tcc                ; Tick counter reload constant

        dtmf_sc                 ; Sample counter for 1 tone
        dtmf_st                 ; Sample state (0-3)
        dtmf_cs                 ; Latest sample

        dtmf_sl                 ; sum/avg low
        dtmf_sh                 ; sum/avg high
        dtmf_ml                 ; max low
        dtmf_mh                 ; max high

        endc




        org     0

;------------------------
; Power-up initialisation
;------------------------

pwrup   bsf     status,rp0      ; Select bank 2
; Set RB pullups, falling edge interrupt, increment t0 on instruction cycle,
; assign prescaler to WDT (not used), prescale 1:1.
        movlw   (1<<psa)
        movwf   option_reg
        clrf    trisa           ; Set port A to outputs
        movlw   0FFh
        movwf   trisb           ; Set port B to inputs
        clrf    status          ; Back to bank 0.


demo_dtmf_listen
        bcf     intcon,GIE      ; Disable interrupts
        call    dtmf_listen     ; Do it...
; W contains code
        movwf   porta           ; Show on LEDs attached to port A.
        goto    demo_dtmf_listen        ; ...forever



;--------------------------------------------------------------
; dtmf_listen:  Detects and decodes DTMF tones applied to the ADC.
;
; Interrupts must be disabled (as written) since instruction cycle
; counting is used to perform timing.  This restriction may be removed
; if rewritten to use interrupts to perform controlled timebase sampling.
;
; On return W contains the DTMF signal (0-15) with following correspondence
; to standard telephone keypad:
; W     Key
; -     ---
; 0     1
; 1     4
; 2     7
; 3     *
; 4     2
; 5     5
; 6     8
; 7     0
; 8     3
; 9     6
; A     9
; B     #
; C     A
; D     B
; E     C
; F     D
; i.e the 2 LSBs of W index the low tone group, and the other 2 bits
; index the high tone group.
; In the case that no tone was reliably detected, W contains FF.
;
; Note:  8-bit ADC is used.  For maximum reliability, the DTMF tone
; should use the full range of the ADC.  However, if the low- and
; high-group tones are within 1dB of each other, it is possible to
; use a 1-bit ADC (i.e. comparator)
;
; Algorithm:
; The eight possible tones used in DTMF are searched for one-by-one.
; This approach is allowable because of the 200ms required for each
; signal.  78.2us are allocated for sampling (a total of 4*10*8
; samples; this is repeated for reliability hence sampling takes
; 156.4ms which allows the rest of the application a few ms to determine
; whether a tone is present.
;
; Since one tone is being searched for at a time, the following method
; is appropriate:  Sample at 4 times the frequency of the search tone,
; for a total of 10 waves.  This allows correlation of the signal with
; the search tone; a high correlation indicating the tone is present.
; 10 waves are selected since the DTMF tones differ by 10% steps.  The
; other tones will thus cancel out when correlated with the search tone
; (as a consequence of the orthogonality of the sine function basis).
;
; The 8 tones are tried in sequence.  The largest correlation from each
; of the high and low tone groups is selected.  If the highest correl in
; each group is greater than or equal 1.5 times the average, then
; that tone is adjudged present.  If both the high and low tone group
; is present, an initial DTMF code is decided.  The process is repeated
; and, if the same code is present the next time, that code is returned.
; Otherwise, the 'uncertain' code (FF) is returned.
;
;--------------------------------------------------------------
SAMPSPC equ     4               ; Sample points per tone cycle.  Don't change!
WAVES   equ     10              ; Number of tone cycles to sample
SINTVL  equ     250000*CLKSPD/SAMPSPC ; PIC cycles between samples at f=1Hz
                                ; - divide by tone (Hz) to get cycles.
                                ; For DTMF, will range from 897 (697Hz)
                                ; to 382 (1633Hz) at 10MHz xtal.
FIXDLAY equ     36              ; Fixed delay to make following <= 256.
       ; Select according to the formula:
       ; FIXDLAY = 29.89*CLKSPD - 263.7
       ; (round up).
SLCYCLES equ    (FIXDLAY-1)*3+29  ; PIC cycles taken in the dtmf_lp loop
                                ; not counting the wait loop at dtmf_wtim
                                ; Code is designed so this is constant!
TLCYCLES equ    3               ; Number of PIC cycles per wait loop,
                                ; 1 less than this on last execution.
; Loop constants for each DTMF tone. Must be 1..256 inclusive.  TONEL0
; is the largest and should be made as close as possible to 256 by adjusting
; the constant FIXDLAY (and hence SLCYCLES).  The smallest constant
; (TONEH3) should also be as large as possible for accuracy - this places
; limits on the lowest clock speed that will work.  Min clock speed should
; be around 3.7MHz.  If clock is faster than 17.3MHz, then the code will
; need to be modified to add yet more delay since FIXDLAY will be over 256.
TONEL0  equ     (SINTVL/697-SLCYCLES)/TLCYCLES
TONEL1  equ     (SINTVL/770-SLCYCLES)/TLCYCLES
TONEL2  equ     (SINTVL/852-SLCYCLES)/TLCYCLES
TONEL3  equ     (SINTVL/941-SLCYCLES)/TLCYCLES
TONEH0  equ     (SINTVL/1209-SLCYCLES)/TLCYCLES
TONEH1  equ     (SINTVL/1336-SLCYCLES)/TLCYCLES
TONEH2  equ     (SINTVL/1477-SLCYCLES)/TLCYCLES
TONEH3  equ     (SINTVL/1633-SLCYCLES)/TLCYCLES

dtmf_listen
        call    dtmf_lg         ; Sample for low group => dtmf_r
done_lg btfsc   dtmf_r,7        ; Done lg: check result reg (0-3 or FF)
        retlw   0FFh            ; Return if uncertain
        movf    dtmf_r,w
        movwf   dtmf_code1      ; Move to code result 1
        call    dtmf_hg         ; Sample for high group => dtmf_r
done_hg btfsc   dtmf_r,7        ; Done hg: check result
        retlw   0FFh            ; return if uncertain
        bcf     status,C
        rlf     dtmf_r
        rlf     dtmf_r,w        ; *4, into W
        iorwf   dtmf_code1,f    ; Or in low group
; Now repeat to make sure...
        call    dtmf_lg         ; Sample for low group => dtmf_r
        btfsc   dtmf_r,7        ; check result reg (0-3 or FF)
        retlw   0FFh            ; Return if uncertain
        movf    dtmf_r,w
        movwf   dtmf_code2      ; Move to code result 2
        call    dtmf_hg         ; Sample for high group => dtmf_r
        btfsc   dtmf_r,7
        retlw   0FFh            ; return if uncertain
        bcf     status,C
        rlf     dtmf_r
        rlf     dtmf_r,w        ; *4, into W
        iorwf   dtmf_code2      ; Or in low group (result in W)
; Check if both agree
        subwf   dtmf_code1,f    ; code1 = code1 - code2
        btfss   status,Z        ; equal?
        retlw   0FFh            ; no, return uncertain.
        return                  ; else return w = DTMF code.


; Sample for all low group tones, select the largest.
dtmf_lg clrf    dtmf_sl         ; Clear sum
        clrf    dtmf_sh
        clrf    dtmf_ml         ; Clear max
        clrf    dtmf_mh
        clrf    dtmf_rr         ; Tone 0
        movlw   TONEL0
        movwf   dtmf_tcc        ; Set timer constant for low tone 0.
        call    dtmf_samp       ; Sample etc.
        incf    dtmf_rr
        movlw   TONEL1
        movwf   dtmf_tcc        ; Set timer constant for low tone 1.
        call    dtmf_samp       ; Sample etc.
        incf    dtmf_rr
        movlw   TONEL2
        movwf   dtmf_tcc        ; Set timer constant for low tone 2.
        call    dtmf_samp       ; Sample etc.
        incf    dtmf_rr
        movlw   TONEL3
        movwf   dtmf_tcc        ; Set timer constant for low tone 3.
        call    dtmf_samp       ; Sample etc.
dtmf_g  bcf     status,C
        rrf     dtmf_sh         ; Div sum by 4 to get average
        rrf     dtmf_sl
dtmf_01 bcf     status,C
        rrf     dtmf_sh
        rrf     dtmf_sl
; while dtmf_mh non zero, div m and s by 2.
        movf    dtmf_mh,w
        btfsc   status,Z
        goto    dtmf_02
        bcf     status,C
        rrf     dtmf_mh
        rrf     dtmf_ml
        goto    dtmf_01
; Now results in LSB.  Mult s (avg) by 1.5
dtmf_02 bcf     status,C
        rrf     dtmf_sl,w
        addwf   dtmf_sl,w
        btfsc   status,C
        goto    dtmf_03         ; Return uncertain if carry.
; Compare with m (max).  If max is not >= 1.5*avg, then uncertain.
        subwf   dtmf_ml,w       ; w = max - 1.5*avg.  C set if >= 0 (OK)
        btfsc   status,C
        return                  ; return OK (result in dtmf_r)
dtmf_03 movlw   0FFh
        movwf   dtmf_r          ; set uncertain result
        return


; Sample for all high group tones, select the largest.
dtmf_hg clrf    dtmf_sl         ; Clear sum
        clrf    dtmf_sh
        clrf    dtmf_ml         ; Clear max
        clrf    dtmf_mh
        clrf    dtmf_rr         ; Tone 0
        movlw   TONEH0
        movwf   dtmf_tcc        ; Set timer constant for high tone 0.
        call    dtmf_samp       ; Sample etc.
        incf    dtmf_rr
        movlw   TONEH1
        movwf   dtmf_tcc        ; Set timer constant for high tone 1.
        call    dtmf_samp       ; Sample etc.
        incf    dtmf_rr
        movlw   TONEH2
        movwf   dtmf_tcc        ; Set timer constant for high tone 2.
        call    dtmf_samp       ; Sample etc.
        incf    dtmf_rr
        movlw   TONEH3
        movwf   dtmf_tcc        ; Set timer constant for high tone 3.
        call    dtmf_samp       ; Sample etc.
        goto    dtmf_g          ; Go to common processing for lo, hi groups

;
; This routine performs all samples for a particular tone.  It selects
; the largest correlation into dtmf_t1l/h (which is the return value).
;
dtmf_samp movlw SAMPSPC*WAVES   ; Total samples
        movwf   dtmf_sc         ; move to sample counter
        clrf    dtmf_st         ; Initial state
        clrf    dtmf_t1l        ; Clear correlation accumulators
        clrf    dtmf_t1h
        clrf    dtmf_t2l
        clrf    dtmf_t2h
;
; Main sample loop.  The code is written to take a constant number of
; cycles no matter what.  This explains some of the odd things like
; seemingly unnecessary goto's.
;
dtmf_lp movf    portb,w         ; 1 - Get sample (e.g. from port B)
        movwf   dtmf_cs         ; 2 -Save it
        movlw   high dtmf_bstate ; 3
        movwf   pclath          ; 4
        movf    dtmf_st,w       ; 5 - Get current state
        addwf   pcl,f           ; 6,7 - Branch on state (0-3)
dtmf_bstate
        goto    dtmf_st0        ; 8,9 - Check asm list to make sure all
        goto    dtmf_st1        ; of these 4 instr's on same 256-byte page
        goto    dtmf_st2        ;
        goto    dtmf_st3        ;

dtmf_st0 movf   dtmf_cs,w       ; 10 - Get sample
        addwf   dtmf_t1l,f      ; 11 - Add to accumulator
        btfsc   status,C        ; 12,13 - Test carry out
        incf    dtmf_t1h        ; 13 - Increment high byte if carry out
        goto    dtmf_join       ; 14,15 - continue common

dtmf_st1 movf   dtmf_cs,w       ; Get sample
        addwf   dtmf_t2l,f      ; Add to accumulator
        btfsc   status,C        ; Test carry out
        incf    dtmf_t2h        ; Increment high byte if carry out
        goto    dtmf_join       ; continue common

dtmf_st2 comf   dtmf_cs,w       ; Get 'negative' sample
        addwf   dtmf_t1l,f      ; Add to accumulator
        btfsc   status,C        ; Test carry out
        incf    dtmf_t1h        ; Increment high byte if carry out
        goto    dtmf_join       ; continue common

dtmf_st3 comf   dtmf_cs,w       ; Get 'negative' sample
        addwf   dtmf_t2l,f      ; Add to accumulator
        btfsc   status,C        ; Test carry out
        incf    dtmf_t2h        ; Increment high byte if carry out
        goto    dtmf_join       ; continue common

dtmf_join incf  dtmf_st,w       ; 16
        andlw   3               ; 17
        movwf   dtmf_st         ; 18 - Update state
        decfsz  dtmf_sc         ; 19,20 - Decrement sample count
        goto    dtmf_tmr        ; 20,21 - If not finished, sample again

; Finished samples, select largest from dtmf_t1 and dtmf_t2 into dtmf_t1.
; Average expected value is subtracted, abs value taken.  Avg expected
; value is WAVES in high byte, zero in low byte.
dtmf_lx movlw   WAVES
        subwf   dtmf_t1h,f      ; t1 -= expavg
        btfss   dtmf_t1h,7      ; t1 -ve?
        goto    $+3             ; no, skip next
        comf    dtmf_t1h        ; Negate (don't worry about the 1 diff!)
        comf    dtmf_t1l
        movlw   WAVES
        subwf   dtmf_t2h,f      ; t2 -= expavg
        btfss   dtmf_t2h,7      ; t2 -ve?
        goto    $+3             ; no, skip next
        comf    dtmf_t2h
        comf    dtmf_t2l
        movf    dtmf_t1h,w
        subwf   dtmf_t2h,w      ; w = t2h-t1h (C set if t1h<=t2h)
        btfss   status,C
        goto    dtmf_04         ; cont if t1 > t2
        btfss   status,Z
        goto    dtmf_swap       ; move t2->t1 if t2>t1
        movf    dtmf_t1l,w      ; high equal, test low
        subwf   dtmf_t2l,w      ; w = t2l-t1l
        btfss   status,C
        goto    dtmf_04         ; cont if t1 > t2
dtmf_swap movf  dtmf_t2h,w      ; Set t1 = t2 (return value)
        movwf   dtmf_t1h
        movf    dtmf_t2l,w
        movwf   dtmf_t1l
;
; Sum.
dtmf_04 movf    dtmf_t1l,w
        addwf   dtmf_sl,f
        movf    dtmf_t1h,w
        btfsc   status,C
        addlw   1
        addwf   dtmf_sh,f
;
; Compare with current maximum.  If greater, set new max.
dtmf_05 movf    dtmf_mh,w
        subwf   dtmf_t1h,w      ; w = t1h-mh (C set if mh<=t1h)
        btfss   status,C
        return                  ; ret if m > t1
        btfss   status,Z
        goto    dtmf_sw2        ; move t1->m if t1>m
        movf    dtmf_ml,w       ; high equal, test low
        subwf   dtmf_t1l,w      ; w = t1l-ml
        btfss   status,C
        return                  ; ret if m > t1
dtmf_sw2 movf   dtmf_t1h,w      ; Set m = t1
        movwf   dtmf_mh
        movf    dtmf_t1l,w
        movwf   dtmf_ml
        movf    dtmf_rr,w       ; Tone number
        movwf   dtmf_r          ; Set new tone number
        return

; Waste time until ready for next sample
dtmf_tmr movlw  FIXDLAY         ; 22 - Constant delay
        movwf   dtmf_tc         ; 23
        decfsz  dtmf_tc         ; 24,25
        goto    $-1             ;
        movf    dtmf_tcc,w      ; 26+3(n-1)
        movwf   dtmf_tc         ; 27 - Load tick counter constant
dtmf_wtim decfsz dtmf_tc        ; Decrement tick counter
        goto    dtmf_wtim       ; Tight loop until zero
        goto    dtmf_lp         ; 28,29 - Get next sample



        end

