Searching \ for '[EE] Looking for low current DTMF decoder' in subject line. ()
Make payments with PayPal - it's fast, free and secure! Help us get a faster server
FAQ page: www.piclist.com/techref/pots/dtmf.htm?key=dtmf
Search entire site for: 'Looking for low current DTMF decoder'.

Exact match. Not showing close matches.
PICList Thread
'[EE] Looking for low current DTMF decoder'
2010\03\26@132435 by Dwayne Reid

flavicon
face
Good day to all.

I'm currently working on a project where the current consumed by a
cm8870 or mt8870 DTMF decoder is a bit more than I can tolerate.  The
'8870 decoders need 5V @ 3-7mA and I have a little over 1ma available.

Does anyone know of another DTMF decoder with lower operating
current?  Google hasn't been much help so far.

Many thanks!

dwayne

--
Dwayne Reid   <spam_OUTdwaynerTakeThisOuTspamplanet.eon.net>
Trinity Electronics Systems Ltd    Edmonton, AB, CANADA
(780) 489-3199 voice          (780) 487-6397 fax
http://www.trinity-electronics.com
Custom Electronics Design and Manufacturing

2010\03\26@143103 by M. Adam Davis

face picon face
Have you looked at implementing it in a low power processor, such as a 16F722?

They consume under 500uA running at 1MHz.  Not sure what the ADC
consumes running at 4kHz (DTMF highest tone is 1,633Hz), but you may
be able to meet your needs by running the goertzel algorithm discussed
on the list a bit ago:

http://www.google.com/search?q=goertzel+tone+detection

Or the more efficient DTMF detections routines discussed in this TI
msp430 app note:

http://www.google.com/search?q=slaae16

You may get even lower power with a PIC18F13K22 or similar since you
can clock it more slowly and use the multiplyer for whichever
algorithm you choose.

If you need even more power savings, you can probably read the ADC in
bursts, and go to sleep in between bursts, depending on how quickly
you have to detect a tone.

These chips aren't terribly expensive - ~$2.50 each for one, $1.50
each in quantity 100. The 16f722 is about $1 each in quantity 100.
Looks like you'd not only get a power consumption savings, but a cost
savings as well.

But I don't know the power consumption of the running ADC, so you'd
still have to do a bit of testing to see if it's feasible.  Running it
in bursts with a resistor fed capacitor, though, should get the
average current to the range you need.

-Adam


On Fri, Mar 26, 2010 at 1:24 PM, Dwayne Reid <.....dwaynerKILLspamspam@spam@planet.eon.net> wrote:
{Quote hidden}

>

2010\03\26@144539 by Dario Greggio

face picon face
M. Adam Davis ha scritto:
>
> [...]
> consumes running at 4kHz (DTMF highest tone is 1,633Hz), but you may
> be able to meet your needs by running the goertzel algorithm discussed
> on the list a bit ago:


yeah, was going to suggest the same: do it in software.


--

Ciao, Dario
--
Cyberdyne

2010\03\27@174143 by Dwayne Reid

flavicon
face
At 12:31 PM 3/26/2010, M. Adam Davis wrote:
>Have you looked at implementing it in a low power processor, such as a 16F722?
>
>They consume under 500uA running at 1MHz.  Not sure what the ADC
>consumes running at 4kHz (DTMF highest tone is 1,633Hz), but you may
>be able to meet your needs by running the goertzel algorithm discussed
>on the list a bit ago:
>
>http://www.google.com/search?q=goertzel+tone+detection
>
>Or the more efficient DTMF detections routines discussed in this TI
>msp430 app note:
>
>http://www.google.com/search?q=slaae16
>
>You may get even lower power with a PIC18F13K22 or similar since you
>can clock it more slowly and use the multiplier for whichever
>algorithm you choose.

Many thanks for the suggestion.  Unfortunately, I don't have the time
to learn how to write this code right now - I have to deliver 22
finished, working boxes in 3 weeks.  Yeah - its a small project but
its for a good customer.

Does anyone have code snippets available?  Links to existing
code?  I'd like to learn how to do this.

Luckily for me, my customer just relaxed his requirements.  Now I
need to detect only a single DTMF tone pair.  First thought was a
couple of NE567 tone decoder chips but the supply current for even
just one of those is higher than the mt8870.  But: a pair of cd4046
CMOS PLLs might just do the trick.  Add a cd4001 quad NOR gate for
lock detection - and I'm done.  I'll start playing with this on Monday.

That said - I *still* want to learn how to do this on a micro-controller.

Many thanks!

dwayne

--
Dwayne Reid   <.....dwaynerKILLspamspam.....planet.eon.net>
Trinity Electronics Systems Ltd    Edmonton, AB, CANADA
(780) 489-3199 voice          (780) 487-6397 fax
http://www.trinity-electronics.com
Custom Electronics Design and Manufacturing

2010\03\27@175030 by Dario Greggio

face picon face
Dwayne Reid ha scritto:

> Many thanks for the suggestion.  Unfortunately, I don't have the time
> to learn how to write this code right now - I have to deliver 22
> finished, working boxes in 3 weeks.  Yeah - its a small project but
> its for a good customer.
>
> Does anyone have code snippets available?  Links to existing
> code?  I'd like to learn how to do this.


Hi Dwayne
I found an application (code + hw) for a CALLER-ID detector on a 18F2550
USB device.
I adapted the code to a 16F in assembler and got it working soon enough.
It uses the Goertzel and you might easily adapt it to DTMF - basically
similar range of frequencies.

Also the HW side (1 or 2 Op-Amps and a transformer to split the 2 sides)
should be almost similar.


--

Ciao, Dario
--
Cyberdyne

2010\03\28@012730 by MCH

flavicon
face
567s are notoriously unstable.

Joe M.

Dwayne Reid wrote:
{Quote hidden}

2010\03\28@082444 by enkitec

picon face
part 0 44 bytes
his is a multi-part message in MIME format.
part 1 17336 bytes content-type:text/plain; charset=ISO-8859-1; format=flowed (decoded 7bit)

On 27-Mar-10 18:41, Dwayne Reid wrote:
>
> Does anyone have code snippets available?  Links to existing
> code?  I'd like to learn how to do this.
>
>    

    Found this one long time ago. Works 100%!

    Mark

==========================================

This is DTMF decoding program, provided by:
Stephen James Hardy (T/A SW Tekno) Canberra, Australia
EraseMEhardyspam_OUTspamTakeThisOuTsweng.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




part 2 17211 bytes content-type:text/plain;
(decoded base64)

This is DTMF decoding program, provided by:
Stephen James Hardy (T/A SW Tekno) Canberra, Australia
hardyspamspam_OUTsweng.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<        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


part 3 35 bytes content-type:text/plain; charset="us-ascii"
(decoded 7bit)

2010\03\28@093231 by Olin Lathrop

face picon face
enkitec@gmail.com wrote:
> ; The eight possible tones used in DTMF are searched for one-by-one.

I noticed this code was for a PIC 16, which doesn't have a multiplier, but I
don't remember what PIC the OP is using.  If the target PIC has a
multiplier, then it will not be hard to search for all possible DTMF
frequencies simultaneously.

Basically multiply the incoming signal by the sine and cosine of each of the
DTMF frequencies.  Add the products for each frequency, then low pass filter
those sums individually.  The result will be the square of the voltage
contribution of each of the frequencies.  Since you're only trying to detect
their presence, you can do the threshold comparison on the squared values
just as well.  Chose the filter characteristics such that 1394Hz (twice the
lowest DTMF frequency of 697Hz, which is what you get due to squaring) is
well attenuated, but so that it settles well enough within the 50mS minimum
tone length window.  For example, two poles with a filter fraction of 1/128
will settle to 90% in 500 samples, but the gain at 1394Hz assuming 100uS
sample period is way under 1%.

You want to sample around every 100uS, or every 1000 instructions on a PIC
18 running at 10MHz instruction rate.  That leaves 125 instructions per tone
per sample.  That's certainly enough to do the calculations, but they will
take a significant fraction of the processor cycles.  Doing it on a dsPIC or
its derivatives would be easier and would leave most of the processor for
other things.


********************************************************************
Embed Inc, Littleton Massachusetts, http://www.embedinc.com/products
(978) 742-9014.  Gold level PIC consultants since 2000.

2010\03\28@223830 by Dwayne Reid

flavicon
face
At 11:27 PM 3/27/2010, MCH wrote:
>567s are notoriously unstable.

Actually, my experience is quite the opposite.  I have several
designs that use 25HZ, 35Hz, 400Hz, 1KHz, 8KHz, 20KHz and 24KHz
signaling and they have apparently all been reliable for more than 20
years.  Some units for more than 30 years.

I occasionally will get one of the units in for repair or just
checking out and one of the things we check is the center frequency
on the 567 tone decoders.  They are always bang on the money.

I think the choice of capacitor and resistor & trimmer have far more
to do with the stability than the chip itself.

dwayne

--
Dwayne Reid   <@spam@dwaynerKILLspamspamplanet.eon.net>
Trinity Electronics Systems Ltd    Edmonton, AB, CANADA
(780) 489-3199 voice          (780) 487-6397 fax
http://www.trinity-electronics.com
Custom Electronics Design and Manufacturing

2010\03\30@003921 by Russell McMahon

face picon face
> Basically multiply the incoming signal by the sine and cosine of each of the
> DTMF frequencies.  Add the products for each frequency, then low pass filter
> those sums individually....

Fourier would be proud.
(SFT strikes again :-) )

              Russell

2010\03\30@080028 by Olin Lathrop

face picon face
Russell McMahon wrote:
>> Basically multiply the incoming signal by the sine and cosine of
>> each of the DTMF frequencies. Add the products for each frequency,
>> then low pass filter those sums individually....
>
> Fourier would be proud.
> (SFT strikes again :-) )

I'm not sure what your point is.  I'm assuming SFT is supposed to mean Slow
Fourier Transform, as apposed to FFT (Fast Fourier Transform)?  Do you see a
way to apply the FFT algorithm to checking for the existance of a few known
frequencies that aren't in a nice evenly spaced progression?

Remember that the FFT algorithm is a very clever optimization for performing
time domain to frequency domain conversion under just the right
circumstances.  It's not clear to me the circumstances apply here to the
point where doing a FFT would be less computation than what I described.

What I described requires 16 multiplies per sample, plus some low pass
filtering and of course the integer indexing and bookkeeping operations.  If
this is done on a machine with a hardware multiplier, these really aren't a
big deal.


********************************************************************
Embed Inc, Littleton Massachusetts, http://www.embedinc.com/products
(978) 742-9014.  Gold level PIC consultants since 2000.

2010\03\30@095116 by Dave Tweed

face
flavicon
face
Olin Lathrop wrote:
{Quote hidden}

The FFT is an optimization of the more general DFT (discrete Fourier transform)
that takes advantage of the many redundaant operations in the latter. If you
only care about a few of the frequency bins in the output of the DFT, it is
often better (in terms of CPU cycles) to ignore those redundancies and simply
calculate those bins directly with the DFT algorithm. This also gives you the
opportunity to do a "sliding window" version of the DFT, which is more or less
what Olin is describing. I've used this technique myself on several occasions.

I would also point out that in a tone-detection application, in which you only
care about magnitude and not about phase, the low-pass filtering doesn't need
to be a separate step. Instead of an IIR low-pass filter at the output, you
can create an implicit FIR low-pass filter by applying a "window" function
directly to the sine/cosine coefficients. Essentially, what you then have is
a pair of narrow quadrature-phase bandpass filters for each frequency, and
you're measuring the magnitude of their combined outputs (sum of squares).

-- Dave Tweed

2010\03\30@100744 by Russell McMahon

face picon face
>> Fourier would be proud.
>> (SFT strikes again :-) )

> I'm not sure what your point is.

Sorry! That wasn't meant to be a criticism.
Just noting the "classic" approach as opposed to the 'throw an FFT at
it' approach.
A nice choice.

> I'm assuming SFT is supposed to mean Slow  Fourier Transform

Yes.

>, as apposed to FFT (Fast Fourier Transform)?  Do you see a
> way to apply the FFT algorithm to checking for the existence of a few known
> frequencies that aren't in a nice evenly spaced progression?

Possibly yes, or one of the arcane variants of it - but that wasn't my aim.
Hmm - deep memory or random firing neuron suggests Winograd transform.
Gargoyles - nope - addition biased method.

WikiP suggests Guo & Burns, Shentov and Edelman (Sparse and
approximate) - and I'll happily admit to having heard of none of them
before.

Long long ago I played with optimising in place butterflies for use on
VERY limited memory VERY slow processors (not my actual project) but
nowadays I'd be as likely to accidentally end up with a Lorentz
Butterfly as a Tukey one.

Hours of fun -

       http://en.wikipedia.org/wiki/Fast_Fourier_transform

 Russell

2010\03\30@101005 by enkitec

picon face
On 30-Mar-10 10:51, Dave Tweed wrote:
>
> I would also point out that in a tone-detection application, in which you only
> care about magnitude and not about phase, the low-pass filtering doesn't need
> to be a separate step. Instead of an IIR low-pass filter at the output, you
> can create an implicit FIR low-pass filter by applying a "window" function
> directly to the sine/cosine coefficients. Essentially, what you then have is
> a pair of narrow quadrature-phase bandpass filters for each frequency, and
> you're measuring the magnitude of their combined outputs (sum of squares).
>
> -- Dave Tweed
>    



    Isn't Goertzel algorithm like that?

    Mark Jordan

2010\03\30@102819 by Dave Tweed

face
flavicon
face
enkitec@gmail.com wrote:
> On 30-Mar-10 10:51, Dave Tweed wrote:
> > I would also point out that in a tone-detection application, in which you
> > only care about magnitude and not about phase, the low-pass filtering
> > doesn't need to be a separate step. Instead of an IIR low-pass filter at
> > the output, you can create an implicit FIR low-pass filter by applying a
> > "window" function directly to the sine/cosine coefficients. Essentially,
> > what you then have is a pair of narrow quadrature-phase bandpass filters
> > for each frequency, and you're measuring the magnitude of their combined
> > outputs (sum of squares).
>
> Isn't Goertzel algorithm like that?

No, not really.

The Goertzel algorithm converts a one-bin nonrecursive DFT calculation
into an equivalent recursive form (requiring much less data storage and very
few CPU cycles per sample) that comes up with the same answer. As a result,
it cannot be implemented as a sliding-window version.

-- Dave Tweed

More... (looser matching)
- Last day of these posts
- In 2010 , 2011 only
- Today
- New search...