Objective of this project, is to create simple and low cost function generator. Of course, it must have arduino interface (!) to set output frequency. I’ve been considering 4 option:
- AD98XX;
- XR2206;
- Wein bridge.
- OPA, with triangle – to – sine shaper.
First one, DDS high tech miracle AD98XX, was put aside due high price, and simplicity, there just wouldn’t be any fun in connecting couple wires -);
XR2206 had better chance, but I decided that minimum 10 V supply voltage is a serious disadvantage for arduino interfaced circuits. Boosting voltage up from 5 to 10 may be not very difficult task, same time interface between low (arduino) / high (IC) sides may present a technical problem, and increase cost substantially.
Moving back in time, next alternative is a Wein Bridge Sinewave (most important waveform for me) Oscillator. Well, it would be possible to create low-distortion generator, using circuits from 80-x with modern single power (+5V) rail-to-rail OPA. The technical complexity I only see, is that in order to cover wide frequency range ( 1 : 10 000 ), I would have to use a switchable bank of capacitors, (btw, same apply for XR2206). Other things, if I go with 2 digital pots as interface to digital world of arduino (Wein bridge require 2 identical resistors), I’d get frequency quantization issue plus non-linear quantization. Using Photo-Resistive (LDR) optocouplers in design, I resolve this problem, the same time it’s practically impossible to have 2 LDR with parameters close to 1% match. My understanding of Wein bridge oscillator theory, is that you can’t get distortion level you wish to have ( > 0.01 % ) w/o high precision resistors AND capacitors. More over, precise “switchable” capacitors would complicate everything even more, as any switch would introduce temperature dependent error. Probably, it may still be possible, but price tag would jump up well above option #1 (AD98XX) or #2 (XR2206).
And finally, I choose OPA based ( integrator / Schmitt trigger ) topology. Summary:
- Low price, about 2 $ in basic configuration (w/o user interface);
- Single power supply +5V (from arduino, no need for separate PS);
- Wide frequency range 30 Hz – 300.000 kHz;
- Sine / Triangle / Square outputs;
- Frequency Lock Loop (FLL) +/- 10 Hz (arduino or standalone AtMega328);
- Distortion (THD) less than 1 %.
Details on hardware side of the Function Generator itself, you may see from the schematic above. OPA is Microchip MCP6022, data sheet says: “wide bandwidth (10 MHz), low noise (8.7 nV/√Hz), low input offset voltage and low distortion (0.00053% THD+N)”. There are many faster OPA, with high slew-rate, but most of them ether require dual power supply +-5V (single more than 10V) or doesn’t have JFET at the input stage, which is important in this design, otherwise it’d not be possible to cover 1:10 000 band span w/o using switchable bank of capacitors, as I mention already. Using MCP6022 with relatively modest 7 V/usec slew-rate, upper frequency limit about 400 – 450 kHz ( with lower integrator’s capacitor value ) when sine magnitude 2.2 V p-p. To get higher, up to 1 MHz, would be necessary to decrease triangle amplitude to about 1V, and using quad OPA like MCP6024 build additional amplifier stage to drive triangle-to-sine shaper properly. Shaper isn’t optimized, and applying higher voltage with more diodes in the circuits ‘d help to lower distortion level. Only, I decided to put simplicity as factor number 1 for this design.
For “Instructables” – how to do a Photo-Resistive (LDR) optocoupler, look at the pictures:
In basic configuration circuit doesn’t require user interface = LCD + Mouse, which you can see on a photo, there is always an alternative like serial monitor, or CLI (command line interface) to change settings. If you decided to replicate Function Generator for your home -/- garage lab, than I’d recommend to start with a basic form / structure, of course, to simplify debugging.
Adjustment.
There is 1 k pot, to set reference voltage exactly at 1/2 of the power line voltage. You may use an oscilloscope, and simply turn pot till square wave reach 50 % duty cycle. Or you may use any available spectrum analyzer, or even your computer sound card and software version of the SA, and do adjustment based on even number harmonics magnitude. Another constant value resistor 3 k, which installed in series with diode’s shaper, may be trimmed slightly in order to minimize odd harmonics content below 1 %.
Software.
The essence of this project is Frequency Lock Loop, that implemented around TIMER1 external input counting feature and TIMER2 as a gate clock and PWM driver. Here is code:
ISR (TIMER2_OVF_vect) // Counter < 64 usec
{
ovf_tick++;
if( ovf_tick > SET_CLOCK )
{
ovf_tick = 0;
frq_tmr1 = TCNT1;
TCNT1 = 0;
int16_t diffr = set_freq – frq_tmr1;
int8_t ln2 = 0, sign = 1;
if ( diffr < 0 ) sign = -1;
diffr = abs(diffr);
while( diffr > acr_freq )
{
ln2++;
diffr >>= 1;
}
pwm_tmr2 += (sign * ln2);
uint16_t temp = pwm_tmr2 + 32768;
OCR2A = temp & 0xFF;
OCR2B = (temp >> 8) & 0XFF;
sec_tmr1++;
}
}
Timer1 is pre-configured to count pulses (square) on pin 5. Timer2 is running on internal clock 16 MHz w/o preselector, to have maximum resolution and highest PWM frequency possible 16 M / 256 / 2 = 31250 kHz. Counting number of overflow interrupts and compare this value to 3124, defines 0.1 seconds time interval, gate or window for Timer1 based frequency measurements. Timer1 (16-bits) never overflows, as at maximum 300 kHz it counts up to 30 000 only. This gives 10 Hz frequency resolution.
Why not 1 Hz, you may ask? To have 1 Hz resolution up to 300 kHz upper limits would require minimum 19 bits, (better 22 – 24 to accommodate high optocoupler’s LED non linearity ) of PWM resolution. Even, there is still Timer0 pin 6 PWM available, I decided not to do it, as it would require better than 5 / 1 000 000 = 5 uVolts (approximately) noise floor on power lines of the arduino, which is obviously, out of reach even I put uCPU to sleep. I’m not sure, if combining 2 PWM outputs of the Timer2 to get 16-bits resolution was such a good idea. Nevertheless, it’s working as a charm!
LDR has pretty high attenuation level at 31 kHz, so FM modulation index is quite low, that may be an issue otherwise, To practically eliminate modulation at all, PWM is filtered by electrolytic capacitors (100 uF) before it reaches LED.
Arduino UNO sketch: Function Generator with FLL.
To be continue….? Probably…








































