Author Topic: avr interrupts  (Read 3190 times)

0 Members and 1 Guest are viewing this topic.

Offline richiereynoldsTopic starter

  • Full Member
  • ***
  • Posts: 112
  • Helpful? 3
avr interrupts
« on: April 16, 2010, 07:16:47 AM »
Hi there, I've got an interrupt question. I'm using webbotlib but don't think that's particularly relevant.
In pseudocode, I want to do this:
Code: [Select]
attach an ISR routine "process" for external interrupt to pins A, B and C

process
    if the pin is high
        do some stuff
    clear any pending interrupts for A, B and C

The bit I don't know how to do is the "clear any pending interrupts for A, B and C".
Are there flags i can set to clear the queues? (Well, I call them queues but I guess there can only be a max of one pending call for each as they've probably only got a call/don't call flag).

Thanks, Richard.

Offline tim_wang

  • Jr. Member
  • **
  • Posts: 32
  • Helpful? 0
Re: avr interrupts
« Reply #1 on: April 16, 2010, 08:02:43 AM »
External interrupts are cleared by writing a logic 1 to the corresponding external interrupt flag.

Offline richiereynoldsTopic starter

  • Full Member
  • ***
  • Posts: 112
  • Helpful? 3
Re: avr interrupts
« Reply #2 on: April 16, 2010, 08:11:00 AM »
thanks Tim, just to be sure, that means that any of those interrupts queued to fire won't execute? (Rather than just disabling the interrupt, which I dont want to do. i.e. I do want to listen for interrupts that occur after my routine has executed, just don't want to execute due to any interrupts that were queued while my routine was executing.)

Offline richiereynoldsTopic starter

  • Full Member
  • ***
  • Posts: 112
  • Helpful? 3
Re: avr interrupts
« Reply #3 on: April 16, 2010, 04:33:35 PM »
including a question about how I actually do the specifics with webbotlib and an axon 2, does this look ok?
Code: [Select]
void myHandler(const IOPin* io, boolean high, volatile void* data)
{
    if(high)
    {
        // do some stuff
    }
    PCIFR = _BV(PCINT2);
}

TICK_COUNT appInitSoftware(TICK_COUNT loopStart)
{
    // snip
    pin_change_attach(K5, &myHandler, NULL);
    pin_change_attach(K6, &myHandler, NULL);
    pin_change_attach(K7, &myHandler, NULL);
}

I'm hoping the PCIFR = _BV(PCINT2); clears all pending pin change interrupts on K0..K7, which in this case is fine. I do find that a bit odd though e.g. what if I just wanted to clear any pending on K6 only, is there a way to do that?

Offline Webbot

  • Expert Roboticist
  • Supreme Robot
  • *****
  • Posts: 2,165
  • Helpful? 111
Re: avr interrupts
« Reply #4 on: April 16, 2010, 07:39:03 PM »
Looks about right - but you can get rid of all that 'PCIFR=_BV(PCINT2)" stuff - WebbotLib does all that for you.
With WebbotLib you should NEVER need to access ANY registers yourself.
Webbot Home: http://webbot.org.uk/
WebbotLib online docs: http://webbot.org.uk/WebbotLibDocs
If your in the neighbourhood: http://www.hovinghamspa.co.uk

Offline tim_wang

  • Jr. Member
  • **
  • Posts: 32
  • Helpful? 0
Re: avr interrupts
« Reply #5 on: April 16, 2010, 07:49:48 PM »
thanks Tim, just to be sure, that means that any of those interrupts queued to fire won't execute? (Rather than just disabling the interrupt, which I dont want to do. i.e. I do want to listen for interrupts that occur after my routine has executed, just don't want to execute due to any interrupts that were queued while my routine was executing.)

Yes, writing a logic 1 to an interrupt flag will only prevent that particular interrupt service routine from executing. It will not disable global interrupts.

Offline Webbot

  • Expert Roboticist
  • Supreme Robot
  • *****
  • Posts: 2,165
  • Helpful? 111
Re: avr interrupts
« Reply #6 on: April 16, 2010, 08:10:49 PM »
That's going to give you problems (I think).....

When a pin change interrupt happens the registers are in groups of 8 I/O pins. ie one interrupt happens if ANY of the 8 io pins change. So in order to work out 'which' of the 8 pins has 'caused' the interrupt then I have to store the old I/O pin values (from the previous interrupt). An exclusive-or then says which pins out of the 8 have actually changed.

So assume PINx was low, and PINy was low.
If PINx goes high and causes and interrupt then I know that its coz of PINx changing.
If PINy goes high at around the same time - but you have cleared the interrupt whilst processing PINx- then the lib never knows that it went high. So when PINy goes low, and causes another interrupt then the lib may get confused as it never knew that PINy was high in the first place - so it won't think PINy has actually changed.

So you may well loose 'pin changes'
Webbot Home: http://webbot.org.uk/
WebbotLib online docs: http://webbot.org.uk/WebbotLibDocs
If your in the neighbourhood: http://www.hovinghamspa.co.uk

Offline Admin

  • Administrator
  • Supreme Robot
  • *****
  • Posts: 11,702
  • Helpful? 173
    • Society of Robots
Re: avr interrupts
« Reply #7 on: April 16, 2010, 08:57:47 PM »
Just a silly question but . . . it sounds like you expect a bunch of interrupts to get triggered all at once? Perhaps at the *exact* same time? But you only want your mcu to respond once to this event, no matter which pins were triggered?

This would mean you'd need to clear all interrupts before leaving the interrupt event handling function.

I don't see any way to do this in the WebbotLib manual . . .

My best guess is to detach the pins within the interrupt, and then reattach after exiting the interrupt function.

Offline Webbot

  • Expert Roboticist
  • Supreme Robot
  • *****
  • Posts: 2,165
  • Helpful? 111
Re: avr interrupts
« Reply #8 on: April 17, 2010, 08:21:29 AM »
The way the hardware works is as follows:-

1. All of the port K pins are effectively OR'd together and it just set one 'interrupt bit' saying that 'something' on Port K has changed

2. There is no way to interrogate the hardware as to which pin(s) on port K have caused it, nor whether it was caused by a high-low or low high transition.

So I have to remember, in software, what the previous state was of each pin. Then when the interrupt happens the handler reads the current state of each pin and compares it with the previous known state. This way it can work out which bit(s) have changed, and in what direction, and call the user supplied handler for each one.

Your code example, where it clears the interrupt in PCIFR, wont work as my interrupt handler will still call your handlers for each bit that has changed.

Quote
what if I just wanted to clear any pending on K6 only, is there a way to do that?
No - you can't do it in hardware - for the reasons mentioned above ie there is no 'interrupt request bit for K6 alone'.

You may be able to do it in WebbotLib though as follows:

When the interrupt happens - my handler tests the pins, in ascending order: K0, K1 .... K7 ie K0 is highest priority, K7 the lowest.

If your handler for K0 wanted to 'forget' about any changes on pin K6 then it could read the K6 pin and store the value in the 'lastValue' member of the PIN_CHANGE structure for K6. Your K0 handler then returns to my handler, as normal, which then tests the other bits.
It should now ignore K6 as you have changed its 'remembered' value to be the new 'value' and so my handler wont think it has changed.

What you may choose to do is set up the interrupts so that they all point to the handler. Your handler could then change the remembered value for all 8 pins. This way your handler would only get called once irrespective of which pin changed.


Webbot Home: http://webbot.org.uk/
WebbotLib online docs: http://webbot.org.uk/WebbotLibDocs
If your in the neighbourhood: http://www.hovinghamspa.co.uk

Offline tim_wang

  • Jr. Member
  • **
  • Posts: 32
  • Helpful? 0
Re: avr interrupts
« Reply #9 on: April 17, 2010, 10:03:46 AM »
Just a silly question but . . . it sounds like you expect a bunch of interrupts to get triggered all at once? Perhaps at the *exact* same time? But you only want your mcu to respond once to this event, no matter which pins were triggered?

This would mean you'd need to clear all interrupts before leaving the interrupt event handling function.

I don't see any way to do this in the WebbotLib manual . . .

My best guess is to detach the pins within the interrupt, and then reattach after exiting the interrupt function.

You would indeed need to clear all the applicable interrupts before restoring the status register and leaving the ISR. For example to clear INTF0, the interrupt flag for external interrupt 0, you would do EIFR |= 0x01.

Offline richiereynoldsTopic starter

  • Full Member
  • ***
  • Posts: 112
  • Helpful? 3
Re: avr interrupts
« Reply #10 on: April 17, 2010, 10:05:06 AM »
When a pin change interrupt happens the registers are in groups of 8 I/O pins. ie one interrupt happens if ANY of the 8 io pins change. So in order to work out 'which' of the 8 pins has 'caused' the interrupt then I have to store the old I/O pin values (from the previous interrupt). An exclusive-or then says which pins out of the 8 have actually changed.
Ahh, I see, think I'm starting to understand how the hardware works now and hence how the library handles this.

So you may well loose 'pin changes'
I see what you mean, may mess up future interrupts that I actually do want to process.

Just a silly question but . . . it sounds like you expect a bunch of interrupts to get triggered all at once? Perhaps at the *exact* same time? But you only want your mcu to respond once to this event, no matter which pins were triggered?
Yep, that's pretty much it.

This would mean you'd need to clear all interrupts before leaving the interrupt event handling function.
I don't see any way to do this in the WebbotLib manual . . .
yep, discussing this now

My best guess is to detach the pins within the interrupt, and then reattach after exiting the interrupt function.
I did try that actually but I think the flags for "interrupt event happened" would still get set even though the callback was dettached so when I re-attached they'd get examined and my routine would get called. ( Though my understanding could be completely skew whiff here :) )


If your handler for K0 wanted to 'forget' about any changes on pin K6 then it could read the K6 pin and store the value in the 'lastValue' member of the PIN_CHANGE structure for K6. Your K0 handler then returns to my handler, as normal, which then tests the other bits.
It should now ignore K6 as you have changed its 'remembered' value to be the new 'value' and so my handler wont think it has changed.

What you may choose to do is set up the interrupts so that they all point to the handler. Your handler could then change the remembered value for all 8 pins. This way your handler would only get called once irrespective of which pin changed.
SOunds good, I'll give that a go!

« Last Edit: April 17, 2010, 10:15:12 AM by richiereynolds »

Offline tim_wang

  • Jr. Member
  • **
  • Posts: 32
  • Helpful? 0
Re: avr interrupts
« Reply #11 on: April 17, 2010, 10:49:30 AM »
That's going to give you problems (I think).....

When a pin change interrupt happens the registers are in groups of 8 I/O pins. ie one interrupt happens if ANY of the 8 io pins change. So in order to work out 'which' of the 8 pins has 'caused' the interrupt then I have to store the old I/O pin values (from the previous interrupt). An exclusive-or then says which pins out of the 8 have actually changed.

So assume PINx was low, and PINy was low.
If PINx goes high and causes and interrupt then I know that its coz of PINx changing.
If PINy goes high at around the same time - but you have cleared the interrupt whilst processing PINx- then the lib never knows that it went high. So when PINy goes low, and causes another interrupt then the lib may get confused as it never knew that PINy was high in the first place - so it won't think PINy has actually changed.

So you may well loose 'pin changes'

Assuming PINx and PINy both trigger the same pin change interrupt, and both pins changed at the same time so the ISR for that group of pins is executed. By the time the ISR is executed, both pins have already changed. So when you capture and compare the current state of the pins with their previous states, you can always figure out which pins changed, whether it was just PINx, or both PINx and PINy. The pin change interrupt does not care whether the pins transitioned from low to high or from high to low, it only cares that a pin change occurred. So what is there to be confused about?

Offline Webbot

  • Expert Roboticist
  • Supreme Robot
  • *****
  • Posts: 2,165
  • Helpful? 111
Re: avr interrupts
« Reply #12 on: April 17, 2010, 03:52:52 PM »
That's going to give you problems (I think).....

When a pin change interrupt happens the registers are in groups of 8 I/O pins. ie one interrupt happens if ANY of the 8 io pins change. So in order to work out 'which' of the 8 pins has 'caused' the interrupt then I have to store the old I/O pin values (from the previous interrupt). An exclusive-or then says which pins out of the 8 have actually changed.

So assume PINx was low, and PINy was low.
If PINx goes high and causes and interrupt then I know that its coz of PINx changing.
If PINy goes high at around the same time - but you have cleared the interrupt whilst processing PINx- then the lib never knows that it went high. So when PINy goes low, and causes another interrupt then the lib may get confused as it never knew that PINy was high in the first place - so it won't think PINy has actually changed.

So you may well loose 'pin changes'

Assuming PINx and PINy both trigger the same pin change interrupt, and both pins changed at the same time so the ISR for that group of pins is executed. By the time the ISR is executed, both pins have already changed. So when you capture and compare the current state of the pins with their previous states, you can always figure out which pins changed, whether it was just PINx, or both PINx and PINy. The pin change interrupt does not care whether the pins transitioned from low to high or from high to low, it only cares that a pin change occurred. So what is there to be confused about?

That was my response if he started fiddling with the interrupt request bits in the hardware registers - without the lib knowing
Webbot Home: http://webbot.org.uk/
WebbotLib online docs: http://webbot.org.uk/WebbotLibDocs
If your in the neighbourhood: http://www.hovinghamspa.co.uk

Offline Webbot

  • Expert Roboticist
  • Supreme Robot
  • *****
  • Posts: 2,165
  • Helpful? 111
Re: avr interrupts
« Reply #13 on: April 17, 2010, 03:55:29 PM »

My best guess is to detach the pins within the interrupt, and then reattach after exiting the interrupt function.
I did try that actually but I think the flags for "interrupt event happened" would still get set even though the callback was dettached so when I re-attached they'd get examined and my routine would get called. ( Though my understanding could be completely skew whiff here :) )

Admin is correct - you can detach and re-attach handlers. When an interrupt happens it calls the handler if there is one - otherwise it just throws the interrupt away. So attaching a handler will only cause it to be called for any 'new' interrupts
Webbot Home: http://webbot.org.uk/
WebbotLib online docs: http://webbot.org.uk/WebbotLibDocs
If your in the neighbourhood: http://www.hovinghamspa.co.uk

Offline tim_wang

  • Jr. Member
  • **
  • Posts: 32
  • Helpful? 0
Re: avr interrupts
« Reply #14 on: April 18, 2010, 11:02:59 AM »
According to page 17 of the ATmega640 datasheet,

Quote
When an interrupt occurs, the Global Interrupt Enable I-bit is cleared and all interrupts are disabled.
The user software can write logic one to the I-bit to enable nested interrupts. All enabled
interrupts can then interrupt the current interrupt routine. The I-bit is automatically set when a
Return from Interrupt instruction RETI is executed.

Nested interrupts must be manually enabled in your ISR. So how about not manually enabling nested interrupts, instead of manually detaching and reattaching handlers? If you are extra paranoid like me, then you can manually disable global interrupts in your ISR with the CLI instruction.  ;)

Offline Webbot

  • Expert Roboticist
  • Supreme Robot
  • *****
  • Posts: 2,165
  • Helpful? 111
Re: avr interrupts
« Reply #15 on: April 18, 2010, 05:47:25 PM »
According to page 17 of the ATmega640 datasheet,

Quote
When an interrupt occurs, the Global Interrupt Enable I-bit is cleared and all interrupts are disabled.
The user software can write logic one to the I-bit to enable nested interrupts. All enabled
interrupts can then interrupt the current interrupt routine. The I-bit is automatically set when a
Return from Interrupt instruction RETI is executed.

Nested interrupts must be manually enabled in your ISR. So how about not manually enabling nested interrupts, instead of manually detaching and reattaching handlers? If you are extra paranoid like me, then you can manually disable global interrupts in your ISR with the CLI instruction.  ;)

Great source info - ie the official data sheet !!!
Believe me I have seen it before when writing WebbotLib!

But of course your quote has nothing 'specifically' to do with pin change interrupts. Having written a library which is used by 100's of users across loads of devices then I kinda 'understand what an interrupt is' and how it its used!!

So rather than a 'hardware' explanation then perhaps you'd like to comment on 'pinChange.c' in WebbotLib. Am assuming you use WebbotLib since that is what the original post is all about - ie 'not' the hardware.
 


Webbot Home: http://webbot.org.uk/
WebbotLib online docs: http://webbot.org.uk/WebbotLibDocs
If your in the neighbourhood: http://www.hovinghamspa.co.uk

Offline tim_wang

  • Jr. Member
  • **
  • Posts: 32
  • Helpful? 0
Re: avr interrupts
« Reply #16 on: April 18, 2010, 10:12:14 PM »
I have not used WebbotLib, as I tend to write my own code to suit my needs. As you pointed out hundreds of people use your library, so I assume that it is functional.

From this thread it seems to me the original poster wanted to do something that is outside the scope of your library. So I merely pointed out how to accomplish his goals using a few simple instructions. What else was he suppose to do with a library that does not meet his needs other than writing his own functions?

Offline richiereynoldsTopic starter

  • Full Member
  • ***
  • Posts: 112
  • Helpful? 3
Re: avr interrupts
« Reply #17 on: April 19, 2010, 02:37:22 AM »
Tried a few things there but still can't seem to get it to process the first interrupt and ignore all others until the first routine is over and then start listening again from scratch. There always seems to be a few calls still in the queue that get handled immediately after the first routine exits.

I read that the global interrupt flag is disabled by default when processing of an interrupt begins and enabled again afterwards so I was never worried about nested interrupts, should I be?

Have tried detaching and re-attaching at the end of my routine (was my first attempt).
I'm thinking that when the interrupt gets called, another one could get triggered before my handling routine even starts up so it'll be in the queue before I dettach the handler. Would this one get fired even though I've detached? Or fired when I re-attach?

Have tried PCIFR = _BV(PCINT2);

Have tried setting the lib lastValue before my routine exits, webbot, perhaps you could confirm I'm doing this correctly? -
pcCallbacks[21].lastValue = pin_get(K5);
pcCallbacks[22].lastValue = pin_get(K6);
pcCallbacks[23].lastValue = pin_get(K7);
And, if I am doing it right, is there a sensible way to get the 21, 22 and 23? I think you use __io_to_pc_index but that's local to a lib c file so I didn't really want to use it externally.

Anyway, my routine is using 3 sensors and is quite messy, I'm going to write something much more straightforward and deterministic to test all this. When I've done that I'll get back with the results.

P.S. got something that might be useful for inclusion in the next webbotlib, if there's nothing that does this already, I'm using an lcd and a terminal over bluetooth and got fed up with re-init-ing rprintf for the occasional lcd message -

#define rrprintf(writer, fmt, args...) { \
    Writer currentWriter = rprintfInit(writer); \
    rprintf(fmt, ## args); \
    rprintfInit(currentWriter); \
}

Just thinking, this might be handy too, if it had a sensible name, for when you have no main default output -

#define rrrprintf(writer, fmt, args...) { \
    rprintfInit(writer); \
    rprintf(fmt, ## args); \
}
« Last Edit: April 19, 2010, 04:01:27 AM by richiereynolds »

 


Get Your Ad Here

data_list