tags:

views:

329

answers:

3

I have an ISR defined to trigger on an external interrupt. The external interrupt may not always be enabled, but under certain circumstances I want to be able to register a function to be called ONCE within the interrupt from within the main code. The function might be replaced by another one, or removed, before the next interrupt.

I don't know much about techniques for synchronisation on the PIC32, but I've come up with the following:

volatile BOOL callbackInterrupted = FALSE;
volatile BOOL callbackWritten = FALSE;
void (*myCallback)() = NULL;

void RegisterCallback(void (*callback)())
{
    do
    {
        callbackWritten = FALSE;
        myCallback = callback;
    }
    while(callbackInterrupted);

    callbackWritten = (callback != NULL);
}

void interrupt MyExternalInterrupt() @EXTERNAL_1_VCTR
{
    // Do a bunch of other things here...

    if(callbackWritten)
    {
        myCallback();
        myCallback = NULL;
        callbackInterrupted = TRUE;
        callbackWritten = FALSE;
    }
}

I'm having trouble reasoning about it though. Does this actually do what I hope, ie. prevent the ISR calling a half-set function pointer, or calling a function twice? Is the do ... while loop superfluous? Is there a better way?

Added: disabling this interrupt is out of the question. It is used for timing.

Instructions generated for flag = TRUE:

lui         s1,0x0
ori         s1,s1,0x1
addiu       at,s1,0
or          t0,at,zero

Instructions generated for fnc1 = &testfunc:

lui         a2,0x9d00
ori         a2,a2,0x50
or          a1,a2,zero
sw          a1,16376(gp)
A: 

I would use only a function pointer and check that for non-null in the interrupt, call and set it to null. Where you set the pointer the standard solution would be to disable interrupts.

starblue
Disabling the interrupts is out of the question, this particular one is used for timing external pulses. Given that, I don't think it would be enough to use the function pointer itself as a flag.
detly
+1  A: 

Given the fact that the ISR is there to time pulses why call the function within the ISR at all? Why not call it in the main code where ever it is that the main code checks up on the results of the pulse timing?

If your answer is that its critical the code is executed on the fire of the ISR, then i assume its also critical that you have the opportunity to set a function to call prior to every execution of the interrupt. In this case your only options are to determine the proper function to call for the next interrupt within the ISR or to disable the interrupt while you determine the proper function to call elsewhere in the code. If timing is critical you should also make sure this ISR can't be bumped by a higher priority interrupt.

Mark
"then i assume its also critical that you have the opportunity to set a function to call prior to every execution of the interrupt" — not quite. The function will engage a peripheral as soon as possible after a pulse, but it doesn't have to be the very next one (if the next one is only a few clock cycles away, and we miss it, no big deal). But the comment about "determine the proper function to call for the next interrupt within the ISR" is a very good point.
detly
+1  A: 

Assuming setting the bool is an atomic operation (disassemble & read the manual to be sure) - I would use a flag to set the function pointer, but this flag shold only be read by the ISR, and only written by the normal code, giving you a simple semaphore. If writing the function pointer is atomic (again, check by disassembling), you can use it instead of the flag.

Like this (off the top of my head)

void (*myCallback)() = NULL;        

void RegisterCallback(void (*callback)())        
{        
   myCallback = callback;
}        

void interrupt MyExternalInterrupt() @EXTERNAL_1_VCTR        
{        
   // Do a bunch of other things here...        

   if (myCallback!=NULL)
      myCallback();        
   myCallback = NULL;        
}  

Edit After seeing disassembled instructions, using the function pointer as a flag will work. Modified code to show usage.

Jeff
I've added the disassembled instructions for the two operations. As far as I understand it, they are not atomic.
detly
Jeff
So I CAN get rid of the boolean flag and simply check `myCallback != NULL`... can you please update your answer with that info?
detly
There you go - good luck with everything else
Jeff