Can you use attachInterrupt() in a class?

Hey All,

I've developed an interrupt-driven serial receive function. It works great as a single file but I can't implement it as a class. I'm testing it against an EM-406A GPS sending NMEA sentences at 4800 baud.

This is the working single-file sketch

#define BUFFER_LENGTH 100
#define BAUD 4800

int symbolPeriod = 1000000/BAUD;  //micro-seconds
int lockPeriod = 10 * symbolPeriod;

volatile boolean bufferOverrun = false;
volatile char buffer[BUFFER_LENGTH];
volatile int bufferWrite = 0;
         int bufferRead  = 0;
volatile boolean lock = false;
volatile int flag = 0;
int byteCount = 0;

void setup()
{
  Serial.begin(115200);
  Serial.println("setup()");
  attachInterrupt(1, spike, CHANGE);
}

boolean availableBuffer()
{
  return !(bufferRead == bufferWrite);
}

byte readBuffer()
{
  if(availableBuffer)
  {
    byte out = buffer[bufferRead];
    bufferRead++;
    bufferRead %= BUFFER_LENGTH;
    return out;
  }
}

void writeBuffer(byte& in)
{
  buffer[bufferWrite] = in;
  if(bufferWrite == bufferRead) bufferOverrun = true;
  bufferWrite++;
  bufferWrite %= BUFFER_LENGTH;
}

void loop()
{
  while(availableBuffer()) Serial.print(readBuffer());
}

boolean bufferAvailable()
{
  return bufferRead < bufferWrite;
}

void spike()
{
  static boolean polarity = HIGH;
  static unsigned long tick;
  static unsigned long tock;
  static unsigned long gap;
      static unsigned long ulMax = -1;
      
  static int symbols = 0;         // number of bits in timing window
  static int offset = -2;         // bit position in output byte
  static int count = 0;           // general loop counter
  static byte character;          // output byte
  
  tock = tick;
  tick = micros();
      if(tick > tock)
            gap = tick - tock;
      else
            gap = tick + ulMax - tock;            // Prevents one-in-70-minute loss of lock. See: http://arduino.cc/en/Reference/Micros

  if(gap > lockPeriod) 
  {
    polarity = HIGH;
    lock = true;                  //indicate start of data found...
    return;                       // ... but dump the first "gap" anyway.
  }
  if(!lock) return;               //ignore incoming data until locked. Waits for "start" of pulse train.
  polarity = !polarity;
  symbols = (gap + 8) / symbolPeriod;    // 8 being twice the maximum resolution on micros() function
  for(count = 0; count < symbols; count++)
  {
    if(offset > 7)                // ran out of data bits, into wait bits
    {
      if(!polarity) lock = false; // some kind of data/timing error occurred. Expected polarity HIGH wait bits, got LOW.
      offset = -2;                // restart bit positions in output byte
                  writeBuffer(character);
      character = 0;              // reset output byte
      return;                     // drop remaining bits
    }
    offset++;
    if(offset < 0) continue;      // drop start bit
    character |= polarity << offset;
  }
}

The problem comes when I try to move the code out to a class. So far I'm just testing the attachInterrup part and can't get it to work. The error I'm getting at the moment is:

In constructor 'ISRSerialRX::ISRSerialRX()':
error: argument of type 'void (ISRSerialRX::)()' does not match 'void (*)()

Here's the main sketch for the "class version" of my code

#include "ISRSerialRX.h"

ISRSerialRX gps;

void setup()
{
  Serial.begin(115200);
  Serial.println("Setup()");
}

void loop()
{
}

Header File:

#ifndef ISRSerialRX_h
#define ISRSerialRX_h

#include "WProgram.h"

class ISRSerialRX
{
  public:
    ISRSerialRX();
    void spike();  //ISR
  private:
};

#endif

Implementation File:

#include "ISRSerialRX.h"

ISRSerialRX::ISRSerialRX()
{
  attachInterrupt(1, spike, CHANGE);
}

void ISRSerialRX::spike()
{
  static unsigned long count = 0;
  count++;
  if(count % 1000 == 0) Serial.println(count);
}

What do you think? Do I have a syntax issue or am I trying to do something attachInterrupt() is not designed for?

Thanks in advance. Sorry for the long post; I see "where's the code" all the time - so here it all is!

am I trying to do something attachInterrupt() is not designed for?

Yes. attachInterrupt works with a "pointer to a function". ISRSerialRX::spike is a "method". The biggest difference is that spike is passed a hidden parameter to this.

Someone else will probably have to help overcome the problem. I haven't had any experience with "pointers to methods". I know it can be done but I believe it requires a replacement for attachInterrupt (or something called a thunk which may not work on AVR processors).

But, if you were using Delphi, I could slap out a solution in my sleep!

Thanks Coding Badly.

If the method in the class is declared static, that one instance is shared between all instances of the class. Then, you can use:

  attachInterrupt(1, &ISRSerialRX::spike, CHANGE);

There are tricks you can perform if the static method needs access to other class methods, and the class is a singleton (only one instance will ever be created at one time).

It does not appear that this is true in your case. Unless there is more to the class than is shown, I'm not seeing the need for a class.