Controlling switching frequency

Hi everyone,

I am using this code to generate 3 SPWM signals to be fed to a 3 phase inverter:

I have two doubts:

  1. How can I control the switching frequency of the signal?
  2. What exactly is the const double refclk=31376.6; // measured output frequency

If any of you have any idea regarding this, please help :slight_smile:
Thanks in advance

dfreq = analogRead(0);
Set 'dfreq' to the desired frequency in Hz. The code expects a value from 0 to 1023 Hz.
From dfreq and refclk it calculates unsigned long tword_m. That's a 32-bit number added to the phase_accumulator every timer overflow. The top byte of the phase_accumulator is use to point into the sine table. The higher 'tword_m' is the faster you step through the sine wave and the higher the frequency.

It appears to be the actual measured clock frequency when a timer is set to 31250 Hz. Looks like their system clock was about 0.4% fast.

Look like the sine table is 256 samples so the highest frequency you can get that uses every sample is about 122 Hz. Higher frequencies will start skipping samples.

dfreq = analogRead(0);
Set 'dfreq' to the desired frequency in Hz. The code expects a value from 0 to 1023 Hz.
From dfreq and refclk it calculates unsigned long tword_m. That's a 32-bit number added to the phase_accumulator every timer overflow. The top byte of the phase_accumulator is use to point into the sine table. The higher 'tword_m' is the faster you step through the sine wave and the higher the frequency.

The output signal from this code may contain components of two frequencies- dfreq (for eg., 50 Hz) and a carrier frequency. If we apply a RC low pass filter, we'll get the 50 Hz signal. I wanted to know how to know the current carrier frequency and control it. Did you understand my doubt?

The carrier frequency is always the same: 31.250 kHz (or as close as your system clock allows).

So is it possible to change it to a different value? Because I have a requirement that carrier frequency needs to be controlled.

What different value do you want?

I need to set it to 10kHz or atleast 20kHz. 30kHz is a bit too much. Is it possible to do so?

You have two ways to change frequency: prescale to get lower frequencies and decreasing TOP to get higher frequencies. The smallest prescale factor is 8 so that would take you from a carrier of 31.250 kHz to 3.90625 kHz. To get up to a 10 kHz carrier from there you would need to set TOP to 199. You would then have to re-scale your Sine table for a maximum value of 199 instead of 255. If you still use 256 samples, the highest frequency sine wave you could generate without skipping samples is 39 Hz.

I am setting dfreq to 50Hz. Did you mean that if I use 256 samples, I cannot generate 50Hz, but only upto a maximum of 39 Hz?

No, I said: "If you still use 256 samples, the highest frequency sine wave you could generate without skipping samples is 39 Hz." At 10 kSPS you only have time to play 200 samples per 50 Hz cycle.

Are you SURE you want to use an artificially lowered sample rate?!? The native rate of 31.25 kHz is easier to filter out AND produces a smoother representation of the sine wave. In fact, if you wanted a fixed frequency output, like 50 Hz, you could get an even better sine wave with 625 samples instead of 256.

Yes I need to control the switching frequency. Can you please give a bit more details into your points to adjust it?
Which one should be prescaled to 8? Timer 1?

  // Timer1 Clock Prescaler to : 1
  sbi (TCCR1B, CS10);
  cbi (TCCR1B, CS11);
  cbi (TCCR1B, CS12);

And what exactly is TOP? How can I set it to 199? Is it done by rescaling he Sine table?

It has to be both. You re using two PWM channels from one timer and one PWM channel from the other so BOTH have to be running at 10 kHz.

// Look Up table of a single sine period divied up into 256 values.
// Max PWM value is 99
const uint8_t sine256[256] PROGMEM =
{
  50,  51, 52, 53, 54, 56, 57, 58,
  59, 60, 62, 63, 64, 65, 66, 67,
  69, 70, 71, 72, 73, 74, 75, 76,
  77, 78, 79, 80, 81, 82, 83, 84,
  85, 85, 86, 87, 88, 89, 89, 90,
  91, 91, 92, 93, 93, 94, 94, 95,
  95, 96, 96, 97, 97, 97, 98, 98,
  98, 98, 99, 99, 99, 99, 99, 99,
  99, 99, 99, 99, 99, 99, 98, 98,
  98, 98, 97, 97, 97, 96, 96, 96,
  95, 95, 94, 94, 93, 92, 92, 91,
  90, 90, 89, 88, 87, 87, 86, 85,
  84, 83, 82, 81, 81, 80, 79, 78,
  77, 76, 75, 73, 72, 71, 70, 69,
  68, 67, 66, 65, 63, 62, 61, 60,
  59, 57, 56, 55, 54, 53, 51, 50,
  49, 48, 46, 45, 44, 43, 42, 40,
  39, 38, 37, 36, 34, 33, 32, 31,
  30, 29, 28, 27, 26, 24, 23, 22,
  21, 20, 19, 18, 18, 17, 16, 15,
  14, 13, 12, 12, 11, 10, 9,  9,
  8,  7,  7,  6,  5,  5,  4,  4,
  3,  3,  3,  2,  2,  2,  1,  1,
  1,  1,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  1,  1,
  1,  1,  2,  2,  2,  3,  3,  4,
  4,  5,  5,  6,  6,  7,  8,  8,
  9,  10, 10, 11, 12, 13, 14, 14,
  15, 16, 17, 18, 19, 20, 21, 22,
  23, 24, 25, 26, 27, 28, 29, 30,
  32, 33, 34, 35, 36, 37, 39, 40,
  41, 42, 43, 45, 46, 47, 48, 50
};

// Output pins on UNO or Nano
int PWM1 = 3;  // Phase 1 = OC2B
int PWM2 = 9;  // Phase 2 = OC1A
int PWM3 = 10; // Phase 3 = OC1B

const int Phase2Offset = 256 / 3; // 120° phase shift
const int Phase3Offset = (256 * 2) / 3; // 240° phase shift

double OutputFrequency = 50.0;
const double CarrierFrequency = 10000.0;    // Carrier frequency in Hz

volatile unsigned long TuningValue;  // dds tuning word m, refer to DDS_calculator (from Martin Nawrath) for explination.


// Timer 1 setup
// WGM=8 Phase/Frequency Correct PWM with TOP in ICR1
// To get 10 kHz we need TOP = (16 MHz / 10 kHz / prescale / 2) - 1 = (1600/8/2) - 1 = 99
// Note: We have to use the same TOP on the 8-bit Timer2 so we can't use prescale=1 (799 won't fit in 8 bits)
void Setup_timer1(void)
{
  TCCR1B = 0;  // Stop the counter (clock select = 0)
  TCCR1A = 0;  // Clear the register
  TIMSK1 = 0;  // Disable all Timer1 interrupts

  ICR1 = 99; // 10 kHz with prescale = 8;

  TCCR1A |= _BV(COM1A1) | _BV(COM1B1); // Enable PWM on A and B

  TCCR1B |= _BV(WGM13);  // WGM = 8
  TCCR1B |= _BV(CS11);   // Prescale = 8
}

// Timer 2 setup
// WGM=5 Phase Correct PWM with TOP in OCR2A
// To get 10 kHz we need TOP = (16 MHz / 10 kHz / prescale / 2) - 1 = (1600/8/2) - 1 = 99
void Setup_timer2()
{
  TCCR2B = 0;  // Stop the counter (clock select = 0)
  TCCR2A = 0;  // Clear the register
  TIMSK2 = 0;  // Disable all Timer2 interrupts

  OCR2A = 99; // 10 kHz with prescale = 8;

  TCCR2A |= _BV(COM1B1); // Enable PWM on B only. (OCR2A holds TOP)

  TCCR2A |= _BV(WGM20);  // WGM = 5
  TCCR2B |= _BV(WGM22) | _BV(CS21);   //  WGM = 5, Prescale = 8

  TIMSK2 |= _BV(TOIE2);  // Enable Timer2 Overflow Interrupt
}

void setup()
{
  pinMode(PWM1, OUTPUT);      //sets the digital pin as output
  pinMode(PWM2, OUTPUT);      //sets the digital pin as output
  pinMode(PWM3, OUTPUT);      //sets the digital pin as output

  // The tuning value
  TuningValue = pow(2, 32) * OutputFrequency / CarrierFrequency; //calulate DDS new tuning word

  Setup_timer1();
  Setup_timer2();
}


// Timer2 Overflow Interrupt (10 KHz)
ISR(TIMER2_OVF_vect)
{
  static uint32_t phase_accumulator = 0;
  phase_accumulator += TuningValue;

  uint8_t current_count = phase_accumulator >> 24;   // use upper 8 bits of phase_accumulator as frequency information

  // read value fron ROM sine table and send to PWM1
  OCR2B = pgm_read_byte_near(sine256 + current_count);

  // read value fron ROM sine table (120° out of phase) and send to PWM2
  OCR1A = pgm_read_byte_near(sine256 + (uint8_t)(current_count + Phase2Offset));

  // read value from ROM sine table (240° out of phase) and send to PWM3
  OCR1B = pgm_read_byte_near(sine256 + (uint8_t)(current_count + Phase3Offset));
}

void loop() {}

This code runs on Arduino UNO right ? Or Arduino Mega?. Also does this code generate 3 SPWM signals which are 120 degrees phase shifted?

It's supposed to.

Thank you so much. The code worked perfectly. Can you please explain to me how to get calculate the values and set the prescalar for each frequency. I need to set the switching frequency as 15 kHz and 20 kHz for two other cases also.

1 Like

Hi,
What is the project that requires the PWM frequency to be controlled?

Tom... :grinning: :+1: :australia: :coffee:

Thank you. The code worked perfectly. Could you please either give a similar code for 15kHz and 20 kHz (instead of 10kHz), or please explain how to set it, because we need a code generating these frequencies also.

To get 15 kHz we need TOP = (16 MHz / 15 kHz / prescale / 2) - 1 = (1066.667/8/2) - 1 = 66 (Actually gets 14925.37+ kHz, about 0.5% slow)

To get 20 kHz we need TOP = (16 MHz / 20 kHz / prescale / 2) - 1 = (800/8/2) - 1 = 49

WARNING: Since you are changing the maximum count you are also changing the range of PWM and must have separate Sine tables for values up to 66 and values up to 49.

Thanks for the reply. But I did not understand how to calculate the values in the sine table. Could you please help me with that also?

See how the sine table is a 256-sample sine wave that goes from 0 to 99 instead of -1 to +1? Do that for 66 and 49.