cancel
Showing results for 
Search instead for 
Did you mean: 

How to generate a sinusoidal PWM with variable frequency?

mftochetto
Associate II

I'm trying to generate a sinusoidal PWM on STM8, but it's for an equipmetn that the user will change the frequency of the output sine wave while the PWM is running. I was able to generate a sinusoidal pwm easily for a fixed frequency, using a table I calculated before starting the PWM.

But I'm not having success in changing the output frequency, because for that I need to recalculate the table and this is a slow process. I also tried to calculate the value every PWM cycle, but since I'm using 15kHz for the carrier wave, the calculation is too slow.

I've also thought about using two tables, so that one is used while the other is being recalculated, but this use more than the 1kB of microcontroller RAM.

Has anyone ever faced something like this and could indicate some way to be followed to solve the problem?

PS.: I'm using the STM8S003F3, and besides the PWM I need to read an ADC channel and communicate some bytes via UART, so I need to worry about leaving a little space for that too...

Regards,

3 REPLIES 3
NandanV
Associate III

Maybe consider a waveform generator to make your life easier.

mftochetto
Associate II

Thanks for your answer.

You are correct, it would be much easier. However, due to cost constraints, I will have to find a solution with the STM8.

I will work more on this to find a solution...

Wolfgang Pupp
Associate III

A solution might be to pre-calculate a single table, but instead of setting the duty-cycle to the next table-entry after every PWM period, you interpolate the duty cycle between two table entries (or even just pick the closest entry) depending on the desired frequency:

Example:

This completely untested, but it should give you an idea of what I mean:

#define TBL_SIZE 256
// holds a full sine wave
uint16_t sineTable[TBL_SIZE];
 
// for fixed frequency:
void onPwmTimerUpdate(void)
{
    static uint8_t index;
    dutyCycle = sineTable[index];
    ++index;
    ...
}
 
// For variable frequency:
void onPwmTimerUpdate(void)
{
    /* fIdx is a "fractional" index into the sineTable: 
     * the high byte corresponds to a table index
     * the low byte tells how close we are: 0 means right at the table entry indicated by the high byte, 
     *                                      0xff means right before the table entry coming AFTER
     */
    static uint16_t fIdx;
    const uint8_t idx0 = fIdx >> 8;
    const uint8_t idx1 = idx0 + 1;
    const uint8_t frac = fIdx & 0xff;
    dutyCycle = (sineTable[idx0] * (256 - frac) 
               + sineTable[idx1] * frac) >> 8;
    delta = (((uint32_t)sineFrequency << 16) / PWM_FREQ) >> 8;
    fIdx += delta;
}
 

 Note also that you only have to re-calculate your delta whenever you change the sine frequency, not on every PWM update.

You probably also only need half or quarter of the complete sineTable because of symmetry.

You can not change the buffer size arbitrarily because unsigned overflow is used to simplify wrapping.