Go Down

Topic: Reseting ATTiny85 timer so millis and delay will work properly (Read 1 time) previous topic - next topic

pir800

I am currently using an ATTiny85 using the core found at:

http://hlt.media.mit.edu/?p=1695

I'm using the 1MHz clock option

I have a PWM and timer setup to control an RGB LED and it's working fine. Here is my setup:

Code: [Select]
TCCR0A = 2<<COM0A0 | 2<<COM0B0 | 3<<WGM00;
TCCR0B = 0<<WGM02 | 1<<CS00;
TCCR1 = 0<<PWM1A | 0<<COM1A0 | 1<<CS10;
GTCCR = 1<<PWM1B | 2<<COM1B0;


From here I assign my Red/Green/Blue with the following method:

Code: [Select]
void showColor(byte red, byte green, byte blue) {
 
    OCR0A = ~red;
    OCR0B = ~green;
    OCR1B = ~blue;
}


It works great. But now whenever I use delay or millis the values are not correct. My project has different modes. One mode allows the RGB color to be set, but I also want to reset my timer(s) in another mode that will give proper values for delay/millis.

Can this be done at runtime?

I looked at the datasheet and it says that TCCR0A, TCCR0B, TCCR1, GTCCR are 0x00 by default, but when I set these registers to 0x00 my program stops. I assume because I have turned off the timers.

My questions are these:

1. Does someone have the default values for the timer registers that the Arduino ISP uses?

2. Can someone tell me where to find this information?

3. Is there a problem with setting the timers at runtime after setup?



pylon

Quote
2. Can someone tell me where to find this information?


it's in hardware/arduino/cores/arduino/wiring.c

The Timer0 interrupt has to be called once every 1024 ┬Ás. So set the prescaler and overrun value appropriately.


Quote
3. Is there a problem with setting the timers at runtime after setup?


No, except that the millis() won't be the milliseconds after startup in this case.

pir800

Ok.. I just went through the process to reset the timer to default values and I wanted to get an opinion on my thought process. Bare with me please...

timing section in wiring.c:

Code: [Select]
// the prescaler is set so that timer0 ticks every 64 clock cycles, and the
// the overflow handler is called every 256 ticks.
#define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(64 * 256))

// the whole number of milliseconds per timer0 overflow
#define MILLIS_INC (MICROSECONDS_PER_TIMER0_OVERFLOW / 1000)

// the fractional number of milliseconds per timer0 overflow. we shift right
// by three to fit these numbers into a byte. (for the clock speeds we care
// about - 8 and 16 MHz - this doesn't lose precision.)
#define FRACT_INC ((MICROSECONDS_PER_TIMER0_OVERFLOW % 1000) >> 3)
#define FRACT_MAX (1000 >> 3)

volatile unsigned long timer0_overflow_count = 0;
volatile unsigned long timer0_millis = 0;
static unsigned char timer0_fract = 0;

#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
SIGNAL(TIM0_OVF_vect)
#else
SIGNAL(TIMER0_OVF_vect)
#endif
{
// copy these to local variables so they can be stored in registers
// (volatile variables must be read from memory on every access)
unsigned long m = timer0_millis;
unsigned char f = timer0_fract;

m += MILLIS_INC;
f += FRACT_INC;
if (f >= FRACT_MAX) {
f -= FRACT_MAX;
m += 1;
}

timer0_fract = f;
timer0_millis = m;
timer0_overflow_count++;
}

unsigned long millis()
{
unsigned long m;
uint8_t oldSREG = SREG;

// disable interrupts while we read timer0_millis or we might get an
// inconsistent value (e.g. in the middle of a write to timer0_millis)
cli();
m = timer0_millis;
SREG = oldSREG;

return m;
}



I'm not seeing how this gets me a default prescale and overrun value, but I do see this in the init section:

Code: [Select]
// on the ATmega168, timer 0 is also used for fast hardware pwm
// (using phase-correct PWM would mean that timer 0 overflowed half as often
// resulting in different millis() behavior on the ATmega8 and ATmega168)
#if defined(TCCR0A) && defined(WGM01)
sbi(TCCR0A, WGM01);
sbi(TCCR0A, WGM00);
#endif 

// set timer 0 prescale factor to 64
#if defined(__AVR_ATmega128__)
// CPU specific: different values for the ATmega128
sbi(TCCR0, CS02);
#elif defined(TCCR0) && defined(CS01) && defined(CS00)
// this combination is for the standard atmega8
sbi(TCCR0, CS01);
sbi(TCCR0, CS00);
#elif defined(TCCR0B) && defined(CS01) && defined(CS00)
// this combination is for the standard 168/328/1280/2560
sbi(TCCR0B, CS01);
sbi(TCCR0B, CS00);
#elif defined(TCCR0A) && defined(CS01) && defined(CS00)
// this combination is for the __AVR_ATmega645__ series
sbi(TCCR0A, CS01);
sbi(TCCR0A, CS00);
#else
#error Timer 0 prescale factor 64 not set correctly
#endif

// enable timer 0 overflow interrupt
#if defined(TIMSK) && defined(TOIE0)
sbi(TIMSK, TOIE0);
#elif defined(TIMSK0) && defined(TOIE0)
sbi(TIMSK0, TOIE0);
#else
#error Timer 0 overflow interrupt not set correctly
#endif


If I'm tracing the code properly it seems like I should execute the following because TCCR0B and TIMSK are present:

Code: [Select]
sbi(TCCR0B, CS01);
sbi(TCCR0B, CS00);
sbi(TIMSK, TOIE0);


The datasheet says that setting CS00 and CS01 is the clkio/64 (from prescaler)

Then it looks like I need to set the overrun value back to 0xFF. The datasheet says that WGM0[2:0] = 0 should do the trick.

This is what I end up with.. Does this make sense?

RGB Control Mode:
Code: [Select]
TCCR0A = 2<<COM0A0 | 2<<COM0B0 | 3<<WGM00;
TCCR0B = 0<<WGM02 | 1<<CS00;
TCCR1 = 0<<PWM1A | 0<<COM1A0 | 1<<CS10;
GTCCR = 1<<PWM1B | 2<<COM1B0;


Timer Mode:
Code: [Select]
TCCR0A = 0<<COM0A0 | 0<<COM0B0 | 0<<WGM00;
TCCR0B = 0<<WGM02 | 3<<CS00;
TIMSK |= 1<<TOIE0;


I'm not at home so I can't compile this until later tonight, so I wanted to see if this makes sense.


pylon

Add

Code: [Select]
TIMSK &= ~(1<<TOIE0);

to your PWM setup to disable the timer interrupt during the PWM phase.

pir800


Go Up
 


Please enter a valid email to subscribe

Confirm your email address

We need to confirm your email address.
To complete the subscription, please click the link in the email we just sent you.

Thank you for subscribing!

Arduino
via Egeo 16
Torino, 10131
Italy