Pages: [1] 2   Go Down
Author Topic: ISR() macro  (Read 9835 times)
0 Members and 1 Guest are viewing this topic.
Dallas, Texas
Offline Offline
God Member
*****
Karma: 0
Posts: 861
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

If I define an interrupt handler for a particular vector, does my definition automatically take precedence over an instance of that vector in one of the libraries? For example, USART_RX_vect is used in the HardwareSerial library. What happens if I write something like
Code:
ISR(USART_RX_vect){...}
in my program?

Does it override the
Code:
SIGNAL(USART_RX_vect){...}
in HardwareSerial.cpp?
 
What happens if I use
Code:
Serial.begin(long baudrate);
in that same program?

I have read the article at http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html and if it answers my questions, then I missed it. I want to learn how to write ISRs that might use the same vectors as the library routines and I'll read anything that will help me with that, if anyone can make any suggestions.

Is there a forum Arduino interrupts guru?

« Last Edit: July 21, 2009, 03:36:43 pm by EmilyJane » Logged

Offline Offline
Edison Member
*
Karma: 3
Posts: 1001
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

You may find the following link helpful (at least it was to me).

http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html
Logged

Dallas, Texas
Offline Offline
God Member
*****
Karma: 0
Posts: 861
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thank you! I'll check it out.

Oops! That's a different copy of the one I linked to in my original post. Thanks anyway!
« Last Edit: July 21, 2009, 01:02:52 pm by EmilyJane » Logged

Offline Offline
Edison Member
*
Karma: 3
Posts: 1001
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Your link didn't work for me so I wasn't aware of this.

The basic concept of interrupts (as I understand it) is a table of interrupt handler addresses at a fixed memory address. When an interrupt occurs, the CPU will execute the handler with an address loaded from an architecture defined offset in this table. The table will exist within the skecth irrespective of  interrupts being defined/enabled or not - and default entries will simply point to an Interrupt return (IRET) instruction. So in case the specific interrupt is enabled (but not defined with a handler) no harm will occur even if a handler is not provided.

The C compiler adds some protection to prevent duplicate handler definitions in that a unique global symbol per vector is defined at compile time to hold the address of the handler. So as long as you use the C macros, you should get a compile time error in case you attempt to replace a handler that is already defined (elsewhere in your source or in any library you may include).

If you examine the available macros, you will also find options for chaining interrupts. This may be useful if you just want to add to existing library/core functionality without fully replacing the handler.
Logged

Global Moderator
Dallas
Online Online
Shannon Member
*****
Karma: 210
Posts: 13039
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Good questions!

Quote
If I define an interrupt handler for a particular vector, does my definition automatically take precedence over an instance of that vector in one of the libraries?

Sort of.

Quote
Does it override the SIGNAL code in HardwareSerial.cpp?

Sort of.

Quote
What happens if I use Serial.begin in that same program?

The answer lies with the linker and the hardware.  

How's that for good solid answers!  Enough fooling around.  Time for the good stuff...


First, the hardware details...

USART_RX_vect is specific to an Atmel family.  The Mega uses SIG_USART0_RECV, SIG_USART1_RECV, SIG_USART2_RECV, and SIG_USART3_RECV for each of the four interrupt vectors.  The ATmega8 uses SIG_UART_RECV.  In other words, USART_RX_vect doesn't mean anything special when building a Sketch for the Mega.  Searching in HardwareSerial.cpp for "store_char" shows the various interrupt service routines, the "magic function names", and the matching pre-processor symbols.

Now for that pesky linker...

When a reference is made to a symbol (like Serial.begin), the linker tries to find a match.  When the linker finds a match, it includes the entire object file that contains the matching symbol (either all of HardwareSerial.cpp is included in the final program or none of it).

So, if you build for a Diecimila and have a ISR(USART_RX_vect) function in your Sketch and do not reference anything in HardwareSerial.cpp then your interrupt service routine is the interrupt service routine used in the final program.

Does that make any sense or have I confused you even more?  :-?
« Last Edit: July 21, 2009, 03:30:06 pm by bcook » Logged

Dallas, Texas
Offline Offline
God Member
*****
Karma: 0
Posts: 861
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Oh, sorry, that was dumb, I figured everyone had a copy local on their machine and culled the url a little to much for it to work. I'll change it to the online one.

I'm quite familiar with the way interrupts work on the hardware.

So if I redefine an existing ISR then I'll get a compiler error. That's fine.

Quote
If you examine the available macros, you will also find options for chaining interrupts. This may be useful if you just want to add to existing library/core functionality without fully replacing the handler.

There is no mention of that in the link you posted. Where would I find these macros?
Logged

Offline Offline
Edison Member
*
Karma: 3
Posts: 1001
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
So if I redefine an existing ISR then I'll get a compiler error.

Thankfully - yes, so there should be no real need to worry unless a compile time error is raised.

As for interrupt chaining, I did not find it either - so possibly my memory is at fault. I may have confused this with the ALIAS_OF macro, but this will only allow you to share a handler for multiple interrupt vectors - sorry for that (you may be able to roll your own however if you need this and carefully examine how the macros add to the compile time symbol table).
Logged

Dallas, Texas
Offline Offline
God Member
*****
Karma: 0
Posts: 861
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Okay, thanks! That's a big help. I'll just proceed on like I know what I'm doing and hopefully I'll get some meaningful error messages when I do something wrong.smiley
Logged

Dallas, Texas
Offline Offline
God Member
*****
Karma: 0
Posts: 861
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Does that make any sense or have I confused you even more?  

No that makes perfect sense! In fact that is what my intuition made me think would happen. Thanks for a very good sort of answer. smiley-grin

Now, I just used the uart receive interrupt as an example because I am likely to include HardwareSerial in just about any program so I was curious what would happen if I tried to override its handler. What I'm actually going to do is write some functions to handle a 16D450 style USART which will include an ISR to service its interrupt. From what I think you have said, I ought to be able to find a little used vector to commandeer for the job.

Does that all sound reasonable?
« Last Edit: July 21, 2009, 04:32:00 pm by EmilyJane » Logged

0
Offline Offline
Faraday Member
**
Karma: 8
Posts: 2526
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Now for that pesky linker...

When a reference is made to a symbol (like Serial.begin), the linker tries to find a match.  When the linker finds a match, it includes the entire object file that contains the matching symbol (either all of HardwareSerial.cpp is included in the final program or none of it).

But the arduino compile process use some compiler and linker flags to tell it to include only the function(s) it uses, so you get more or less the functionality of a good ol' ar type library (libsomething.a) without having to build said library.

-j
Logged

Global Moderator
Dallas
Online Online
Shannon Member
*****
Karma: 210
Posts: 13039
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

kg4wsv:

Quote
to include only the function(s) it uses
Quote

Yikes!  Sorry about that.  :-[  Yes, the linker strips out unreferenced things.

But, that must not be true for interrupt service routines.  There's no actual reference to them.  ISRs must be marked to always be included if the linker ever encounters them.  In other words, dead code removal has to be disabled for ISRs.  So I should have written...

When a reference is made to a symbol (like Serial.begin), the linker tries to find a match.  When the linker finds a match, it includes everything that was referenced plus any interrupt service routines defined in the same object file (the ISRs in HardwareSerial.cpp).

In either case, the result is the same for EmilyJane.  If an interrupt service routine is pulled-in because of something she's referenced, it will conflict with the one she's trying to define.
« Last Edit: July 21, 2009, 08:28:55 pm by bcook » Logged

Dallas, Texas
Offline Offline
God Member
*****
Karma: 0
Posts: 861
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

That all makes sense. Thanks everyone. I guess I'll just dive in. If I don't post in a few days you'll know I've gone over to the dark side. Or else my protocol just wasn't simple enough and I'm lost in an indeterminate fog. smiley-wink

Edit:
It looks like one of the external interrupt vectors ought to be safe. Does anyone know otherwise?
« Last Edit: July 21, 2009, 08:38:59 pm by EmilyJane » Logged

Global Moderator
Dallas
Online Online
Shannon Member
*****
Karma: 210
Posts: 13039
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
From what I think you have said, I ought to be able to find a little used vector to commandeer for the job.

I believe most of the vectors are unused so you probably won't have to search to long.

I've played around a little bit with the pin change interrupts.  It was easy to setup and just as easy to define the interrupt service routine.  If I remember correctly, there's a great deal of variation...

  • Some of the pin change interrupts are passed through a single vector (I guess "multiplexed" would be a good term to use)
  • A few of the pins get their own vector
  • Some pins support an interrupt on any change (rising, falling, either, both, level)
  • Some pins support a subset of the possible changes (just rising or just falling but not both)

If you're interested, I'll try to find the Sketch.  There isn't much too it so it won't be much more than an introduction.


Quote
Does that all sound reasonable?

It does.

If I were wearing your shoes, I'd start with the most specific possibilities first.  In other words, I'd target a single processor and choose (hard-code) the specific pin to use.  I'd also start with generating an interrupt on a button press that (as much as possible) mimicked what the processor will see from the 16D450.

Even though it was easy working with interrupts, I'd take such tiny baby steps because the Atmel interrupts are a new beast for me.    Debugging interrupt service routines can be extremely difficult.  I'd try to build up as much bug-free code as possible before taking the leap of faith and letting it run free.


Quote
It looks like one of the external interrupt vectors ought to be safe. Does anyone know otherwise?

I'd say either the external interrupt vectors or the pin change interrupts will work depending on how the 16D450 signals an interrupt.  If you use pin change interrupts, you'll have to do everything yourself.  The Arduino libraries don't provide any assistance.


[Note to self: Don't take so long to reply!  It looks bad when the thread author posts a final thank you while you're still typing!]
« Last Edit: July 21, 2009, 08:56:30 pm by bcook » Logged

Dallas, Texas
Offline Offline
God Member
*****
Karma: 0
Posts: 861
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
If you're interested, I'll try to find the Sketch.  There isn't much too it so it won't be much more than an introduction.

That would be great. Thanks a lot.

I'm targeting my Pro 328 8MHz. That's what's running my loft network and that's where I'll be using it. I don't intend to let it free range. It's tempting to use the PCINT right next to the I2C pins because it'll make the minishield easy. I don't know how much work those interrupts are yet. The USART has an open drain active low IRQ.

It'll take me a while to get proficient on the AVR interrupt structure. It's not as well designed as some of the others, in my opinion.

Quote
[Note to self: Don't take so long to reply!  It looks bad when the thread author posts a final thank you while you're still typing!]

Happens to me all the time. smiley
Logged

Global Moderator
Dallas
Online Online
Shannon Member
*****
Karma: 210
Posts: 13039
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

The entire Sketch is at the bottom.  Fair warning: it's a mess!  I was toying around with: a Teensy, the watchdog timer / interrupt, and pin change interrupts.  First, some highlights...


Quote
It's tempting to use the PCINT

That's what I used...

Code:
ISR(PCINT0_vect)
{
  if ( ! digitalRead( PIN_BUTTON_ACTION ) )
  {
    LED = ! LED;
  }
}


I saved a bookmark here...

http://webhome.csc.uvic.ca/~mcheng/samples/mcwilliam/site/project1/JoystickControl.html

...that may help.


This is all it took to enable the interrupt...

/* PCMSK0 is the Pin Change Interrupt Mask, there are 8 pin change interrupts on various ports. We are using pin 6 on port B (PCINT6). */
PCMSK0 |= (1<<PCINT6);
/* Then pin change interrupts must be enabled globally by enabling the Pin Change Interrupt Enable 0 (PCIE0) flag. */
PCICR |= (1<<PCIE0);


Code:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/wdt.h>

#include <compat/ina90.h>

#include <EEPROM.h>


/*--------------------------------------------------------------------
  Define names that match the printing on the Teensy.  Arduino
  functions like pinMode and digitialWrite expect numbers 0 to 20.  
  This lets you use a name, and of course you can define different
  names if something else is more meaningful to you.
--------------------------------------------------------------------*/

#define PIN_D0     0
#define PIN_D1     1
#define PIN_D2     2
#define PIN_D3     3
#define PIN_D4     4
#define PIN_D5     5
#define PIN_D6     6
#define PIN_D7     7
#define PIN_SS     8
#define PIN_SCLK   9
#define PIN_MOSI  10
#define PIN_MISO  11
#define PIN_B4    12
#define PIN_B5    13
#define PIN_B6    14
#define PIN_B7    15
#define PIN_C7    16
#define PIN_C6    17
#define PIN_C5    18
#define PIN_C4    19
#define PIN_C2    20


/*
typedef TrdWakeFromWatchdog

16 ms
32 ms
64 ms
0.125 s
0.25 s
0.5 s
1.0 s
2.0 s
4.0 s
8.0 s
+16.0 s

*/


class TrdWakeFromWatchDog
{
public:
  void turnOff( void );
};

void TrdWakeFromWatchDog::turnOff( void )
{
  /* Disable interrupts. */
  uint8_t SaveSREG = SREG;
  _CLI();
  /* Reset the watch dog timer */
  wdt_reset();
  /* Clear WDRF in MCUSR */
  MCUSR &= ~(1<<WDRF);
  /* Write logical one to WDCE and WDE */
  /* Keep old prescaler setting to prevent unintentional time-out */
  WDTCSR |= (1<<WDCE) | (1<<WDE);
  /* Turn off WDT */
  WDTCSR = 0x00;
  /* Restore interrupts. */
  SREG = SaveSREG;
}


// Note: Doesn't seem to work correctly.
#define USE_ARDUINO_INTERRUPT        0

#if USE_ARDUINO_INTERRUPT
  #define PIN_BUTTON_ACTION PIN_D2
#else
  #define PIN_BUTTON_ACTION PIN_B6
#endif

/* rmv
#define PIN_LED_GREEN       PIN_D5
#define PIN_LED_RED         PIN_D7
#define PIN_BEEPER          PIN_B4
*/

#define PIN_LED_ONBOARD     PIN_D6


volatile uint8_t LED;
volatile unsigned long Seconds;


ISR(PCINT0_vect)
{
  if ( ! digitalRead( PIN_BUTTON_ACTION ) )
  {
    LED = ! LED;
  }
}


ISR(WDT_vect)
{
/*
  LED = ! LED;
*/
/*
  ++Seconds;

  if ( Seconds == 60 )
  {
    LED = ! LED;
  }
*/
}


//****************************************************************
// 0=16ms, 1=32ms,2=64ms,3=128ms,4=250ms,5=500ms
// 6=1 sec,7=2 sec, 8=4 sec, 9= 8sec
/*
void setup_watchdog(int ii)
{
  byte bb;
  int ww;
  
  if (ii > 9 ) ii=9;
  bb=ii & 7;
  if (ii > 7) bb|= (1<<5);
  bb|= (1<<WDCE);
  ww=bb;

  MCUSR &= ~(1<<WDRF);
  // start timed sequence
  WDTCSR |= (1<<WDCE) | (1<<WDE);
  // set new watchdog timeout value
  WDTCSR = bb;
  WDTCSR |= _BV(WDIE);
}
*/

#define _NOP() do { __asm__ __volatile__ ("nop"); } while (0)

void setup_watchdog( int ii )
{
  MCUSR &= ~(1<<WDRF);

  /*
    Disable interrupts.
  */
//  uint8_t SaveSREG = SREG;
//  _CLI();

  /*
    Reset the watch dog timer
  */
  wdt_reset();

/*
  // 1. Set WDCE and WDE
  WDTCSR |= (1<<WDCE) | (1<<WDE);
  // 2. Load the divider factor into WDTCKD
  WDTCKD  = (1<<WDEWIE);
  // 3. Wait WDCE being automatically cleared (just wait 2 more cycles)
//  _NOP();
//  _NOP();
*/
  // 4. Set again WDCE and WDE
  WDTCSR |= (1<<WDCE) | (1<<WDE);
  // 5. Clear WDE, set WDIE and load the prescaler factor into WDTCSR in a same operation
  WDTCSR  = (1<<WDIE) | (1<<WDP2) | (1<<WDP1);
//  WDTCSR  = (1<<WDIE) | (1<<WDP1) | (1<<WDP0);
}

void setup_watchdog_1(int ii)
{
  /*
    WDE is overridden by WDRF in MCUSR. This means that WDE is always
    set when WDRF is set. To clear WDE, WDRF must be cleared first.
    This feature ensures multiple resets during conditions causing
    failure, and a safe start-up after the failure.
  */
  MCUSR &= ~(1<<WDRF);
  /*
    Disable interrupts.
  */
  // save SREG
  // cli
  /*
    Reset the watchdog timer.
  */
  // wdr
  wdt_reset();
  /*
    In the same operation, write a logic one to the Watchdog change
    enable bits WDCE and WDE. A logic one must be written to WDE
    regardless of the previous value of the WDE bit and even if it
    will be cleared after the operation.
  */
  WDTCSR |= (1<<WDCE) | (1<<WDE);
  /*
    Within the next four clock cycles, write the WDE and Watchdog
    prescaler bits (WDP) as desired, but with the WDCE bit cleared.
    This must be done in one operation.
  */
  WDTCSR = (1<<WDCE) | (1<<WDP2) | (1<<WDP1);
  /*
    When this bit is written to one and the I-bit in the Status
    Register is set, the Watchdog Interrupt is enabled. If WDE is
    cleared in combination with this setting, the Watchdog Timer is
    in Interrupt Mode, and the corresponding interrupt is executed if
    time-out in the Watchdog Timer occurs.
  */
  WDTCSR |= (1<<WDIE);
  /*
    Restore interrupts.
  */
  // SREG = saved value
}


#if USE_ARDUINO_INTERRUPT

void CheckButtons( void )
{
  if ( ! digitalRead( PIN_BUTTON_ACTION ) )
  {
    LED = ! LED;
  }
}

#endif


uint16_t SaveIndex;


void setup()
{
  /*
    Initialize Serial.  On the Teensy, full USB speed is always
    used and the baud rate is ignored.
  */
  Serial.begin( 38400 );
  Serial.print( "Hello world!" );

/* Note: Doesn't seem to make a difference.
  for ( uint8_t i=0; i <= 20; ++i )
  {
    if ( i != PIN_LED_ONBOARD )
    {
      pinMode( i, INPUT_PULLUP );
    }
  }
*/
  
  pinMode( PIN_BUTTON_ACTION, INPUT_PULLUP );
  pinMode( PIN_LED_ONBOARD, OUTPUT );
  

#if USE_ARDUINO_INTERRUPT

  attachInterrupt( 1, CheckButtons, CHANGE );

#else

  // http://webhome.csc.uvic.ca/~mcheng/samples/mcwilliam/site/project1/JoystickControl.html
  
  /* PCMSK0 is the Pin Change Interrupt Mask, there are 8 pin change interrupts on various ports. We are using pin 6 on port B (PCINT6). */

  PCMSK0 |= (1<<PCINT6);

  /* Then pin change interrupts must be enabled globally by enabling the Pin Change Interrupt Enable 0 (PCIE0) flag. */

  PCICR |= (1<<PCIE0);

#endif

  set_sleep_mode( SLEEP_MODE_PWR_DOWN );

/* Note: Cripples millis.
  set_sleep_mode( SLEEP_MODE_STANDBY );
*/

//  set_sleep_mode( SLEEP_MODE_IDLE );

/* Note: Doesn't seem to make a difference.
  // Turn off the SPI clock
  PRR0 |= (1<<PRSPI);
*/
  
  /* Turn off the USB clock */
//  PRR1 |= (1<<PRUSB);

/* Note: Doesn't seem to make a difference.
  // Turn off the USART1 click
  PRR1 |= (1<<PRUSART1);
*/

/* Note: Doesn't seem to make a difference.
  // Turn off the Analog Comparator
  ACSR |= (1<<ACD);
*/

/*
  setup_watchdog( 6 );
*/

/*
  for ( SaveIndex=0; SaveIndex < 512; ++SaveIndex )
  {
    EEPROM.write( SaveIndex, 0 );
  }
*/
}

void loop()
{
/*
  Note: Not available.
  sleep_bod_disable();
*/

/*
  Note: Not available.
  power_adc_disable();
*/

/*
  Note: Doesn't seem to make a difference.
  power_spi_disable();
*/

/*
  Note: Doesn't seem to make a difference.
  power_timer0_disable();
*/

/*
  Note Not available.
  power_timer1_disable();
*/

/*
  Note: Doesn't seem to make a difference.
  power_timer2_disable();
*/

/*
  Note Not available.
  power_twi_disable();
*/
/*
  sleep_enable();
  long Start = millis();
*/
/*
  wdt_reset();
  sleep_mode();
*/
/*
  delay( 1000 );
*/
/*
//  delay( 10 );
  long Stop = millis();
  long Delta = Stop - Start;
  sleep_disable();
*/

//  Serial.println( Delta, DEC );

/*
  EEPROM.write( SaveIndex+0, Delta & 0xFF );
  EEPROM.write( SaveIndex+1, Delta >> 8 );
  SaveIndex = (SaveIndex + 2) & 511;
*/

/*
  if ( Delta != 0 )
  {
    LED = ! LED;
  }
*/

  digitalWrite( PIN_LED_ONBOARD, LED );
//  delay( 100 );
  sleep_mode();
}
Logged

Pages: [1] 2   Go Up
Jump to: