Multiple high speed PWM with variable duty cycle

I read in the forum about timers and pwm but I can't find a solution to my problem. If I use the timer1 library, I can do want I want (high speed pwm around 400khz and I can select the duty cycle). I would like to have 4 (even 8 would be the best) like this, how can I do, is it possible ? Using the same pins as the original pwm is not a problem I forgot to say that I use the Duemilanove with atmega 328 Thanks for your help

In fact, my question is how I can write the same library as TimerOne for Timer0 and Timer2.
I try changing the different register, but I don’t get any output.

This is my modify library for Timer2 (don’t look the comment as there are the original ones from TimerOne):

#include "TimerTwo.h"

TimerTwo Timer2;              // preinstatiate

void TimerTwo::initialize(long microseconds)
{
  TCCR2A = 0;                 // clear control register A 
  TCCR2B = _BV(WGM22);        // set mode as phase and frequency correct pwm, stop the timer
  setPeriod(microseconds);
}

void TimerTwo::setPeriod(long microseconds)
{
  TCCR2B = _BV(WGM22);                                                           // reset clock select register
  long cycles = (F_CPU * microseconds) / 2000000;                                // the counter runs backwards after TOP, interrupt is at BOTTOM so divide microseconds by 2
  if(cycles < RESOLUTION)              clockSelectBits = _BV(CS20);              // no prescale, full xtal
  else if((cycles >>= 3) < RESOLUTION) clockSelectBits = _BV(CS21);              // prescale by /8
  else if((cycles >>= 3) < RESOLUTION) clockSelectBits = _BV(CS21) | _BV(CS20);  // prescale by /64
  else if((cycles >>= 2) < RESOLUTION) clockSelectBits = _BV(CS22);              // prescale by /256
  else if((cycles >>= 2) < RESOLUTION) clockSelectBits = _BV(CS22) | _BV(CS20);  // prescale by /1024
  else        cycles = RESOLUTION - 1, clockSelectBits = _BV(CS22) | _BV(CS20);  // request was out of bounds, set as maximum
  pwmPeriod = cycles;                                                     // ICR1 is TOP in p & f correct pwm mode
}

void TimerTwo::setPwmDuty(char pin, int duty)
{
  unsigned long dutyCycle = pwmPeriod;
  dutyCycle *= duty;
  dutyCycle >>= 10;
  if(pin == 11)       OCR2A = dutyCycle;
  else if(pin == 3) OCR2B = dutyCycle;
}

void TimerTwo::pwm(char pin, int duty, long microseconds)  // expects duty cycle to be 10 bit (1024)
{
  if(microseconds > 0) setPeriod(microseconds);
  if(pin == 11) {
    DDRB |= _BV(PORTB3);                                   // sets data direction register for pwm output pin
    TCCR2A |= _BV(COM2A1);                                 // activates the output pin
  }
  else if(pin == 3) {
    DDRD |= _BV(PORTD3);
    TCCR2A |= _BV(COM2B1);
  }
  setPwmDuty(pin, duty);
  start();
}

void TimerTwo::disablePwm(char pin)
{
  if(pin == 11)       TCCR2A &= ~_BV(COM2A1);   // clear the bit that enables pwm on PB1
  else if(pin == 3) TCCR2A &= ~_BV(COM2B1);   // clear the bit that enables pwm on PB2
}


void TimerTwo::start()
{
  TCNT2 = 0;
  TCCR2B |= clockSelectBits;
}

void TimerTwo::stop()
{
  TCCR2B &= ~(_BV(CS20) | _BV(CS21) | _BV(CS22));          // clears all clock selects bits
}

this is the one for timer0

#include "TimerZero.h"

TimerZero Timer0;              // preinstatiate

void TimerZero::initialize(long microseconds)
{
  TCCR0A = 0;                 // clear control register A 
  TCCR0B = _BV(WGM02);        // set mode as phase and frequency correct pwm, stop the timer
  setPeriod(microseconds);
}

void TimerZero::setPeriod(long microseconds)
{
  TCCR0B = _BV(WGM02);                                                           // reset clock select register
  long cycles = (F_CPU * microseconds) / 2000000;                                // the counter runs backwards after TOP, interrupt is at BOTTOM so divide microseconds by 2
  if(cycles < RESOLUTION)              clockSelectBits = _BV(CS00);              // no prescale, full xtal
  else if((cycles >>= 3) < RESOLUTION) clockSelectBits = _BV(CS01);              // prescale by /8
  else if((cycles >>= 3) < RESOLUTION) clockSelectBits = _BV(CS01) | _BV(CS00);  // prescale by /64
  else if((cycles >>= 2) < RESOLUTION) clockSelectBits = _BV(CS02);              // prescale by /256
  else if((cycles >>= 2) < RESOLUTION) clockSelectBits = _BV(CS02) | _BV(CS00);  // prescale by /1024
  else        cycles = RESOLUTION - 1, clockSelectBits = _BV(CS02) | _BV(CS00);  // request was out of bounds, set as maximum
  pwmPeriod = cycles;                                                     // ICR1 is TOP in p & f correct pwm mode
}

void TimerZero::setPwmDuty(char pin, int duty)
{
  unsigned long dutyCycle = pwmPeriod;
  dutyCycle *= duty;
  dutyCycle >>= 10;
  if(pin == 6)       OCR0A = dutyCycle;
  else if(pin == 5) OCR0B = dutyCycle;
}

void TimerZero::pwm(char pin, int duty, long microseconds)  // expects duty cycle to be 10 bit (1024)
{
  if(microseconds > 0) setPeriod(microseconds);
  if(pin == 6) {
    DDRD |= _BV(PORTD6);                                   // sets data direction register for pwm output pin
    TCCR0A |= _BV(COM0A1);                                 // activates the output pin
  }
  else if(pin == 5) {
    DDRD |= _BV(PORTD5);
    TCCR0A |= _BV(COM0B1);
  }
  setPwmDuty(pin, duty);
  start();
}

void TimerZero::disablePwm(char pin)
{
  if(pin == 6)       TCCR0A &= ~_BV(COM0A1);   // clear the bit that enables pwm on PB1
  else if(pin == 5) TCCR0A &= ~_BV(COM0B1);   // clear the bit that enables pwm on PB2
}


void TimerZero::start()
{
  TCNT0 = 0;
  TCCR0B |= clockSelectBits;
}

void TimerZero::stop()
{
  TCCR0B &= ~(_BV(CS00) | _BV(CS01) | _BV(CS02));          // clears all clock selects bits
}

Can someone tell me what is wrong ?
Thank you

The fact that you can get what you want out of timer1 but not timer0 or timer2 suggests that you may not be correctly accounting for the fact that timer0 and timer2 are 8-bit counters rather than 16-bit. It is not clear to me how your code correctly bounds the values of OCR{0|2}{A|B} to the range 0-255.

You should read this thread. It really helped me in this area:

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1235060559/4#4

I finally get what I wanted.
I modify the Timer1 library so now I have 4 PWM easily adjustable.
It uses timer0 pin 5, Timer1 pin 9 and 10 and Timer2 pin 3
So period can be different between 3, 5 and (9, 10) and dutycycle is independent for each pin
The use is the same as Timer1 except that there is no interrupt management.
I test it and it does what I want.
If somebody want to test it, I paste the code here (I don’t know how to upload a file).
Let me know if there is something wrong :slight_smile:

/*
 *  PWM utilities for 16 bit Timer1 and 8 bit Timer0 and Timer2 on ATmega168/328
 *  this is based on the Timer1 library
 *      Original code by Jesse Tane for http://labs.ideo.com August 2008
 *  Modified March 2009 by Jérôme Despatis and Jesse Tane for ATmega328 support
 *  Adaptation by Christophe Nicolas on April 2009
 *
 *  This is free software. You can redistribute it and/or modify it under
 *  the terms of Creative Commons Attribution 3.0 United States License. 
 *  To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/us/ 
 *  or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.
 *
 */

#include <avr/io.h>
#include <avr/interrupt.h>

#define RESOLUTION8 256          // Timer0 and Timer2 are 8 bit
#define RESOLUTION16 65536      // Timer1 is 16 bit

class TimerPWM
{
  public:
  
    // properties
    unsigned int pwmPeriod0;
    unsigned int pwmPeriod1;
    unsigned int pwmPeriod2;
    unsigned char clockSelectBits0;
    unsigned char clockSelectBits1;
    unsigned char clockSelectBits2;

    // methods
    void initialize(int timer,long microseconds=1000000);
    void start(int timer);
    void stop(int timer);
    void pwm(int timer,char pin, int duty, long microseconds=-1);
    void disablePwm(int timer, char pin);
    void setPeriod(int timer, long microseconds);
    void setPwmDuty(int timer,char pin, int duty);
};
extern TimerPWM Tpwm;
/*
 *  PWM utilities for 16 bit Timer1 and 8 bit Timer0 and Timer2 on ATmega168/328
 *  this is based on the Timer1 library
 *      Original code by Jesse Tane for http://labs.ideo.com August 2008
 *  Modified March 2009 by Jérôme Despatis and Jesse Tane for ATmega328 support
 *  Adaptation by Christophe Nicolas on April 2009
 *
 *  This is free software. You can redistribute it and/or modify it under
 *  the terms of Creative Commons Attribution 3.0 United States License. 
 *  To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/us/ 
 *  or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.
 *
 */

#include "TimerPWM.h"

TimerPWM Tpwm;              // preinstatiate


void TimerPWM::initialize(int timer,long microseconds)
{
  switch(timer){
      case 0 :
            TCCR0A = _BV(WGM00);        // set mode as phase and frequency correct pwm, stop the timer
            TCCR0B = _BV(WGM02);        
        break;
      case 1 :
            TCCR1A = 0;                 // clear control register A 
            TCCR1B = _BV(WGM13);        // set mode as phase and frequency correct pwm, stop the timer
      break;
      case 2 :
            TCCR2A = _BV(WGM20);
            TCCR2B = _BV(WGM22);        // set mode as phase and frequency correct pwm, stop the timer
            ASSR = 0;
      break;
      }
  setPeriod(timer,microseconds);
}

void TimerPWM::setPeriod(int timer,long microseconds)
{
  long cycles = (F_CPU * microseconds) / 2000000;                                // the counter runs backwards after TOP, interrupt is at BOTTOM so divide microseconds by 2
  switch(timer){
      case 0 :
            TCCR0B = _BV(WGM02);
            TCCR0A = _BV(WGM00);                                                                       // reset clock select register
            if(cycles < RESOLUTION8)              clockSelectBits0 = _BV(CS00);                    // no prescale, full xtal
            else if((cycles >>= 3) < RESOLUTION8) clockSelectBits0 = _BV(CS01);                    // prescale by /8
            else if((cycles >>= 3) < RESOLUTION8) clockSelectBits0 = _BV(CS01) | _BV(CS00);        // prescale by /64
            else if((cycles >>= 2) < RESOLUTION8) clockSelectBits0 = _BV(CS02);                    // prescale by /256
            else if((cycles >>= 2) < RESOLUTION8) clockSelectBits0 = _BV(CS02) | _BV(CS00);        // prescale by /1024
            else        cycles = RESOLUTION8 - 1, clockSelectBits0 = _BV(CS02) | _BV(CS00);        // request was out of bounds, set as maximum
            OCR0A = pwmPeriod0 = cycles;                                                           // OCR0A is TOP in p & f correct pwm mode
        break;
      case 1 :
            TCCR1B = _BV(WGM13);                                                                       // reset clock select register
            if(cycles < RESOLUTION16)              clockSelectBits1 = _BV(CS10);                    // no prescale, full xtal
            else if((cycles >>= 3) < RESOLUTION16) clockSelectBits1 = _BV(CS11);                    // prescale by /8
            else if((cycles >>= 3) < RESOLUTION16) clockSelectBits1 = _BV(CS11) | _BV(CS10);        // prescale by /64
            else if((cycles >>= 2) < RESOLUTION16) clockSelectBits1 = _BV(CS12);                    // prescale by /256
            else if((cycles >>= 2) < RESOLUTION16) clockSelectBits1 = _BV(CS12) | _BV(CS10);        // prescale by /1024
            else          cycles = RESOLUTION16 - 1, clockSelectBits1 = _BV(CS12) | _BV(CS10);  // request was out of bounds, set as maximum
            ICR1 = pwmPeriod1 = cycles;                                                           // ICR1 is TOP in p & f correct pwm mode
      break;
      case 2 :
            TCCR2B = _BV(WGM22);                                                                                         // reset clock select register
            TCCR2A = _BV(WGM20);
            if(cycles < RESOLUTION8)              clockSelectBits2 = _BV(CS20);                                      // no prescale, full xtal
            else if((cycles >>= 3) < RESOLUTION8) clockSelectBits2 = _BV(CS21);                                      // prescale by /8
            else if((cycles >>= 2) < RESOLUTION8) clockSelectBits2 = _BV(CS21) | _BV(CS20);                          // prescale by /32
            else if((cycles >>= 1) < RESOLUTION8) clockSelectBits2 = _BV(CS22);                                      // prescale by /64
            else if((cycles >>= 1) < RESOLUTION8) clockSelectBits2 = _BV(CS22) | _BV(CS20);                          // prescale by /128
            else if((cycles >>= 1) < RESOLUTION8) clockSelectBits2 = _BV(CS22) | _BV(CS21);                          // prescale by /256
            else if((cycles >>= 2) < RESOLUTION8) clockSelectBits2 = _BV(CS22)  | _BV(CS21) | _BV(CS20);        // prescale by /1024
            else        cycles = RESOLUTION8 - 1, clockSelectBits2 = _BV(CS22)  | _BV(CS21) | _BV(CS20);        // request was out of bounds, set as maximum
            OCR2A = pwmPeriod2 = cycles;                                                                             // OCR2A is TOP in p & f correct pwm mode
      break;
      }
}

void TimerPWM::setPwmDuty(int timer,char pin, int duty)
{
  unsigned long dutyCycle;
  switch(timer){
      case 0 :
            dutyCycle = pwmPeriod0;
            dutyCycle *= duty;
            dutyCycle >>= 10;
            if(pin == 5) OCR0B = dutyCycle;
        break;
      case 1 :
            dutyCycle = pwmPeriod1;
            dutyCycle *= duty;
            dutyCycle >>= 10;
            if(pin == 1 || pin == 9)       OCR1A = dutyCycle;
            else if(pin == 2 || pin == 10) OCR1B = dutyCycle;
      break;
      case 2 :
            dutyCycle = pwmPeriod2;
            dutyCycle *= duty;
            dutyCycle >>= 10;
            if(pin == 3) OCR2B = dutyCycle;
      break;
      }
}

void TimerPWM::pwm(int timer,char pin, int duty, long microseconds)  // expects duty cycle to be 10 bit (1024)
{
  if(microseconds > 0) setPeriod(timer,microseconds);
  switch(timer){
      case 0 :
            if(pin == 5) {
                  DDRD |= _BV(PORTD5);                  // sets data direction register for pwm output pin
                  TCCR0A |= _BV(COM0B1);                  // activates the output pin
                  }
            setPwmDuty(0,pin, duty);
            start(0);
        break;
      case 1 :
            if(pin == 1 || pin == 9) {
                  DDRB |= _BV(PORTB1);                  // sets data direction register for pwm output pin
                  TCCR1A |= _BV(COM1A1);               // activates the output pin
            }
            else if(pin == 2 || pin == 10) {
                  DDRB |= _BV(PORTB2);                  // sets data direction register for pwm output pin
                  TCCR1A |= _BV(COM1B1);                  // activates the output pin
            }
            setPwmDuty(1,pin, duty);
            start(1);
      break;
      case 2 :
            if(pin == 3) {
                  DDRD |= _BV(PORTD3);                  // sets data direction register for pwm output pin
                  TCCR2A |= _BV(COM2B1);                  // activates the output pin
            }
            setPwmDuty(2,pin, duty);
            start(2);
      break;
      }
}

void TimerPWM::disablePwm(int timer,char pin)
{
  switch(timer){
      case 0 :
            if(pin == 5) TCCR0A &= ~_BV(COM0B1);                                       // clear the bit that enables pwm on PD5
        break;
      case 1 :
            if(pin == 1 || pin == 9)       TCCR1A &= ~_BV(COM1A1);         // clear the bit that enables pwm on PB1
            else if(pin == 2 || pin == 10) TCCR1A &= ~_BV(COM1B1);         // clear the bit that enables pwm on PB2
      break;
      case 2 :
            if(pin == 3) TCCR2A &= ~_BV(COM2B1);                                       // clear the bit that enables pwm on PD3
      break;
      }
}

void TimerPWM::start(int timer)
{
  switch(timer){
      case 0 :
            TCNT0 = 0;
            TCCR0B |= clockSelectBits0;
      break;
      case 1 :
            TCNT1 = 0;
            TCCR1B |= clockSelectBits1;
      break;
      case 2 :
            TCNT2 = 0;
            TCCR2B |= clockSelectBits2;
      break;
      }
}

void TimerPWM::stop(int timer)
{
  switch(timer){
      case 0 :
            TCCR0B &= ~(_BV(CS00) | _BV(CS01) | _BV(CS02));          // clears all clock selects bits
      break;
      case 1 :
            TCCR1B &= ~(_BV(CS10) | _BV(CS11) | _BV(CS12));          // clears all clock selects bits
      break;
      case 2:
            TCCR2B &= ~(_BV(CS20) | _BV(CS21) | _BV(CS22));          // clears all clock selects bits
      break;
      }
}

is PWM suitable for arduino piano to generate sine wave sounds?