Luc Pauwels

Beeper

Alonzo Gariepy

Date: 28 Jan 90 17:14:48 GMT
Subject: Beeeeeeeeeeeeeeeeeeep!
From: alonzo@microsoft.UUCP (Alonzo GARIEPY)
Newsgroups: comp.sys.handhelds
Organization: Microsoft Corp., Redmond WA

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