System tick timer and protothreads - advice needed

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

Good day!

I'm working on laying down some kind of framework for my projects (MCU is ATmega8A at the moment), here are my ingredients:

- System tick timer on TIMER0 (code from Arduino project actually)
- Protothreads (original implementation from here: http://www.sics.se/~adam/pt/)

Demo is blinking two LEDs with different rates.

I've a couple of architectural questions (code snippets below):

- System tick timer routine uses uint32_t -- seems to big for 8bit MCU?
- Is there any proven system tick timer implementation?
- Am I using prothreads the right way?
- Is it good to combine system tick timer and protothreads this way, does this all have any sense :)?

Thanks in advance for any input!

Example code:

static struct pt blink1_pt, blink2_pt;

static PT_THREAD(blink1(struct pt *pt))
{
	static uint32_t last_timer;

	PT_BEGIN(pt);

	last_timer = st_millis();

	#if DEBUG_BUILD
		puts_P(PSTR("Blinking protothread #1 started..."));
	#endif
	
	while(1)
	{
		PT_WAIT_UNTIL(pt, (st_millis() - last_timer) >= 500);
		last_timer = st_millis();
		TOGGLE(LED1);
		#if DEBUG_BUILD
			printf_P(PSTR("Blink #1 at %i\n"), last_timer);
		#endif
	}
	PT_END(pt);
}

static PT_THREAD(blink2(struct pt *pt))
{
	static uint32_t last_timer;

	PT_BEGIN(pt);

	last_timer = st_millis();

	#if DEBUG_BUILD
		puts_P(PSTR("Blinking protothread #2 started..."));
	#endif

	while(1)
	{
		PT_WAIT_UNTIL(pt, (st_millis() - last_timer) >= 1000);
		last_timer = st_millis();
		TOGGLE(LED2);
		#if DEBUG_BUILD
			printf_P(PSTR("Blink #2 at %i\n"), last_timer);
		#endif
	}
	PT_END(pt);
}


int main(void)
{
	init_io();
	st_init();

	sei();
	
	PT_INIT(&blink1_pt);
	PT_INIT(&blink2_pt);

	while(1)
	{
		blink1(&blink1_pt);
		blink2(&blink2_pt);
	}
}

System tick timer implementation:

#include 

#include "st.h"

volatile static uint32_t timer0_overflow_count = 0;
volatile static uint32_t timer0_millis = 0;
static uint8_t timer0_fract = 0;

ISR(TIMER0_OVF_vect)
{
	// copy these to local variables so they can be stored in registers
	// (volatile variables must be read from memory on every access)
	uint32_t m = timer0_millis;
	uint8_t 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++;
}

uint32_t st_millis(void)
{
	uint32_t 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;
}

void st_init(void)
{
	// Set prescaler to 64
	TCCR0 |= (_BV(CS01) | _BV(CS00));

	// Enable timer 0 overflow interrupt
	TIMSK |= _BV(TOIE0);
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why do you think you need protothreads? What kind of applications do you expect?

Regards,
Steve A.

The Board helps those that help themselves.

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

I posted a similar concept a while back, in a way very similar to the SysTick concept utilized by CMSIS (1.3) - I think the call names are even identical.

I can look through my files to see if I can find it.

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

Quote:
Why do you think you need protothreads? What kind of applications do you expect?

Here are my motivations:

- Almost any of my demo app require some sort of flags that are set from ISR. I'd like to have a common approach to lay down applicaiton logic (something more clear than one big switch), so I choose protothreads. They are state machines under the hood, but with clean syntax.

- I'd like to use existing libs (UART, LCD 16x2, 1-wire etc) from here http://www.jump.to/fleury or here http://www.siwawi.arubi.uni-kl.d... If I use some OS I need to rewrite\use different all of these.

Are these right points to get considered about?

Quote:

I posted a similar concept a while back, in a way very similar to the SysTick concept utilized by CMSIS (1.3) - I think the call names are even identical.

Thanks for this, here are link:
https://www.avrfreaks.net/index.p...
SysTick is almost what I was looking for, I've googled this in NXPXpresso (ARM Cortex dev board) forums :)

Thank you for the responses!

A couple of questions:

1. You also use:

volatile unsigned long SysTick; 

Does using 32 bit integer slow things?
How do you handle overflow of this variable?

2. How accurate is timer like this? For reading it value we need to disable interrupts (to read it atomically), does this affect accuracy?

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

Protothreads are quite lightweight compared to OS, and yes they are just a way to write state machines, but still adds a dependency and layer of complexity to use them, even for really simple stuff. It is nice to see your example, as I have once thought I could use protothreads for something.

Most libraries are not OS specific either, so they should be fine if you just call the routines within an OS, protothreads or just plain C code, but bear in mind that the routines are not re-entrant, so you cannot for example write to LCD in an interrupt if you write to LCD in main loop. Not that anyone should write to LCD in an interrupt, but that was just an example.

But for something to fill Atmega8, is protothreads necessary? Without taking consideration how the actual led_blinker threads/handlers do their stuff internally, the main loop can be just written like this:


int main(void)
{
   init_io();
   st_init();

   sei();
   
   ledblinker1_init();
   ledblinker2_init();

   while(1)
   {
      ledblinker1_handler();
      ledblinker2_handler();
   }
} 

As per your questions, 32-bit integers on a 8-bit micro is a bit slower, but you can simplify your timekeeping a bit, if you generate your timer tick every millisecond, so no fractional timekeeping is needed, just increment a millisecond counter. Handling overflow depends how you want to do it. Sometimes you handle it by just letting the values overflow.

The timer is as accurate as your clock source, how you configure the timer, and how you keep track of time. But a timer interrupt that modifies the variables is not allowed to modify variables in the middle when you are reading the variables, so it will execute a bit later when interrupts are again enabled. But the timer still runs in the background, so it won't affect accuracy.

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

Quote:
Thank you for the responses!

there are multiple issues with that piece of code, as discussed there.

Quote:
Does using 32 bit integer slow things?

it does but not noticeably - on the us scale.

I used a unsigned long so the code is compatible with CMSIS. you should re-evaluate that and see if it fits your own application.

Quote:
How do you handle overflow of this variable?

the routine itself didn't. However, I did figure out how to write the comparison against SysTick in one of those compilers (iar or icciar) so that the overflow is not an issue, but never figured out how to do it for other compilers. I can dig it out if you want.

one important note: unlike the hardware SysTick in the CM3 chips, the software SysTick here has jitters (but otherwise accuracy over the long term). the size of the jitter depends on the size of the timer used (8-bit vs. 16bit - larger timers generate larger jitters. for example, an 8-bit timer @ 1 tick / us can have a maximum jitter of 255us), the use of prescaler (the bigger the prescaler, the bigger the jitter). Obviously, the use of a larger timer / prescaler lessens the load on the mcu.

the code was written for a generic timer. you obviously can localize it to your chip and produce a SysTick specific to your hardware.

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

having looked at the code now, I do realize that it utilizes the ctc mode so my comments earlier on jitter are off.

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

Jepael wrote:
It is nice to see your example, as I have once thought I could use protothreads for something.

For now the only code I have with protothreads is blinking example above... That's just proof of concept :)

Jepael wrote:
Most libraries are not OS specific either, so they should be fine if you just call the routines within an OS

Most of libraries I'm using enable and disable interrupts in their code, have own interrupts handlers etc. Does this interfere with OS? I'm looking to get something like FemtoOS (http://www.femtoos.org/).

Jepael wrote:
But for something to fill Atmega8, is protothreads necessary? Without taking consideration how the actual led_blinker threads/handlers do their stuff internally, the main loop can be just written like this

The problem is when I'll have a couple of buttons and some other devices my main loop will became unmanageable. So the main goal with protothreads is to separate this to "threads" and don't have giant switch in main... May be I'm wrong...

Quote:
As per your questions, 32-bit integers on a 8-bit micro is a bit slower, but you can simplify your timekeeping a bit, if you generate your timer tick every millisecond, so no fractional timekeeping is needed, just increment a millisecond counter.

My goal was to use TIMER0 (8 bit on ATmega8/168/328) to left other more advanced timers free for PWM or something else. It seems that at 8Mhz system clock I can't configure TIMER0 in the way it will trigger ISR each 1ms? 1024 is max prescaler value?

Quote:
But a timer interrupt that modifies the variables is not allowed to modify variables in the middle when you are reading the variables, so it will execute a bit later when interrupts are again enabled. But the timer still runs in the background, so it won't affect accuracy.

One quick question -- what if timer overflows more than once while interrupts are disabled? Will it retrigger ISR multiple times?

millwood wrote:

However, I did figure out how to write the comparison against SysTick in one of those compilers (iar or icciar) so that the overflow is not an issue, but never figured out how to do it for other compilers. I can dig it out if you want.

I've re-read your original thread but I haven't found the code that handle overflows. Can you share it with me?

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

Quote:
My goal was to use TIMER0 (8 bit on ATmega8/168/328) to left other more advanced timers free for PWM or something else. It seems that at 8Mhz system clock I can't configure TIMER0 in the way it will trigger ISR each 1ms? 1024 is max prescaler value?

you don't have to use a prescaler.

two (or more) approaches to it:

1) in the timer isr, increment a global variable and then test to see if it has go over a preset limit. if so, adjust it and set a flag so you can act on the flag in your main(). this approach will have jitter but will retain its long-term accuracy.
2) or reload an offset in the timer isr to get precise timing (see 100us) and count SysTick every 10 interrupt to trigger an action. this approach has no jitter but consumes more mcu processing power.

Quote:
Can you share it with me?

not a problem. just let me dig around a little.

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

millwood wrote:

1) in the timer isr, increment a global variable and then test to see if it has go over a preset limit. if so, adjust it and set a flag so you can act on the flag in your main(). this approach will have jitter but will retain its long-term accuracy.

That's the same in my (actual -- Arduino team) code snippet above. They have additional logic to handle fract part...

Quote:

not a problem. just let me dig around a little.

Thanks!

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

no luck yet but here is a modified piece that will address the overflow issue (partially).

//demo code to show the implementation of CMSIS systick-type tmr
//resources used: tmr1, channel A only.
//utilizes a generic timer, has jitter
#include 
#include 
#include "gpio.h"

#ifdef F_CPU
  #undef F_CPU
#endif

#define F_CPU			1000000ul         			//cpu running at 8Mhz

#define LED_PORT		PORTB
#define LED_DDR			DDRB
#define LED				(1<<0)						//led on pb.0
#define LED_DLY			10               			//delay, in terms the number of of systick interrupts (1ms in this case)

volatile unsigned long SysTick=0xfffffff0;			//global variable holding SysTick
unsigned short _SysTick_Duration = 0;				//duration for systick timer

void SysTick_Config(unsigned short duration);

ISR(TIMER1_OVF_vect) {         						//tmr1 overflow / systick tmr
   TCNT1 += _SysTick_Duration;						//advance the offset
   SysTick++;
}

void SysTick_Config(unsigned short duration) {
	TCCR1A =	(0<<COM1A1) | (0<<COM1A0) |      	//normal operation for com1a
				(0<<COM1B1) | (0<<COM1B0) |      	//normal operation for channel b
				(0<<COM1C1) | (0<<COM1C0) |      	//normal operation for channel c
				(0<<WGM11) | (0<<WGM10);      		//wgm13..0=0b0100, CTC mode
	TCCR1B =	(0<<ICNC1) |               			//input caputure noise canceller: disabled
				(0<<ICES1) |               			//input caputure edge select: falling edge
				(0<<WGM13) | (0<<WGM12) |      		//normal 16-bit tmr mode
				(0<<CS12) | (0<<CS11) | (1<<CS10);	//cs2..0=0b001, timer activated, 1:1 prescaler
	TCCR1C =	(0<<FOC1A) |               			//channel a output disabled
				(0<<FOC1B) |               			//channel b output disabled
				(0<<FOC1C);                  		//channel c output disabled
	//OCR1A =		duration;                  			//set the top of CTC on channel a
	_SysTick_Duration = -duration;
	TCNT1 =		_SysTick_Duration;                        			//reset timer counter
	TIMSK1 |=	(0<<ICIE1) |						//input capture interrupt: disabled
				(0<<OCIE1C) |						//output compare interrupt for channel c disabled
				(0<<OCIE1B) |						//output compare interrupt for channel b disabled
				(0<<OCIE1A) |						//output compare interrupt for channel a disabled
				(1<<TOIE1);               			//tmr1 overflow interrupt: enabled
	sei();                              			//enable global interrupt
}

void mcu_init(void) {
   IO_CLR(LED_PORT, LED);                  			//clear led
   IO_OUT(LED_DDR, LED);                  			//led as output
}

int main(void) {

	unsigned long tmp;

	mcu_init();                           			//reset the mcu
	SysTick_Config(F_CPU/1000);               		//1000 tmr ticks (1000us = 1ms) per interrupt

	while(1) {
		//tmp=SysTick + LED_DLY;
		//while (SysTick < tmp) continue;   		//wait for led_dly to pass
		tmp=SysTick + LED_DLY;
		while (SysTick ^ tmp) continue;   			//wait for led_dly to pass
		IO_FLP(LED_PORT, LED);               		//flip led
	}
}

it does not rely on the ctc mode. instead, it utilizes timer offset so it can be easily ported to other chips / tmr0. aka it is approach #2 from the above.

as to the overflow, I rewrote the logic so that it compares SysTick (current time) vs. the desired time (tmp = SysTick + LED_DLY;) and if the two are the same, it drops out of the while loop.

in this particular case, I offset SysTick so that it takes about 16ms for it to overflow (from 0xffffffff to 0x00000000), between 10ms vs. 20ms. On my chip (usb1286) the code performs as expected: it continues to tick at 10ms intervals and unaffected by the overflow.

the downside with this approach is that you have to make sure that you test SysTick at least once within each timer interrupt so you don't miss the desired time.

hope it helps.

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

Quote:

the downside with this approach is that you have to make sure that you test SysTick at least once within each timer interrupt so you don't miss the desired time.

Yes, that's the point. What if set some flag about target time? Like this (pseudo code):

	bool overflow;
	
	while(1) { 

		// Shouldn't we disable interrupts 
		// before reading SysTick (to do this "atomically")?
		cli();
		current_tick = SysTick;
		sei();
		
		tmp = current_tick + LED_DLY;
		overflow = (tmp < current_tick);
	
		while ( (overlow && sysTick > tmp) || (!overflow && SysTick < tmp)) continue; // busy wait
		
		IO_FLP(LED_PORT, LED);
	}

Another interesting approach from comments here (http://www.faludi.com/2007/12/18...):

Quote:

Hi folks,

There’s a much better generic way of handling rollovers on timers. Instead of using unsigned timer values you use signed timer values. Then to decide whether time1 > time2 you simply calculate (time1-time2>0) which returns correct results for all cases where the period itself is b isn’t equivalent to a-b>0, due to the nature of 2?s compliment arithmetic. Thus, if instead we have:

long eventTimeout=(long)millis()+1000;
if((long)millis()-eventTimeout>=0) {
eventTimeout=(long)millis()+1000;
}

We will *never* get a roll-over condition! Consider, in the old code, the if statement would fail when millis=0, and oldMillis was 4294967000. Here, millis-oldMillis (very much) >= 1000 even though the timer should not have expired.

However, with the new code, we instead calculate the eventTimeout and compare with 0, so in the case above (long)millis – eventTimeout = -704, so there’s no timeout, until millis>=704.

You might think this technique would fail when the signed values roll-over to unsigned values. However, it doesn’t. Consider when eventTimeout was 2147483000. When millis() reached that value, so millis()-eventTimeout was >=0, eventTimeout was then set to 2147484000 which is actually -2147483296. The timeout looks like it’s in the past, but because we’re doing signed arithmetic (long)millis()-eventTimeout = 2147483000-(-2147483296) = -1000 and so it sees it as being in the past (which it is). Similarly when (long)millis() crosses from 2147483647 to -2147483648 the subtraction will yield -353, then -352 and will only result in >=0 when (long)millis() = -2147483296 as intended.

This can be combined into a function:

#define smillis() ((long)millis())

boolean after(long timeout)
{
return smillis()-timeout>0;
}

Then your code will always work if you do:

long timeout=smillis()+1000;

…
if(after(timeout)) {
… do something.
timeout=1000;
}

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

Doing read-modify-write on actual timer counter may be problematic, especially when prescaler is not 1.

If this kind of value-modifying approach is wanted, it is better to read-modify-write the compare register, or just written, if the next value is held in ram variable already.

Best is to use CTC mode, available on all AVRs. Just set prescaler to 64 and limit the count to 125 ticks (TOP=124) and you have exactly 1000Hz timer from 8MHz. Different models do have different prescalers, so of course that may have to be modified.

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

Unfortunatelly on ATmega8A TIMER0 doesn't have CTC mode...

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

artvolk wrote:
Unfortunatelly on ATmega8A TIMER0 doesn't have CTC mode...

Yeah, sorry I did not see that part, and I thought the timer0 had CTC.

In that case it is best to do read-modify-write on counter value to get 1000Hz, or just keep time in other units to begin with, and then convert to something more reasonable like milliseconds, and accept the jitter while still being able to keep accurate time in the long run.

For example, setting the prescaler to 256, gives you timer tick about every 122 Hz, so your time increment is a bit over 8ms, usually 8ms but sometimes 9ms. And if you must have 1ms resolution, your interrupts would have to happen at over 1kHz so that your time increment is either 0ms or 1ms.

In one project I had a chance to configure a 16-bit timer to advance precisely 1 tick every millisecond, so my timekeeping just involved reading the timer count value once per mainloop, and count the time delta from previous read, and accumulate that in places that need timing. I just have to make sure I read the timer at least every 65535 milliseconds or about 65 seconds, which is an eternity really. It could calculate 100us time units too for more precision, 6.5 seconds is also an eternity. But with just those prescaler values you have on 8-bit timer, the maximum overflow period is about 32ms..

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

how about this one?

int main(void) {

	unsigned long tmp, t;

	mcu_init();                           			//reset the mcu
	SysTick_Config(F_CPU/1000);               		//1000 tmr ticks (1000us = 1ms) per interrupt

	while(1) {
		//tmp=SysTick + LED_DLY;
		//while (SysTick < tmp) continue;   		//wait for led_dly to pass

		//tmp=SysTick + LED_DLY;
		//while (SysTick ^ tmp) continue;   			//wait for led_dly to pass

		tmp = SysTick;
		do {
			if (SysTick >= tmp) t=SysTick - tmp;
			else t=0xfffffffful-tmp+1+SysTick;
		} while (t < LED_DLY);
		IO_FLP(LED_PORT, LED);               		//flip led
	}
}

essentially, an overflow takes place if SysTick < tmp. when that happens, the true time elapsed is [maximum unsigned long - tmp] + SysTick, rather than SysTick - tmp.

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

millwood wrote:
how about this one?

if (SysTick >= tmp) t=SysTick - tmp;
else t=0xfffffffful-tmp+1+SysTick;

essentially, an overflow takes place if SysTick < tmp. when that happens, the true time elapsed is [maximum unsigned long - tmp] + SysTick, rather than SysTick - tmp.

The overflow does not need to be handled separately.
Assuming 32-bit integers, there is no difference in IF and ELSE branches mathematically, as 0xFFFFFFFFul+1 is 0, so in either case, you are left with t=SysTick-tmp.

So t always means difference between two reads, and may be accumulated somewhere else for timekeeping.

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

Quote:

or example, setting the prescaler to 256, gives you timer tick about every 122 Hz, so your time increment is a bit over 8ms, usually 8ms but sometimes 9ms. And if you must have 1ms resolution, your interrupts would have to happen at over 1kHz so that your time increment is either 0ms or 1ms.

It seems that's the approach I've copied from Arduino's timer:

#include 

#include "st.h"

#define clockCyclesToMicroseconds(a) ( ((a) * 1000L) / (F_CPU / 1000L) )

// 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 static uint32_t timer0_millis = 0;
static uint8_t timer0_fract = 0;

ISR(TIMER0_OVF_vect)
{
	// copy these to local variables so they can be stored in registers
	// (volatile variables must be read from memory on every access)
	uint32_t m = timer0_millis;
	uint8_t f = timer0_fract;

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

	timer0_fract = f;
	timer0_millis = m;
}

void st_init(void)
{
	// Set prescaler to 64
	TCCR0 |= (_BV(CS01) | _BV(CS00));

	// Enable timer 0 overflow interrupt
	TIMSK |= _BV(TOIE0);
}

Quote:

In one project I had a chance to configure a 16-bit timer to advance precisely 1 tick every millisecond

Do you mean something like this:


ISR(TIMER1_COMPA_vect)
{
	TOGGLE(LED2);
}

....

// Timer1 -----------------------------------------
	TCCR1B |= _BV(CS12); // 8000000 / 256 = 31250
	TCCR1B |= _BV(WGM12); // Configure timer 1 for CTC mode
	OCR1A = 31250;
	TIMSK |= _BV(OCIE1A);

Quote:
Assuming 32-bit integers, there is no difference in IF and ELSE branches mathematically, as 0xFFFFFFFFul+1 is 0, so in either case, you are left with t=SysTick-tmp.

millwood, can I kindly ask you to test this with your setup?

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

artvolk wrote:

It seems that's the approach I've copied from Arduino's timer:

Yes it seems to do that at a quick glance. Many different ways to end up in same result. For example, you could just add time in crystal ticks, and when counter reaches 8000, you have one millisecond, or 8000000 for one second. I would do that for a wall clock perhaps, because it enables fine tuning if clock leads or lags, but perhaps not for just system timer ticks.

Quote:

Do you mean something like this:


// Timer1 -----------------------------------------
	TCCR1B |= _BV(CS12); // 8000000 / 256 = 31250
	TCCR1B |= _BV(WGM12); // Configure timer 1 for CTC mode
	OCR1A = 31250;
	TIMSK |= _BV(OCIE1A);

You configure the timer to advance 1 count at 31250Hz, and then you set the timer to clear on compare, which gives you almost 1Hz overflow interrupts (OCR1A is off by one, to make it count 31250 ticks before overflow, you set it to count from 0 to 31249). Perfect for counting seconds.

My timer advanced 1 count per millisecond, overflowing every 65536 milliseconds. But this is impossible with your hardware anyway.

Quote:

millwood, can I kindly ask you to test this with your setup?

You can use C code, spreadsheet program or paper and pen to verify that yourself.

I have one project running similar code next to me, but it just uses 16-bit integers instead of 32-bit.

// assume timer count increases every 1ms.
// so time difference is millisecond units.
u16 prevcount=0;
u16 currcount=0;
u16 diffcount=0;
    while (1){
        currcount=read_timer();
        diffcount=currcount-prevcount;
        prevcount=currcount;

        do_some_stuff_here();
    }

u16 ledtimer=0;
void do_some_stuff_here(void)
{
    // accumulate passed time
    ledtimer=ledtimer+diffcount;
    // if 500ms time is passed, toggle led
    if (ledtimer>=500)
    {
        toggle_led();
        ledtimer=ledtimer-500;
    }
}

So assuming overflow happens, for example currcount=0 and prevcount=0xFFFF, doing the math any way you want still ends up difference being 1, which is correct. You need to think 0xFFFF means -1 if the bit-pattern is regarded as signed integer.

edit: improved led example.

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

Quote:
millwood, can I kindly ask you to test this with your setup?

I did, and Jepael's approach is correct.

glad that we found a simpler solution.

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

Quote:
You configure the timer to advance 1 count at 31250Hz, and then you set the timer to clear on compare, which gives you almost 1Hz overflow interrupts (OCR1A is off by one, to make it count 31250 ticks before overflow, you set it to count from 0 to 31249). Perfect for counting seconds.

Thanks for the tip, I really should set it to 31249 instead of 31250 to get 1Hz blinking.

Quote:
So assuming overflow happens, for example currcount=0 and prevcount=0xFFFF, doing the math any way you want still ends up difference being 1, which is correct. You need to think 0xFFFF means -1 if the bit-pattern is regarded as signed integer.

Thanks for the tip, I'll test and post here complete example.

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

I've tested my implementation by setting:

volatile static uint32_t timer0_millis = UINT32_MAX - 10*1000;

And it seems it works with overflows without any additional code, becaus I test the time like this:

(st_millis() - last_timer) >= 1000	

Thanks!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
volatile static uint32_t timer0_millis = UINT32_MAX - 10*1000; 

that's the same as

volatile static uint32_t timer0_millis = - 10*1000; 

because of two's compliments.

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

I think 0xFFFFFFFF-N is not equal to 0x00000000-N for any number of N, since they always differ by one. It has nothing to do with two's complement.