Skip to content

7. Interrupt Service Routine

Sara Damiano edited this page Aug 13, 2024 · 4 revisions

(We have received an interrupt signal, what should we do?)

7.1 handleInterrupt()

Passes off responsibility for the interrupt to the active object.

inline void SDI12::handleInterrupt(){
  if (_activeObject) _activeObject->receiveChar();
}

7.2 receiveChar()

This function quickly reads a new character from the data line in to the buffer. It takes place over a series of key steps.

7.2.1 - Check for the start bit. If it is not there, interrupt may be from interference or an interrupt we are not interested in, so return.

7.2.2 - Make space in memory for the new character "newChar".

7.2.3 - Wait half of a SPACING to help center on the next bit. It will not actually be centered, or even approximately so until delayMicroseconds(SPACING) is called again.

7.2.4 - For each of the 8 bits in the payload, read wether or not the line state is HIGH or LOW. We use a moving mask here, as was previously demonstrated in the writeByte() function.

The loop runs from i=0x1 (hexadecimal notation for 00000001) to i<0x80 (hexadecimal notation for 10000000). So the loop effectively uses the masks following masks:

  • 00000001
  • 00000010
  • 00000100
  • 00001000
  • 00010000
  • 00100000
  • 01000000
  • and their inverses.

Here we use an if / else structure that helps to balance the time it takes to either a HIGH vs a LOW, and helps maintain a constant timing.

7.2.5 - Skip the parity bit. There is no error checking.

7.2.6 - Skip the stop bit.

7.2.7 - Check for an overflow. We do this by checking if advancing the tail would make it have the same index as the head (in a circular fashion).

7.2.8 - Save the byte into the buffer if there has not been an overflow, and then advance the tail index.

void SDI12::receiveChar()
{
  if (digitalRead(_dataPin))   // 7.2.1 - Start bit?
  {
   uint8_t newChar = 0;   // 7.2.2 - Make room for char.

    delayMicroseconds(SPACING/2);  // 7.2.3 - Wait 1/2 SPACING

    for (uint8_t i=0x1; i<0x80; i <<= 1) // 7.2.4 - read the 7 data bits
    {
      delayMicroseconds(SPACING);
      uint8_t noti = ~i;
      if (!digitalRead(_dataPin))
        newChar |= i;
      else
        newChar &= noti;
    }

    delayMicroseconds(SPACING);    // 7.2.5 - Skip the parity bit.
 delayMicroseconds(SPACING);   // 7.2.6 - Skip the stop bit.

       // 7.2.7 - Overflow? If not, proceed.
    if ((_rxBufferTail + 1) % _BUFFER_SIZE == _rxBufferHead)
    { _bufferOverflow = true;
    } else {      // 7.2.8 - Save char, advance tail.
      _rxBuffer[_rxBufferTail] = newChar;
      _rxBufferTail = (_rxBufferTail + 1) % _BUFFER_SIZE;
    }
  }
}

7.3 Interrupt Vectors

Check if the various interrupt vectors are defined. If they are the ISR is instructed to call _handleInterrupt() when they trigger.

#if defined(PCINT0_vect)
ISR(PCINT0_vect){ SDI12::handleInterrupt(); }
#endif

#if defined(PCINT1_vect)
ISR(PCINT1_vect){ SDI12::handleInterrupt(); }
#endif

#if defined(PCINT2_vect)
ISR(PCINT2_vect){ SDI12::handleInterrupt(); }
#endif

#if defined(PCINT3_vect)
ISR(PCINT3_vect){ SDI12::handleInterrupt(); }
#endif