ATtiny45 PWM Frequency Selection

Hi Coding Badly,

I am using (what I believe is your) own arduino-tiny core, and your knock-bang debugging too - I am in your debt already! :roll_eyes:

Thank you for the replies :slight_smile:

Here is the code I use for setting a given frequency on the ATtiny45. I developed it originally for IR remote control at a variable frequency.

// Set the frequency that we will get on pin OCR1A but don't turn it on
void setFrequency(uint16_t freq)
{
  uint32_t requiredDivisor = (F_CPU/2)/(uint32_t)freq;

  uint16_t prescalerVal = 1;
  uint8_t prescalerBits = 1;
  while ((requiredDivisor + prescalerVal/2)/prescalerVal > 256)
  {
    ++prescalerBits;
    prescalerVal <<= 1;
  }
  
  uint8_t top = ((requiredDivisor + (prescalerVal/2))/prescalerVal) - 1;
  TCCR1 = (1 << CTC1) | prescalerBits;
  GTCCR = 0;
  OCR1C = top;
}

// Turn the frequency on
void on()
{
  TCNT1 = 0;
  TCCR1 |= (1 << COM1A0);
}

// Turn the frequency off and turn off the IR LED.
// We let the counter continue running, we just turn off the OCR1A pin.
void off()
{
  TCCR1 &= ~(1 << COM1A0);
}

Thank you for your reply dc42,

In order to use your code, should I supply the frequency in hertz, like '32000'?

Also, your Off function appears to turn the pin off, whereas I would like variable PWM duty cycle, it is a throttle that I am programming and the idea is that I can set 0v to 5v over PWM using analogWrite() - is it still possible to use analogueWrite with 255 representing 5v after i've used your code to turn the pin on?

Thanks again for your help :slight_smile:

John

johnamon:
Thank you for your reply dc42,

In order to use your code, should I supply the frequency in hertz, like '32000'?

Yes, the parameter to setFrequency is in Hz.

johnamon:
Also, your Off function appears to turn the pin off, whereas I would like variable PWM duty cycle, it is a throttle that I am programming and the idea is that I can set 0v to 5v over PWM using analogWrite() - is it still possible to use analogueWrite with 255 representing 5v after i've used your code to turn the pin on?

I'm sorry, I didn't read your original post properly. The code I supplied is intended to provide a variable frequency square wave from timer 1 rather than for PWM. If you are already using Coding Badly's core, then I think all you need to do is to set the prescaler of the timer you are using for the PWM output to 128. That should give you a PWM frequency of 8MHz/256 = 31.25kHz. However, if the timer you are using for PWM is the same one used for the micros() function, then this would mess up the values returned by micros().

Thanks again dc42,

I can see from the datasheet that I want to set the prescaler bits to 1001 or something equivalent:

Do you know how I correctly write that 1001 row into the prescaler instruction?

johnamon:
I am using (what I believe is your) own arduino-tiny core,

Got it.

and your knock-bang debugging too - I am in your debt already!

Excellent.

By default, millis is on timer 1. If you alter the timer 1 prescaler, millis (and its ilk) will not work correctly. Does your application use millis, micros, delay, or delayMicroseconds?

The millis timer is configured for "fast PWM". The other timer is configured for "phase-correct PWM". This detail is important when calculating the output frequency. Fast PWM is much more appropriate for a regulator.

How easily can you change the the output pin? Do you have a circuit board made? Are you working on a bread board?

Hi Coding Badly,

Thanks for taking another look. I am using millis for button debouncing, but I can adjust the software timings to suit if I end up speeding up / slowing down the timer clock.

I am currently working on a breadboard, so pins are completely flexible.

Thanks again,

John

Of the two timer/counters, only #1 has a prescaler option of 128. So one option is to use timer 1 to generate your PWM signal and move the millis function to timer 0. Alternatively, switch timer 0 to Fast PWM mode and set its prescaler to 256.

wow, I don't know how to do any of that!

32kHz is roughly 8MHz/256 - so I believe that I want a prescaler of 256?? Or is there a halving of the frequency going on somewhere that I'm not aware of which means I require a prescaler of 128?

If there is the option to have fast mode PWM running at 32kHz and maintain millis and delay working normally, I'd sure be grateful for the how-to! :slight_smile:

In addition to what @dc42 said, timer 1 on the X5 family is designed for doing what you are trying to do. It has lots more prescaler options and the base clock can be changed to 64 MHz (or 32 MHz).

I suggest swapping the timers so timer 1 is available to use as you please...

Bear in mind, that changes the core. All your ATtinyX5 applications are affected.

The Tiny Core includes a (partial) hardware veneer that should make changing the "user timer" a bit easier. Give this a try...

#include <UserTimer.h>

void setup( void )
{
  UserTimer_SetToPowerup();
  UserTimer_SetWaveformGenerationMode( UserTimer_(Fast_PWM_FF) );
  UserTimer_ClockSelect( UserTimer_(Prescale_Value_1) );
  // Frequency is F_CPU / (1 * (0xFF+1))
  // (8 megahertz) / 256 = 31.25 kilohertz
}

void loop( void )
{
}

The code changes timer 0 when millis is on timer 1 and vice versa.

Thank you very much Coding Badly - I really appreciate your help.

So to be clear, I should change Prescale_Value_1 in your code below to 255 (255+1 = 256)?

How does the timer change affect the pin mapping? I was (arbitrarily) going to use physical Pin5 (or Pin0) for my PWM source, would this still be the pin to use? The datasheet shows all the PWM pins having a OC0A or 0C0B reference making it not clear to my inexperienced eyes as to which PWM timers are mapped to which pins?

johnamon:
So to be clear, I should change Prescale_Value_1 in your code below to 255 (255+1 = 256)?

No. The "1" is the prescaler in the formula...

Frequency is F_CPU / ( 1 * (0xFF+1))

"(0xFF+1)" is the point the timer overflows (8 bit value).

johnamon:
How does the timer change affect the pin mapping?

Open the datasheet...

Navigate to the 1. Pin Configurations section.

Pins marked with OC (oh-see) are Output Compare (PWM capable) pins. The number after OC is the timer.

If there is a line above the marking the output is inverted. The Tiny Core does not make any use of inverted outputs.

If you're using timer 0, OC0A and OC0B are candidates (PB0 and PB1). If you're using timer 1, OC1A and OC1B are candidates (PB1 and PB4).

This compile-time switch...

http://code.google.com/p/arduino-tiny/source/browse/trunk/hardware/tiny/cores/tiny/core_build_options.h#113

...determines which output (OC0B versus OC1A) the Tiny Core uses for PB1....

http://code.google.com/p/arduino-tiny/source/browse/trunk/hardware/tiny/cores/tiny/core_pins.h#184

If FAVOR_PHASE_CORRECT_PWM is 1 then OC0B is used (timer 0 controls PB1). If FAVOR_PHASE_CORRECT_PWM is 0 then OC1A is used (timer 1 controls PB1).

Decide which timer you want to use for PWM. Pick which of the two pins you want to use. Set the two compile-time switches to match.

...making it not clear to my inexperienced eyes as to which PWM timers are mapped to which pins?

Pins are numbered by the bit: PB0 = digital pin 0, PB1 = digital pin 1, PB2 = digital pin 2, etcetera.

Thank you again, your depth of knowledge, and ability to communicate what you know is humbling!

I don't know why - but I thought you were in Australia.... It's past midnight here and I need to go to sleep! Anyway I have realised that you are donationware - I'll get on that right away! :blush:

Thank you again for 'babysitting' me through this.

What I need to do now is document all this, as I can see a lot of this information has already been posted and I think we've stitched a lot of it together here.. :slight_smile:

johnamon:
Thank you again...

You are welcome.

I don't know why - but I thought you were in Australia....

By the hours I keep and the dog I own, I should be.

Anyway I have realised that you are donationware - I'll get on that right away!

Thank you!

Thank you dc42 and Coding Badly. Due to the amount of information presented in the previous posts I thought I would summarise what I plan to do - if you guys let me know that the steps below are correct, then I will edit the first post in this thread to explain clearly how we got 32kHz frequency PWM from my ATtiny45.

Summary:

I would like the PWM output from an ATtiny45 to produce a variable voltage of 0v to 5v, via a RC low pass filter. I have calculated that a 32kHz PWM frequency would be ideal, the high frequency allows low ripply and relatively fast 'settle' time.

To achieve 32kHz, I need to divide the system 8MHz clock by 256 (prescaler of 1 in Fast Mode PWM). However the mills() and delay() programming functions utilise Timer1 - so to maintain programming functionality we will assign Timer0 to mills and delay (assigning Timer1 to PWM is dealt with later in the thread):

Execution:

Because my analogue voltage requirement is best served by 'Fast PWM', I perform the modification to core_build_options.h:

Because we have favoured FAST PWM, OC1A is used to control PB1, and also PB0 - so Physical Pins 5 and 6 may be used to generate the PWM signal that is required.

As-yet Timer1 has not had it's frequency set to 32kHz - so in this step we do that...

#include <UserTimer.h>

void setup( void )
{
  UserTimer_SetToPowerup();
  UserTimer_SetWaveformGenerationMode( UserTimer_(Fast_PWM_FF) );
  UserTimer_ClockSelect( UserTimer_(Prescale_Value_1) );
  // Frequency is F_CPU / (1 * (0xFF+1))
  // (8 megahertz) / 256 = 31.25 kilohertz
}

void loop( void )
{
analogWrite(PB0,255); //Gives 5V analogue output
}

Thanks again

John

johnamon:
To achieve 32kHz, I need to divide the system 8MHz clock by 256 (prescaler of 256).

No, you don't need to a prescaler of 256, you need a prescaler of 1. If you use one of the 8-bit counter/timers with its maximum TOP value of 255 (as is usual when using it to generate a PWM signal), then the counter is already dividing by either 256 or 512 (depending on the mode you select), giving you a frequency close to 32KHz or 16kHz.

oK - I can see from Page 76 of the datasheet that in Fast Mode PWM the frequency pre-scaler should be 1 as the system clock is already divided by 256.....

Thank you again dc42 - this is a fair bit more complicated than I expected when I was ordering bits on the Farnell website, I couldn't have got here without you guys!