Go Down

Topic: ATtiny45 PWM Frequency Selection (Read 4753 times) previous topic - next topic

johnamon

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

Coding Badly

Hi,

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

Quote
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.

Coding Badly


johnamon

#3
Jan 26, 2013, 12:58 pm Last Edit: Jan 26, 2013, 05:26 pm by johnamon Reason: 1
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!  :smiley-roll:

Thank you for the replies :)

dc42

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.

Code: [Select]

// 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);
}
Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

johnamon

#5
Jan 26, 2013, 07:33 pm Last Edit: Jan 26, 2013, 07:37 pm by johnamon Reason: 1
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

dc42


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.


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().
Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

johnamon

#7
Jan 26, 2013, 08:26 pm Last Edit: Jan 26, 2013, 08:28 pm by johnamon Reason: 1
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?

Coding Badly

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


Got it.

Quote
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?

johnamon

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

dc42

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.
Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

johnamon

#11
Jan 26, 2013, 11:02 pm Last Edit: Jan 26, 2013, 11:15 pm by johnamon Reason: 1
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! :)

Coding Badly

#12
Jan 27, 2013, 12:05 am Last Edit: Jan 27, 2013, 12:15 am by Coding Badly Reason: 1

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 core.  All your ATtinyX5 applications are affected.

Coding Badly


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

Code: [Select]
#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.

johnamon

#14
Jan 27, 2013, 12:52 am Last Edit: Jan 27, 2013, 01:01 am by johnamon Reason: 1
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?  

Go Up