Is there any rule to change registers inside interrupts?

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

Hi,

Is there any rule to make changes to registers inside interrutps?

I have a fast pwm running on a 16 bit timer, with a clock source of 12Mhz prescaled by 8 and two interrupts: OVF and COMPA

On comp A I toggle the led. On overflow I wanted to change the duty cycle dynamicly, to fade the led.

If I manually set the OCR1A value to a determined value it works. If I use a variable it doesn fade, just stays.

This is what I am doing:

ISR(TIMER1_OVF_vect)
{
	//OCR1A = 0x000f;

	if (value>=0x0fff)
		value = 1;

	OCR1A = value;

	value+=1000;
}

Any tips?

Thanks,

Nuno

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

volatile?

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

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

Jim,

Why so cryptic?

Nuno,

As Jim points out, all variables declared outside of an interrupt but accessed inside the int must be declared volatile.

A

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

Quote:
As Jim points out, all variables declared outside of an interrupt but accessed inside the int must be declared volatile.

I'd change that "accessed" to "assigned to" - a (global) variable you only read in the ISR doesn't need to be volatile. A variable you change in the ISR does need to be declared volatile. You don't want the compiler (in the mainline code) to use an outdated version in its zeal for optimization.

Chuck Baird

"I wish I were dumber so I could be more certain about my opinions. It looks fun." -- Scott Adams

http://www.cbaird.org

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

Chuck,

I learn something new here every day. Seems I have lot more volatile variables in my code than I need.

Thanks,

A

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

After I jumped in there and made that blanket statement as if I knew something, I realized there's a possibility that a compiler might not reload variables on entry to an ISR (such as keeping them in dedicated registers). My guess is that no compiler would do such a terrible thing; if one does it's one more vote for joining the Samperi's Imagined Utopia in Assembly Heaven.

Chuck Baird

"I wish I were dumber so I could be more certain about my opinions. It looks fun." -- Scott Adams

http://www.cbaird.org

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

Anytime a function is entered, any global variable will be read from SRAM the first time it is accessed. This includes interrupts. The problem really only happens when a variable is accessed multiple times in the same function.

Regards,
Steve A.

The Board helps those that help themselves.

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

And, after I got away from the computer and couldn't easily post, I realized a register resident variable is by its very nature volatile, so ignore my parenthetical aside above.

Thanks, Steve, for the clarification. All is right in the heavens (even Assembly Heaven).

Chuck Baird

"I wish I were dumber so I could be more certain about my opinions. It looks fun." -- Scott Adams

http://www.cbaird.org

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

Quote:

The problem really only happens when a variable is accessed multiple times in the same function.


Not necessarily; in fact this is one of the classic situations. Say I've got a variable "tick", and the timer ISR sets it to 1. Without volatile, the compiler doesn't need to read it each repetition of

while (!tick) {...}

And for even more hair-pulling (though admittedly multiple accesses in the same function--never really thought about that before) is

tick = 0;
while (!tick) {...}

and the whole while() loop is tossed.

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

But Lee, your first example is multiple accesses in the same function. An access is an execution, not a reference in a statement.

Chuck Baird

"I wish I were dumber so I could be more certain about my opinions. It looks fun." -- Scott Adams

http://www.cbaird.org

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

So a little OT, but related...

If I want to burn some time with

for (i = 0; i < 30000; i++) ;

will declaring i volatile keep the optimizing hounds at bay? I've always assumed it would, but have never checked (being a second class user of a non-optimizing compiler).

Chuck Baird

"I wish I were dumber so I could be more certain about my opinions. It looks fun." -- Scott Adams

http://www.cbaird.org

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

Chuck - in Lee's example there's a timer interrupt setting the variable 'tick' - so we have two threads accessingthe same variable.

with your example, yes, declaring the variable volatile will force the compiler not to optimise the loop away.

Then we have the issue of atomicity - but that's another story, which I wrote about in the tutorial section.

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

Quote:
Chuck - in Lee's example there's a timer interrupt setting the variable 'tick' - so we have two threads accessingthe same variable.

Sure, I understand this. It's just that Steve said the problem only comes up when there are multiple accesses in a function (the first access fetches a fresh copy out of SRAM; if there are no other references in that function you don't have to worry about using a stale copy).

Lee countered with his one line example which may fail if the variable isn't volatile. And I say, even though it's one line (and one physical reference), it isn't a counterexample to Steve's comment, since during execution it is indeed multiple references.

And thanks for the validation on my loop question. Even a blind hog finds an acorn now and then.

Chuck Baird

"I wish I were dumber so I could be more certain about my opinions. It looks fun." -- Scott Adams

http://www.cbaird.org

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

Furthermore, if you only access the variable within the isr and you want it persistance (ie remembers the value between isr calls), declare the variable inside the isr as 'static'

ISR(TIMER1_OVF_vect) 
{ 
static unsigned int value = 0;//only gets initialised on startup!
   //OCR1A = 0x000f; 

   if (value>=0x0fff) 
      value = 1; 

   OCR1A = value; 

   value+=1000; 
} 

No need for volatile here as no-one else can see the variable 'value' and thus no need to share it.

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

My rules for persistent variables shared with interrupts are (compiler optimization on required):

    1. Variable only accessed in one ISR -> static within interrupt handler. 2. Variable shared between multiple blocked interrupts -> not volatile. The interrupts can not be interrupted and volatile leads to full RAM access on every read -> larger footprint.
    3. Variable shared between interruptible section (main thread / unblocked ISR - complete handler or partially by calling sei() ) and blocked interrupt: volatile. In case multiple accesses within the blocked section are required, create a local copy / do modifications / and write back to prevent full RAM access on every read
    4. Shared Variables > 8Bit need atomic access within interruptible sections.
Any suggestions / complains :?:

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

Hi,

I have read about volatile before reading your answers. I added the volatile prefx to the variable. However i happens that OCR1A doesnt change dynamicly at all...

What else could be in this case?

Thx,

Nuno

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

Try

  
OCR1AH=OCR1Avalue>>8;
OCR1AL=(unsigned char)OCR1Avalue;

Regards
A.R.Khorasani
http://www.instrumentalanalysis.com

Regards
A.R.Khorasani
http://www.instrumentalanalysis.com

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

Quote:

Try

Why? GCC provides the 16 bit access.

But to the original question I'd like to see where 'value' is defined and any other code that might access it. I'd also be interested to learn how we even know that the overflow ISR is being called at all?

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

All,

It may not be best practice but I thing I will stick to declaring all variables accessed by an int as volatile. I just don't have the time for enigmas.

A

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

Andrew,

That's a curious strategy. Of all the code in an AVR it's often the case that the only really time critical code is that in the ISRs so why slug them with the most inefficient code if it's not needed? I'd learn the significance of 'volatile' and only use it when necessary. Somewhere round here it's summarised in one sentence... ;-)

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

cliff,

The difficulty I have is that all of the code I now run on an AVR was ported from an 8051. I am probably dealing with around 200 variables that are accessed by the interrupt. I have moved as much of the data processing out of the interrupts but they still contain around 2000 (including comments)lines of code. I am working to reduce this but it requires a rethink of the hardware. (An additional 1Meg of print buffer space).

How well does my code bloated interrupts work you ask?

Well I have attached a picture of a print sample. We can print this info on a box traveling at around 200FPM at a rate of 100 boxes a minute. All the info is updated in real time. Clocks and box counts update for each box.

I understand your comment, but in my case I think if it works, don't fix it.

Thanks,

A

Attachment(s): 

Last Edited: Thu. May 28, 2009 - 06:09 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sinosoidal wrote:
What else could be in this case?
Post more code snippets.

What is "value" before the first instance of the O.F. ISR? Is it zero?

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

Very nice!
I've done a few thermal printers with AVRs, I'm not sure how to scale it directly..
Think in terms of 1000 pixels at > 2000 pixel lines/sec, on an 8 MHz AVR.

Tons of variables to play with, printheads segmented up, thermal history, etc.

How many pixels wide are the narrow elements in your barcode?

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

dbvanhorn,

Thanks.

It works out to about 196 dpi. The inks have lots of bleed on virgin cardboard. The hole thing runs out of a handheld controller. Truth be told, I am working as fast as I can to add an internal buffer and MMC card slot. It is not easy to edit visual stuff on a 4X16 LCD character only display.

A

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

schnitzeltony wrote:

The interrupts can not be interrupted
Any suggestions / complains :?:

Yeah, interrupts can be interrupted if you enable global interrupts again in the ISR.

Useful when you have a REALLY important interrupt that you need to happen ASAP

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

neddly - nesting interrupts is fine if you understand the implications - it multplies the problem of determining stack usage and presents but another source of potential atomicity problems.

The statement schnitzelboy makes about atomic access of variables > 8bits is not the only thing to be aware of - anytime you read/test/write or read/modify/write a shared variable presents a potential atomic access problem as these operations take a few instructions to complete and thus could be interrupted. Failure to address this will give you the defect from hell.

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

Kartman wrote:
Furthermore, if you only access the variable within the isr and you want it persistance (ie remembers the value between isr calls), declare the variable inside the isr as 'static'

ISR(TIMER1_OVF_vect) 
{ 
static unsigned int value = 0;//only gets initialised on startup!
   //OCR1A = 0x000f; 

   if (value>=0x0fff) 
      value = 1; 

   OCR1A = value; 

   value+=1000; 
} 

No need for volatile here as no-one else can see the variable 'value' and thus no need to share it.

Yesterday I was around this problem again. I tried volatile, non volatile, static, non static and I couldn't understand why it wasn't changing the value of OCR1A dynamicly. :S

However I didn't tried this approach. I'll try and post the results after.

Thanks,

Nuno

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

Nuno - what exactly are you trying to achieve? What do you expect you code to do?

I think the problem is no longer with your choice of variable but rather that your code does not do what you expect.

[edit] Actually in the first post you told us what you wanted to do! Try removing these lines:

if (value>=0x0fff)
value = 1;

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

Hi,

I'm trying to make a led dimmer. I have a timer set to 20*256 Hz (20 Hz with 256 bits intensity levels).

In compA interrupt I check if the counter is equal to the brigthness level I want. At that point I turn off the led. When the counter reaches 256 I put it at zero as turn on the led again.

Once again the problem arises. If I put the led to toggled only in the interrupt body the led turns on, but with the control code it does nothing.

I don't udnerstand what is happening here since I have made this already in other chips like the tiny45.

When I started this thread I was using a PWM and I was trying to change the OCR1A value. Now I'm using a CTC and I'm doing pwm control code by myself in order to control more than one led (RGB led). This is my actual code:

int main()
{
	DDRB=0xff;

	sei();

	TCCR1A = _BV(WGM12);
	TCCR1B = _BV(CS10);

	TIMSK1 |= _BV(TOIE1) |  _BV(OCIE1A);

	OCR1A = 0x0927;

	while(1);

	return 0;

}

// mega168 running at 12Mhz

ISR(TIMER1_COMPA_vect)
{
	static unsigned int brightness=10;
	static unsigned int count=0;

	if (count++==0xff)
	{
		count=0;
		PORTB=0xff;
	}

	if (count==brightness)
	{
		PORTB=0x00;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That program enables TWO interrupts and provides one ISR - is this the whole program? If it is you need to add a timer1 overflow ISR

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

clawson wrote:
That program enables TWO interrupts and provides one ISR - is this the whole program? If it is you need to add a timer1 overflow ISR

Thanks for your reply, however that didn't made the thing work. I removed TOIE1 and it still doesnt work.

:S

Yes it is the whole code! What else can it be? I can't see anything wrong in the code? Could it be the chip? Gcc optimization flags?

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

Maybe your code should look a little like this:

volatile unsigned char brightness=10; 


ISR(TIMER1_COMPA_vect) 
{ 
   static unsigned char count = 0; 

   OCR1A += 0x927;
   count++;          //incrementing an unsigned char will roll over automatically
   if (count == 0)
      {
      PORTB = 0xff;
      }
 
   if (count==brightness) 
   { 
      PORTB=0x00; 
   } 
} 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

I have tried your code... Nothing! It doesn't light up.

But it is not the interrupt, if I take all the interrupt body out, and put there a line like PORTB ^= 0xFF; it will light up. What it seems is that the variable is not incrementing at all or else, the comparisions are not being done.

This is very weird because at work I was working with an tiny45 and pwm, and I was changing the duty cycle inside the interrupt, and here I can't.

The only difference is that here i'm in a Mac and i'm using a mega168.

MACKIE:~ nunosantos$ avr-gcc --version
avr-gcc (GCC) 4.3.2

:(

Any more tips?

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

Hi people,

I tried to make a simple led blink correctly and even that was failing...

I decided to try on another chip and the very same code is working.

Something is wrong with the chip. What? I don't know and right now I just don't care! :)

Problem solved! Thanks for your help.