2
votes

I am using a 328P (Running on the internal 8mhz) to generate a Square wave at around 111K hz or 120K hz with and adjustable duty cycle.

I'm fairly new to doing this level of hardware manipulation so please excuse my ignorance, I spent 3 days looking online and in the datasheet but I'm afraid my understanding of what the acronyms and abbreviations mean is still too limited.

So far I have come up with the following code after doing the research online and in the datasheet. But I feel like I'm missing something, What register manipulates frequency and what register manipulates duty cycle in (Datasheet 20.12.5) Phase and Frequency Correct PWM Mode?

void setup(){
    DDRB |= (1 << DDB1); //Set PB1 as output
    OCR1A = (Unsure of what TOP should be here);
    TCCR1A = (1 << WGM10) | (1 << COM1B0) | (1 << COM1A0);
    TCCR1B = (1 << CS10) | (1 << WGM13);
}
void loop(){
    //manipulate duty cycle with other code here
}

What am I missing or what should I be doing differently here? I tried to use the online AVR Timer Calc to help me get clock ticks needed for that frequency. It says a total of 72 Timer ticks with no prescaler and 16 bit timer would yield a 111Khz (approximate) Square wave. Jumping to 73 or 71 jumps the frequency too much out of the desired range. is there any way to get that closer on an AVR?

2
What frequency is your ATmega328P running at? - David Grayson
8Mhz internal source. - SergeantSeven
What is P&F? I can't find that string in the datasheet. Please write out abbreviations. - David Grayson
20.12.5. Phase and Frequency Correct PWM Mode - SergeantSeven

2 Answers

2
votes

In the title, you asked "ATMega328P How to set TIMER1 PWM Frequency". You tune the frequency by setting the TOP value to what you want it to be. A smaller TOP value means the counter reaches TOP sooner, so the frequency is higher. The prescaler and clock source you use for the timer determines how fast it counts up, so that also affects the frequency, what ranges of frequencies are possible, and the resolution you get for setting the frequency. Your TOP value also determines how much resolution you have when setting the duty cycle, since the duty cycle should be an integer less than or equal to TOP.

Near the bottom of your post, you asked "is there any way to get that closer on an AVR?" Since your AVR is running at 8 MHz, every frequency you can generate with its timers will be of the form (8 MHz)/N, where N is some integer. That's because the timers can't do anything or change anything between ticks of the clock source you are supplying to them. Timer 1 will get a clock tick 8 times per microsecond, and each time that clock tick happens it can advance its count and/or control a PWM pin and/or fire an interrupt. I think you already figured this out using the online AVR timer calculator. Here are some of the achievable PWM frequencies you can get on an AVR running at 8 MHz:

  • 8000 kHz / 73 = 109.6 kHz
  • 8000 kHz / 72 = 111.1 kHz
  • 8000 kHz / 71 = 112.7 kHz

If you need more precise frequency tuning, you need to get a faster clock source in your system one way or another. You could get a faster microcontroller or maybe figure out how to supply a faster clock as an input to a timer on the microcontroller.

2
votes

After a lot of research along side a friend of mine who is also an EE and works with embedded systems we came to find the best solution for getting what I needed out of the 328p. I will post the solution below for anyone who is interested and needs something similar.

void setup() {
  // put your setup code here, to run once:
  //Set Timer1 for around 109Khz signal
  cli();//stop interrupts
  TCCR1A = 0;// set entire TCCR1A register to 0
  TCCR1B = 0;// same for TCCR1B
  TCNT1  = 0;//initialize counter value to 0
  //Set register bits 
  TCCR1A = _BV(COM1A1) | _BV(WGM11);
  TCCR1B = _BV(CS10) | _BV(WGM12) | _BV(WGM13);
  ICR1 = 73; // frequency = 8000000/73 = 109.589 kHz
  OCR1A = 0; //0% Duty Cycle or Full Off.
  sei();//allow interrupts
}

void loop() {
  // put your main code here, to run repeatedly:
  OCR1A = 36; //50% Duty Cycle 73/2 = 36.5 Can be changed as needed.
}

This code sets the registers for Timer 1 so it shouldn't interfere with millis or other timing functions in the Arduino libraries. I needed to manipulate a specific pin anyways and PB1 (OC1A) (or Arduino digital pin 9) is the pin that this will oscillate.

You can change ICR1 to any value you need based on some simple math, Your clock frequency divided by the value of the counter equals the approximate frequency produced. OCR1A sets the duty cycle of the signal.

You are limited in the exact frequency but for my needs this worked out OK. I was still able to use it to drive the transducer I was using.

This was a quick answer to the initial problem and allows me to change the duty cycle as a bonus. I don't remember the exact information on the registers we set, When I have time I will update this answer with the info from the data sheet pertaining to that.