Direct Digital Synthesis of Sine Waves
For the most part we live in an analog world and when using a microcontroller, it is often necessary to be able to convert a digital value into its analog equivalent (or, for that matter, an analog value to a digital value – but that’s not what this post is about). Many of the low end and low cost microcontrollers available today do not implement a built in hardware digital to analog converter (DAC). However, most do contain an 8 bit or 16 bit timer that is capable of producing Pulse Width Modulation (PWM).
Using Pulse Width Modulation as a Digital to Analog Converter
Pulse Width Modulation is primarily used to control power to a load such as a motor or a lamp by varying the pulse width (duty cycle) of a fixed frequency square wave. The load responds to the average voltage that represents the duty cycle of the waveform. Increasing the pulse width will result in an increase in the average voltage level to the load along with a corresponding increase in the motors speed or the lamps intensity.
However, Pulse Width Modulation, in conjunction with a simple resistor and capacitor (RC) filter, can also be used to generate specific DC voltage levels or various analog waveforms such as sine or triangle waves (also referred to as Direct Digital Synthesis or DDS).
As an example, to produce a DC voltage that is 50% of the MCU’s supply voltage, it is only necessary to output a waveform that has a 50% duty cycle and filter it with a simple RC filter. In a similar manner, to produce an output voltage that is either 25% or 75% of the supply voltage, the duty cycle is changed to either 25% or 75%. It should be noted that the PWM frequency used should be as high as possible in order to keep the values of the RC components small.
Low Frequency Sine Waves
Accurate sine waves can be created by using Pulse Width Modulation, an RC filter and a look-up table with the correct sine wave values. Low frequency sine waves are useful in electronic music applications such as adding tremolo or creating rotating sound effects.
The circuit used for the sine wave generator is similar to the one used in the digital square wave generator (see an earlier post) with the exception of different firmware and the addition of a low pass filter.
The microcontroller (Freescale MC9S08QD4) circuit was designed to allow adjusting the frequency of the sine wave output. The frequency output can be adjusted from 141.5 milliHerz (0.1415 Hz) to 141.5 Hz. A push button switch is used to adjust the rate (step) of frequency increase.
A rotary encoder is used to adjust the frequency output. The encoder is a BOURNS PE12LS (2-bit quadrature output), with 24 pulses per rotation and also has an illuminated shaft.
The encoder is used in conjunction with the key board interrupt to increase or decrease the value of the delay time between changes in the timer value registers (TPMC0V – i.e. the values in the look-up table). A longer delay time results in a lower frequency. The encoder increases or decreases the delay time in either 50 delay cycles per detent (COARSE ADJ) or 2 delay cycles per detent (FINE ADJ).
The two different delay increment values (coarse or fine) are selected using a single pushbutton in conjunction with another interrupt routine. A yellow LED indicates which increment value is selected (LED on = fine adjustment value). The sine wave frequency value is maintained when switching from coarse to fine adjustment.
The slight drawback to this method is that there is some loss in resolution when setting a high frequency sine wave value due to the smaller ratio between the adjustment value and the delay value – most noticeable when setting the higher frequencies.
The MC9S08QD4 has a maximum bus frequency of 4 MHz. The timer clock was configured to run at this frequency.
Timer Clock = 4000000 Hz
Timer Modulus = 256
Timer Frequency = 4000000 / 256 = 15625 Hz
Edge aligned PWM mode (eight bits used) uses the normal up counting mode of the timer counter. The duty cycle is determined by the value in the timer value register (TPMC0V) which is located in the look-up table. A value of 0 in the timer value register will result in a duty cycle of 0%. A value greater than the modulus (256) will result in a duty cycle of 100%. The period of the PWM is based on the value in the modulus register (TPMMODH:TPMMODL). The frequency of the PWM is therefore 15.625 Khz.
Low Pass Filter
Two different low pass filters were used to convert the PWM output into sine waves. The simple R/C filter uses a 10K resistor along with a 1.0 uF film capacitor. This simple filter was found to work very well at all frequencies. However, it has a high output impedance and cannot be loaded down without distorting the waveform output. There is also some variation in output amplitude across the frequency range.
A second low pass filter was created using a LM3900 Norton Op Amp. This low pass filter was also found to work very well and has a lower output impedance and exhibited less amplitude variations across the frequency range. Both filters are shown in the schematic.
SINE WAVE GENERATOR PROGRAM CODE
// OCTOBER 25 2012
// SIMPLE PWM SINE WAVE GENERATOR
// ENCODER TO PWM TO SINE WAVE
// REQUIRES SIMPLE RC NETWORK ON OUTPUT
// TIMER INTERRUPT NOT USED
// ENCODER OUTPUT
// CLOCKWISE >>
// 00 01 11 10 00
// << COUNTERCLOCKWISE
//for EnableInterrupts macro
//include peripheral declarations
//set clock trim using BDM
#define NV_ICSTRM (*(const char * _FAR)0x0000FFAF)
interrupt VectorNumber_Vkeyboard1 void encoder_isr();
//interrupt to control pwm delay rate
interrupt VectorNumber_Virq void pwm_isr();
//yellow led (on) in fine adjust mode
#define fineadj_led PTAD_PTAD3
void init_interrupts (void);
void loop_dly(unsigned int value);
//timer values to create sine wave output
const unsigned char sine=
signed char encoder_1;
signed char encoder_2;
signed int delay_value;
unsigned char delay_flag;
unsigned char delay_increase;
unsigned char counter;
unsigned char index;
//fine freq adjust value
delay_increase = 1;
//led off at large increment value
fineadj_led = 0;
//set coarse or fine adjust
delay_flag = 0;
TPMMOD = 255;
//buss clock divide by 1
TPMSC = 0x08;
//edge aligned PWM
TPMC0SC = 0x28;
//enable keyboard (encoder) interrupt
KBISC_KBIE = 0;
KBIES_KBEDG1 = 0;
KBIPE_KBIPE1 = 1;
KBISC_KBIMOD = 0;
KBISC_KBACK = 1;
KBISC_KBIE = 1;
//maximum delay value
//sets minimum sine frequency
delay_value = 5000;
//main program loop
TPMC0V = sine[index];
//store current encoder value
encoder_1 = PTAD & 0x06;
WAVEFORM SCREEN SHOTS
All the oscilloscope screen shots shown below are using the LM3900 Low Pass Filter.
The following waveform screen shots represent a comparison of the sine wave generator output and an accurate reference sine waveform. The blue trace (CH1) is the output from the digital sine wave generator and the red trace (CH2) is the output from a Heath Model SG1273 function generator (sine distortion < 1%).
The two waveforms were matched as closely as possible in both frequency and amplitude and offset by 180°. The waveforms were then added together using the algebraic ADD math function on a Tektronix DPO3014 oscilloscope. The result of the waveform addition is indicated by the cyan colored line located in the middle of each screen shot.
The flatness of the cyan colored line indicates that both wave forms closely match, indicating reasonable accuracy from the sine wave generator.
BLUE TRACE: DIGITAL SINE WAVE GENERATOR
RED TRACE: REFERENCE SINE WAVE
CYAN TRACE: ALGEBRAIC ADDITION OF BOTH WAVEFORMS