Path: utzoo!attcan!uunet!microsoft!alonzo From: alonzo@microsoft.UUCP (Alonzo GARIEPY) Newsgroups: comp.sys.handhelds Subject: Re: Phone tones Message-ID: <53547@microsoft.UUCP> Date: 14 Mar 90 01:20:09 GMT References: <511@ohs.UUCP> <51114@microsoft.UUCP> <5135@ccncsu.ColoState.EDU> Reply-To: alonzo@microsoft.UUCP (Alonzo GARIEPY) Organization: Microsoft Corp., Redmond WA Lines: 173 Back by popular demand... Newsgroups: comp.sys.handhelds Subject: Beeeeeeeeeeeeeeeeeeep! Here is a code fragment for controlling the beeper. Before the code is executed, register B must contain a time constant that determines the frequency, and register D the number of half cycles in the duration. intoff move.5 #c0000,d1 ; address of keymask move.x @d1,c ; c = #0xx (keymask is #0fe on hp48) out.x c ; turn beeper off move.1 #2,p move.p1 #8,c ; c = #8xx move.1 #4,p move.5 #c00e6,d0 ; address of abort flag loop: swap.1 p,c,#2 ;\ out.x c ;-alternately output #4xx and #8xx move.x b,a ;\ pulse: dec.x a ; time half wavelength (b is timing constant) brcc pulse ;/ dec.w d ; decrement and test duration brcs done ; d is # of half wavelengths in duration move.a @d0,a ; check abort flag brz.a a,loop ; abort if flag set (user interrupt?) done: move.x @d1,c ;\ out.x c ;-turn beeper off inton ================================================================= | | HP28 ROM #22ad23 - #22bde BEEPER | | You can find the HP code that does this by disassembling | the ROM between #22ad3 and #22bde. It does more than my | simple example above. It expects the duration in 1000ths | of a second to be in register C, and frequency in cycles- | per-second to be in register D. The calibration factor | at location #c000f is then used to work out a pulse time | constant, which goes into register B. Next, the duration | is converted to a pulse count with the equation, C * D *.002 | (pulses = C seconds/1000 * D cycles/second * 2 pulses/cycle) | which then goes into register D. The rest is similar to | my example, except that it decrements the duration count | in two steps (I don't know whether this was done for speed, | consistency, or as a bug fix.) | | The calculation of the time constant is still murky to me. | ================================================================= I would not be surprised to find that the decrement instruction terminates early when there is no borrow from the next nibble. This optimization could cause some timing inconsistencies. The above code should also shed some light on the use of C00E6 (marked ??? in Dave Kaffine's indispensable RAM map). I assume that pressing the ON key puts something in this address. I don't know what interrupts are disabled by the intoff instruction, but I'll guess it isn't the ones caused by the ON key or the timer. Most interesting to me are the use of #4FE, #8FE, and #0FE to control the beeper. I presume that their respective functions are positive pulse, negative pulse, and beeper off. Let's look at how you might combine two frequencies to generate dialing tones. ________ ________ ________ ____ __ ________ ________ ________ 3000 Hz + + ______ ______ ______ ______ ____ __ ______ ______ ______ ______ 4000 Hz = = ______ __ ____ ____ __ ____ ____________ ____ __ 3000 Hz + 4000 Hz __ ____ __ ______ If you can't imagine the vertical lines, here is a slightly more intuitive representation: ______ ______ ______ / \ / \ / \ 3000 Hz \______/ \______/ \______/ + + ____ ____ ____ ____ / \ / \ / \ / \ 4000 Hz \____/ \____/ \____/ \____/ __ = = / \ /\ / \__ ____/\____________ ____/ \__ 3000 Hz + 4000 Hz \ / \/ \ / \/ \__/ The above has an uncanny resemblance to the plot you get with: RAD 'SIN(3*X)+SIN(4*X)' STEQ 1.8 *H DRAW The first representation of the wave is bipolar pulse width modulated--the distortion is horrendous. And how do you get the zero intermediate values between pulses? Perhaps #0FE. If not, an intermediate value can be achieved by pulsing back and forth at some ridiculously high frequency (e.g., 20kHz). If you add more than two frequencies, you need more different intermediate values. About the only way I can see to do that is to modulate the signal on a carrier (i.e., 20kHz) and then represent the carrier using bipolar pulse width modulation. Standard pulse width modulation, known as PDM (pulse duration modulation), may also serve here, but I'm not sure yet. Below, I summarize the touch-tone dialing combinations. 697 1 2 3 - 770 4 5 6 - 852 7 8 9 - 941 * 0 # - Hz 1209 1336 1477 1633 These frequency combinations were originally chosen to avoid harmonic relationships that are open to signal interference. This has an unfortunate side-effect of making them harder to synthesize. Below is a list of modified frequency combinations that can be synthesized with minimal data (because they repeat at least every 17 cycles) yet remain within 1% of the nominal frequency. The standard allows for a 2% drift in frequency, leaving us less than 1% error for synthesis. The synthesis may also suggest better, fractional frequencies. 1 696:1218 4:7 2 702:1326 9:17 3 696:1479 8:17 4 770:1210 7:11 5 764:1337 4:7 6 783:1479 9:17 7 847:1210 7:10 8 847:1331 7:11 9 848:1484 4:7 * 945:1215 7:9 0 938:1340 7:10 # 938:1474 7:11 Since I don't have an oscilloscope, I encourage others to analyse the accuracy of the current tone generation and to develop programs for generating sound from a waveform. I won't be able to pursue this further for some time. Alonzo Gariepy alonzo@microsoft