1 ms system tick, whats the concensus?

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

So I need to implement a 1 ms systick on a 16mhz atmega164, it has 2 8 bit timers and one 16.

My kneejerk reaction is to run the 16bit time OCR and increment systick in the IRQ.

( Yes I understand atomic access and IRQ's )

 

I see the arduino uses the 8 bit timer0 w a /64 prescale and overflow at 256

but thats 976ms.... o.O

I suppose they are trying to save timer1 for applications?

 

I normally use Pic's and Arms that have more 16 bit timers

 

Anyway, is there a consensus on ticking in the atmega world?

 

Thanks

Keith, new to Atmga, not new to 8 bit

Keith Vasilakes

Firmware engineer

Minnesota

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

No real consensus. How  you do it depends entirely on the availability of timers. 

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

Wait a minute, why not use the 8-bit in CTC mode with prescale of 64 and count of 250 (meaning, load 249 into the compare register)?

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

The Arduino happens to use Tiimer0 in OVF mode.    e.g. 16MHz / 64 / 256 = 976Hz or 1.024ms

You could use it in CTC mode.   e.g. 16MHz / 64/ 250 = 1000Hz or 1.00ms    (OCR0A = 249)

 

I would save Timer1 for better things.

I would save Timer2 for ASSR.

 

Since most Arduino hardware uses ceramic resonators,    the timekeeping is not very accurate.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 250.000 kHz
// Mode: CTC top=OCR0A
// OC0A output: Disconnected
// OC0B output: Disconnected
// Timer Period: 1 ms
TCCR0A=(0<<COM0A1) | (0<<COM0A0) | (0<<COM0B1) | (0<<COM0B0) | (1<<WGM01) | (0<<WGM00);
TCCR0B=(0<<WGM02) | (0<<CS02) | (1<<CS01) | (1<<CS00);
TCNT0=0x00;
OCR0A=0xF9;
OCR0B=0x00;

Wait a minute, why not use the 8-bit in CTC mode with prescale of 64 and count of 250 (meaning, load 249 into the compare register)?

 

That's the way CodeVision wizard would do it.  Also avrcalc and kavrcalc.

 

I don't know where 976ms comes from -- I don't think an AVR8 8-bit timer can reach that far, much less at 16MHz.

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.

Last Edited: Fri. Nov 14, 2014 - 11:56 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

16MHz / 64 / 256 is 976 _Hz_

 

OP, it's actually easier to use an 8-bit timer to generate an accurate tick at a faster rate (like your 1ms) than at a slower rate.  The problem is mostly the absurdly low selection of prescale values.

 

Suppose one needed a 10ms tick, and that it could have some tick-to-tick jitter as long as the long-term rate was exactly 1000/sec.  The math says 16000000/1024/100 = 156.25, so we would need our 8-bit timer to tick every 156.25 counts - an obvious impossibility.

 

But what we can do is to tick 156, 156, 156, 157, 156...  That is, every 4th tick, we increase the compare value from 156 to 157, then back again.  This results in a maximum per-tick timing error of 0.75/156.25 or 0.48%, and a long-term timing error of 0%.

 

Here is code I've written in the past to do this.  The approach I took only uses 8-bit math, to keep things as efficient as possible on an 8-bit chip.

 

// since our goal is a tick count of 156.25, 
// we will switch to 157 1 time out of every 4

#define CNT_BASE 156
#define CNT_NUM  1
#define CNT_DEN  4   // 1/4

ISR(8_BIT_CTC_MODE) // fill in correct vector name
{
    static int8_t acc = 0;
    
    ...first do all regular tick ISR work...
    
    // this is the additional clock-slipping code, all 8-bit math
    acc -= CNT_NUM;
    if (acc < 0)
    {
        acc += CNT_DEN;
        OCR0A = CNT_BASE+1; // slip one additional clock this time
    }
    else
    {
        OCR0A = CNT_BASE;  // don't slip
    }
}

This will work with any value of CNT_DEN < 128, I think.  Normally CNT_DEN won't be greater than 8 or maybe 16, and CNT_NUM will always be < CNT_DEN.

 

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

theusch wrote:

// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 250.000 kHz
// Mode: CTC top=OCR0A
// OC0A output: Disconnected
// OC0B output: Disconnected
// Timer Period: 1 ms
TCCR0A=(0<<COM0A1) | (0<<COM0A0) | (0<<COM0B1) | (0<<COM0B0) | (1<<WGM01) | (0<<WGM00);
TCCR0B=(0<<WGM02) | (0<<CS02) | (1<<CS01) | (1<<CS00);
TCNT0=0x00;
OCR0A=0xF9;
OCR0B=0x00;

Wait a minute, why not use the 8-bit in CTC mode with prescale of 64 and count of 250 (meaning, load 249 into the compare register)?

 

That's the way CodeVision wizard would do it.  Also avrcalc and kavrcalc.

 

I don't know where 976ms comes from -- I don't think an AVR8 8-bit timer can reach that far, much less at 16MHz.

 

oops 976 us if I did my math right,I usually have to try a couple of times :P

16,000,000 /64/256 = 976.56

 

using the ooutput compare at 250 would be spot on 1 ms, much betterer

 

that looks like the winner

 

Keith Vasilakes

Firmware engineer

Minnesota

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

oops 976 us if I did my math right,I usually have to try a couple of times :P

16,000,000 /64/256 = 976.56

976.n Hz, as mentioned earlier. 1.024ms. Check your units -- you are starting with Hz and dividing by unitless numbers.

 

[Tell more about this app with the need for a fast >>and<< accurate tick.]

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

dang units, yes 976 hz

no requirement for high accuracy, but no need to be inaccurate on purpose.

 

the output compare irq every 250 counts is plenty good

Keith Vasilakes

Firmware engineer

Minnesota

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
ISR(TIMER0_OVF_vect)
{
	// Full count overflows happen every 1.024ms at 16MHz
	// Restart counter above zero for other rates
	// Never do this if using PWM on this timer 
	// (or at least never let the OCR value below the start value)
	//TCNT0 = 131;	// for .5ms
	TCNT0 = 6;	// for 1ms
	
	// Update various task timers
	if (positionupdatetime > 0)  positionupdatetime--;
	if (stateUpdateTime > 0) stateUpdateTime--;
	if (ledupdatetime != 0) 	ledupdatetime--;
	if (swdebouncetime > 0) 	swdebouncetime--;

}

You can stop the timer at 250 using the appropriate timer mode or start the timer at 6 in the Overflow ISR

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
	TCNT0 = 6;	// for 1ms

For the benefit of others.     This method is guaranteed to give you jitter and inaccurate long term timing.    However it is fine for tasks that just need approximate timing.

 

Use the CTC if you want to maintain a 'system time' or RTC.     The hardware will work jitter-free.

And be perfectly accurate for all types of timing tasks.

 

David.

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

Teensyduino and I assume Arduino, correct for the 1mSec interrupt rate error by adjusting the count within the ISR every few interrupts.

 

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

Yes, Arduino maintains two variables: timer0_millis and timer0_fract.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

Of course you can make a software adjustment to 100% jitter-free hardware.

Making a latency-dependent hardware adjustment to the hardware is going to produce a cumulative error of all the latencies.

 

Yes,    it is a little academic with most Arduino boards.      They have inaccurate ceramic resonators in the first place.

However,   a regular HF crystal will keep reasonable accuracy for a RTC.   e.g. displaying hh:mm for weeks.

 

David.

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

Arduino uses the OVF interrupt at "approximately" 1ms, because that frees up ALL of the compare registers for use by PWM output.

(It was very disconcerting when I tried to move the logic to MSP430, and discovered that I *had* to dedicate a compare register to the periodic interrupt...)

 

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

david.prentice wrote:

	TCNT0 = 6;	// for 1ms

For the benefit of others.     This method is guaranteed to give you jitter and inaccurate long term timing.    However it is fine for tasks that just need approximate timing.

David,

Do I understand correctly that the source of the jitter the uncertainty that the counter will be set to 6 before the next count? I can see the potential for higher priority interrupts blocking. Perhaps not such a big deal with larger prescale settings.

I learned this trick on older chips where timer 0 does not have compare functions and never thought of the missing counts (nor had an application so time critical)

Kirk

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

KirkCharles wrote:

 

david.prentice wrote:

 

	TCNT0 = 6;	// for 1ms

For the benefit of others.     This method is guaranteed to give you jitter and inaccurate long term timing.    However it is fine for tasks that just need approximate timing.

 

 

David,

Do I understand correctly that the source of the jitter the uncertainty that the counter will be set to 6 before the next count? I can see the potential for higher priority interrupts blocking. Perhaps not such a big deal with larger prescale settings.

I learned this trick on older chips where timer 0 does not have compare functions and never thought of the missing counts (nor had an application so time critical)

Kirk

Yes, I learned the technique on the 8051.

 

Remember, you have no idea what the counter actually reads when you load it - that is, you have no idea how much time has elapsed from the overflow interrupt to reaching that part of the ISR code.  A much better way (but still subject to 1-count jitter) is 

TCNT += 6;

Then if the counter has been advancing, this will almost compensate for that.  The "almost" fails when the clock overflows during the addition RMW operation.

 

And yes, the problem becomes less or even goes away with larger prescales.