Tactics when using a soft timer?

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

I've setup timer0 on an ATmega168 so that it fires an ISR every 0.1ms and promoted a uint16_t variable called soft_tick. I would like to use the soft_tick to trigger various events like start an ADC reading 40 times a second, in order to do that I need to check if soft_tick can be divided to 250 with modulo = 0. That seems like too much for for something this simple. Should I use another variable (8bit) or some other technique?

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

Don't do modulo math on non-powers-of-two, it's too slow.

 

Check a threshold instead:

volatile uint16_t soft_tick;
.
.
.
uint16_t adc_taken_at;
.
.
.
#define ADC_INTERVAL_TICKS 250U
.
.
.
// infinte loop in main
while (1) {
.
.
.
    if ((soft_tick - adc_taken_at) >= ADC_INTERVAL_TICKS) {
        adc_taken_at += ADC_INTERVAL_TICKS;
        your_adc_function();
    }

Now it's just one subtraction and one compare, plus an addition when you've got a 'hit'.  Much faster than a divide.

 

The above code doesn't handle atomicity.  This does:

#include <avr/atomic.h>
.
.
.
volatile uint16_t soft_tick = 0;
.
.
.
uint16_t adc_taken_at = 0;
.
.
.
#define ADC_INTERVAL_TICKS 250U
.
.
.
// infinte loop in main
while (1) {
.
.
.
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
        uint16_t soft_tick copy = soft_tick;
    }
    if ((soft_tick_copy - adc_taken_at) >= ADC_INTERVAL_TICKS) {
        adc_taken_at += ADC_INTERVAL_TICKS;
        your_adc_function();
    }

All untested.

"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]

 

Last Edited: Tue. Oct 21, 2014 - 08:03 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

slow_rider wrote:
I've ... promoted (sic?) a uint16_t variable called soft_tick

Pardon?

 

Quote:
to do that I need to check if soft_tick can be divided to 250 with modulo

Would 256 be close enough...?

 

Or just have a byte count up to (or down from) 250...?

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Take a look at my tutorial, seems tailor-made for what you want to do.

 

http://www.embeddedrelated.com/s...

(esp: A Cyclic Executive Framework)

 

 

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

I've setup timer0 on an ATmega168 so that it fires an ISR every 0.1ms and promoted a uint16_t variable called soft_tick. I would like to use the soft_tick to trigger various events like start an ADC reading 40 times a second, in order to do that I need to check if soft_tick can be divided to 250 with modulo = 0. That seems like too much for for something this simple. Should I use another variable (8bit) or some other technique?

Why in the world would you use 100us ticks to time events that are many milliseconds long?  As you find, the numbers get big.

 

Now, never say never.  Perhaps you have an app that really justifies a 100us tick. (Tell more...) 

 

Anyway, depending on the app I might have several levels of timekeeping, and perhaps soft timers at each level.  An example of a normal app:

-- 10ms base timer tick.  When it fires, do "every 10ms" stuff.  E.g. button/switch/input fetch and debounce.

-- 100ms to 500 ms:  Count the 10ms ticks for the next "level".  Typical stuff would be "closing the loop" with averaged ADC readings, and display update.

-- 1 second:  Count the coarse ticks to get 1 second.  When that fires, this is the most typical of my soft timers in industrial apps as setpoints and displays are in seconds.  Sometimes count-up; sometimes count-down.  If nearly all soft timers are on direction or the other, then invert the oddball(s) for display.  When I have an array of these soft timers (some apps have 16), I often have a state flag for each--ARMED; EXPIRED; etc.

 

In some apps I count the seconds for minutes and/or hours for e.g. uptime counters.

 

Remember that if you have a spare timer you can use ADATE.

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

@theusch - so in the timer ISR you update a few variables or you update just one and update the rest when conditions are met in the main loop?

For example:

while(1)
{
    if(1ms_soft_tick==1000)
    {
        1sec_soft_tick++;
        1ms_soft_tick=0; // should probably have atomic access
    }
        
    if(1sec_soft_tick==60)
    {
        1mic_soft_tick++;
        1sec_soft_tick=0;
    }
    
    ...
}

 

Last Edited: Wed. Oct 22, 2014 - 08:31 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

BTW, I agree that I'd like to hear your justification for 100us timing ticks...

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

@theusch - so in the timer ISR you update a few variables

;// **************************************************************************
;// *
;// *		T I M E R 2 _ C O M P A _ I S R
;// *
;// **************************************************************************
;//
;// Timer 2 output compare interrupt service routine
;//
;// Main timer "tick", occurs every 5ms
;//
;interrupt [TIM2_COMPA] void		timer2_compa_isr	(void)
; 0000 0A5A {
_timer2_compa_isr:
; 0000 0A5B 
; 0000 0A5C // Flag to main loop that a tick has occured
; 0000 0A5D 	tick = 1;
	SBI  0x1E,0
; 0000 0A5E 
; 0000 0A5F 
; 0000 0A60 }
	RETI
;

 Typical ISR above -- one instruction.

 

Everything else is done in the main loop, "at my leisure".

 

That implies that the main loop will run every 10ms (my time base in this case).  In certain apps with shorter ticks and occasional lengthy main loop operations (e.g., EEPROM structure copy), a tick could be missed and throw off timekeeping.  So I go about it a bit different...

 

//
// **************************************************************************
// *
// *		T I M E R 2 _ C O M P A _ I S R
// *
// **************************************************************************
//
// Timer 2 output compare interrupt service routine
//
// Main timer "tick", occurs every 10ms
//
interrupt [TIM2_COMPA] void		timer2_compa_isr	(void)
{

// Flag to main loop that a tick has occured
	tick = 1;
	ticks_isr++;
}

...and the main loop servicing might become ...

 

//
// **************************************************************************
// *
// *		M I S C E L L A N E O U S   D E F I N E S
// *
// **************************************************************************
//

#define	DELAY_STARTUP		123
#define	TICKS_PER_TOCK		50		// 50 for a 500ms tock when tick is 10.0ms

#define	TICKS_PER_CLICK		10		// NOTE:  this app requires TICKS_PER_TOCK to be mod 10
#define	CLICKS_PER_SECOND	10		// for 100ms click

#define	CLICKS_NO_KEY		100		// 10 second timeout for certain operations

...
//
// **************************************************************************
// *	T I M E R   T I C K
// **************************************************************************
//
//	Clear "tock";  it is only set for one scan of main-loop code.
//	If a tick has occurred, update and see if it is time for tock.
//
//	"tack" is a one second timer, toggled every tock.
//
//	Presumes a 10ms. tick.
//
	tock = 0;
	if (tick)
		{
		tick = 0;
// With the calculations and output, there may be long delays before we get back here.
//	Ensure no loss of timekeeping.
#asm ("cli")
		ticks += ticks_isr;
		ticks_isr = 0;
#asm ("sei")


		if (ticks >= TICKS_PER_TOCK)
			{
			ticks -= TICKS_PER_TOCK;
			tock = 1;
			tack ^= 1; // toggle tack every 500 ms.
			tocks++;
			}


// ***********
// Inputs
// ***********
//

		// Fetch input state every 10ms.
		input_prev = input_current;	// for calculating edges

 		scratch = key_fetch();		// strobe and process switch inputs

		input_current = signal_debounce(scratch, inputs);		//  and create edge triggers & current state

// Rising & falling edges
//
// Note:  no edges.  If needed:
//	Rising & falling edges = composite_io ^ previous_io;
//	Rising edges = (composite_io ^ previous_io) & composite_io;
//	Falling edges = (composite_io ^ previous_io) & previous_io;
//
//		rising_io = (composite_io ^ previous_io) & composite_io;
//		falling_io = (composite_io ^ previous_io) & previous_io;

		input_rising = (input_current ^ input_prev) & input_current;
//		input_falling = (input_current ^ input_prev) & input_prev;

		if (input_current)
			{
			clicks_no_key = 0;
			}

// ***********
// Application Soft Timers
// ***********
		if (input_current & SW_MENU)
			{
			if (ticks_menu_held < 250)
				{
				ticks_menu_held++;	// about 2.5 seconds max
				}
			}
		else
			{
			ticks_menu_held = 0;
			}

//
// ***********
// Every 100 ms. "CLICK"
// ***********
//
		if ((ticks%TICKS_PER_CLICK) == 0)
			{
			// Indicate 100ms processing to be done
			click = 1;

			// Wrap click counter
			if (++clicks >= CLICKS_PER_SECOND)
				{
				clicks = 0;
				}

// ***********
// Switch-Held-Down Soft Timers
// ***********
//	500ms. tock is too slow for some operations.
//
//	Set up 8-bit variables that can "count" up to 25 seconds.
//
//	Note that it is expected that there is a fresh key_debounce() switch_flags & switch_current
//

			if (input_current & SW_UP)
				{
				if (++tenths_up_held > 250)
					{
					// Avoid rollover
					tenths_up_held = 250;
					}
				}
			else
				{
				tenths_up_held = 0;
				}

			if (input_current & SW_DOWN)
				{
				if (++tenths_down_held > 250)
					{
					// Avoid rollover
					tenths_down_held = 250;
					}
				}
			else
				{
				tenths_down_held = 0;
				}


// ***********
// 100ms. autorepeat code
// ***********
...
//
// **************************************************************************
// *	T I M E R   " C L I C K "
// **************************************************************************
//
// Take action here if "click" is set--100ms. time periods
//
//	-- "no key pressed" counter
//	--	gather all the flowmeter sample counts from the last 100ms,
//		and update the 1 second rolling average buffer
//	-- calculate the total counts for the last second--conveniently in Hz.
//	-- calculate the 24-bit DAC output for each channel, proportional
//			to the max Hertz setpoint for each channel (the 20mA point)
//	-- signal to the output driver to update the DACs
//

// clicks = which 100ms time period this second (0-9 for 100ms click and 1 second accumulation)
//	chan_raw[] = edges this period (front-end is a flip-flop so count each edge)
//	chan_total[] = edges the past second
//		Note:  could re-add every 100ms, or subtract old from total and add new to total.
//	chan_samples[NUM_CHANNELS, CLICKS_PER_SECOND] = the 100ms sample storage; "wraps"
//

	if (click)
		{
		click = 0;
// Debugging timing
SET_SPARE();
// (measurements indicate about 1.3ms to process a click--no problem for 10ms. tick)

// Capture new channel counts from _raw
		for (looper = 0; looper < NUM_CHANNELS; looper++)
			{
			// Get a channel count for the last 100ms, and clear.
			//	8-bit value allows for 256x10=2560Hz; way more than spec.
			#asm ("cli")
			chan_samples[looper][clicks] = chan_raw[looper];
			chan_raw[looper] = 0;
			#asm ("sei")

			chan_total[looper] = 0;	// init for the summing below
			}

// Calculate the full-second totals-- Hertz
		ptr = chan_samples[0];
		iptr = chan_total;

		grand_total = 0;

		for (looper = 0; looper < NUM_CHANNELS; looper++)
			{
			// Unroll the loop CLICKS_PER_SECOND times (should be faster than nested loop)
			//  Revised to nested loop
			for (scratch = 0, worknum = 0; scratch < CLICKS_PER_SECOND; scratch++)
				{
				worknum += *ptr++;
				}
			*iptr++ = worknum;
			grand_total += worknum;
			}

// Calculate the DAC output value
//	(could be merged into the loops above)
		iptr = chan_total;
		for (looper = 0; looper < NUM_CHANNELS; looper++)
			{
			// Get 4mA and 20mA 16-bit count numbers into register variables
			workmin = ee_min[looper];
			workmax = ee_max[looper];

			// Note:  ee_setpoint (for the 20mA max value) is in Hertz*10
			worknum = ee_setpoint[looper];


//			chan_setpoint[looper] = (DAC_MAX * 10ul) * (unsigned long)*iptr / worknum;
// calculate setpoint in 32-bit for overflow purposes

			worklong.u32 = (DAC_MAX * 10ul) * (unsigned long)*iptr / worknum;

			if (worklong.u32 > DAC_MAX)
				{
				chan_setpoint[looper] = DAC_MAX;
				}
			else
				{
				chan_setpoint[looper] = worklong.u16[0]; // low 16 bits
				}


			iptr++;
			}

// Signal to apply new data
		new_data = 1;		// flag for output driver

// Idle timer(s)
		if (input_current == 0)
			{
			if (clicks_no_key < 250)
				{
				clicks_no_key++;
				}
			}

			// *******
		}	// [ end of "if (click)" ]
			// *******

...
//
// **************************************************************************
// *	T I M E R   " T O C K "
// **************************************************************************
//
// Take action here if "tock" is set--the long timebase of TICKS_PER_TOCK ticks.
//
// Typical intervals might be between .25 seconds and 1 second.
// Initial design is set for .5 seconds.
//

	if (tock)
		{
		do_refresh = 1;

		if (!tack)
			{
			}

			// *******
		}	// [ end of "if (tock)" ]
			// *******


...

 

Now you know more than you probably care to. ;)  As earlier mentioned, in some apps with many soft timers I make an array of parameters/timer structures, and the #defined name has the index value.  In the code above, each soft timer is individually named and has its own semantics.  YMMV.

 

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

I use the same idea as LT. I have a timer interrupt that increments tics, usually an unsigned int. I do use 100usec in a couple of situations... one is dimming a 7seg display. The other is reading the next of several encoders. I have a function called generateoneshots0 that turns off ints, reads tics into ticslocal, turns ints back on, then calcs how long the last pass took by subtracting tics from ticslast, and I use this delatTin100usectics to age my oneshot accumulators. I'll typically have 20, 100, and 250ms oneshots that are set in the generateonshots function. In the main loop, I can say if(os250){ os250=0; call250msstuff() }; etc. I can elaborate more if someone is interested.

 

 

Imagecraft compiler user

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

@theusch - thanks for the code! Let me know if I got it down correctly.

tick is a flag to indicate the ISR fired and an action is required in the main loop.

 

ticks_isr is a variable holding the # of "hard ticks" (by ISR) since last updated on the main loop, so if for some reason the code in the main loop is not executed at a time period long enough for the ISR to fire at least twice, ticks_isr will be incremented twice in the ISR and that will be updated (to ticks) in the main loop so timekeeping catches up.

 

I've noticed that you disable interrupts before updating ticks and resetting the isr flag, from obvious reasons, is it the same as using the atomic.h macros?

 

 

 

 

 

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

Yes,   there is no mystery in <atomic.h>

 

Lee knows he is using interrupts.     So he can just use a CLI, SEI pair.

 

A more general arrangement involves save=SREG / cli() / ... / SREG=save

i.e. a few more cycles.   may be only one extra cycle if save is in a register.

 

David.

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

is it the same as using the atomic.h macros?

 

I don't use GCC.

 

Lee knows he is using interrupts.     So he can just use a CLI, SEI pair.

 

A more general arrangement involves save=SREG / cli() / ... / SREG=save

i.e. a few more cycles.   may be only one extra cycle if save is in a register.

Indeed--good catch.  My mainlines always run with interrupts enabled.  So for my work, and my routines, I can just do the CLI/SEI.  But in general one must need to do it properly.

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 for the help, I've adjusted my code so that now I can get a 'tick' on the main loop every 1ms, I did have to lower the output compare register a bit so it would actually be 1ms.

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

Post the code. Maybe someone can make it smaller/faster/more readable?

Imagecraft compiler user

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

1st, setting up the timer and ISR code:

volatile bool     soft_tick_flag=false;
volatile uint8_t  soft_ticks=0;     // ISR ticks
volatile uint8_t  one_ms_counter=0; // Counts 10 events in order to increment soft timer every 1ms
         uint16_t ticks=0;          // Software ticks

void soft_timer_init()
{
	power_timer0_enable();
	OCR0A=226;                    // 250 = 0.1ms, actually adjusted so output will be 10ms
	TCCR0A|=(1<<WGM01)|(1<<CS01); // CTC mode
	TCCR0B|=(1<<CS01);            // Clk/8
	TIMSK0|=(1<<OCIE0A);          // Timer match interrupt enable
}

ISR(TIMER0_COMPA_vect)
{
	if(one_ms_counter++==10)
	{
		one_ms_counter=0;
		soft_tick_flag=true;
		soft_ticks++;	
	}
}

And inside the main while(1) loop:

if(soft_tick_flag)
		{
			soft_tick_flag=false;
			
			ATOMIC_BLOCK(ATOMIC_FORCEON)
			{
				ticks+=soft_ticks;
				soft_ticks=0;
			}
			
			// DO STUFF HERE
		}

 

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

Why is your timer ISR running 10 times faster than you need it?

 

And your check 

if(one_ms_counter++==10)

actually loops 11 times, not 10.  That's why you had to fudge the 226 value (should be 249).

 

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

Should increment before the check?

if(one_ms_counter++==10)

I need a 1ms time base, however I can get it from the timer because my clock is 20MHz

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

just my input to this thread:

If the interrupt is so simple that it only set a flag then you don't need a ISR just check the timer flag (and clear it).

If you think about fast code only use 8 bit values so you don't need to think about  atomic instructions.

If main need to set a "set a SW delay timer" that is more than 8 bit and (always the same), but never read it back, then use a flag, and make the counter var local to ISR (remember static), and then let the ISR deal with it.

The compiler make faster code if you count down to 0, (and not ++var==10)

 

If you write ASM you can make 16 bit code atomic by using movw for load, but it has limitations.  

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

If the interrupt is so simple that it only set a flag then you don't need a ISR just check the timer flag (and clear it).

Perhaps.  If indeed it is a fast (for my apps) 1ms tick, then a mainline operation as simple as updating a 16-bit EEPROM variable may result in a lost tick.  (Upon reflection, that would happen after 2x tick time regardless of polling or ISR.)

 

Thus, as I showed above, in some apps I increment a counter in the ISR.  Not too much overhead if an 8-bit "register" variable.

 

 

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

I'm confused...

The timer should throw an interrupt every 1ms (or faster if you REALLY need it and know you can hit it, good luck )

In that interrupt increment a uint32

 

In your state machine state check that the time has elapsed.

I use a function called IsTimedOut()

Using a uint32 solves the rollover problem when you go beyond 0xFFFFFFFF

 

Declaration;

bool IsTimedOut(uint32_t period, uint32_t start)
{
  uint32_t current = msTimeTicks();
  return(current - start > period);
}

 

Usage;

uint32_t UpdateTime = msTimeTicks();
uint32_t delaytime = 250; //ms

...

switch(state)
{
....

case MyState:
if(IsTimedOut(delaytime, UpdateTime))
{
    DoStuff();

}
break;
.....



Keith Vasilakes

Firmware engineer

Minnesota

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

In my last project I interrupted with 64KHz, (about 16 us) and in that I handle all fast SW timers, data from remote RC, 2 uarts etc.

The benefit is that it's simple to handle and know that everything is handled in time. (last time I checked worst case was about 140 clk but normally about 70clk), but I don't really care if I just have 1 MIPS left for the non time critical it would be fine, (update display, run a slow PID (like) regulator, read  4 ADC values, that don't change fast).

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

keith v wrote:

 

Using a uint32 solves the rollover problem when you go beyond 0xFFFFFFFF

I'm curious about this statement.  Can you elaborate?

 

 

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

Being unsigned, everything wraps around. 

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

kk6gm wrote:

 

keith v wrote:

 

 

Using a uint32 solves the rollover problem when you go beyond 0xFFFFFFFF

 

I'm curious about this statement.  Can you elaborate?

 

 

 

Why a big ol uint32?

uint16 rolls over in 1 minute, way too short

uint32 rolls over in 49 days, pretty long, should cover everything

 

The best way is t do a thought experiment or excel spreadsheet showing what happens when you roll over using the method shown.

 

but it's like this...

 

Timer is at fff7

you want to wait F ms

Using this timeout function;

 

//return true if it's been period ms since start

bool IsTimedOut(uint32_t period, uint32_t start)

{

uint32_t current = millis();

return(current - start > period);

}

 

using 16 bit unsigned for simplicity

period is f

start is fff7

 

current is fff7

current - start is 0 which is  < f

 

current is ffff

current - start is 8 which is  < f

 

current is 0000

current - start is 9 which is  < f

 

current is 0006

current - start is f which is  == f

 

current is 0007

current - start is 10 which is  > f

 

current is 0008

current - start is 11 which is  > f

 

 

Keith Vasilakes

Firmware engineer

Minnesota

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

keith v wrote:

Why a big ol uint32?

uint16 rolls over in 1 minute, way too short

uint32 rolls over in 49 days, pretty long, should cover everything

Then the issue boils down to the roll-over period. If the OP doesn't need to handle any period longer than a minute, a 16-bit counter is sufficient. Otherwise they'll need to use a 32-bit or larger type (or change the granularity from 1ms to, eg, 10ms).

Last Edited: Mon. Oct 27, 2014 - 03:47 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

christop wrote:

 

keith v wrote:

 

Why a big ol uint32?

uint16 rolls over in 1 minute, way too short

uint32 rolls over in 49 days, pretty long, should cover everything

 

 

Then the issue boils down to the roll-over period. If the OP doesn't need to handle any period longer than a minute, a 16-bit counter is sufficient. Otherwise they'll need to use a 32-bit or larger type (or change the granularity from 1ms to, eg, 10ms).

 

Yes it's the uint part that solves the wrap problem thanks to fixed length math

Keith Vasilakes

Firmware engineer

Minnesota

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

keith v wrote:

 

christop wrote:

 

 

keith v wrote:

 

Why a big ol uint32?

uint16 rolls over in 1 minute, way too short

uint32 rolls over in 49 days, pretty long, should cover everything

 

 

Then the issue boils down to the roll-over period. If the OP doesn't need to handle any period longer than a minute, a 16-bit counter is sufficient. Otherwise they'll need to use a 32-bit or larger type (or change the granularity from 1ms to, eg, 10ms).

 

 

 

Yes it's the uint part that solves the wrap problem thanks to fixed length math

Yes, that's what I was asking about, whether you were referring to the uint part or the 32 part.

 

I do something similar except that I incorporate the period into a future deadline, and I check if the timer has reached/exceeded the deadline.  As you say, the nature of unsigned math handles the rollover automatically, as long as the future deadline is less than 2^^(N-1) clock ticks away (that is, half the period of the tick timer).

 

I usually make the choice to have a 16 bit tick timer, and a separate set of seconds/minutes/hours variables.  Either choice is reasonable, IMO.  For an ARM or other 32-bit part, I'd use a 32 bit tick timer, even if I had seconds/minutes/hours variables.