This example demonstrates a method of generating more complicated analog signal using simple DAC. To be more precise, sine wave but the same method can be used to generate any type of wave.
In the previous example it was shown how to generate voltage level using PWM and low pass RC filter.
If you change voltage level you get not horizontal line but wave. The shape of the wave depends on the way you change voltage level.
To set output voltage from RC filter, RMS value of PWM must be changed what means output voltage is proportional to OCRx.
The wave on the above picture has only 32 levels to show the idea. To get nice smooth wave, we need to increase the number of levels for one sine period. In connection with RC filter this give us sine wave that looks like true analog not discrete wave.
In the example timer0 is used in Fast PWM mode to achieve the maximum frequency of the sine wave.
Wave table generation
For this example we use 256 samples. sin() function gives values from -1 to 1. PWM duty cycle is controller by OCR0 register, so we need values from 0 to 255.
Equation for the n sample is:
256 is the number of samples.
For practical implementation see
InitSinTable() function in the sample code.
We use the same type of filter as in previous example but with different parameters.
In the previous example frequency of the wave was 0Hz, that is we assumed output is constant in time. For sine wave this is not true and new values of C and R must be used.
This article is not about low pass filters, so filter theory is limited to minimum. In other words, just use R=10kΩ and C=0.1µF.
The most important parameter of the filter is cut-off frequency. For RC low pass filter it is:
which gives us fcutoff=159.2Hz. The filter passes frequency from 0 to 159Hz. Good filter should generate smooth waves in desired frequency of sine wave.
Sine wave frequency
We use timer0 in Fast PWM mode and prescaler 1 to generate PWM signal. For 8MHz clock:
Period is equal to 1/31250=0.000032s (32µs). As 256 samples is used to generate sine wave, period of the sine wave is 256×0.000032s=0.008192s. From that frequency is: 1/0.008192=122.07Hz.
In other words frequency of the resulting wave is always:
where Samples is the number of samples. Note, that this equation is true for any wave not only sine wave.
The same modified equation gives the answer what PWM frequency use to achieve specific analog wave frequency:
To change the frequency of the sine wave you can:
- Change PWM frequency (set prescaler or system clock or use different PWM mode with smooth frequency control)
- Change the number of samples
OC0 pin as output.
DDRB |= _BV(PB3);
Configure Fast PWM mode of timer0
TCCR0 |= _BV(WGM01) | _BV(WGM00); //mode 1, Fast PWM TCCR0 |= _BV(COM01); //Clear OC0 on compare match, set OC0 at BOTTOM TCCR0 |= _BV(CS00); //prescaler divider 1
The example code (pwm_sinusoid.zip) generates sine wave on OC0 pin. This signal is then filtered by low pass RC filter. To see the results an oscilloscope must be connected to the output of RC filter (see diagram at the beginning).
First timer0 is initialized (Fast PWM Mode). Then Timer/Counter0 Overflow Interrupt is enabled. The interrupt routine changes PWM frequency every time timer0 overflows. That automatically synchronize PWM frequency change and the end of PWM period. Fast PWM mode is not phase correct, so this simple solution prevents from changing PWM frequency in the middle of the period.
COMPUTE_SINE_WAVE is defined program compute sine table at startup. Playing a bit with
InitSinTable() function it is possible to generate any wave. By default program uses predefined sine table with 256 samples.
To increase or decrease the number of samples value of
WAVE_PROBES must be changed. In addition
COMPUTE_SINE_WAVE must be defined to force program to compute new samples.
Sine wave - output from the RC filter. Grid: 1ms/0.5V.
Two signals: PWM (yellow) and sine wave (blue). PWM duty cycle grows from left to right. Grid: 50µs/0.5V.