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