ATtiny45 PWM Frequency Selection

Hi Guys,

I am not experienced in the under-the-hood details of microcontrollers and I wondered if anyone could help me? I have settled on a nice RC filter arrangement to produce a 'true analogue' voltage, with the RC calcs based on 32kHz PWM frequency.

I would like to set my 8MHz ATtiny45's PWM frequency to approximately 32kHz, but I'm not sure if a guide that I have found is quite correct....

Reading the guide below, the author suggests that he has achieved 32kHz PWM with the following like

http://nicknorton.net/?q=node/7

TCCR0B = TCCR0B & 0b11111000 | 0b001

That all looks great, but the post here suggests that the line above will only result in a PWM frequency of 15.6kHz... Unfortunately I don't have access to an oscilloscope to perform my own confirmation :(

I also don't understand why there is a 'b' at the end of the Timer set line (my lack of understanding showing through). By looking at the datasheet I'd have thought that the Timer select line should read something like:

TCCR0B = TCCR0B & 0b11111000 | 1001

Does anyone have a definative answer?

Thanks in advance

John

Hi,

johnamon: I also don't understand why there is a 'b' at the end of the Timer set line (my lack of understanding showing through).

The "b" informs the compiler that the constant is to be treated as a binary value (as opposed to decimal or hexadecimal).

By looking at the datasheet I'd have thought that the Timer select line should read something like: TCCR0B = TCCR0B & 0b11111000 | 1001

"1001" is one-thousand and one in decimal. "0b1001" is nine in decimal.

Which core are you using?

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 :)

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 :)

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! :)

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... http://arduino.cc/forum/index.php/topic,142948.msg1073712.html#msg1073712

Bear in mind, that changes the [u]core[/u]. 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…
http://www.atmel.com/devices/attiny85.aspx?tab=documents

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.. :)

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