Searching \ for 'DTMF - results' 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: 'DTMF - results'.

Truncated match.
PICList Thread
'DTMF - results'
1996\06\23@233030 by Steve Hardy

flavicon
face
I recently became the proud owner of a Clearview xx ICE.  The first
application just _had_ to be the DTMF generator.  For those who are
interested, here are the results:

1) Hardware.

In-circuit emulator used at 10MHz, with PIC16C74 pod.  None of the magic
smoke has leaked out yet - always a consideration at >$300 a pop.

The PIC has enough grunt to directly drive a headphone speaker.  The
PWM output was coupled via a 820ohm/0.1uF low pass RC directly into the
23ohm headphone.  Surprisingly, this may be heard from quite a distance
in spite of the inefficiency and impedance mismatch.  I suspect that
quite a loud sound may be produced from an 8ohm speaker if one of those
little 1K:8ohm 'transistor radio' transformers were used.

The acoustic output was coupled into the telecom network by holding the
handset against the headphone.  As far as I am aware, my home is
connected to a state-of-the-art digital exchange (we have pretty good
telecom here in Australia).

2) Software.

The DTMF generator code was used with a few mods (such as to direct
output to PWM).  The PWM was set at a base rate of 19KHz.  As a test,
my mobile phone number could be signalled at various rates; the
fastest rate being 50ms per tone with 50ms pause between tones.

3) Results.

At a rate of 100ms per tone, the setup was able to dial successfully.
At 75ms it was OK with the odd error.  At 50ms the network got
awfully confused.  Even at 100ms per tone, this is much faster than
any other automatic dialling equipment I have ever heard.  In fact,
it is hardly recognisable as DTMF dialling.

When I first heard the tones produced, the thing that struck me was
that there is quite a high level of subharmonic content.  This is
because the output sequence repeats at a rate substantially less than
the nominal output frequency.  The subharmonics do not have
any affect on the DTMF receivers at the exchange, but they may be
considered detrimental if the PIC is to be used for musical purposes.

Regards,
SJH
Canberra, Australia

1996\06\24@142750 by Scott Dattalo

face
flavicon
face
(CAUTION: If you don't have much patience for long-winded e-mails then delete
this
message now.)

Steve,

In the very first posting of your DTMF threads you said that the sine waves for
the
two DTMF tones can be generated in 202 cycles (per sample). I have another way
to
generate sine waves that only takes 65 cycles per sample or for both tones, 130
cycles.
This could reduce the burden of your analog filters by allowing you to supply
more
samples per cycle of each sine wave. It may also give you that extra little
boost to
get you over the 50ms hurdle in the DTMF decoder.

Your approach which follows that of AN616, is based on a trigonometric recursion
relationship. It's the basis of the Goertzel algorithm -- an algorithm that
efficiently
computes the DFT (discrete Fourier Transform).

In essence, you want to compute a series of sine wave samples that are equally
spaced in
frequency. We can define the starting angle as a and the step in angles as b. So
the
series looks like this:

sin(a), sin(a + b), sin(a + 2*b), sin(a + 3*b), ..., sin(a + n*b)

The goal is to recursively compute sin(a+n*b) using the two previous values in
the sine
series, sin(a + (n-1)*b) and sin(a + (n-2)*b):

sin(a + n*b) = x * sin(a + (n-2)*b) + y * sin(a + (n-1)*b)

Here, x and y are two constants that need to be determined. Note that the next
value or
the n'th value depends on the previous two values, n-2 and n-1.
Re-arranging and simplifying:

sin(a + n*b) = x * sin(a + n*b - 2*b) + y * sin(a + n*b - 1*b)

sin(a + n*b) = x * [ sin(a + n*b) * cos(2*b) - cos(a + n*b) * sin(2*b)] +

              y * [ sin(a + n*b) * cos(b) - cos(a + n*b) * sin(b)]

sin(a + n*b) = [x * cos(2*b) + y * cos(b)] * sin(a + n*b) +

              [x * sin(2*b) + y * sin(b)] * cos(a + n*b)

For this to be true for all n, we must have the two expressions in brackets
satisfy:

[x * cos(2*b) + y * cos(b)] = 1

[x * sin(2*b) + y * sin(b)] = 0

which, when solved yields

x = -1,       <== A very convenient constant, turns multiplication into
subtraction

y = 2*cos(b), <== Another constant that requires multiplication.

Finally, substituting into the original equation we get:

sin(a + n*b) = -sin(a + (n-2)*b) + 2*cos(b) * sin(a + (n-1)*b)
---------------------------------------------------------------!

This algorithm is excellent for DSP's. For the low and mid range PICs, it is
hindered
slightly by the 8x8 multiplication. However, there is another algorithm that has
just
as much accuracy as this one. If you read "Embedded Systems" (I don't any more
since
they refused to forward my subscription to my new job) you might have read a
series by
Jack Crenshaw on how to efficiently compute the sine function. His approach,
which I've
implemented in PIC assembly, uses a look-up table plus first order linear
interpolation.
You can read the comments in the program to see how it works. But here's a brief
summary of its features:

1) 50 words of program memory, including 18 for a look-up table.
2) 65 cycles of execution time (for all cases)
3) 9 bytes of RAM - 5 are temporary (actually you can save one off of this)
4) 8 bit signed output (+/- 1 count accuracy)
5) 0.022 degrees of frequency resolution (360 / 16384 ~ .022).  With a few extra
words, this could be improved to 0.0055 degrees (360 /65536).
6) Sine values are randomly accessable (as opposed to the recursive, serially
accessed method of the Goertzel algorithm). This gives an extra flexibility on
dynamically tweaking the frequency, which is something I've found useful for
analog
phase locked loops.



 cblock  0x20

       f_hi    ;Low byte of frequency variable
       f_lo
       x1
       x2
       interp
       index
       product

       fstep_lo
       fstep_hi
 endc

ODD_QUADRANT    equ     4       ;In f_hi
NEG_QUADRANT    equ     5       ;In f_hi
INDEX_MASK      equ     0x0f    ;In f_hi
INTERP_MASK     equ     0xf0    ;In f_lo


       ORG     0               ;Reset Vector

       GOTO    Main

       ORG     4               ;Interrupt Vector


Main

       BCF     STATUS,RP0      ;Point to BANK 0

   ;Initialize the step in frequency
       MOVLW   0x10
       MOVWF   fstep_lo        ;One unit of fstep corresponds to 360/16384
degrees
       CLRF    fstep_hi        ;i.e. 0.02197 degrees. Set fstep = 455 (1c7) to
step
                               ;10 degrees for example

       CLRF    f_hi            ;Start off at 0 degrees
       CLRF    f_lo

xxx
       CALL    sine
       NOP

   ;Now that the sine has been calculated, we could do something with it other
than
   ;throwing it away.

   ;Make a frequency step. Note, the frequency step doesn't necessarily have to
be
   ;constant. E.g. it could be changed as part of a PLL algorithm.

       MOVF    fstep_lo,W
       ADDWF   f_lo,F
       SKPNC
         INCF  f_hi,F
       MOVF    fstep_hi,W
       ADDWF   f_hi,F

       GOTO    xxx


;--------------------------------------------------------
;sine
;
;  The purpose of this routine is to take the sine of the
;16-bit frequency variable f_hi:f_lo to produce a signed
;8-bit result that is within +/- 1 count of the true sine
;value.
;  Only the lower 14 bits of the frequency variable are
;actually used. The frequency variable maps into degrees
;by the following transfer function:
;  degrees = (f & 0x3fff) * 360 / 0x4000
;  The sine output is approximately
;sine = int(127*sin( (f & 0x3fff) * 360 / 0x4000) )
; where
; sin() is the true sine function
; int() is the nearest integer function
;
;The technique used to obtain the sine value is a combination
;of table look-up and first order linear interpolation. Sixteen
;equidistant frequency samples of the first quadrant of sin(x)
;are stored in sine_table.
;
; The frequency variable is broken down as follows:
; xxQQTTTT IIIIPPPP
; where
;  xx - don't care
;  QQ - Quadrant: 00 = quadrant 1, 01 = quadrant 2, etc.
;  TTTT - Index into the sine_table.
;  IIII - Interpolation between successive entries in the table
;  PPPP - Phase accumulation (not needed in this function, it's
;         only used to increase the dynamic range in frequency
;         steps).
;Once the frequency has been broken down in to these parts, the
;sine function for the first quadrant can be calculated as follows:
;  x1 = sine_table[index]
;  x2 = sine_table[index+1]
;  sine = x1 + ((x2-x1) * interp) / 16
;The first term, x1, is seen to be a first order approximation to
;sine function. The second term improves on that approximation by
;interpolating between x1 and x2. The interpolation variable interp
;is 0 <= interp <= 15 and it is divided by 16. Consequently, the
;interpolation factor ranges between 0 and 15/16.
;
;The sine function in the other three quadrants can be obtained
;from calculations based on the first quadrant by using the following
;trig identities:
; first, let 0 <= f <= 90, i.e. f covers the first quadrant.
;  quadrant 2:  u = 90 + f,   90 < u < 180
;     sin(u-90) = sin(f)
;     x1 = sine_table(16-index), x2 = sine_table(15-index)
;  quadrant 3:  u = 180 + f,  180 < u < 270
;     sin(u) = sin(f+180) = -sin(f)
;     x1 = -sine_table(index), x2 = -sine_table(index+1)
;  quadrant 4:  u = 270 + f,  270 < u < 360
;     sin(u-90) = sin(f+180) = -sin(f)
;     x1 = -sine_table(16-index), x2 = -sine_table(15-index)
;
;Thus, for quadrants 2 and 4, the sine table is indexed in reverse
;order and for quadrants 3 and 4 the values from the sine table
;are negated. A slight change is made on this indexing and negation
;scheme so that the operation (x2-x1) * interp / 16 only deals with
;positive numbers. This significantly simplifies the multiplication.
;The modification changes the formula for each quadrant as follows:
; quadrant 1:   (no change)
;   x1 = sine_table[index],  x2 = sine_table[index+1]
;   sine = x1 + ((x2-x1) * interp) / 16
; quadrant 2:
;   x1 = sine_table[15-index],  x2 = sine_table[16-index]
;   sine = x2 - ((x2-x1) * interp) / 16
; quadrant 3:
;   x1 = sine_table[index],  x2 = sine_table[index+1]
;   sine = -(x1 + ((x2-x1) * interp) / 16)
; quadrant 4:
;   x1 = sine_table[15-index],  x2 = sine_table[16-index]
;   sine = -(x2 - ((x2-x1) * interp) / 16)
;
;Input
; f_hi:f_lo  -  16-bit frequency variable
;Output
; W = int(127*sin( (f & 0x3fff) * 360 / 0x4000) )
;
;Execution time: 65 Cycles (for all cases)
;

sine
   ;Get the 4-bit interpolation factor
       SWAPF   f_lo,W
       ANDLW   0xf
       IORLW   0x80            ;Set the MSB. Used below in the multiply loop
                               ;as the "loop counter".
       MOVWF   interp

   ;Get the 4-bit index and add 1 to it.
       MOVF    f_hi,W
       ANDLW   INDEX_MASK
       ADDLW   1
       BTFSC   f_hi,ODD_QUADRANT
        SUBLW  17                      ;Odd quadrants, index = 16 - index
                                       ;Actually: (index + 1) = 17 - (index +
1)
                                       ;                      = 16 - index
       MOVWF   index
       CALL    sine_table              ;Get x2=sin(index+1)
       MOVWF   x2

       DECF    index,W
       CALL    sine_table              ;Get x1=sin(index)
       MOVWF   x1

       SUBWF   x2,W                    ;W=x2-x1, This is always positive.

   ;Initialize the product of (x2-x1)*interp/16 to 1/2. Note 8/16 == 1/2
   ;(This rounds the product to the nearest integer.)
       CLRF    product
       BSF     product,3       ;(note, product and index could be aliased to
                               ; save one byte of ram).

   ;multiply interp and x2 - x1 and divide by 16. This is actually a 4 by 8
   ;bit multiplication. The division by 16 is implemented with a shift right
   ;one position for each of the four passes through the loop. Four passes
   ;are determined by monitoring the migration of the MSB of interp. It is
   ;initialized to 1 above and is shifted right one position each pass
   ;through the loop. On the fourth pass, it will be in bit position 3.

s1      RRF     interp,F        ;If the LSB is set
       SKPNC
         ADDWF product,F       ;Then add (x2-x1) to the product
       RRF     product,F       ;Divide the product by two
       BTFSS   interp,3        ;If the MSB has not made it here yet
         goto  s1              ;then we need to loop again

       MOVF    product,W
       BTFSS   f_hi,ODD_QUADRANT
         ADDWF   x1,W
       BTFSC   f_hi,ODD_QUADRANT
         SUBWF   x2,W
       BTFSC   f_hi,NEG_QUADRANT
         SUBLW 0
       RETURN

sine_table
       ADDWF   PCL,F
       RETLW   0       ;127*sin(0 * 90/16)
       RETLW   12      ;127*sin(1 * 90/16)
       RETLW   25      ;127*sin(2 * 90/16)
       RETLW   37      ;127*sin(3 * 90/16)
       RETLW   49      ;127*sin(4 * 90/16)
       RETLW   60      ;127*sin(5 * 90/16)
       RETLW   71      ;127*sin(6 * 90/16)
       RETLW   81      ;127*sin(7 * 90/16)
       RETLW   90      ;127*sin(8 * 90/16)
       RETLW   98      ;127*sin(9 * 90/16)
       RETLW   106     ;127*sin(10 * 90/16)
       RETLW   112     ;127*sin(11 * 90/16)
       RETLW   117     ;127*sin(12 * 90/16)
       RETLW   122     ;127*sin(13 * 90/16)
       RETLW   125     ;127*sin(14 * 90/16)
       RETLW   126     ;127*sin(15 * 90/16)
       RETLW   127     ;127*sin(16 * 90/16)

       END



Scott

PS. Sorry about the length of the message.
PPS. This same algorithm implemented on a TMS320C50 executes in around 10
cycles. My guess
is that the Goertzel algorithm is comparable (probably faster).

1996\06\24@180724 by John Payson

flavicon
face
One approach I was considering for DTMF which would have been a little
tricky on the 16C71 [not quite as much RAM as I'd like] but should work
nicely on the '71A would be to use a "running-phase" counter which runs
from 0x0000-0x2FFF and do something like the following:

       movf    FreqLow,w
       addwf   PhaseLow,f
       btfsc   C
        incf   PhaseHi,f
       movf    FreqHi,w
       addwf   PhaseHi,f
       rrf     PhaseHi,w
       andlw   $1C
Boing:
       addwf   PC

Phase0:
       movf    Source,w
       addwf   Slot0
       goto    Gone
Phase1:
       movf    Source,w
       addwf   Slot1
       goto    Gone
Phase2:
       movf    Source,w
       subwf   Slot2
       goto    Gone
Phase3:
       movf    Source,w
       subwf   Slot0
       goto    Gone
Phase4:
       movf    Source,w
       subwf   Slot1
       goto    Gone
Phase5:
       movf    Source,w
       subwf   Slot2
       goto    Gone
Phase6:
       movlw   $30
       subwf   PhaseHi
       movf    Source,w
       addwf   Slot0
Gone:
       ...

Fourteen cycles per frequency per sample to produce 3 buckets; the largest
absolute value among (Slot0+Slot1), (Slot1+Slot2), (Slot2-Slot0) will
correspond pretty well to the amount of energy at the desired frequency
(note: there will be a little bumpiness since your sampling points are
60 degrees apart, and you will pick up some 5th and 7th harmonics though
this method will filter out the 2nd and 3rd and all multiples thereof.
Running this algorithm on a PC yielded pretty good results, and it would
seem it should run decently on a 16MHz PIC (looking for 8 tones, sampling
rate of about 8KHz).

What do people think of this as an approach?

1996\06\24@215538 by Steve Hardy

flavicon
face
> From: John Payson <spam_OUTsupercatTakeThisOuTspamMCS.COM>
>
> One approach I was considering for DTMF which would have been a little
> tricky on the 16C71 [not quite as much RAM as I'd like] but should work
> nicely on the '71A would be to use a "running-phase" counter which runs
> from 0x0000-0x2FFF and do something like the following:
[code deleted]
{Quote hidden}

I may have missed something, but what is sampling at 6 points per cycle
times 14 cycles gaining you over my approach of sampling at 4 points
per cycle times 10 cycles?

With DTMF, I wouldn't think that one would have to worry too much about
picking up spurious harmonics, since the harmonics will not overlap
until over the 4th harmonic which is high enough to be attenuated by
the telecom network.

Regards,
SJH
Canberra, Australia

1996\06\25@125905 by Scott Dattalo
face
flavicon
face
John Payson wrote:
> <snip>
>         andlw   $1C
> Boing:
>         addwf   PC
>
> Phase0:
>         movf    Source,w
>         addwf   Slot0
>         goto    Gone
> Phase1:
>         movf    Source,w
>         addwf   Slot1
>         goto    Gone

<snip>

Maybe I'm missing something (I must admit I did not try running this program),
but isn't it true that ANDing W with $1c will produce 0,4,8,$C,$10,$14,$18, and
$1C? However, the _case statements_ are only separated by three instructions.


Also, how large is the variable FreqLow:Hi? If it is small, then it seems that
consecutive samples will be binned together. If it is large, then consecutive
samples will go to separte bins. Could you please clarify this?

Thanks

Scott

1996\06\25@173533 by John Payson

flavicon
face
>
> John Payson wrote:
> > <snip>
> >         andlw   $1C
> > Boing:
> >         addwf   PC
> >
> > Phase0:
> >         movf    Source,w
> >         addwf   Slot0
> >         goto    Gone
> > Phase1:
> >         movf    Source,w
> >         addwf   Slot1
> >         goto    Gone
>
> <snip>
>
> Maybe I'm missing something (I must admit I did not try running this program),
> but isn't it true that ANDing W with $1c will produce 0,4,8,$C,$10,$14,$18,
and
> $1C? However, the _case statements_ are only separated by three instructions.

Oops... :-)  Well, I did say 'something like...'.  Also, to be useful each
slot should probably hold 16 bits rather than 8 (which also slows things
down).  Instead of AND'ing with $1C it would probably be better to AND with
$38; this would allow room to do a 16-bit ADD and a Goto after each phase
spot.

> Also, how large is the variable FreqLow:Hi? If it is small, then it seems that
> consecutive samples will be binned together. If it is large, then consecutive
> samples will go to separte bins. Could you please clarify this?

Assuming we spread the movwf/add thingies 8 cycles apart so the phase went
$0000-$2FFF, FreqHi:FreqLo should be 12288*Freq/SampRate where Freq is the
desired detection frequency in Hz and SampRate is the input signal sampling
rate (also in Hz).  Assuming code is put in for the cases where PhaseHi & $38
is $30 or $38, the highest permissible FreqHi:FreqLow value would be $10:$00
which would correspond to a frequency of SampRate/3.

1996\06\25@175625 by John Payson

flavicon
face
> > From: John Payson <.....supercatKILLspamspam@spam@MCS.COM>

> > Fourteen cycles per frequency per sample to produce 3 buckets; the largest
> > absolute value among (Slot0+Slot1), (Slot1+Slot2), (Slot2-Slot0) will
> > correspond pretty well to the amount of energy at the desired frequency
>
> I may have missed something, but what is sampling at 6 points per cycle
> times 14 cycles gaining you over my approach of sampling at 4 points
> per cycle times 10 cycles?

I would have to look at your code again, but all the DTMF-stuff I remember
seeing here required doing a multiply for each sample as part of the DFT;
on a PIC such multiplies are somewhat expensive.  While it would be possible
to only take 4 suitably-timed samples each wave and assume the cosine and
sine terms at the desired frequency are (s2-s0) and (s3-s1), this would be
highly succeptible to all odd harmonics (including the third).  By contrast,
taking six samples each wave provides effective cancelation of the second,
third, fourth, and sixth harmonics.

1996\06\25@215754 by Steve Hardy

flavicon
face
{Quote hidden}

No, the multiply was required only for generation.  My decoding routine
requires only 16-bit addition/subtraction as does your routine.

Regarding harmonics:  Harmonics greater than 4th may be effectively
ignored because of the 3KHz bandwidth of the PSTN (Public Switched
Telephone Network).  Other harmonics are only a problem if they are 1)
of sufficient intensity 2) aliased onto a frequency within 2% of the
tone being sampled.  Take the lowest tone, 697Hz, as an example.  If
sampled at 4 times this, the nyquist frequency is 2*697 = 1394Hz.  Find
where the harmonics of the other tones are aliased to, and see if any
get aliased to 697Hz +/- 2%.  Apply the same logic to the other tones.
Although I don't have time to do the arithmetic, I don't think its a
problem.

1996\06\26@002300 by John Payson

flavicon
face
> No, the multiply was required only for generation.  My decoding routine
> requires only 16-bit addition/subtraction as does your routine.
>
> Regarding harmonics:  Harmonics greater than 4th may be effectively
> ignored because of the 3KHz bandwidth of the PSTN (Public Switched
> Telephone Network).  Other harmonics are only a problem if they are 1)
> of sufficient intensity 2) aliased onto a frequency within 2% of the
> tone being sampled.  Take the lowest tone, 697Hz, as an example.  If
> sampled at 4 times this, the nyquist frequency is 2*697 = 1394Hz.  Find
> where the harmonics of the other tones are aliased to, and see if any
> get aliased to 697Hz +/- 2%.  Apply the same logic to the other tones.
> Although I don't have time to do the arithmetic, I don't think its a
> problem.

Well, 2091Hz is well within the PSTN bandwidth and would alias very nicely
to 697Hz.  If you use seperate filters/ADCs for your high and low band inp-
uts, this may not be an issue (since your low-band filter could probably
take out 2091Hz pretty well) but if not, the aliasing here could be a
problem.  In the method I'm proposing, by contrast, your lowest alias is
at 5x, which would be 3485Hz--high enough to filter even if the telco doesn't.

Thinking about it, it might make sense to do a 6x sample for the lower tones
and a 4x sample for the upper ones.  The 4x sample is a little easier to
do, probably, and the upper tones are high enough that you shouldn't get even
any 3rd harmonic signal.  How does that sound for an approach?

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