# Change timer frequency

Hey, I’m trying to create a variable frequency PWM signal. I don’t understand a lot about the registers, but I managed to get something basic by reading the documentation. I can adjust the duty cycle quite nicely, but I don’t know how to adjust the frequency. I did a different design, and I was able to adjust the prescaler, but that only gave me 8 different frequencies, whereas I’d like something much smoother.

I also saw something about adjusting the ICR1 register, but I can’t get that working…

I also have an issue with the OCR0B register. Changing that adjusts the duty cycle, but I can’t seem to get a duty cycle of 0. It goes to about 1%, but that’s it.

I’m using an ATmega2560. Any help would be great.

Here’s what I have so far.

``````int pot_1 = 0;

void setup() {
pinMode(4,OUTPUT);
pinMode(13,OUTPUT);
TCCR0A |= (1<<WGM00)|(1<<WGM01)|(1<<COM0B1);
TCCR0B |= (1<<CS01)|(1<<CS00)|(1<<FOC0B);

}

void loop() {
if(duty_1<5) duty_1 = 0;
if(duty_1>511) duty_1 = 511;
OCR0B = duty_1/2;

}
``````

Here is an example I wrote:

``````// Generating Variable-Frequency PWM on Timer1 of an Arduino UNO (Pins 9 and/or 10)
// Good for integer frequencies from 1 Hz to 65535 Hz.
// Written June 19th, 2020 by John Wasser

uint16_t TimerTOP = 0xFFFF;
byte PercentPWM_A = 50;  // Default to 50% duty cycle
byte PercentPWM_B = 50;  // Default to 50% duty cycle

void FPWMBegin()
{
digitalWrite(9, LOW);
pinMode(9, OUTPUT);
digitalWrite(10, LOW);
pinMode(10, OUTPUT);

// Stop Timer/Counter1
TCCR1A = 0;  // Timer/Counter1 Control Register A
TCCR1B = 0;  // Timer/Counter1 Control Register B
TIMSK1 = 0;   // Timer/Counter1 Interrupt Mask Register

// Set Timer/Counter1 to Waveform Generation Mode 8: Phase and Frequency correct PWM with TOP set by ICR1
TCCR1B |= (1 << WGM13);  // WGM=8
TCCR1A |= (1 << COM1A1);  // Normal PWM on Pin 9
TCCR1A |= (1 << COM1B1);  // Norml PWM on Pin 10

FPWMSetFrequency(1000);  // Default to 1 kHz
}

bool FPWMSetFrequency(uint16_t frequency)
{
byte prescaleBits; // 1, 2, 3, 4, 5
uint16_t prescaleFactor;  // 1, 8, 64, 256, 1024
uint32_t top32;

// Clear the three clock select bits to stop the timer
TCCR1B &= ~((1 << CS12) | (1 << CS11) | (1 << CS10));

// Find the smallest prescale factor that will fit the TOP value within 16 bits.
// frequency = F_CPU / (2 * prescale *  TOP)
// TOP = F_CPU / (2UL * prescale * frequency);

prescaleBits = 1;
prescaleFactor = 1;  // Used for 123-65535 Hz
top32 = F_CPU / (2UL * prescaleFactor * frequency);
if (top32 > 65535UL) // Too many clocks to count in 16 bits?
{
prescaleBits = 2;
prescaleFactor = 8;  // Used for 16-122 Hz
top32 = F_CPU / (2UL * prescaleFactor * frequency);
if (top32 > 65535UL) // Too many clocks to count in 16 bits?
{
prescaleBits = 3;
prescaleFactor = 64;  // Used for 2-15 Hz
top32 = F_CPU / (2UL * prescaleFactor * frequency);
if (top32 > 65535UL) // Too many clocks to count in 16 bits?
{
prescaleBits = 4;
prescaleFactor = 256; // Only used for 1 Hz
top32 = F_CPU / (2UL * prescaleFactor * frequency);
if (top32 > 65535UL) // Too many clocks to count in 16 bits?
{
prescaleBits = 5;
prescaleFactor = 1024;
top32 = F_CPU / (2UL * prescaleFactor * frequency);
if (top32 > 65535UL) // Too many clocks to count in 16 bits?
{
return false;
}
}
}
}
}

//  Serial.print("Freq: ");
//  Serial.print(frequency);
//  Serial.print(" prescale: ");
//  Serial.print(prescaleFactor);
//  Serial.print(" TOP: ");
//  Serial.println(top32);

if (top32 < 16)
return false; // Require at least 16 levels of PWM

TimerTOP = top32;

OCR1A = (PercentPWM_A * (uint32_t)TimerTOP) / 100;
OCR1B = (PercentPWM_A * (uint32_t)TimerTOP) / 100;

ICR1 = TimerTOP;

TCCR1B |= prescaleBits; // Set clock prescale bits to start the timer
return true;
}

void FPWMSetDutyCyclePercentageA(byte percent)
{
PercentPWM_A = min(percent, 100);
OCR1A = (PercentPWM_A * (uint32_t)TimerTOP) / 100;
}

void FPWMSetDutyCyclePercentageB(byte percent)
{
PercentPWM_B = min(percent, 100);
OCR1A = (PercentPWM_B * (uint32_t)TimerTOP) / 100;
}

void setup()
{
Serial.begin(115200);

FPWMBegin();  // Start output at 1000 Hz, 50% duty cycle
}

void loop()
{
// Call FPWMSetFrequency(uint16_t frequency) any time to change the output frequency
// Call FPWMSetDutyCyclePercentageA(byte percent) at any time to change duty cycle
// Call FPWMSetDutyCyclePercentageB(byte percent) at any time to change duty cycle
}
``````

Hey,

Thank you for your reply. That program worked perfectly once I changed the pins to 11 and 12 for the ATMega2560 chip. I'm going to reverse engineer the code and make sure I understand it. Thank you for sharing!