Handling interrupts during device communication
You could implement a small state machine like this:

// global, volatile (accessed from ISR) variable 
// (should also be 1-byte to prevent multibyte access issues on 8-bit system)
volatile CurrentState;

main() {
    Spi_Init();
    CurrentState = StartMeasurement;

    // your main loop
    while(true) {
        // other main processing here            

        // spi processing
        switch (CurrentState) {
           case StartMeasurement:
               Spi_StartMeasurement();
               CurrentState = WaitForResult;
               break;
           case WaitForResult:
               // nothing todo -&gt; no blocking main -&gt; this switch is just for clarification and can even be removed
               break;
           case ResultReady:
               Spi_GetResult();
               CurrentState = StartMeasurement;
               break;
           default:
               // CurrentState was set to invalid value -&gt; error handling
               break;
        }
    }
}

ISR_ResultReady() {
    if (CurrentState == WaitForResult) {   // this should always be true but its cleaner to check it
        // signal main that result is ready
        CurrentState = ResultReady;
    }
}


I hope you get the idea. There are cleaner, more abstract ways to do this, but for small applications it also overcomplicates things.