Hall sensors and AttachInterrupt()

Hi, I'm writing a little library to process interrupts from a hall probe to work out the RPM of wheel.

The code was working just fine in a sketch, but won't work (i.e. won't compile) when I move it to a library.

Heres the relevant snippet from the .cpp library file...

/**************************************************************************
  Set the pin that the probe is connected to.
**************************************************************************/
void HallPulseCounter::begin(int pin)                       
{
  int interrupt =0;

  switch (pin) 
  {
    case 2:
      interrupt = 0;
      break;
    case 3:
      interrupt = 1;
      break;
    case 21:
      interrupt = 2;
      break;
    case 20:
      interrupt = 3;
      break;
    case 19:
      interrupt = 4;
      break;
    case 18:
      interrupt = 5;
      break;
    default: 
      Serial.print("error HallPulseCounter::begin - invalid pin");
      break;
  }

  // define the pin as an INPUT
  pinMode(pin, INPUT);

  // attach the appropriate interrupt
  attachInterrupt(interrupt,recPulse,FALLING);

  // Clear the array 
  arrayZeroUnsignedLong(_pulse,HALL_ARRAY_SIZE);
}


/**************************************************************************
  Record the moment that a pulse was detected by the hall probe
**************************************************************************/
void HallPulseCounter::recPulse()
{

  // Shift the array of values one step sideways. This will discard _pulse[HALL_ARRAY_SIZE] and
  // reset _pulse[0] to zero.
  arrayShiftUnsignedLong(_pulse,HALL_ARRAY_SIZE);

  // Load the timestamp for this pulse into the top of the array. 
  _pulse[0] = millis();

}

Here's the sketch....

#include <stdlib.h>                        // needed for 'free'
#include "WProgram.h"                      // needed for 'Serial'
#include <Wire.h>                          // Library to control I2C comunications
#include <avr/pgmspace.h>                  // Needed for PROGMAN stuff
#include <ProgmemConst.h>                  // Global progmem constants

#include <HallPulseCounter.h>              // Combined altitude library based on both pressure and ultrasonic values.

int rpm = 0;

HallPulseCounter cadence1;                

void setup() 
{
  Serial.begin(115200); // start serial communication at 9600bps 

  Serial.println("##################################################################");
  Serial.println("####################     START     ###############################");
  Serial.println("##################################################################");
  
  cadence1.begin(2);  // Connect hall sensor to pin 2
}

void loop() 
{
  rpm=cadence1.getFreq();

  Serial.print("rpm <");  Serial.print(rpm);  Serial.println("> (cm)");
  delay(500); // wait for half a second
}

And this is the (only) error I get when I compile....
In member function 'void HallPulseCounter::begin(int)':
error: argument of type 'void (HallPulseCounter::)()' does not match 'void (*)()'

If I comment out this line...

attachInterrupt(interrupt,recPulse,FALLING);

then the code compiles just fine.

Heres the relevant snippet from the .cpp library file...

Generally: don't post snippets, post the full code, with a snippet we always have to guess what the rest may be.

Your problem is that a object method is not just a subroutine:

void HallPulseCounter::recPulse()

and

attachInterrupt(interrupt,recPulse,FALLING);

If you have declared the recPulse method static, you can use that code:

attachInterrupt(interrupt,HallPuleCounter::recPulse,FALLING);

but you cannot use object variables in the method because it doesn't get an object reference.

Thanks for the response, but I'm still having trouble.

If you have declared the recPulse method static

I know how to declare an individual variable as static, but I've not tried this with a method before, and when I do it just seems to generate more errors. As you can see from the sketch I've actually managed to work around the problem by setting up the interrupt in the sketch and causing it to call a function from the library, but I'd still like to know how to do this properly!

I've attached a stripped down, but working version of the files (working except for the compile error!)

Here's the full .h file...

#ifndef HallPulseCounter_h                                   // These lines (together with the #endif at the end of the file) prevent
#define HallPulseCounter_h                                   // any problems occuring if this library should be included twice.

extern "C" {
  #include <string.h>
}

#include <stdlib.h>                                          // needed for 'free'
#include "WProgram.h"                                        // needed for 'Serial'
#include <avr/pgmspace.h>                                    // Needed for PROGMAN stuff
#include <ProgmemConst.h>                                    // Global progmem constants

const int HALL_ARRAY_SIZE=6;                                 // Size of array that holds pulse timestamps 
const int MAX_AGE=6000;                                      // Maximum age of any recorded pulse.

// If defined, data will be writen to a logfile as well as displayed on screen
//#define DEBUG_LOG

class HallPulseCounter
{
  public:
    HallPulseCounter();                                      // Constructor
    void begin(int pin);                                     // Set the pin that the probe is connected to
    //int getFreq(void);                                       // Return the pulse frequency in RPM
    void recPulse();                                         // Record the moment a pulse occurs

  private:
    // Member variables
    char * _info;                                            // Temporary storage used for holding strings (normally error messages) 
                                                             // for logging to SD card.
    unsigned long _pulse[HALL_ARRAY_SIZE];                   // array of timestamps of most recent pulses.

};

#endif

And the full .cpp file....

extern "C" {
  #include <string.h>
}

#include <stdlib.h>                        // needed for 'free'
#include "WProgram.h"                      // needed for 'Serial'
#include "HallPulseCounter.h"              // header for this library
#include <avr/pgmspace.h>                  // Needed for PROGMAN stuff
#include <ProgmemConst.h>

#ifdef DEBUG_LOG
SingLog2 *logComp = SingLog2::GetLogger();  // Global (to this class) singleton logger.
#endif


/**************************************************************************
  Constructor
**************************************************************************/
HallPulseCounter::HallPulseCounter()
{
  _info = gMedBuff;                    // link the local info buffer to the global buffer (defined in ProgmemConst.cpp)
}


/**************************************************************************
  Set the pin that the probe is connected to.
**************************************************************************/
void HallPulseCounter::begin(int pin)                       
{
  int interrupt =0;

  // define the pin as an INPUT
  pinMode(pin, INPUT);

  // attach the appropriate interrupt
  attachInterrupt(interrupt,recPulse,FALLING);
  //attachInterrupt(interrupt,HallPulseCounter::recPulse,FALLING);
  //attachInterrupt(interrupt,HallPulseCounter.recPulse,FALLING);
  //attachInterrupt(interrupt,HallPulseCounter->recPulse,FALLING);
  //attachInterrupt(interrupt,this->recPulse,FALLING);

}


/**************************************************************************
  Record the moment that a pulse was detected by the hall probe
**************************************************************************/
void HallPulseCounter::recPulse()
{

  
  // Load the timestamp for this pulse into the top of the array. 
  _pulse[0] = millis();

}

And sketch to test....

#include <stdlib.h>                        // needed for 'free'
#include "WProgram.h"                      // needed for 'Serial'
#include <Wire.h>                          // Library to control I2C comunications
#include <avr/pgmspace.h>                  // Needed for PROGMAN stuff
#include <ProgmemConst.h>                  // Global progmem constants
#include <HallPulseCounter.h>              // Rev-counter library based on hall-effect probe.

int rpm = 0;

HallPulseCounter cadence1;                

void setup() 
{
  Serial.begin(115200); // start serial communication at 9600bps 

  Serial.println("##################################################################");
  Serial.println("####################     START     ###############################");
  Serial.println("##################################################################");
  
  cadence1.begin(3);  // Connect hall sensor to pin 3 (interrupt 1)
  
  attachInterrupt(1,recordPulse,FALLING);
  
}

void loop() 
{
  //rpm=cadence1.getFreq();

  Serial.print("rpm <");  Serial.print(rpm);  Serial.println("> (revs per min)");
  delay(100); // wait for half a second
}

void recordPulse()
{
  cadence1.recPulse();
}
    void recPulse();                                         // Record the moment a pulse occurs

You haven't declared your function static (you must not do that in the .cpp file, only in the header file, so in the class definition), that means you cannot call it directly.

The problem is, you cannot call an instance method because the method will never get the needed object reference to access the instance variables. As you might have seen you cannot provide the interrupt routine with a parameter, so how should the called function get that needed reference?

The following link describes the problem and provides a solution for it. This type of template programming is usually a complete overkill for embedded programming and it doesn't work with the attachInterrupt() function.

The problem is, you cannot provide an object to the attachInterrupt() function but only a function pointer. That means you have to provide functions for all possible interrupts, register them and call there the object method you registered for that interrupt:

HallPulseCounter *cbobjects[] = { NULL, NULL }; // code is useful for UNO only, for Mega it has to be extended

void int0cb(void) {
  if (cbobjects[0]) {
    cbobjects[0]->recPulse();
  }
}

void int1cb(void) {
  if (cbobjects[1]) {
    cbobjects[1]->recPulse();
  }
}

void HallPulseCounter::begin(int pin)                       
{
  int interrupt = pin == 2 ? 0 : 1;

  // define the pin as an INPUT
  pinMode(pin, INPUT);

  // attach the appropriate interrupt
  cbobjects[interrupt] = this;
  attachInterrupt(interrupt, interrupt == 0 ? int0cb : int1cb,FALLING);

}

Thanks for your help Pylon,

I think I understand the issue better now, although I still haven't managed to successfully code an elegant solution for this.

I have however managed to code a work-around, simply by defining my interrupts within the sketch, referencing functions that then called the desired routines within the library.

Thanks for your time

@BigusDickus

Hey man,

I'm also handling a little Hall sensor right now, and i'm having a lot of trouble with reading the signal. It's an ABS speed sensor from a ford fiesta R2 rally car. I posted a topic about it earlier and i'm still working on it, but i read your post and would it be possible to share the library? I might give it a try and see all my problems solved in a second... Would be great :wink:

anyhow, good luck with it!