Reseting ATTiny85 timer so millis and delay will work properly

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

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:

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:

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?

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

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

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:

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

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

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:

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:

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.

Add

TIMSK &= ~(1<<TOIE0);

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

Excellent. It's working. Thanks for your help!

pir800, would you mind to share your code. I struggle with the same issue and ms are not real millisecond.