4

I have a class I wrote for Arduino which uses interrupts. Currently I need to create an instance of the ISR in the main Arduino sketch, then pass it to the class' initializing function, which runs "attachInterrupt". This is very bad style (why should the user know that I'm even using interrupts?), so I want the whole thing to be contained inside the class's header and source files.

I tried making the ISR a static friend function, but then it can't reach any of the class' non-static members. So now I'm a bit confused about what should and shouldn't be static for this approach to work. what I tried to do looks something like that (source and header combined here for easy reading)

class myClass{
    friend void ISR();
    void init(){attachInterrupt(ISR,..,..);}
}

static void ISR(){
    all sort of stuff using myClass.members;
}

But the compiler yells at me for using non static members in a static function. I would really appreciate some help in understanding how I can make it work.

1
  • "why should the user know that I'm even using interrupts?" because you are using a unique feature of the microcontroller, so he needs to know it, so he can plan how to manage it. If you are using the interrupt-on-change for one pin, and you don't want him to see it, then he won't be able to use interrupt-on-change for every other pin, since the ISR is already implemented by you. Just make a function he has to call in the ISR, or write the ISR in a macro and ask him to write it in the main code Commented Jan 4, 2017 at 8:55

2 Answers 2

3

Interrupts must be static functions (if they are member functions) in order to work correctly, so if you want to use non-static members, you need to get an instance. The only way you can realistically do this is with global variables.

Here is a sketch of how you can do that:

class MyClass {
    static MyClass *instance;

    void init() {
        instance = this;
        attachInterrupt(...)
    }

    // Forward to non-static member function.
    static void ISRFunc() {
        instance->ISR();
    }

    // Do your work here.
    void ISR() {
        // ...
    }
}

This is one of many ways to set this up, but you can't avoid the fact that interrupts are inherently global. There are a number of "gotchas" in the above implementation which I'm omitting, hopefully you're aware of those.

P.S. Also note that "static" has multiple meanings. It has one meaning when declaring class members, and a completely different meaning when declaring functions outside a class (as in your declaration static void ISR()). Modern C++ coding style leans using anonymous namespaces instead of static in the second case.

Sign up to request clarification or add additional context in comments.

8 Comments

Defining 'instance' as a static member means it is shared by all objects of type MyClass, so there is no MyClass::instance that I can assign to in each MyClass object
@shayelk: Are you saying that you can't add a static member variable to your class? Why not?
I can add a static member variable, but it will then be a single variable shared by all instances of the class. I need each instance of the class to have its own interrupt function which changes its own members
@shayelk: So you're registering for multiple different interrupts with different instances of the same class?
@shayelk Must understand, interrupt occur in random (from point of view main algorithm) time, asynchronous. No one part of simple code can guess "this" pointer. In other words: interrupt handler have NOT knowledge about instance ("this" pointer/refference) - this is very basic fact. Some implementation can be done, but not simple
|
1

As @DietrichEpp said, Interrupts must be static functions. But here is another approach by using the inheritance in a class.

Step 1 - create a Interrupt base class with a virtual pure InterServ() function and an array of instance based to a set of enum.

class IntBase {
public:
    virtual void InterServ() = 0;
};

enum eIntNum {
    INT_DEV1 = 0,
    INT_DEV2,
    // ...
    INT_MAX
};

static IntBase *tInstance[INT_MAX];

Step 2 - each use-class interrupt will have:

  • a constructor to store the instance,
  • a static function ISRFunc() dedicate to that class
  • a virtual function InterServ(), called from the static ISRFunc().

class IntDev1 with the number INT_DEV1

class IntDev1 : public IntBase {
public:
    IntDev1() {
        tInstance[INT_DEV1] = this;
        // attachInterrupt(...)
    }
    virtual void InterServ() {
        // access to local members
    }
    static void ISRFunc() {
        tInstance[INT_DEV1]->InterServ();
    }
};

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.