• AVR Freaks

LockedVolatile and Interrupts in C18??

Author
Guest
Super Member
  • Total Posts : 80503
  • Reward points : 0
  • Joined: 2003/01/01 00:00:00
  • Location: 0
  • Status: online
2005/01/04 02:27:18 (permalink)
0

Volatile and Interrupts in C18??

Hello,

I have read from the C18 user manual that if a variable is used in the interrupt service routine and in the main this variable should use the "volatile" qualifier. Also I have seen recommend that if an ISR call a function then it should used save=section(".tmpdata"), save PROD,...

But then, I have downloaded the App.note AN930 and the J1939 C library. In the example 5b, I have seen that the ISR calls J1939_ISR();

void InterruptHandlerHigh( void )
{
if (PIR3 != 0x00)
J1939_ISR();
}

------------------------------

The J1939_ISR() calls J1939_ReceiveMessages() and also modifies RXQueueCount which is also modified in the Main.

So my question is: Is this OK? or it should use volatile and save??

Thanks a lot and best regards,

Javidv
#1

1 Reply Related Threads

    Guest
    Super Member
    • Total Posts : 80503
    • Reward points : 0
    • Joined: 2003/01/01 00:00:00
    • Location: 0
    • Status: online
    RE: Volatile and Interrupts in C18?? 2005/01/04 14:37:30 (permalink)
    +2 (1)
    The 'volatile' qualifier tells the compiler that the variable may change spontaneously. The compiler shouldn't rely on a cached copy of the variable which may be left over from a previous calculation, but should fetch a fresh copy each and every time it uses the variable. If a variable is used in an ISR and in a function outside the ISR, it should be declared 'volatile'.

    But wait, that's not all! If you're accessing a 16-bit variable, you may get the interrupt between the read of the high and low bytes. So even if you've declared the variable as 'volatile' it could get corrupted very badly by this. Say the main code reads a variable. The interrupt occurs in the middle of the read, so the main code ends up with the high byte from before the interrupt and the low byte from after the interrupt. This could lead to bizarre random errors that are a pain in the @$$ to track down.

    You need to make sure that each access to a shared variable is atomic. The easiest way to do that is to disable interrupts before reading the variable and re-enable them afterwards. This makes it impossible to get the variable in the half-updated state.


    static volatile int16_t irq_counter; // Our counter variable

    /*****************************************************************************
    **
    ** Accessor routines
    **
    *****************************************************************************/
    int16_t getCounter(void)
    {
    in16_t val;
    int8_t gieh;

    gieh = INTCONbits.GIEH; // Save interrupt status
    INTCONbits.GIEH = 0; // Disable interrupts
    val = irq_counter; // Read the counter
    INTCONbits.GIEH = gieh; // Restore interrupt status
    return(val); // Return counter value
    }

    void putCounter(int16_t val)
    {
    int8_t gieh;
    gieh = INTCONbits.GIEH; // Save interrupt status
    INTCONbits.GIEH = 0; // Disable interrupts
    irq_counter = val; // Write the counter
    INTCONbits.GIEH = gieh; // Restore interrupt status
    return;
    }

    /*****************************************************************************
    **
    ** ISR
    **
    *****************************************************************************/
    #pragma interrupt isr_high save=PROD
    void isr_high(void)
    {
    if (INTCONbits.TMR0IE && INTCONbits.TMR0IF)
    {
    irq_counter = TMR0L | (int16_t)(TMR0H) << 8;
    INTCONbits.TMR0IF = 0;
    }
    }


    As to whether or not you need to save PROD, .tmpdata, etc... That's tricky. In general, if you call any functions from your ISR you probably need to save .tmpdata. There are exceptions when this is not the case, and the only way to tell if any particular function call needs .tmpdata saved is to read the assembly code for the ISR and the function. Since .tmpdata can be large it can take a considerable amount of time to save. It's best if you can avoid the situation altogether by avoiding function calls from ISRs.

    PROD is even worse. In my opinion you should always save the PROD register, unless your ISR is really trivial. The compiler likes to use PROD as a scratch register, so failing to save it could cause intermediate results to get corrupted. Again, there are exceptions, but you'll have to read the assembly. If the assembly for you ISR does not use PROD, you're safe not saving it. But if you ever change the code, you'll have to check the assembly again. It's far easier to save it every time.
    #2
    Jump to:
    © 2020 APG vNext Commercial Version 4.5