C++ 如何在Arduino类中创建ISR?

C++ 如何在Arduino类中创建ISR?,c++,class,arduino,interrupt,C++,Class,Arduino,Interrupt,我为Arduino写了一个类,它使用中断。目前,我需要在主Arduino草图中创建一个ISR实例,然后将其传递给类的初始化函数,该函数运行“attachInterrupt”。这是一种非常糟糕的风格(为什么用户应该知道我甚至在使用中断?),所以我希望整个内容都包含在类的头文件和源文件中 我尝试将ISR作为一个静态友元函数,但是它无法访问任何类的非静态成员。所以现在我有点困惑,什么应该是静态的,什么不应该是静态的。我试图做的事情看起来是这样的(这里的源代码和标题结合在一起以便于阅读) 但是编译器因为

我为Arduino写了一个类,它使用中断。目前,我需要在主Arduino草图中创建一个ISR实例,然后将其传递给类的初始化函数,该函数运行“attachInterrupt”。这是一种非常糟糕的风格(为什么用户应该知道我甚至在使用中断?),所以我希望整个内容都包含在类的头文件和源文件中

我尝试将ISR作为一个静态友元函数,但是它无法访问任何类的非静态成员。所以现在我有点困惑,什么应该是静态的,什么不应该是静态的。我试图做的事情看起来是这样的(这里的源代码和标题结合在一起以便于阅读)

但是编译器因为我在静态函数中使用非静态成员而对我大喊大叫。
如果您能帮助我理解如何使其工作,我将不胜感激。

中断必须是静态函数(如果它们是成员函数),才能正常工作,因此如果您想使用非静态成员,您需要获得一个实例。现实地做到这一点的唯一方法是使用全局变量

以下是您如何做到这一点的示意图:

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() {
        // ...
    }
}
这是设置中断的许多方法之一,但您无法避免中断本质上是全局的这一事实。在上面的实现中有许多“陷阱”,我省略了它们,希望您知道这些


另请注意,“静态”有多种含义。它在声明类成员时有一个含义,在声明类外的函数时有一个完全不同的含义(如声明中的
static void ISR()
)。在第二种情况下,现代C++编码风格使用匿名命名空间代替代码>静态< /代码>。< /P> < P> AS @ DietrichEpp说,中断必须是静态函数。但这里有另一种方法,在类中使用继承

步骤1-使用虚拟纯InterServ()函数和基于一组枚举的实例数组创建中断基类

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

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

static IntBase *tInstance[INT_MAX];
步骤2-每个使用类中断将具有:

  • 用于存储实例的构造函数
  • 用于该类的静态函数
    ISRFunc()
  • 一个虚拟函数
    InterServ()
    ,从静态
    ISRFunc()
    调用
class IntDev1
带编号的
INT\u DEV1


将“instance”定义为静态成员意味着它由MyClass类型的所有对象共享,因此在每个MyClass中没有我可以分配给的MyClass::instanceobject@shayelk:您是说不能向类中添加静态成员变量吗?为什么不呢?我可以添加一个静态成员变量,但它将是一个由类的所有实例共享的单个变量。我需要这个类的每个实例都有自己的中断函数,它改变了自己的中断函数members@shayelk:那么您正在使用同一类的不同实例注册多个不同的中断?@shayelk必须理解,中断是在随机(从主算法的角度)时间异步发生的。简单代码的任何一部分都猜不到“this”指针。换句话说:中断处理程序不了解实例(“this”指针/引用)-这是一个非常基本的事实。可以实现一些功能,但不是简单的“为什么用户应该知道我在使用中断?”因为您使用的是微控制器的独特功能,所以他需要知道它,这样他就可以计划如何管理它。如果您对一个管脚使用更改时中断,并且您不想让他看到,那么他将无法对每个其他管脚使用更改时中断,因为您已经实现了ISR。只需要做一个函数,他必须调用ISR,或者在宏中编写ISR,并要求他在主代码中编写它
class IntBase {
public:
    virtual void InterServ() = 0;
};

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

static IntBase *tInstance[INT_MAX];
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();
    }
};