When should I use the Keyword "Volatile"

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

Hello all,

Here is the situation:
Device Mega128
Compiler: Imagecraft V6.31A

I needed to increase the delay in which the WDT times out. The maximum timeout on a Mega128 is 2.1 seconds. So without having to scatter WDR()s all over my code I am using a global variable "count" that gets incremented every second inside a timer interrupt. When this "count" reaches a predefined count it calls a function that turns on the WDT and resets my application. I reset the global variable "count = 0" inside my main().

The global variable is defined in my timer.c file as "unsigned char count" and is linked to my main.c file using "extern unsigned char count".

Should I have declared my "unsigned char count" as "volatile unsigned char count"..??

I'm a little confused as to when to use the keyword volatile.

Thanks,
Oc.

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

If the function that does the "count"ing is also the one that calls the
"trigger WDReset" function, it doesn't need to be volatile (nor extern, as
far as that goes, given that it is initially 0).

But, um.. What you've described sounds like a WatchDog Timer in
software. Doesn't that kind of defeat the purpose?

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

Murphy says you'll get a situation where the program is hung in a loop, but the interrupt keeps ticking and resetting the wdt, and the reset you want to happen never comes.

Imagecraft compiler user

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

I guess >>my<< confusion would be a microcontroller program that didn't "cycle" in more than 2 seconds--what could possibly be going on?!?

I guess my small mind cannot encompass not looking around to see what is going on in over 2 seconds.

So I cannot address OP's situation directly. But here is something that served me well in what I thought was a complex situation. I wanted to make sure interrupts were still firing (as mentioned), that the main loop was still scanning (also mentioned), and an external device was still alive (e.g., polling or responding to polls).

What I did is have a single WDR in the main loop, but conditionally executed: A series of bits needed to be set before the WDR was carried out. A bit was set by each pertinent ISR. A bit was set after a successful polling sequence. Etcc for other conditions.

After the WDR the flag byte was cleared of the set bits.

This worked well since the poll sequence happened faster than the watchdog period.

I have a couple of places in apps where I do an additional WDR or two, but it isn't "all over". One category is doing a series of EEPROM writes in newer AVRs, such as when re-configuring. With a nominal 8ms./byte and a 250ms. watchdog period, a bunch of EEPROM writes would cause problems. So I do a few WDRs during that sequence.

Lee

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 replies guys...I guess I am going about this the wrong way..

theusch:

The reason why my program may not cycle in 2 seconds is the I use delays like this: Which now looks like a bad idea...

void Delay(ushort Time)
{
    Delay_Timer = Time;
    while(Delay_Timer);
}
 

Where Delay_Timer is a global ushort that is decremented in a timmer interrupt. I guess I can put WDR() inside the loop?

It looks like what you did in your application is what I want to do, so I am going to try it your way.

Bob:
The interrupt just increase the count (ie count++) and checks to see if has reached the predetermined time out. If it has reached the time out it then enables the watchdog to reset immediately. "count" is continuously reset in main() so if the program gets stuck somewhere other then main() the program will reset.

Thanks again for the help,
Oc.

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

Jack Ganssle (ganssle.com) has a paper -- originally a
series of articles in Embedded Systems Programming -- which discusses
Watchdog timers. Interesting reading, with many considerations I
wouldn't have thought of.

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

Why sit in a hard loop waiting for a long period? I suppose it makes sense, if your application has >>absolutely nothing<< to do 99+% of the time. Then the WDT doesn't serve much of a critical function protecting equipment or personnel.

I do a lot of "soft timers" counting ticks from a timer interrupt. Meanwhile, my main loop is running many thousands of times each second.

I set a "tick" flag in the timer ISR--and typically >>only<< that. When the main loop sees the tick flag it does whatever needs to be done. A typical tick might be 10ms. with soft timers built up from that. A typical sequence:

// **************************************************************************
// *	S O F T   T I M E R S
// **************************************************************************
//
//	General Approach:  the soft timers are "running" when the count-down value
//	is not 0.  They "expire" when the value reaches 0, and action should be taken
//	or a flag set that the event occurred.
//
//	"tmr_" are the time-base bit flags:  second, minute, hour, etc.
//
//	"sec_", "min_", "hrs_" prefixes in the soft-timer name indicate the time base.
//
//
//	Once a second
//	=============
	if (tmr_second)  
		{
		tmr_second = 0;

	// Application once-a-second timers
	//	-------------------------------

		if (sec_holdoff)
			{
			sec_holdoff--;
			if (!sec_holdoff)
				{
				flg_sec_holdoff = 1;			// set  flag
				}
			}

Later in the code I take action on the "timing event" signified by the flg_ variable and clear it.

So I can be taking care of whatever business I desire, handle the "timing event" when it occurs, and I'm not locked into any hard loops.

An awful waste of a '128 to idle that long. :)

Now, here is what you >>could<< do with just a minor change: in your wait loop, reset the watchdog--but ONLY when the Delay_Timer value changes. That ensures that your ISR is still firing.

And, yes, in this case Delay_Timer should be volatile. And, to really confuse you, if it were a 16-bit value you could not do the loop the way you are oing it, since there might be an interrupt between reading the two value bytes for the comparison.

Lee

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

Wow…my head is spinning now :shock:

So in your main loop do you essentially have several Handler functions that go off and process their specific task depending if the flag is set?...

Thanks for help theusch I appreciate it. Learning C and using the AVR is a real treat and getting help from this forum makes learning that much more fun.

In hindsight it is ridiculous to waste all that time in that loop while I should be out processing data.

Mckenney: Thanks for the link. I'll go look it up.

Thanks,

Oc.

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

Just another small question...

Would if I need to pulse an output. Say I needed to pulse a output off for 500ms then on for 200ms?? Would I not have to use a delay loop for that?

This is beginning to snowball.... :(

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

ocnek wrote:
Would if I need to pulse an output. Say I needed to pulse a output off for 500ms then on for 200ms?? Would I not have to use a delay loop for that?

No way. A couple of milliseconds is FOREVER in a microcontroller app.

Full disclosure: :) I >>do<< use CodeVision's delay_ms() functions in almost every one of my apps. But only for my startup delay to let newly-powered-up peripheral devices settle. I find the library routine to be clear, small, accurate, & I don't have to calculate timing loops when I change crystals. I've also used delay_us() [microseconds] a few times when I needed a few microsecond delay after, say, a chip select.

Anyway, you are 80% of the way to pulsing your relay already--you have a working repeating timer interrupt. Many posters here are partial to using "output compare with reset" instead of overflow interrupt; then there is no reload needed, less latency, and more consistent timing.

So, you set up a timer interrupt for 10ms. (nominal), using the AVRCalc utility (search this site for "avrcalc") to help set up the numbers. Let's say I've got a Mega8 running at 7.3728MHz, and want a 10ms. tick. And I'm going to continually excite a relay 500ms. on & 200ms. off.

// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: 3.600 kHz
// Mode: CTC top=OCR2
// OC2 output: Disconnected
ASSR=0x00;
TCCR2=0x0F;
TCNT2=0x00;
	// w/ /1024 prescaler, 3.6864MHz crystal, OC2 interupt:
	// 2.50ms.=8; 5.00ms.=17; 10.0ms.=35
OCR2=0x23; // 10 ms.

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x80;
...
//
// **************************************************************************
// *
// *		T I M E R 2 _ C O M P _ I S R
// *
// **************************************************************************
//
// Timer 2 output compare interrupt service routine
//
// Main timer "tick", occurs every 10.0ms
//
interrupt [TIM2_COMP] void timer2_comp_isr(void)
{
// Flag to main loop that a tick has occured
	tick = 1;
}
...
#define TICKS_ON 50
#define TICKS_OFF 20
#define MY_RELAY PORTD.2
...
ticks_off = TICKS_OFF; // start the soft "off" timer cycling
ticks_on = 0; // stop the soft "on" timer

	if (tick)
		{
		tick = 0;
// "Off" timer
		if (ticks_off > 0)
			{
			if (--ticks_off <= 0)
				{
				MY_RELAY = 1;  // turn on relay
				ticks_on = TICKS_ON; // arm the "On" timer
				}
// "On" timer
		if (ticks_on > 0)
			{
			if (--ticks_on <= 0)
				{
				MY_RELAY = 0;  // turn off relay
				ticks_off = TICKS_OFF; // arm the "Off" timer
				}
			}
			// *******
		}   // [end of "if (tick)" ]
			// *******

You get the picture. Every app has different design goals and needs. Some posters here are big fans of an RTOS to give a structure to their events and "tasks". Others prefer to do de-facto event handling. Above, I've used at least one "event" and have two timed "tasks" running.

In my typical app waiting for something to happen, whether it be an input or a timer, I'll run through the main loop tens of thousands of times a second on the average. Almost all of the time there are no "tasks" ready to run.

On other apps there may be a lot of processing to do and the main loop may only be serviced several hundred times per second. It all depends. But I rarely if ever have any hard delay loops for any length of time.

Lee

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

ocnek wrote:
I'm a little confused as to when to use the keyword volatile.

Don't feel bad, I've worked with professional embedded software engineers for many years and find that most of them are still confused.

Check out Introduction to the Volatile Keyword in the Find/Post your Online C Books & Tools here .... sticky post at the top of the AVR forum.

Don

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

Hey Lee,

Thanks for sharing that trick with setting flag bits for resetting the WDT. Never thought of that but certainly see the utility!

Learn something new every day here... :)

Please note - this post may not present all information available on a subject.

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

refields wrote:
Hey Lee,

Thanks for sharing that trick with setting flag bits for resetting the WDT. Never thought of that but certainly see the utility!

Learn something new every day here... :)


I'll second that!

Four legs good, two legs bad, three legs stable.