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.
(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:
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)
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
;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.
;--------------------------------------------------------
;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
;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
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).
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
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).
> From: John Payson <spam_OUTsupercatTakeThisOuTMCS.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}
> 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?
>
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.
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?
>
> 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.
> > 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.
> From: John Payson <supercatKILLspamMCS.COM>
>
> > > From: John Payson <.....supercatKILLspam.....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
> >
> > From SJH <EraseMEhardyspam_OUTTakeThisOuTsweng.stortek.com>
> > 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.
>
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.
> 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?