1

I have code like this: (very simplified code)

// This is code for Microchip 8-bit microcontroller, XC8 compiler (GCC based)

#define TIMER_COUNT 8;
volatile uint16_t timer_values[TIMER_COUNT];
volatile uint16_t timer_max_values[TIMER_COUNT];

// executes every 100ms
void timer1_interrupt_handler()
{
    for (uint8_t i = 0; i < TIMER_COUNT ; i++){
        timer_values[i];
    }
}

void main(){

    // (...) some initialization, peripherial and interrupt handler setup

    while(1){
        for (uint8_t i = 0; i < TIMER_COUNT ; i++) {

            // what if interrupt happens in the middle of comparison?
            if (timer_values[i] >= timer_max_values[i]) 
            {
                 timer_values[i] = 0;   // reset timer
                 executeTimerElapsedAction(i);
            }

        }
    }
}

The problem with this is that this is 8-bit microcontroller and when interrupt happens in the middle of non-atomic operation on 16 bit variable:

timer_values[i] >= timer_max_values[i]

or this:

timer_values[i] = 0; 

it is possible, that half of uint16_t will be rewriteen by interrupt handler and everything is messed up.

This is not RTOS, so I have no builtin locks.

What can I do? How to make lock from scratch?

I was thinking about creating "critical section" like this:

GlobalInterruptsDisable();     // enter critical section

if (timer_values[i] >= timer_max_values[i]) 
{
    timer_values[i] = 0;   // reset timer
    GlobalInterruptsEnable();     // exit critical section
    executeTimerElapsedAction(i);
}

but I'm afraid that I will skip some interrupts meanwhile (I'm using 2 timers, 2 UARTs, and I2C interrupts) and something else will mess up.

Additional question:

If I disable interrupts for about 20-30 processor cycles and then some data comes to UART - I will skip this data or interrupt handler will execute later after I enable interrupts?

Kamil
  • 13,363
  • 24
  • 88
  • 183
  • Afaik, c99 doesn't define any atomic operations and those would be needed to implement locks. – Ouroborus Jul 07 '19 at 00:45
  • 1
    You should protect your 16 Bit value before using it. And yes the ISR will execute later if you reenable the interrupts. – Mike Jul 11 '19 at 06:23
  • @Mike , you can post this as an answer. But please explain what you mean by "protect your 16 Bit value"? Disable interrupts before and re-enable after accessing it? – Kamil Jul 11 '19 at 18:21

1 Answers1

1

You could write a function for a save parameter copy which stops the interrupt, do the copy and then restart the interrupt.

#define protectedXfer(destination, source) protectedMemcpy(&(destination), \
                                                          &(source), \
                                                          sizeof(destination))

void protectedMemcpy(void *destination, const void *source, size_t num)
{
    int volatile oldIpl;
    oldIpl = SRbits.IPL;
    SRbits.IPL2 = 1; // If an Interrupt occurs here
    SRbits.IPL1 = 1; // the IPL bits are saved/restored
    SRbits.IPL0 = 1; // from the stack
    memcpy(destination, source, num);
    SRbits.IPL = oldIpl;
}

Know you can do a save transfer from your timer values and check afterwards.

protectedXfer(destinaion, source);
Mike
  • 4,041
  • 6
  • 20
  • 37
  • I don't understand part with setting IPL flags to 1. It turns off interrupts? (I'm not using interrupt priority levels and I have no experience with this part of SR register). – Kamil Jul 12 '19 at 11:53
  • Can I use `INTCONbits.GIE = 1` and `INTCONbits.GIE = 0` instead of IPL bits manipulation? – Kamil Jul 12 '19 at 11:59
  • @Kamil Yes of course if you don't use the priority levels. – Mike Jul 29 '19 at 09:22