Skip to main content
formatted code
Source Link
jsotola
  • 1.6k
  • 2
  • 13
  • 22

Any input on this is highly appreciated! :)

// ---------------------------------------------------------------------------
// 3-Channel synchronized PWM (software using Timer1) for high freq. PWM (10kHz)
// + State machine for custom actuation frequency (0.25Hz) (case1 → case2 → case3)
// ---------------------------------------------------------------------------

// choose desired HV output: HV = -0.0104*D^2 + 0.5791*D + 1.3643
// --> D_BUCK = (0.5791 - sqrt(0.5791*0.5791 - 4 * 0.0104 * (HV - 1.3643))) / (2 * 0.0104)
const uint16_t HV = 5; // [kV]
const uint16_t D_HV = (0.5791 - sqrt(0.5791 * 0.5791 - 4 * 0.0104 * (HV - 1.3643))) / (2 * 0.0104);

// prescaler*(1+TOP)=f_arduinoNano/f_PWM=16MHz/10kHz   and TOP=resolution
const uint16_t prescaler = 1; 
const uint16_t fPWM = 10000; // [Hz]
const uint16_t TOP = 16000000 / fPWM / prescaler - 1; // 1599 for 10 kHz with prescaler=1

volatile uint16_t dutyBUCK = 0;
volatile uint16_t dutyOC1 = 0;
volatile uint16_t dutyOC2 = 0;

// choose PWM 3 pins:
const uint8_t BUCK_pin = 5;
const uint8_t OC1_pin = 6;
const uint8_t OC2_pin = 7;

// Case switching variables
uint8_t caseNum = 1; // start in idle
unsigned long lastSwitch = 0;
const unsigned long switchInterval = 2000; // [ms]    4s = 0.25 Hz --> Charge&dis- same duration --> 4s/2

void setup() {
  pinMode(BUCK_pin, OUTPUT);
  pinMode(OC1_pin, OUTPUT);
  pinMode(OC2_pin, OUTPUT);

  // Timer1 configuration
  cli();   // disable interrupts

  TCCR1A = 0;
  TCCR1B = 0;

  // fast mode of Timer1: WGM13 1 WGM12 1 WGM11 1 WGM10 0
  TCCR1A |= (1 << WGM11); // contains WGM10 and WGM11
  TCCR1B |= (1 << WGM13) | (1 << WGM12); // contains WGM12 and WGM13

  ICR1 = TOP; // set TOP (100% duty cycle)

  TIMSK1 |= (1 << TOIE1);  // overflow interrupt
  TIMSK1 |= (1 << OCIE1A); // compare A
  TIMSK1 |= (1 << OCIE1B); // compare B
  TIMSK1 |= (1 << OCIE1C); // compare C

  TCCR1B |= (1 << CS10); // set prescaler=1 for 10 kHz

  sei();   // enable interrupts (TIMSK1) once I finished assigning values to them
}

void loop() {

  unsigned long now = millis();

  // Switch cases every 2 seconds
  if (now - lastSwitch >= switchInterval) {
    lastSwitch = now;

    caseNum++;
    if (caseNum > 3) caseNum = 1;

    switch (caseNum) {

      // ------------------- IDLE ------------------------
      case 1: // BUCK=OFF, OC1=OFF, OC2=OFF
        dutyBUCK = 0;//D_HV;
        dutyOC1 = 0;
        dutyOC2 = 0;
        break;

      // ------------------- CHARGING ------------------------
      case 2: // BUCK=ON, OC1=ON, OC2=OFF
        dutyBUCK = D_HV;   
        dutyOC1 = TOP; // 100% duty
        dutyOC2 = 0;
        break;

      // ------------------- DISCHARGING ------------------------
      case 3: // BUCK=ON, OC1=OFF, OC2=ON
        dutyBUCK = 0;//D_HV;
        dutyOC1 = 0;
        dutyOC2 = TOP;
        break;
    }
  }
}


// ---------------------------------------------------------------------------
// check if On time for Duty cycles are over for each MOSFET at 10kHz
// ---------------------------------------------------------------------------

// Timer1 overflow → start of new PWM cycle
ISR(TIMER1_OVF_vect) {
  digitalWrite(BUCK_pin, HIGH); // turn all HIGH at beginning
  digitalWrite(OC1_pin, HIGH);
  digitalWrite(OC2_pin, HIGH);

  OCR1A = dutyBUCK; // when to turn BUCK off
  OCR1B = dutyOC1;  // when to turn OC1 off
  OCR1C = dutyOC2;  // when to turn OC2 off
}

// Compare Match A → turn BUCK off
ISR(TIMER1_COMPA_vect) {
  if (dutyBUCK > 0) digitalWrite(BUCK_pin, LOW);
}

// Compare Match B → turn OC1 off
ISR(TIMER1_COMPB_vect) {
  if (dutyOC1 > 0) digitalWrite(OC1_pin, LOW);
}

// Compare Match C → turn OC2 off
ISR(TIMER1_COMPC_vect) {
  if (dutyOC2 > 0) digitalWrite(OC2_pin, LOW);
}
// ---------------------------------------------------------------------------
// 3-Channel synchronized PWM (software using Timer1) for high freq. PWM (10kHz)
// + State machine for custom actuation frequency (0.25Hz) (case1 → case2 → case3)
// ---------------------------------------------------------------------------

// choose desired HV output: HV = -0.0104*D^2 + 0.5791*D + 1.3643
// --> D_BUCK = (0.5791 - sqrt(0.5791*0.5791 - 4 * 0.0104 * (HV - 1.3643))) / (2 * 0.0104)
const uint16_t HV = 5; // [kV]
const uint16_t D_HV = (0.5791 - sqrt(0.5791 * 0.5791 - 4 * 0.0104 * (HV - 1.3643))) / (2 * 0.0104);

// prescaler*(1+TOP)=f_arduinoNano/f_PWM=16MHz/10kHz   and TOP=resolution
const uint16_t prescaler = 1; 
const uint16_t fPWM = 10000; // [Hz]
const uint16_t TOP = 16000000 / fPWM / prescaler - 1; // 1599 for 10 kHz with prescaler=1

volatile uint16_t dutyBUCK = 0;
volatile uint16_t dutyOC1 = 0;
volatile uint16_t dutyOC2 = 0;

// choose PWM 3 pins:
const uint8_t BUCK_pin = 5;
const uint8_t OC1_pin = 6;
const uint8_t OC2_pin = 7;

// Case switching variables
uint8_t caseNum = 1; // start in idle
unsigned long lastSwitch = 0;
const unsigned long switchInterval = 2000; // [ms]    4s = 0.25 Hz --> Charge&dis- same duration --> 4s/2

void setup() {
  pinMode(BUCK_pin, OUTPUT);
  pinMode(OC1_pin, OUTPUT);
  pinMode(OC2_pin, OUTPUT);

  // Timer1 configuration
  cli();   // disable interrupts

  TCCR1A = 0;
  TCCR1B = 0;

  // fast mode of Timer1: WGM13 1 WGM12 1 WGM11 1 WGM10 0
  TCCR1A |= (1 << WGM11); // contains WGM10 and WGM11
  TCCR1B |= (1 << WGM13) | (1 << WGM12); // contains WGM12 and WGM13

  ICR1 = TOP; // set TOP (100% duty cycle)

  TIMSK1 |= (1 << TOIE1);  // overflow interrupt
  TIMSK1 |= (1 << OCIE1A); // compare A
  TIMSK1 |= (1 << OCIE1B); // compare B
  TIMSK1 |= (1 << OCIE1C); // compare C

  TCCR1B |= (1 << CS10); // set prescaler=1 for 10 kHz

  sei();   // enable interrupts (TIMSK1) once I finished assigning values to them
}

void loop() {

  unsigned long now = millis();

  // Switch cases every 2 seconds
  if (now - lastSwitch >= switchInterval) {
    lastSwitch = now;

    caseNum++;
    if (caseNum > 3) caseNum = 1;

    switch (caseNum) {

      // ------------------- IDLE ------------------------
      case 1: // BUCK=OFF, OC1=OFF, OC2=OFF
        dutyBUCK = 0;//D_HV;
        dutyOC1 = 0;
        dutyOC2 = 0;
        break;

      // ------------------- CHARGING ------------------------
      case 2: // BUCK=ON, OC1=ON, OC2=OFF
        dutyBUCK = D_HV;   
        dutyOC1 = TOP; // 100% duty
        dutyOC2 = 0;
        break;

      // ------------------- DISCHARGING ------------------------
      case 3: // BUCK=ON, OC1=OFF, OC2=ON
        dutyBUCK = 0;//D_HV;
        dutyOC1 = 0;
        dutyOC2 = TOP;
        break;
    }
  }
}


// ---------------------------------------------------------------------------
// check if On time for Duty cycles are over for each MOSFET at 10kHz
// ---------------------------------------------------------------------------

// Timer1 overflow → start of new PWM cycle
ISR(TIMER1_OVF_vect) {
  digitalWrite(BUCK_pin, HIGH); // turn all HIGH at beginning
  digitalWrite(OC1_pin, HIGH);
  digitalWrite(OC2_pin, HIGH);

  OCR1A = dutyBUCK; // when to turn BUCK off
  OCR1B = dutyOC1;  // when to turn OC1 off
  OCR1C = dutyOC2;  // when to turn OC2 off
}

// Compare Match A → turn BUCK off
ISR(TIMER1_COMPA_vect) {
  if (dutyBUCK > 0) digitalWrite(BUCK_pin, LOW);
}

// Compare Match B → turn OC1 off
ISR(TIMER1_COMPB_vect) {
  if (dutyOC1 > 0) digitalWrite(OC1_pin, LOW);
}

// Compare Match C → turn OC2 off
ISR(TIMER1_COMPC_vect) {
  if (dutyOC2 > 0) digitalWrite(OC2_pin, LOW);
}

Any input on this is highly appreciated! :)

// ---------------------------------------------------------------------------
// 3-Channel synchronized PWM (software using Timer1) for high freq. PWM (10kHz)
// + State machine for custom actuation frequency (0.25Hz) (case1 → case2 → case3)
// ---------------------------------------------------------------------------

// choose desired HV output: HV = -0.0104*D^2 + 0.5791*D + 1.3643
// --> D_BUCK = (0.5791 - sqrt(0.5791*0.5791 - 4 * 0.0104 * (HV - 1.3643))) / (2 * 0.0104)
const uint16_t HV = 5; // [kV]
const uint16_t D_HV = (0.5791 - sqrt(0.5791 * 0.5791 - 4 * 0.0104 * (HV - 1.3643))) / (2 * 0.0104);

// prescaler*(1+TOP)=f_arduinoNano/f_PWM=16MHz/10kHz   and TOP=resolution
const uint16_t prescaler = 1; 
const uint16_t fPWM = 10000; // [Hz]
const uint16_t TOP = 16000000 / fPWM / prescaler - 1; // 1599 for 10 kHz with prescaler=1

volatile uint16_t dutyBUCK = 0;
volatile uint16_t dutyOC1 = 0;
volatile uint16_t dutyOC2 = 0;

// choose PWM 3 pins:
const uint8_t BUCK_pin = 5;
const uint8_t OC1_pin = 6;
const uint8_t OC2_pin = 7;

// Case switching variables
uint8_t caseNum = 1; // start in idle
unsigned long lastSwitch = 0;
const unsigned long switchInterval = 2000; // [ms]    4s = 0.25 Hz --> Charge&dis- same duration --> 4s/2

void setup() {
  pinMode(BUCK_pin, OUTPUT);
  pinMode(OC1_pin, OUTPUT);
  pinMode(OC2_pin, OUTPUT);

  // Timer1 configuration
  cli();   // disable interrupts

  TCCR1A = 0;
  TCCR1B = 0;

  // fast mode of Timer1: WGM13 1 WGM12 1 WGM11 1 WGM10 0
  TCCR1A |= (1 << WGM11); // contains WGM10 and WGM11
  TCCR1B |= (1 << WGM13) | (1 << WGM12); // contains WGM12 and WGM13

  ICR1 = TOP; // set TOP (100% duty cycle)

  TIMSK1 |= (1 << TOIE1);  // overflow interrupt
  TIMSK1 |= (1 << OCIE1A); // compare A
  TIMSK1 |= (1 << OCIE1B); // compare B
  TIMSK1 |= (1 << OCIE1C); // compare C

  TCCR1B |= (1 << CS10); // set prescaler=1 for 10 kHz

  sei();   // enable interrupts (TIMSK1) once I finished assigning values to them
}

void loop() {

  unsigned long now = millis();

  // Switch cases every 2 seconds
  if (now - lastSwitch >= switchInterval) {
    lastSwitch = now;

    caseNum++;
    if (caseNum > 3) caseNum = 1;

    switch (caseNum) {

      // ------------------- IDLE ------------------------
      case 1: // BUCK=OFF, OC1=OFF, OC2=OFF
        dutyBUCK = 0;//D_HV;
        dutyOC1 = 0;
        dutyOC2 = 0;
        break;

      // ------------------- CHARGING ------------------------
      case 2: // BUCK=ON, OC1=ON, OC2=OFF
        dutyBUCK = D_HV;   
        dutyOC1 = TOP; // 100% duty
        dutyOC2 = 0;
        break;

      // ------------------- DISCHARGING ------------------------
      case 3: // BUCK=ON, OC1=OFF, OC2=ON
        dutyBUCK = 0;//D_HV;
        dutyOC1 = 0;
        dutyOC2 = TOP;
        break;
    }
  }
}


// ---------------------------------------------------------------------------
// check if On time for Duty cycles are over for each MOSFET at 10kHz
// ---------------------------------------------------------------------------

// Timer1 overflow → start of new PWM cycle
ISR(TIMER1_OVF_vect) {
  digitalWrite(BUCK_pin, HIGH); // turn all HIGH at beginning
  digitalWrite(OC1_pin, HIGH);
  digitalWrite(OC2_pin, HIGH);

  OCR1A = dutyBUCK; // when to turn BUCK off
  OCR1B = dutyOC1;  // when to turn OC1 off
  OCR1C = dutyOC2;  // when to turn OC2 off
}

// Compare Match A → turn BUCK off
ISR(TIMER1_COMPA_vect) {
  if (dutyBUCK > 0) digitalWrite(BUCK_pin, LOW);
}

// Compare Match B → turn OC1 off
ISR(TIMER1_COMPB_vect) {
  if (dutyOC1 > 0) digitalWrite(OC1_pin, LOW);
}

// Compare Match C → turn OC2 off
ISR(TIMER1_COMPC_vect) {
  if (dutyOC2 > 0) digitalWrite(OC2_pin, LOW);
}
// ---------------------------------------------------------------------------
// 3-Channel synchronized PWM (software using Timer1) for high freq. PWM (10kHz)
// + State machine for custom actuation frequency (0.25Hz) (case1 → case2 → case3)
// ---------------------------------------------------------------------------

// choose desired HV output: HV = -0.0104*D^2 + 0.5791*D + 1.3643
// --> D_BUCK = (0.5791 - sqrt(0.5791*0.5791 - 4 * 0.0104 * (HV - 1.3643))) / (2 * 0.0104)
const uint16_t HV = 5; // [kV]
const uint16_t D_HV = (0.5791 - sqrt(0.5791 * 0.5791 - 4 * 0.0104 * (HV - 1.3643))) / (2 * 0.0104);

// prescaler*(1+TOP)=f_arduinoNano/f_PWM=16MHz/10kHz   and TOP=resolution
const uint16_t prescaler = 1; 
const uint16_t fPWM = 10000; // [Hz]
const uint16_t TOP = 16000000 / fPWM / prescaler - 1; // 1599 for 10 kHz with prescaler=1

volatile uint16_t dutyBUCK = 0;
volatile uint16_t dutyOC1 = 0;
volatile uint16_t dutyOC2 = 0;

// choose PWM 3 pins:
const uint8_t BUCK_pin = 5;
const uint8_t OC1_pin = 6;
const uint8_t OC2_pin = 7;

// Case switching variables
uint8_t caseNum = 1; // start in idle
unsigned long lastSwitch = 0;
const unsigned long switchInterval = 2000; // [ms]    4s = 0.25 Hz --> Charge&dis- same duration --> 4s/2

void setup() {
  pinMode(BUCK_pin, OUTPUT);
  pinMode(OC1_pin, OUTPUT);
  pinMode(OC2_pin, OUTPUT);

  // Timer1 configuration
  cli();   // disable interrupts

  TCCR1A = 0;
  TCCR1B = 0;

  // fast mode of Timer1: WGM13 1 WGM12 1 WGM11 1 WGM10 0
  TCCR1A |= (1 << WGM11); // contains WGM10 and WGM11
  TCCR1B |= (1 << WGM13) | (1 << WGM12); // contains WGM12 and WGM13

  ICR1 = TOP; // set TOP (100% duty cycle)

  TIMSK1 |= (1 << TOIE1);  // overflow interrupt
  TIMSK1 |= (1 << OCIE1A); // compare A
  TIMSK1 |= (1 << OCIE1B); // compare B
  TIMSK1 |= (1 << OCIE1C); // compare C

  TCCR1B |= (1 << CS10); // set prescaler=1 for 10 kHz

  sei();   // enable interrupts (TIMSK1) once I finished assigning values to them
}

void loop() {

  unsigned long now = millis();

  // Switch cases every 2 seconds
  if (now - lastSwitch >= switchInterval) {
    lastSwitch = now;

    caseNum++;
    if (caseNum > 3) caseNum = 1;

    switch (caseNum) {

      // ------------------- IDLE ------------------------
      case 1: // BUCK=OFF, OC1=OFF, OC2=OFF
        dutyBUCK = 0;//D_HV;
        dutyOC1 = 0;
        dutyOC2 = 0;
        break;

      // ------------------- CHARGING ------------------------
      case 2: // BUCK=ON, OC1=ON, OC2=OFF
        dutyBUCK = D_HV;   
        dutyOC1 = TOP; // 100% duty
        dutyOC2 = 0;
        break;

      // ------------------- DISCHARGING ------------------------
      case 3: // BUCK=ON, OC1=OFF, OC2=ON
        dutyBUCK = 0;//D_HV;
        dutyOC1 = 0;
        dutyOC2 = TOP;
        break;
    }
  }
}


// ---------------------------------------------------------------------------
// check if On time for Duty cycles are over for each MOSFET at 10kHz
// ---------------------------------------------------------------------------

// Timer1 overflow → start of new PWM cycle
ISR(TIMER1_OVF_vect) {
  digitalWrite(BUCK_pin, HIGH); // turn all HIGH at beginning
  digitalWrite(OC1_pin, HIGH);
  digitalWrite(OC2_pin, HIGH);

  OCR1A = dutyBUCK; // when to turn BUCK off
  OCR1B = dutyOC1;  // when to turn OC1 off
  OCR1C = dutyOC2;  // when to turn OC2 off
}

// Compare Match A → turn BUCK off
ISR(TIMER1_COMPA_vect) {
  if (dutyBUCK > 0) digitalWrite(BUCK_pin, LOW);
}

// Compare Match B → turn OC1 off
ISR(TIMER1_COMPB_vect) {
  if (dutyOC1 > 0) digitalWrite(OC1_pin, LOW);
}

// Compare Match C → turn OC2 off
ISR(TIMER1_COMPC_vect) {
  if (dutyOC2 > 0) digitalWrite(OC2_pin, LOW);
}
Source Link

Use of 3 synchronized PWM pins on Arduino Nano

I try to use three PWM pins on an Arduino Nano, each with 10kHz. I know that there are timer0, timer1 and timer3 but I wonder if I can combine them while ensuring all three PWM pins to be in sync and working at 10kHz. Additionally, I want to vary their duty cycle to have 3 cases.

Case 1: pin1 = OFF, pin2 = OFF, pin3 = OFF

Case 2: pin1 = ON, pin2 = ON, pin3 = OFF

Case 3: pin1 = ON, pin2 = OFF, pin3 = ON

The cases are changed at a frequency (f_act) of e.g. 0.25Hz

My code tried to just add a third PWM pin to the timer, which obviously does not work but illustrates my intention.

Any input on this is highly appreciated! :)

// ---------------------------------------------------------------------------
// 3-Channel synchronized PWM (software using Timer1) for high freq. PWM (10kHz)
// + State machine for custom actuation frequency (0.25Hz) (case1 → case2 → case3)
// ---------------------------------------------------------------------------

// choose desired HV output: HV = -0.0104*D^2 + 0.5791*D + 1.3643
// --> D_BUCK = (0.5791 - sqrt(0.5791*0.5791 - 4 * 0.0104 * (HV - 1.3643))) / (2 * 0.0104)
const uint16_t HV = 5; // [kV]
const uint16_t D_HV = (0.5791 - sqrt(0.5791 * 0.5791 - 4 * 0.0104 * (HV - 1.3643))) / (2 * 0.0104);

// prescaler*(1+TOP)=f_arduinoNano/f_PWM=16MHz/10kHz   and TOP=resolution
const uint16_t prescaler = 1; 
const uint16_t fPWM = 10000; // [Hz]
const uint16_t TOP = 16000000 / fPWM / prescaler - 1; // 1599 for 10 kHz with prescaler=1

volatile uint16_t dutyBUCK = 0;
volatile uint16_t dutyOC1 = 0;
volatile uint16_t dutyOC2 = 0;

// choose PWM 3 pins:
const uint8_t BUCK_pin = 5;
const uint8_t OC1_pin = 6;
const uint8_t OC2_pin = 7;

// Case switching variables
uint8_t caseNum = 1; // start in idle
unsigned long lastSwitch = 0;
const unsigned long switchInterval = 2000; // [ms]    4s = 0.25 Hz --> Charge&dis- same duration --> 4s/2

void setup() {
  pinMode(BUCK_pin, OUTPUT);
  pinMode(OC1_pin, OUTPUT);
  pinMode(OC2_pin, OUTPUT);

  // Timer1 configuration
  cli();   // disable interrupts

  TCCR1A = 0;
  TCCR1B = 0;

  // fast mode of Timer1: WGM13 1 WGM12 1 WGM11 1 WGM10 0
  TCCR1A |= (1 << WGM11); // contains WGM10 and WGM11
  TCCR1B |= (1 << WGM13) | (1 << WGM12); // contains WGM12 and WGM13

  ICR1 = TOP; // set TOP (100% duty cycle)

  TIMSK1 |= (1 << TOIE1);  // overflow interrupt
  TIMSK1 |= (1 << OCIE1A); // compare A
  TIMSK1 |= (1 << OCIE1B); // compare B
  TIMSK1 |= (1 << OCIE1C); // compare C

  TCCR1B |= (1 << CS10); // set prescaler=1 for 10 kHz

  sei();   // enable interrupts (TIMSK1) once I finished assigning values to them
}

void loop() {

  unsigned long now = millis();

  // Switch cases every 2 seconds
  if (now - lastSwitch >= switchInterval) {
    lastSwitch = now;

    caseNum++;
    if (caseNum > 3) caseNum = 1;

    switch (caseNum) {

      // ------------------- IDLE ------------------------
      case 1: // BUCK=OFF, OC1=OFF, OC2=OFF
        dutyBUCK = 0;//D_HV;
        dutyOC1 = 0;
        dutyOC2 = 0;
        break;

      // ------------------- CHARGING ------------------------
      case 2: // BUCK=ON, OC1=ON, OC2=OFF
        dutyBUCK = D_HV;   
        dutyOC1 = TOP; // 100% duty
        dutyOC2 = 0;
        break;

      // ------------------- DISCHARGING ------------------------
      case 3: // BUCK=ON, OC1=OFF, OC2=ON
        dutyBUCK = 0;//D_HV;
        dutyOC1 = 0;
        dutyOC2 = TOP;
        break;
    }
  }
}


// ---------------------------------------------------------------------------
// check if On time for Duty cycles are over for each MOSFET at 10kHz
// ---------------------------------------------------------------------------

// Timer1 overflow → start of new PWM cycle
ISR(TIMER1_OVF_vect) {
  digitalWrite(BUCK_pin, HIGH); // turn all HIGH at beginning
  digitalWrite(OC1_pin, HIGH);
  digitalWrite(OC2_pin, HIGH);

  OCR1A = dutyBUCK; // when to turn BUCK off
  OCR1B = dutyOC1;  // when to turn OC1 off
  OCR1C = dutyOC2;  // when to turn OC2 off
}

// Compare Match A → turn BUCK off
ISR(TIMER1_COMPA_vect) {
  if (dutyBUCK > 0) digitalWrite(BUCK_pin, LOW);
}

// Compare Match B → turn OC1 off
ISR(TIMER1_COMPB_vect) {
  if (dutyOC1 > 0) digitalWrite(OC1_pin, LOW);
}

// Compare Match C → turn OC2 off
ISR(TIMER1_COMPC_vect) {
  if (dutyOC2 > 0) digitalWrite(OC2_pin, LOW);
}