cancel
Showing results for 
Search instead for 
Did you mean: 

Sine wave interpolation for pwm generation

con3
Senior

Hey everyone,

I'm still busy scoping out how to do this. I've been looking int methods to generate sine waves and so far it looks like some of the methods use lookup tables or trig functions from cmsis.

Although I don't think this would be very ideal, as I essentially want to generate a sine sweep. I'd like to generate a small amplitude (maybe 0.1 or 0.05V) sine wave with frequency varying from 500 Hz to 90 kHz.

I also want to let this signal have a DC offset of varying from 0.1 o 0.9V

I don't think generating this would be to hard if it can just be fed to the dac, although I have no idea how to accurately and efficiently generate the sine wave.

Any help will really be appreciated!

5 REPLIES 5
Ozone
Lead

Not sure what you want, PWM and DAC are different things.

With DAC output, you could generate the value on-the-fly via periodic timer interrupt, and write it to the DAC. For sweep, i.e. continuous frequency change, you probably need to.

Adding a DC offset and scaling down the output voltage is no problem.

However, check the settling times and output impedance of the DAC, and the intrinsic offset with enabled output buffer.

For PWM, you could apply the same on-the-fly generated amplitude value to the duty cycle of a PWM (timer unit).

> I've been looking int methods to generate sine waves and so far it looks like some of the methods use lookup tables or trig functions from cmsis.

I would suggest to use a value table instead of trigonometric functions. The FPU has no trigonometry support.

Using this method (and DAC output) I managed 100kHz (2us DAC update cycle) on a F103, with sufficient headroom for other tasks.

Hey @Ozone​ ,

Thank you for the reply.

Yeah this explanation I gave is horrible.

I'm going to try and do it a bit better. My end goal is to generate a pwm signal.

The pwm generator usually has some dc reference value that it compares to a triangular waveform to produce the variable duty cycle. I want to generate this dc reference, which will be in the range of 0.2 to 0.9V.

But Essentially I don't just want to feed this 0.2 - 0.9 V reference to the pwm , instead I want to interpolate a small sinusoidal signal on it. So it'll look like a small sinusoid that has a variable offset of 0.2 - 0.9V.

This sine wave will have an amplitude of around 0.05 -> 0.1 V with a frequency which will vary from 500 Hz to about 90 kHZ.

I then want to send this interpolated signal through to a pwm generator to generate the pwm signal.

I'm not sure if that was a better explanation but I really hope so?

> Not sure what you want, PWM and DAC are different things.

Yes, I want to use the DAC to generate the varying frequency interpolated sinusoid and send this to the PWM generator.

> With DAC output, you could generate the value on-the-fly via periodic timer interrupt, and write it to the DAC. For sweep, i.e. continuous frequency change, you probably need to.

I'll need to look into this, you also mention you used a lookup table, although I don't think varying the frequency is possible with a lookup table as far as I've seen?

Thank you for the help!

I think I understood.

For the STM32, carefully study the datasheet. Analog peripherals in general-purpose MCUs are always a compromise, and so is the DAC.

Especially the settling times and the output impedance are to consider. I didn't check the F7 DAC implementation, but assume it is similar to the F1 .. F4 one's.

ST's DAC use to have two output options, unbuffered (1MOhm) or buffered (15kOhm). However, buffered also used to mean up to 200mV offset. This is quite much, compared to your signals. Consider a larger output, and scale it down externally. Or perhaps a cheap serial DAC.

> I'll need to look into this, you also mention you used a lookup table, although I don't think varying the frequency is possible with a lookup table as far as I've seen?

A lookup table just defines a number points (values) of one sinus period. Actually, you need not more then a half or a quarter period, because of symmetries(sin(x) = -sin(x+PI)) . Only the index calculation gets slightly more elaborate .

The output frequency determines the "speed" you cycle trough the lookup table. Instead of using the trigonometric function, you just access the table, with the argument scaled to (Nr_Of_Elements / 2*PI).

Hi @Ozone​ ,

Really appreciate all the help thus far!

I think based on the internal DAC's specs I'm going to need to use an external DAC. Most of these operate with spi, so I'm not entirely sure how to do the following:

> The output frequency determines the "speed" you cycle trough the lookup table. Instead of using the trigonometric function, you just access the table, with the argument scaled to (Nr_Of_Elements / 2*PI).

The lookup table will just be an array that i cycle through, so do I just send the same value to the DAC, if the external dac say updates at 100 kHz, and I want to generate a 100hz sine. Say the lookup table has 65536 values for a 100 hz I would need to cycle through all the values in a period of 1/100.

So I need to send a new element to the dac every (1/100) / 65536 = ‭0,00000152587890625‬ seconds.

I'm just not sure how I would control this step or if there is a different way to do this?

I dont think I completely understood this -> (Nr_Of_Elements / 2*PI)

Thanks for all the help thus far! I really appreciate it !

> The lookup table will just be an array that i cycle through, so do I just send the same value to the DAC, if the external dac say updates at 100 kHz, and I want to generate a 100hz sine. Say the lookup table has 65536 values for a 100 hz I would need to cycle through all the values in a period of 1/100.

I assumed a constant DAC output period/frequency - which seems to apply.

How to access the lookup table:

When generating a sinus signal, you are supposed to know the current phase angle and amplitude (relative in the range 0 ... 2*PI) of the signal, based on the timing.

You then only apply that "phase angle ratio" as lookup table index. The number of elements (defining accuracy) is up to you. You could also interpolate between two table entries, costing you some extra cycles.

Pulled out some example code from a project of mine. Not complete, but I hope it is helpful

#define SMPL_FREQ             1000
 
#define SMPL_FREQ_F           (1000.0f)    /* sampling frequency */
 
#define SMPL_PERIOD_F         (0.001f)     /* sampling period */
 
#define DEFAULT_FREQUENCY  7.8f
 
 
    i = 0;
 
    fVal = DEFAULT_FREQUENCY;  // a float value
 
    fPeriod = 1.0f / fVal;
 
    iPeriod  = (uint32_t) (fPeriod / smplPeriod);
 
...
 
    // called once in each cycle to generate teh current output value;
 
    fVal = getNextDacValue (i, iPeriod, fPeriod);
 
...
 
    if (++i >= SMPL_FREQ)
 
        i = 0;
 
 
/* calculate next DAC output value
 
 * Parameter:
 
 *  - signalperiod = period of frequence to create
 
 *  - periodlen   = number of values per period of the sampling (output) frequency
 
 *  - index       = 'Index', i.e. phase of the signal frequency
 
 */
 
static float getNextDacValue (int index, int periodlen, float signalperiod)
 
{
 
    float value;
 
    int    windex;
 
 
    /* y(t) = sin(2xPi x t/T) */
 
    windex = index % periodlen;
 
    value = sinf (PI_X_2 * (windex * smplPeriod / signalperiod));
 
    return (value);
 
}