Updating interrupt vector.

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

Hi all,

I was was wonder what a good way to update the interrupt vector is?

I am interrupting on PCINT0, and at first I want to run a calibration routine... and once that is done I want to update the vector to point to a completely different function.

I really want to avoid:

ISR(PCINT0_vect)
{
if(calibration)
{
}
else
{
}
}

That just seems sloppy. I really want to change where the interrupt goes.

Thanks,
Sam

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

Quote:
I really want to change where the interrupt goes.
But PCINT0 interrupt can ONLY go to the PCINT0 interrupt.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

Depending on what avr you are using, you may have available another interrupt vector table you could use, then switch to the 'normal' one when calibration is done.

Otherwise you have to test something somewhere, and if the calibration code is simple, you may just want to leave it as your example shows. If calibration code causes too much push/pop in isr (maybe important, maybe not), you can make the calibration an isr and asm call it.

ISR(PCINT0_vect){
  if(calibration){
    asm("call __vector_calibrate");
    asm("cli"); //keep irq's off after reti
  }
  else{
  }
}

ISR(__vector_calibrate){
  //calibration here
}

But unless the added clock cycles are a problem in the first place, I don't know if I would even worry about it (even if you don't like the way it appears).

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

I am coming from an ARM background, so I might be a little off here. But the interrupt vector should contain a list of function addresses correct? And I modified the address in the interrupt vector, when that interrupt occurred the PC would get loaded with the new address?

For example, I am using vector 3 in address 0x0002. I should be able to update the value (which I assume is an address?) with the address of the new ISR that I want to use.

I know it works that way with ARM... each vector is just an address that gets jumped to.

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

You'd have to modify flash to update the vector table. Most likely the check for calibration mode is good enough, IMO.

C: i = "told you so";

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

swinchen wrote:
I am coming from an ARM background, so I might be a little off here. But the interrupt vector should contain a list of function addresses correct?

Unfortunately it depends on the micro.

For the AVR, it is not a list of function addresses, it is actually a table of either JMP instructions (with the operand being the ISR function address), or they are RJMP instructions that do the same thing. It depends on the specific AVR device (usually how big the Flash (code) size available) whether they are JMP or RJMP instructions. RJMP is two bytes and JMP is 4 bytes (see the AVR instruction set datasheet).

I've worked with other microcontrollers where it is like the ARM, just a list of addresses and the jump is implicitly done. The AVR needs an explicit jump instruction.

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

The other part is that the vector table, like all instructions, are in flash. If you want to change the address in the vector table, that's a flash write. The biggest problem with re-writing the flash is that the flash is only guaranteed for 10,000 erase/write cycles.

The best suggestion I've heard so far is to put the calibration routines in the BootLoader Section (BLS) Vector Table and use the IVSEL flag to switch between calibrate and normal modes.

If it was me, I'd stick with the if(), unless response time for that ISR is critical.

Stu

Engineering seems to boil down to: Cheap. Fast. Good. Choose two. Sometimes choose only one.

Newbie? Be sure to read the thread Newbie? Start here!

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

If ISR response time is critical, you should use ASM for the ISR. Then the extra check for the condition is only about 4 cycles. So you are most likely still faster than pure C code.

So the IF(..) solution is good in nearly all cases.

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

There is nothing to stop anybody from having a 'changeable' vector table (although not very efficient). Simple example-

#include 
#include 

void func1(void){
  PORTB=0;
}

void func2(void){
  PORTB=1;
}

void* vector_table_sram[]= {
  func1, //PCINT0
};

ISR(PCINT0_vect){
  ((void(*)(void))vector_table_sram[0])();
}

int main(){
  //to change vector function
  vector_table_sram[0]=func2;
}

The ISR in this case will save all call-used registers, so you can call 'normal' functions which will take care of saving call-saved registers themselves.

If you really had a need to change vectors on the fly, you could change the vector table to call instead of jump, then have all vectors call a single function like above. Which in turn would first grab the single byte needed from the stack (pcl of vector address on the stack, which will then be offset into sram table), load the address from the sram table (offset by pcl just got), and icall (like above). In the end, not very efficient (except for the possible savings in code space by having one prologue/epilog for all isr's).

(actually, that would require an rcall,ret instead of a call in the vector table, to keep the stack correct).

Something like-

vectors:
  jmp reset
  rcall vector_director
  ret
  rcall vector_director
  ret
  ..
  ..

//all irq's come here
vector_director:
  //prologue- like isr, 
  //includes all call-used registers
  in r26,pcl //sp->X
  in r27,pch
  adiw r26,17 //find pcl of vector
  ld r30,X //get pcl
  //compute offset into
  //vector_table_sram address
  //address in sram-> r31:r30
  icall  
  //epilogue
  //back to 'ret' in vector table
  
pgmspace:

reset:

//data
void* vector_table_sram[_VECTORS_SIZE]={
    //fill in function addresses here
    //0x0000 if isr not used, or address of
    //bad_interrupt function, or whatever
}

I'll stop thinking of other variations now.

AVR=ARM :)

(I tested the above theory, changed gcrt, etc., one vector director asm function for all irq's- 36 words + c function to calc icall- 15 words, sram vector table bytes- number of vectors x2, takes 58 cycles to reach a function when irq fires, and for a one cycle instruction in that function +ret, it takes a total of 106 cycles to get back to the code that was initially interrupted. Like I said, not terribly efficient.)

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

Wow, thanks for all the responses. It really looks like the "if" method is the most efficient. seeing I am using 120Hz interrupt, I am trying to make it as lean as possible (I will be doing a lot in that interrupt). Thanks for all the help!

Sam

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

You don't really mean 120Hz do you? If you did then even on just a 1MHz clock you have more than 8,300 cycles to process every interrupt - exactly what were you hoping to do on each interrupt?