\ DTMF.SEQ, Dual Tone Multiple Frequency Synthesizer, 20aug94cht

 

comment:

 

Right to Assemble

Lesson 1.  Dual Tone Multiple Frequency (DTMF) Synthesizer

 

The most important reason to use assembly programming is when the code

produced by high level language is too slow, especially in some time

critical routines.  The advantage in Forth is that one can code an

application entirely in high level.  When time becomes an issue, one

can identify the time-critical parts of the code and substitute them

with routines coded in assembly.  Forth provides an ideal environment

for this optimization, because a program is generally modularized in

words.  A word can be coded either in high level or in assembly.  As

long as the data stack usage and the memory usage are the same, a

word coded in assembly behaves identically as the one coded in high

level.  The only difference is that the assembly word runs faster.

 

DTMF is such an example.  But, you may ask, what is DTMF?

 

DTMF is the dial tones you hear when you dial a telephone number

on a telephone with push-buttons.  When you push a key, say "1", on

this phone, it generates a short pulse of two different tones, one

of 697 Hz and one of 1209 Hz.  These tones are sent to the computerized

switch board in the telephone company, notifuing the switch board that

a "1" is dialed by you.  When you complete pushing the buttons

defining the telephone number you like to get connected, the switch

board decodes the telephone number from the dual-tone pulses and

ring the telephone you want.

 

The advantage of DTMF is that after you are connected to the telephone

you just dialed, you can push the buttons and the corresponding dual-tone

pulses are sent to the destination telephone.  If the destination

telephone is equiped with a DTMF decoder connected to an intelligent

device, you can interact with this device remotely.  The most prevailing

of such devices is the automatic answering machine.  You must have

dealed with many of them, as you hear the message such as: "If you

want to place an order, push 1.  If you want more information, push 2.

If you want ..."  Wonder of DTMF.

 

The frequencies of the dual-tones corresponding to all the keys on

a push-button telephone are listed in the following table:

 

                Col 0   Col 1   Col 2   Col 3

                1209 Hz 1336 Hz 1477 Hz 1633 Hz

Row 0 697 Hz      1       2       3       A

Row 1 770 Hz      4       5       6       B

Row 2 852 Hz      7       8       9       C

Row 3 941 Hz      *       0       #       D

 

Now, how can I generate these dual-tones in my PC?

 

In all PC's and the clones, there is a speaker.  Can one generate

dual-tones using this speaker?  The answer is no.  This speaker is a

simple binary device.  You can turn it on or off to generate a single

tone, but you cannot produce a dual-tone.  You need two such speakers

to do the job.  The easiest way is to hang two small speakers to two

digital output lines in the parallel printer port.  Each of the two

speakers will generate one tone.  If you can hold these two speakers

close to a telephone handset, you can sent the dual-tone pulses into

the telephone.

 

If you connected two small 8 ohm speakers to bit 0 and bit 1 output

pins in the parallel printer port, you can generate two tones by

switching these two bits on and off.  You are producing square waves,

not the sine waves preferred by the telephone company.  However, the

cheap 8 ohm speakers are themselves very good filters which strongly

attenuate the high frequency spectrum in a square wave.  Those

who studied physics carefully would remember that the first harmonics

in a square wave has a frequency 3 times that of the fundamental

frequency.  The lowest DTMF tone has a frequency of 697 Hz.  Its

first harmonic has a frequency of 2091 Hz, which is much higher than

the highest DTMF tone of 1633 Hz.  Therefore, the harmonics in the

square waves do not interfere with the fundamental tones in DTMF.

 

At the other end of the telephone line, the switch board in the

telephone company uses very sharp band-pass filters to detect the

dial tones.  These filters reject noises produced in this DTMF

synthesizer,and we don't have to worry too much about the noises.

 

At the first sight, using a PC to genrate tones is a trivial task,

as the frequency required is below 2000 Hz, and the computer can

execute instrunctions at a rate of a million instructions per second.

However, generating one tone with high precision in frequency is

not that easy, and generating two very accuracy tones simultaneouly

is a tough challenge to a software engineer.  Using high level

languages, there is no solution.  Using assembly, we have a fighting

chance.

 

Experiment 1.  Connecting the speacker

 

Connect two leads of a small 8 ohm speaker to pin 2 and pin 25 of the

DB25 female parallel printer port at the back of the PC.  You may want to

use a parallel port extension cable to bring the parallel port to the

front of the computer so you can see what you are doing.

 

The parallel port is operated by sending byte data to an output port

with an I/O address of hex 3BC or 378.  In Forth, you can use the

commands:

        HEX 1 3BC PC!

to turn on the speaker, and

        0 3BC PC!

to turn off the speaker.

 

Executing these commands in rapid succession will generate a tone on

the speaker.

 

I am using a 4.77 MHz XT type PC.  If you use a different PC, the timing

has to be adjusted accordingly.

 

comment;

 

empty

Hex

 

3BC constant DataPort

\ 378 constant DataPort         \ alternate parallel port address

 

: ramp 1000 0 do                \ bit 0 period: 170 us, 6 KHz

        i dataport pc!

        loop

        ;

 

: rr begin ramp key? until ;

 

comment:

 

Executing rr will produce a tone of about 6 KHz frequency on the

speaker.  If you have an oscilloscope, you can probe pins 2-9 and you

will see square waves of decending frequencies on these pins.

 

It takes about 170 us to switch the speaker on and off once.  This

170 us time resolution is very poor.  You cannot accurately generate

even the single tones required by DTMF, not to mention dual-tones.  The

frequencies of the tones you can generate are 6000/n Hz, where n is

1, 2, 3, 4, ...  You don't have many choices.

 

Experiment 2.

 

Now, let's code the same square-wave producing routine in assembly

and see what kind of improvements we can get.

 

comment;

 

code fastRamp                   \ Bit 0 period 17 us, 60 KHz

        mov cx, 0 #             \ use CX as counter register

        mov dx, DataPort #      \ DX must have output port address

        here

                inc al          \ 8 byt pattern to be sent out

                out dx, al      \ to the parallel port

        loop                    \ repeat 65536 times

        next                    \ return

        end-code

 

: rrr   begin

                fastramp        \ loop 65536 times

                key?            \ allow user to break

        until ;

 

comment:

 

The switching period is thus reduced to 17 us, a 10 times improvement

on the speed.  So the frequencies we can generate is 60000/n Hz.

The accuracy of tones in the 700-1600 Hz region is now about 3% at

the high frequency end and 1% at the low frequency end.  This is

adequate for DTMF.

 

Experiment 3.  Dual Tone Synthesizer

 

To implement a dual tone synthesizer, we need two fast counters to

control the tones and a big counter to control the duration of the

dual-tone pulses.  The fast counters are 8 bit in width and the big

count needs 16 bits.  The algorithm is then:

 

        1.  Set duration counter

        2.  Select output port

        3.  Begin duration loop

        4.  Decrement tone counter 1

        5.  If tone counter 1 is 0, then:

                Reset tone counter 1 to preset counter 1 value

                Toggle bit 0 in output register

        6.  Decrement tone counter 2

        7.  If tone counter 2 is 0, then:

                Reset tone counter 2 to preset counter 2 value

                Toggle bit 1 in output register

        8.  Send pattern in output register to output port

        9.  Decrement duration counter

        10. If duration counter is non-zero, go back to (3.)

        11. Duration counter is zero, done and exit

 

The register allocation is as follows:

 

        AL      Output pattern register

        DX      Output port address register

        CX      Duration counter

        BL      Tone counter 1

        BH      Tone counter 2

 

The frequencies of tone 1 and tone 2 are determined by the contents

in variables Tone1 and Tone2.  The duration of the dual-tone pulse

is determined by the contents in variable Duration.

 

The code which generates a dual-tone pulse is DualTone.  The word

dd is used to test DualTone.

 

comment;

 

variable Tone1                  \ tone 1 counter

variable Tone2                  \ tone 2 counter

variable Duration               \ duration counter

 

code DualTone                   \ 37 us for one loop in this routine

        mov cx, Duration        \ init duration counter

        mov dx, DataPort #      \ set output port

        here                    \ start of big loop

        dec bl                  \ decrement tone 1 counter

        jnz 1 $                 \ jump if not 0

        mov bl, tone1           \ counter 1 is 0, reset it

        xor al, 1 #             \ toggle counter 1 output bit

1 $:    dec bh                  \ decrement tone 2 counter

        jnz 2 $                 \ jump if not 0

        mov bh, tone2           \ counter 2 is 0, reset it

        xor al, 2 #             \ toggle counter 2 output bit

2 $:    out dx, al              \ send out output pattern

        loop                    \ decrement duration counter and loop

        next                    \ duration counter is 0, done

        end-code

 

: dd ( tone1 tone2 -- )         \ convenient test code

        tone2 !                 \ init tone counter 1

        tone1 !                 \ init tone counter 2

        DualTone                \ generrate dual-tone pulse

        ;

 

comment:

 

The principal loop in DualTone averages to 37 us per loop.  This is

the time resolution of this dural-tone synthesizer.  From this time

slice value, we can compute the delay counts for the variables Tone1

and Tone2:

 

        DelayCount = 1000000/(Frequecy*37)

 

From this equation, we can deduce the following table:

 

Frequency       Count   Measured Hz     Error (%)

697             39      690             1.0

770             35      770             0.0

852             32      840             1.5

941             29      940             0.0

1209            22      1220            0.7

1336            20      1320            1.2

1477            18      1520            2.9

1633            16      1690            3.5

 

The dual-tone synthesizer is pretty good up to 1300 Hz.  The highest

frequencies at 1477 and 1633 Hz suffer an error to 3%.  This is about

the best we can do with a 4.77 MHz XT.  A faster CPU can reduce this

error in proportion to the speed.  Using a 10 MHz AT, the error should

be about 1.5%.  With a 33 MHz 386, the error can be reduced to 0.5%,

which will make the phone company very happy.

 

Experiment 4.   The Dial Tones

 

comment;

 

\ tone table

\ row 697, 770, 852, 941 Hz

\ column 1209, 1336, 1477, 1633 Hz

 

decimal

 

10000 Duration !        \ about a 0.5 second pulse

 

39 22 2constant dt1             \ key "1"

39 20 2constant dt2             \ key "2"

39 18 2constant dt3             \ key "3"

39 16 2constant dtA             \ key "A"

 

35 22 2constant dt4             \ key "4"

35 20 2constant dt5             \ key "5"

35 18 2constant dt6             \ key "6"

35 16 2constant dtB             \ key "B"

 

32 22 2constant dt7             \ key "7"

32 20 2constant dt8             \ key "8"

32 18 2constant dt9             \ key "9"

32 16 2constant dtC             \ key "C"

 

29 22 2constant dt*             \ key "*"

29 20 2constant dt0             \ key "0"

29 18 2constant dt#             \ key "#"

29 16 2constant dtD             \ key "D"

 

: PlayDualTone ( char -- )      \ play a dual-tone selected by a character

        CASE

                ascii 0 OF dt0 dd ENDOF

                ascii 1 OF dt1 dd ENDOF

                ascii 2 OF dt2 dd ENDOF

                ascii 3 OF dt3 dd ENDOF

                ascii 4 OF dt4 dd ENDOF

                ascii 5 OF dt5 dd ENDOF

                ascii 6 OF dt6 dd ENDOF

                ascii 7 OF dt7 dd ENDOF

                ascii 8 OF dt8 dd ENDOF

                ascii 9 OF dt9 dd ENDOF

                ascii A OF dtA dd ENDOF

                ascii B OF dtB dd ENDOF

                ascii C OF dtC dd ENDOF

                ascii D OF dtD dd ENDOF

                ascii # OF dt# dd ENDOF

                ascii * OF dt* dd ENDOF

                drop

        ENDCASE

        100 ms

        ;

 

: DIAL  ( play a dual-tone sequence specifiedd by a 11 digit phone number )

        32 word count

        0 do    count PlayDualTone

        loop

        drop

        ;

 

\       Example:   DIAL 0118869651234

\ which dials a phone in Taipei, Taiwan

 

comment:

 

DIAL expects a number string corresponding to the telephone number

you want to dial.  The number string takes numerals 0 to 9, capitalized

characters A to D, "*" and "#".  It will ignor all other characters,

including lower case "a" to "d".  It stops at the end of the string,

terminated by a carriage return or a space.

 

Experiment 5.   Interactive Dialing

 

DIAL sens out a dual-tone sequence.  We can program the PC and use its

numeric keypad as a telephone keyboard.  It then sends out one dual-tone

pulse when a valid key is pressed.  Press "ESC" terminates the manual

dialing.

 

As all the tools are available now, this code is trivial.

 

comment;

 

: ManualDialing

        begin   key dup

                27 = abort" Dialing done"

                PlayDualTone

        again

        ;