=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
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
;**********************************************************************