ATtiny25 Clock Frequency and PWM ??

Go To Last Post
18 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Question about ATtiny25 Clock Frequency (Prescaler) settings.

Plan is: (Drive a Servo) with the ATtiny25 (in Assembly)
Not even sure it can be done (well) using an 8bit PWM (timer)

Wishing to use the (Internal Default) 8MHz Clock (hope good enough)
(As the Device is shipped with the CKDIV8 Fuse Programmed)

I see that it will drift in frequency (so crossing fingers)

I need a PWM (output) frequency range from: 500 Hz to 2000 Hz

---------------
; Required pulse example:
;
;      ____                               ____       _+5.0V
;     |    |                             |    |
;     |    |                             |    |
; ____|    |_____________________________|    |____  _ 0.0V
;
;     |<-->|
;    0.5-2.0 ms -- (2000 Hz to 500 Hz)
;
;     |<-------------------------------->|
;        16-25 ms
;       (60-40 Hz) -- Best use center Frequency 50Hz or 20ms

Can I Pre-Prescale ??

By using the Bits 3:0 in the CLKPS (control register)
in combination to affect the PWM frequency ??

Or:

Is it only controlled (prescaled) by the

TCCR0B - Timer/Counter Control Register B

Using Bits 2:0 - CS0[2:0]: for Clock Select

(Are they only indipendant ??)

The "TCCR0B" is very course (only 8/64/256/1024)

The "CLKPR" has a better range (1/2/4/8/16/32/64/128/256)

Will they combine to affect the PWM ??

That is example:

If I feel I need a divide by 16 (Prescale)

Set the TCCR0B with a divide by 8 and set The "CLKPR" divide by 2 ??

---- And I haven't even touched the PWM Control registers yet..

OMG !!! I'm lost..

Only examples I've found are using an ATMega8 (16bit) PWM.. running a servo..

So.. Kind-a.. Sort-a.. the same..
Then falls into the Crapper.. (in translation attempt)

------ Drives 2 servos - I only need 1
------ This is using fast PWM MODE 14
------ Clear on compare (set at TOP)

(with the /8 prescaler this makes for nice values - in micro seconds).

// OC1A, OC1B outputs
DDRB |= (1<<PB4)|(1<<PB3);
// TOP, set for 50Hz (20ms)
ICR1 = 20000;
// Center outputs (1.5ms)
OCR1A = 1500;
OCR1B = 1500;
// Timer 1 fast PWM mode 14
// Clear on compare, set at TOP
// /8 prescaler
TCCR1A = (1<<COM1A1)|(1<<COM1B1)|(1<<WGM11);
TCCR1B = (1<<WGM13)|(1<<WGM12)|(1<<CS11);

--------
--------

Problem setting this up on the ATTimy25 is:

Only an 8bit PWM.. (no 16bit)

Has OCR0A (But can't go over 255 0xFF) as only 8bits
must pre-scale for a range 50 to 200 to be .5ms to 2ms

NO "ICR1" register (Trying to figure out equivalent).. Don't see any.

No PWM mode 14 (only up to Mode 7).. (I think 7 will work)

TCCR0A -- Not as many settings (should manage to figure things)
TCCR0B -- Also incompatible but (should manage to figure things)

Any "Setup" examples for 8 Bit PWM (for a Servo) in ASM would help.

"We look for things.. Things that make us go."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Wishing to use the (Internal Default) 8MHz Clock (hope good enough)(As the Device is shipped with the CKDIV8 Fuse Programmed)
If CKDIV8 is programmed, then the default is 1MHz, not 8MHz.
Quote:
Not even sure it can be done (well) using an 8bit PWM (timer)
Probably not well with the timer hardware alone. Setting the proper frequency for the PWM would severely limit the duty cycle setting. You can, however, use software PWM driven by a timer.

Your idea of "the tiny25 is smaller, therefore it must be easier to use" is again biting you in the ass.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Plan is: (Drive a Servo) with the ATtiny25 (in Assembly)

Quote:

I need a PWM (output) frequency range from: 500 Hz to 2000 Hz

??? If you are driving a servo--the 50Hz "hobby" servo-- then why do you want a PWM frequency 10x to 40x that frequency? Or is it that you want a servo >>and<< that PWM signal?

There is a fairly extensive thread a while back on driving the "hobby" servo with an 8-bit timer. Try to search it out. IIRC the approach is to use the PWM facility of the timer to generate the on-time pulse, up to about 2ms. When that is completed, the PWM is turned off and timer periods are counted for the "dead time" of ~18ms. Then turn the PWM back on for one cycle. Rinse and repeat.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks "theusch"

I Get what you are saying..

As confused as I am with this project..
I was kind of thinking this was going to be the method.

Not finding an "Equivelant" to the (ICR1 = 20000) Like register.. available for the 16bit PWM's

By using the 8 bit "Timer" for that delay, along with the 8 bit "PWM" generator.. (sort-a 16bit)

I'll see if I can find that thread on driving
a "hobby" servo with an 8-bit timer.

Sounds like what I need.

Hummmmm (Came back to edit)

SMEG on the timers I say.

I'll make my own delays..

Turn Pin HI
Delay (x) clock cycles to equal (.5ms) or to max range of (2ms)
Turn Pin LO
Delay (x) clock cycles to equal required (Off) time
in the 50Hz time frame

Rince Repeat (as you say)

:D (Happy Camper Face)

I'll use the 8 bit timer to blink an LED (all it's good for)

---------------
---------------

Thanks "Koshchi"

If CKDIV8 is programmed, then the default is 1MHz, not 8MHz.

Was just quoting that "CKDIV8" fuse setting from the
data sheet.. (Did not read) the details about the
fuse bit.. (Thought it was 8MHz by deffault)

I'll have to look into that now also to be sure..

And prescale that frequency.. (or what ever the default is)

Sooo many steps..

Just when you want to move away from Push button in LED (Blink) Projects this happens.

"We look for things.. Things that make us go."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This pseudocode shows how to use 8 bit timer for servo.

volatile uint8_t pulse_width; // =100 for 1 ms pulse

ISR(timer_overflow) // 10us overflow
{
static uint16_t ovf_count;

   ovf_count++;

   if(ovf_count >= pulse_width)
   {  servo_output = 0; }   
 
   if(ovf_count > 1800)
   {
      servo_output = 1;
      ovf_count = 0;
   }   
}

//test
   while(1)
   {
      pulse_width = 100; // servo left
      _delay_ms(2000);
      pulse_width = 200; // servo right
      _delay_ms(2000);
   }
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for that Example:

Helps to understand the sequence

I Don't Follow "C" all that well
So I "Try to Create" using (ASM)

But, IF's are IF's and Loops are Loops..

And yup a sequence like that is my new plan..

Forgive me if wrong (as I don't follow "C")

while(1) 
   { 
      pulse_width = 50; // servo left 0.5ms
      _delay_ms(1950); // Remaining OFF Time
      pulse_width = 200; // servo right 2.0ms
      _delay_ms(1800);   // Remaining OFF Time
   } 

IF I use the 8 bit Timer it would be used
to execute "Above Like Loops" for the time
it takes the "Servo" to rotate to that posision.

As it would require " x Seconds " to rotate from 0 to 180 deg

So looping "Those Loops" for period of " x Seconds".
at that point the "Pulse generator" loop could stop

But the true duration will take some fiddling and experimenting..

Just hope I get it to work

"We look for things.. Things that make us go."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am afraid you did not understand this:

   while(1)
   {
      pulse_width = 100; // servo moves to left end
      _delay_ms(2000);   // and stays there for 2 sec
      pulse_width = 200; // servo moves to right end
      _delay_ms(2000);   // and stays there for 2 sec
   } 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
SMEG on the timers I say.
No, again, you chose the wrong AVR. You need to choose an AVR that meets your needs.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks "Visovian"

(I guessed) I would not understand it.

Like I say.. (I am lost with "C")

So.. Now I assume they are (Function Calls) ??
not just delay loops by them selfs.

All I can say that I figured out:

Using only "1" timer to attempt this will not work.
I would need a second timer to count "Off time"

Using the "PWM" 8bit timer as a "One Shot" timer.
Then use it again as a standard Timer for the time
required to be "LO"

Then "Rince Repeat" using (Timer 1) for x amount of Seconds giving the servo time to move (to what ever angle)

Or if worse comes to worse (use all 3 timers)

Either way.. Just some Jazzy nested loops that will be fun.

"We look for things.. Things that make us go."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
IF I use the 8 bit Timer it would be used to execute "Above Like Loops" for the time it takes the "Servo" to rotate to that posision.
No. The "Above Like Loops" are simply test code to set the duty cycle of the PWM on a regular basis. You simply set the duty cycle to the one you want and only change it when you want the position of the servo to change. There is no need to stop the PWM once the servo reaches the position (and in fact, the continued PWM will keep the servo at the proper position even if there is external force trying to move it off position). There is no need to use more than one timer to control a servo.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Koshchi wrote:
Quote:
SMEG on the timers I say.
No, again, you chose the wrong AVR. You need to choose an AVR that meets your needs.

Naaa.. (That would be too easy) This is more fun..

This project just has to move "1 Servo" from 0 to 95 deg's (Aprox)
then back to zero. (When a switch is pressed)

If I put my mind to it .. I could use a 555 timer.
but that will need more support components..

I don't see why I would need the "ATmega 9000 turbo" with 999 pins when all I need are 2 pins..

So.. the 8 bit timer is not that (Great)..

I'll use 2 of them.. (or all 3 if needed)

I feel that using the method that "theusch" & "Visovian" are pointing out.
it now can be done.

So Thanks guys for your ideas.. (every bit helps)

"We look for things.. Things that make us go."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
I'll use 2 of them.. (or all 3 if needed)
You only need 1.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Koshchi wrote:
Quote:
I'll use 2 of them.. (or all 3 if needed)
You only need 1.

OK.. show me in a "Flow Chart" like: for setup steps

As I don't understand "C"..

Register's and (Mode) choice..
as to me it only looks like
it can only use one time base..

Resulting in a 50% duty cycle..

All I can think of doing is First set up
for (HI) cycle then (Re-Config)
for off (LO) cycle.

Not just set and forget.

I don't need it to "Keep Running" just
0 to 90 deg's then back to 0 and it can turn off
to save draw current. (no matter how little that may be)

So to save battery life.

"We look for things.. Things that make us go."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
it can only use one time base..
All that is needed. You only need the timer to interrupt at a sufficient resolution.
Quote:
Resulting in a 50% duty cycle..
Only if the timer is driving the pin directly. In Visovian's code above, he is manipulating the pin manually in the interrupt.

Setting the timer to overflow every 20us, it will overflow 1000 times to get the frequency you need for the servo (50Hz). If you need a 1ms pulse, the pin should be high for 50 overflows, and low for 950. Simply set the pin high, count 50 overflows, set the pin low, count 950 overflows. All this is done in the interrupt.

ISR(timer_overflow) // 20us overflow
{
static uint16_t ovf_count;
   //overflow happened, increment counter
   ovf_count++;

   //if overflow is greater than or equal to the number of overflows that the pulse width takes, set the output to 0
   if(ovf_count >= pulse_width) 
   {
       servo_output = 0;
   }   
 
   //if overflow is greater than the number of overflows needed for the frequency, set the output to 1 and clear the count to start over
   if(ovf_count > frequency)//
   {
      servo_output = 1;
      ovf_count = 0;
   }   
} 

Setting the pin manually not only lets you use which ever pin you want (rather than the fixed pins that the timer allows), it also allows you to use multiple outputs (as many as you have pins available). However, it does take time which can limit your resolution. I would recommend clearing the CLKDIV8 fuse so that you can use 8MHz which gives you more time in the ISR to do things. With Visovians 10us overflow time, 1MHz won't work since you have only 10 clocks between overflows which doesn't give you enough time to complete the ISR before the next one occurs. 8MHz gives you 80 clocks.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ahh.. OK Counting (Just a simple) base timer.. no (PWM) mode output.. (per-say)

I see now.. just change "Pin High / Low"
creating (the correct type) of PWM signal.

And yup... Will (make a note) turn that Fuse Bit off.. to get the full (8Mhz)

And yup.. Set the "OCR0A" register with an "80" for 10us

To bad I don't understand "C".. Thought I did a bit..
but looks like "Not at all"..

Many examples (In "C") how ever hard to apply it when you can't follow the flow of the routines they have.

So Thanks..

"We look for things.. Things that make us go."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Without warranty (I have no tn25 to test)

.include "tn25def.inc"
; osc 8 MHz

# define OUTPUT      PORTB,1
# define OUTPUT_DDR  DDRB,1

.def temp         = r16
.def pulse_width  = r17
.def ovf_countL   = r26  ;xl
.def ovf_countH   = r27  ;xh



.DSEG

.CSEG
.org 0
      rjmp  start

.org OVF1addr          
      rjmp ovf1_isr

;--------------------------------
start:
      ;init I/O
      sbi   OUTPUT_DDR   

      ;init timer
      ldi   temp, (1<<CTC1)|(1<<CS11) ; mode ctc, prescale=8
      out   TCCR1,temp
      ldi   temp, 79
      out   OCR1C, temp               ; 10 us
      ldi   temp, (1<<TOIE1)
      out   TIMSK, temp               ; enable ovf interrupt
      
      ldi   pulse_width, 150          ; servo in center position 
      sei   

main:

rjmp main

;--------------------------------

 ovf1_isr:
      in    r1,SREG 
      
      ;ovf_count++;
      adiw  ovf_countL, 1      

      ;if(ovf_count = pulse_width) OUTPUT=0
      cpi   ovf_countH, 0
      brne  ovf1   
      cp    ovf_countL, pulse_width 
      brne  ovf1
      cbi   OUTPUT
      rjmp  ovf2

      ;if(ovf_count = 1800) {OUTPUT = 1; ovf_count = 0;} 
ovf1: 
      cpi   ovf_countH, high(1800)
      brne  ovf2
      cpi   ovf_countL, low(1800)
      brne  ovf2
      sbi   OUTPUT
      clr   ovf_countL
      clr   ovf_countH
ovf2:
      out   SREG, r1
      reti
     
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
ovf1_isr:

In CTC mode, overflow never actually happens (that would require the timer to reach 255). You really need the compare match interrupt.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks "Visovian"

LOL.. Without warranty

I will use only as flow (and setup) example any way.
I found a good document At Atmel regarding timers.

Setting up a timer for OVerFlow.. So no worries

But.. the "Counting" routines will be a great help..

ADIW (never used that OP before) will have to read the (Instruction Set)

I know what it is (Add Immediate to Word) but..
not sure if supported on the ATTiny25

Hope it is (It will work nice for counting)

-----

And 10-4 "Koshchi" for the Over Flow.. Was planning to use that method.

"We look for things.. Things that make us go."