<< Click to Display Table of Contents >>

Navigation:  »No topics above this level«

Using Frequency Modulation to regulate a DC control voltage - FEZ Duino C#

Return to chapter overview

A PWM-driven R/C circuit can be used to produce a feedback-controlled DC voltage (0..~2V), which can be connected (usually via an opamp for high to low impedance) to a controlled circuit. The code was written in C# for the GHI Electronics FEZ Duino microcontroller. This been very useful for projects like intelligent power supplies, motor speed control, and anything that needs an analog voltage to control it, when you don't have enough D/A outputs. Very similar code can be used to control MOSFET switching. It's also very easy to isolate the digital signal via a fast optocoupler.

Once initialized, the process runs autonomously, controlled by a timer interrupt. Your program is free to go off and do other tasks, updating the setpoint voltage as desired, while the feedback-controlled voltage regulation goes on in the background.



A frequency with a fixed pulse width from a PWM output (so it's really FM not PWM) charges a capacitor through a resistor (e.g. 470K + 220nF). The voltage on the capacitor, or the final output voltage, is fed back via a (16-bit) analog input and is used to regulate the frequency to produce the desired SetPoint voltage.


With calibration, this method is very accurate, better than +-3mV assuming normal ADC noise on the feedback input. Ripple is higher at lower voltages, due to the lower FM charging frequency. It works best for fairly slow processes; the Vout slew rate for a typical 'processFrequency' of 50Hz is about 300 milliseconds-per-volt. I used it for controlling the output of an intelligent variable-voltage power supply (0..50V @ 1A), where it's ok to ramp the voltage up/down at the speed of a manually-operated potentiometer, and the ripple is filtered out further down the line in the control/feedback circuit.

You can reduce ripple by increasing the capacitance, but this slows down the response time. For example, 470K + 1uF (electrolytic) capacitor has a slew rate of 5 seconds-per-volt, with just +-1mV of ripple. See the new "Improved Ripple Reduction" section below for a better way to do it.

The microcontroller output is 3.3V, but due to the voltage-divider effect, the max. output voltage 'Vout' is just over 2V for 470K + 220nF at 20kHz. Vout is typically connected to the ultra-high- impedance input of an opamp, which amplifies the voltage to give the range you want, and the opamp's output controls the process. Vout should only be connected to a high impedance load! A low impedance load will discharge the capacitor too fast and cause additional ripple.

Instead of a fixed frequency and a varying pulse width (PWM), the pulse width is fixed (say 50us) and the frequency is varied (FM). For example, 0..~20KHz for a 50uS pulse width. FM with a fixed pulse width provides a much smoother output voltage, because wider pulse widths (PWM) produce more ripple. The 'pulseWidth' and 'riseAndFallTime' may need to be increased if the R/C circuit is not connected directly to the microcontroller PWM output, e.g. if it's connected through a slow opto-coupler.

At a SetPoint of 0.0V, the frequency output is turned off, so the output is always 'low'.

It's best to get the regulating feedback voltage from the final output voltage. This can be up to 3V, the full range of the 16-bit analog input. If the output range is larger than 0..3V, then reduce it to 0..3V with a simple voltage divider. The calibration values will adjust the range for the control loop. Use 3V as the maximum, not 3.3V, to give it a 10% headroom for resistor tolerances and overshoot. A small capacitor (say 10nF) across the grounded resistor of the voltage-divider pair will reduce noise on the feedback voltage.

'Overshoot' can be prevented by reducing the 'processFrequency', e.g. try 20Hz, but this reduces the response time. Do not run the process faster than necessary, because it may overload the microcontroller.

NOTE! On the FEZ Duino, changing the frequency affects ALL channels on the PWM controller, so choose a PWM controller which has just one channel and supports the maximum frequency of 20kHz.


Improved Ripple Reduction

The Art Of Electronics, X Chapters, Section 4x.25 (Horowitz and Hill, Cambridge University Press), shows a brilliant way of improving the PWM's ripple. Using a simple "ripple canceller": an inverter logic gate (e.g. 74HC04) or a transistor inverter, and a resistor and capacitor in series, connected in parallel with the PWM resistor as in the revised circuit below. The resistor and capacitor have the same values as the others, e.g. 470K and 220nF.



Linear calibration can be used to get a more accurate feedback voltage reading, and to rescale the feedback voltage according to the circuit and voltage divider. Calibration values are taken at SetPoints of (say) 0.2V (200mV) and 2.0V. It cannot be calibrated at 0V because there is no frequency output at 0V. The feedback voltage range may not be the same as the SetPoint voltage range. For example, at a SetPoint of 2.0V, the feedback voltage might be 3.0V (to give full 16-bit analog input range), and will depend on the accuracy of your voltage divider. ADC readings are also relative to the microcontroller's (possibly noisy) supply voltage (3.3V +-100mV). It is possible to add a precision voltage reference to the board, the FEZ Duino board has a jumper (R13) which could be replaced by something like an LM4040-3.3 Voltage Reference connected to the 5V rail.

Calibration procedure
Set the 'calibrationValueLow' and 'calibrationValueHigh' parameters to 0.0 in the constructor call. Set the SetPoint to 2.0V and run the program. When the output is stable, measure the freedback voltage using an accurate voltmeter. This is the 'calibrationValueHigh' value. Set the SetPoint to 0.2V and measure the feedback voltage again, this is the 'calibrationValueLow' value. Varying one has a small effect on the other, so you may need to do this a couple of times to get the optimum values. Reducing the value increases the output voltage. Use these values either in the constructor call, or by setting the properties.

If you need temperature compensation, the calibration values can be updated at runtime by tweaking the calibration values according to the measured temperature of the electronics.


Usage Example

AdcController adcController = AdcController.FromName(SC20100.Adc.Controller1.Id);
PwmController pwmController = PwmController.FromName(SC20100.Timer.Pwm.Controller2.Id);

FrequencyToVoltageController f2v = new();

    adcController, SC20100.Adc.Controller1.PA4,
    pwmController, SC20100.Timer.Pwm.Controller2.PA3,
    50,             // feedback loop processing frequency in Hz
    0.067,          // calibration constant at 200mV setpoint
    1.974);         // calibration constant at 2.0V setpoint

f2v.SetPoint = 1.0;
// ...

The C# code runs on a FEZ Duino microcontroller running TinyCLR, see https://www.ghielectronics.com/sitcore/sbc/

  C# source code   [Click to expand]