Search code examples
arduino-unopwm

Setting up Arduino Uno (ATMEGA328P) PWM with Timer1


I want to setup a custom frequency(12Hz) and duty cycle (20%) with a 16MHz Arduino Uno (ATMEGA328P).

AVR Calculator yields:

ICR1 = 20833
OCR1A = 4167

I have read a tonne of forums and tuts but for some reason I cannot get this to work.

Below is my code:

void setup()
{
  // PB1 is now an output (Pin9 Arduino UNO)
  DDRB |= (1 << DDB1);
  // PB2 is now an output (Pin10 Arduino UNO)
  DDRB |= (1 << DDB2);

      // Set PWM frequency/top value
      ICR1 = 20833;

      // Set PWM duty cycle
      OCR1A = 4167;

      // Set inverting mode (start low, go high)
      TCCR1A |= (1 << COM1A1);
      TCCR1A |= (1 << COM1B1);
      TCCR1A |= (1 << COM1A0);
      TCCR1A |= (1 << COM1B0);

      // Set fast PWM Mode
      TCCR1A |= (1 << WGM11);
      TCCR1B |= (1 << WGM12);
      TCCR1B |= (1 << WGM13);

      // Set prescaler to 64 and starts PWM
      TCCR1B |= (1 << CS10);
      TCCR1B |= (1 << CS11);
    }

    void loop() {
      // Refresh PWM frequency
      OCR1A = 4167;
    }

If someone can help, that would be great!

Thanks,

Dylan


Solution

  • Okay, so I seemed to find the issue. I was not setting up the registers correctly for fast PWM in mode 14 (ATMEGA328P has 15 timer1 modes). After a lot of experimentation and further reading, below is the correct setup for variable frequency and duty cycle. ICR1 denotes the TOP value (controls frequency) and OCR1A gives the switching value (duty cycle).

    // ADJUSTABLE VARIABLES
    // Strobe frequency
    uint16_t timer1Prescaler = 64;
    uint8_t strobeFreq = 20,
            strobeDutyCycle = 20;
    
    void setup
    {
      // Set PB1 to be an output (Pin9 Arduino UNO)
      DDRB |= (1 << PB1);
    
      // Clear Timer/Counter Control Registers
      TCCR1A = 0;
      TCCR1B = 0;
    
      // Set non-inverting mode
      TCCR1A |= (1 << COM1A1);
    
      // Set fast PWM Mode 14
      TCCR1A |= (1 << WGM11);
      TCCR1B |= (1 << WGM12);
      TCCR1B |= (1 << WGM13);
    
      // Set prescaler to 64 and starts PWM
      TCCR1B |= (1 << CS10);
      TCCR1B |= (1 << CS11);
    
      // Set PWM frequency/top value
      ICR1 = (F_CPU / (timer1Prescaler*strobeFreq)) - 1;
      OCR1A = ICR1 / (100 / strobeDutyCycle);
    }
    
    void loop()
    {
      // main loop code
    }
    

    NOTE: Clearing the Timer/Counter Control Registers is important when using the Arduino IDE as it does do a little setup behind the scenes before executing the setup() function.