=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
    Date: Wed, 17 Nov 1999  22:22:09
    From: "Nikolai Golovchenko" 
      To: "pic microcontroller discussion list" 
 Subject: Re: DTMF Tones? [Idea]How to produce sine wave
--------------------------------------------------------------------------------

Hello Tracy.


>Could you perhaps drag up the details showing how this
>algorithm really works?


The algorithm is really simple. You can find the original version in Scenix
Application Note #11 (www.scenix.com). The original version is hard to use
for any frequency because of fixed triangle slope and amplitude. So I
changed the algorithm a little to adjust for DTMF frequencies generation.

First you initialize accumulators. There are two - one is a triangle
accumulator and another is a sine accumulator. Then,  each new sample is
calculated by a common routine. Note, that these accumulators contain
current value of sine/triangle, not phase.

Sine waveform is generated by adding to the last sine value the last value
of triangle. This may be regarded as integration or first-order FIR
filtering of triangle signal. As a result, the fundamental harmonic of
triangle is attenuated significantly less then other harmonics, and you have
almost clean sine wave. The sine wave frequency is equal to that of
triangle. Triangle waveform is primary in this case and generated by adding
pre-calculated fixed step to the triangle accumulator. When the accumulator
reaches top limit (also pre-calculated for the given frequency) the step's
sign is changed. A little nuance here. If the triangle peak lies somewhere
between current and last sample then a correction required to keep the
triangle value correct. After triangle reaches bottom limit, the step's sign
changes again.

Actually, I wrote the sine waveform generation routine for PIC. It takes 64
instructions, 8 RAM bytes, 24 cycles best case, and 41 cycles worst case.
This is 14 cycles savings from your supposed 55. The routine takes input 2
bytes for limit and 2 bytes for step, other 4 bytes are for 16 bit
accumulators.

For comparison I also simulated the routine of Scott Dattalo in MATLAB at 20
kHz sampling rate for all DTMF frequencies. Results are very good. There is
no frequency jitter at all and the frequency error is very small. Sorry for
wrong
suggestions.

The phase steps are:
f, Hz      Phase step
697        571
770        631
852        698
941        771
1209       990
1336       1094
1477       1210
1633       1338

phaseaccstep = round(16384 * f / f0), where f=needed freq. and f0=sampling
freq.

If I had to do DTMF dialer I would choose the phase accumulators method and
Scott Dattalo's routine, because it can provide close approximation to sine
wave, small program size, frequency can be changed smoothly, without phase
jerk.

I suppose other applications may exist where higher speed of my method is
very important.

Thanks for productive dialog. I hope this will help somebody else.


Best regards.
_

Nikolai Golovchenko, Electrical Engineering Student
National Mining University of Ukraine www.nmuu.dp.ua
Dnepropetrovsk, Ukraine
E-mail: golovchenko@mail.ru

;**********************************************************************
;                                                                     *
;    Notes:                                                           *
;                                                                     *
;                                                                     *
;                                                                     *
;                                                                     *
;**********************************************************************


	#include          ; processor specific variable definitions

	__CONFIG   _CP_OFF & _WDT_ON & _PWRTE_ON & _RC_OSC

; '__CONFIG' directive is used to embed configuration data within .asm file.
; The lables following the directive are located in the respective .inc file.
; See respective data sheet for additional information on configuration word.

#define _C STATUS, C

	cblock 0x0c
	SineH, SineL
	TriangH, TriangL
	TriangLimH, TriangLimL
	TriangIncrH, TriangIncrL
	PWMAcc
	endc

;Sine amplitude
#define SineAmp 60


;**********************************************************************
	ORG 0x0000

	movlw -SineAmp		;Initial sine
	movwf SineH		;value
	clrf SineL		;is minus amplitude

	movlw 0x24		;These are initial
	movwf TriangLimH	;
	movlw 0x4d		;values for 697 Hz at 20Mhz/4/256 sampling
	movwf TriangLimL	;
	movlw 0x0a		;(for example)
	movwf TriangIncrH	;
	movlw 0xFb		;
	movwf TriangIncrL	;
	clrf TriangH		;
	clrf TriangL		;
L1
	nop
	call ArtSine		;get the next value

	movf SineH, w		;and move
	movwf PWMAcc		;to PWM accumulator
	goto L1			;repeat
	
;**********************************************************************
;ARTIFICIAL SINE GENERATION
;Input:
;Current sine value -
;SineH - high byte (initial zero)
;SineL - low byte
;Peak to peak size of triangle -
;TriangLimH 
;TriangLimL
;Increment value of triangle -
;TriangIncrH
;TriangIncrL
;Current triangle value
;TriangH (initial value is TriangLim / 2)
;TriangL
;
;Result:
;Next sine value -
;SineH - high byte (used as result)
;SineL - low byte (used as accumulator)
;Next triangle value
;TriangH
;TriangL
;
;Memory used
;64 instructions
;8 RAM bytes
;24 cycles best case
;41 cycles worst cases
;
;Note! TriangLimL must not equal 0 or 255!
;**********************************************************************
ArtSine
;Sine = Sine + Triang
	movf TriangL, w		
	addwf SineL, f
	movf TriangH, w
	btfsc _C
	 incfsz TriangH, w
	  addwf SineH, f
;Triang = Triang + TriangIncr
	movf TriangIncrL, w		
	addwf TriangL, f
	movf TriangIncrH, w
	btfsc _C
	 incfsz TriangIncrH, w
	  addwf TriangH, f
;Check sign of Triang
	btfss TriangH, 7	;13
	 goto ArtSine1
;Negative - add TriangLim	
	movf TriangLimL, w	;15	
	addwf TriangL, w
	movf TriangLimH, w
	btfsc _C
	 incfsz TriangLimH, w
	  addwf TriangH, w
	btfsc _C
	 return		;return if carry ;-> 23
;Triang = - Triang - TriangLim 
	movwf TriangH		;23
	movf TriangLimL, w
	addwf TriangL, f
	comf TriangL, f
	comf TriangH, f
	decf TriangLimL, w  ;attention - may be bad thing if TriangLimL = 0
	subwf TriangL, f
	movf TriangLimH, w
	btfss _C
	 incfsz TriangLimH, w
	  subwf TriangH, f
	comf TriangIncrL, f
	comf TriangIncrH, f
	incfsz TriangIncrL, f
	 decf TriangIncrH, f
	incf TriangIncrH, f
	return			;->40

;Check for overflow if Triang positive
;Triang = Triang - TriangLim
ArtSine1
	movf TriangLimL, w	;16
	subwf TriangL, w
	movf TriangLimH, w
	btfss _C
	 incfsz TriangLimH, w
	  subwf TriangH, w
	btfss _C
	 return		;return if negative result ;->24

;If result positive then overflow occured
;Triang = 2 * TriangLim - Triang
	movwf TriangH		;24
	movf TriangLimL, w
	subwf TriangL, f
	comf TriangL, f
	comf TriangH, f
	incf TriangLimL, w	;attention - may be bad thing if TriangLimL = 255
	addwf TriangL, f
	movf TriangLimH, w
	btfsc _C
	 incfsz TriangLimH, w
	  addwf TriangH, f
	comf TriangIncrL, f
	comf TriangIncrH, f
	incfsz TriangIncrL, f
	 decf TriangIncrH, f
	incf TriangIncrH, f
	return			;->41
;**********************************************************************