简体   繁体   English

c 函数的 C++ 回调

[英]C++ callback for c function

Yes,,, I know there is many threads about this, and I've think I've read most of them, but either I don't understand the answers or I have not been able to adapt them to my "case".是的,我知道有很多关于这个的话题,我想我已经阅读了其中的大部分,但要么我不明白答案,要么我无法将它们改编成我的“案例”。

Be aware, my background is electronic design, not software design so for some of you, my question maybe seem to be stupid, but... I am stuck.请注意,我的背景是电子设计,而不是软件设计,所以对于你们中的一些人来说,我的问题可能看起来很愚蠢,但是......我被卡住了。

I have designed a pcb board for iot purposes.我设计了一个用于物联网的 pcb 板。 It is based on a ESP32 module.它基于 ESP32 模块。 I have 5 buttons connected to the ESP.我有 5 个按钮连接到 ESP。 ESP32-IDF is far to complicated for me so I've tried to go for the Ardiuno framework. ESP32-IDF 对我来说太复杂了,所以我尝试使用 Ardiuno 框架。 Now it starts to get complicated.现在它开始变得复杂。

To detect and debounce the buttons I created a C++ class called Button.为了检测和消除按钮的抖动,我创建了一个名为 Button 的 C++ 类。 A skeleton can be seen below.下面可以看到一个骨架。

class Button {
..
..

private:
  void memberCallback() {
    ...
  }



public:
  Button(const uint8_t gpio ) {
    ..
    attachInterrup(digitalPinToInterrupt(gpio), memberCallback, FALLING);
    ..
  }

..

}

I have not found any way to define "memberCallback" without not causing compile errors or not working at all.我还没有找到任何方法来定义“memberCallback”而不会导致编译错误或根本不工作。

This must be a common problem so, please suggest i solution :)这一定是一个常见问题,所以请建议我的解决方案:)

Edit.编辑。 Seems like I not have expressed myself clearly enough, sorry.好像我表达的不够清楚,抱歉。 - I am aware that if I make memberCallback static, it will at least compile. - 我知道如果我将 memberCallback 设为静态,它至少会编译。 Problem is that I planned to use 5 instances of this.问题是我计划使用 5 个实例。 A static callback means that all instances will run same code.静态回调意味着所有实例都将运行相同的代码。 5 instances means 5 different interrupts. 5 个实例意味着 5 个不同的中断。 How do I identify them.我如何识别它们。

The ESP32 Arduino Core has an example of how to do exactly this at ESP32 Arduino Core 有一个如何做到这一点的例子

https://github.com/espressif/arduino-esp32/tree/master/libraries/ESP32/examples/GPIO/FunctionalInterrupt https://github.com/espressif/arduino-esp32/tree/master/libraries/ESP32/examples/GPIO/FunctionalInterrupt

I'll quote code from it here:我将在这里引用它的代码:

#include <Arduino.h>
#include <FunctionalInterrupt.h>

#define BUTTON1 16
#define BUTTON2 17

class Button
{
public:
    Button(uint8_t reqPin) : PIN(reqPin){
        pinMode(PIN, INPUT_PULLUP);
        attachInterrupt(PIN, std::bind(&Button::isr,this), FALLING);
    };
    ~Button() {
        detachInterrupt(PIN);
    }

    void IRAM_ATTR isr() {
        numberKeyPresses += 1;
        pressed = true;
    }

private:
    const uint8_t PIN;
    volatile uint32_t numberKeyPresses;
    volatile bool pressed;
}

The important things are:重要的是:

  • #include <FunctionalInterrupt.h> - this gets you std::bind() and a slightly different attachInterrupt() declaration that makes this work #include <FunctionalInterrupt.h> - 这让你得到std::bind()和一个稍微不同的attachInterrupt()声明,使这项工作
  • Use std::bind(&Button::isr,this) to bind your interrupt handler to your object.使用std::bind(&Button::isr,this)将您的中断处理程序绑定到您的对象。
  • Make sure you declare your interrupt handler as IRAM_ATTR to ensure the code for it will remain loaded in RAM.确保将中断处理程序声明为IRAM_ATTR以确保它的代码将保持加载到 RAM 中。
  • Make sure you declare any instance variables that you want to modify in the interrupt handler as volatile so that the C++ compiler will know they make change without warning.确保将要在中断处理程序中修改的任何实例变量声明为volatile以便 C++ 编译器知道它们会在没有警告的情况下进行更改。

std::bind() is a standard C++ library function; std::bind()是标准的 C++ 库函数; there's documentation about it online .网上关于它文档

That said, I'm concerned about what you plan to do inside your interrupt handler.也就是说,我担心您打算在中断处理程序中做什么。

Interrupt handlers need to run for a very brief amount of time, and they need to be very careful about calling other functions and making sure that data structures are in an inconsistent state.中断处理程序需要运行很短的时间,他们需要非常小心地调用其他函数并确保数据结构处于不一致的状态。 Unless you lock out interrupts (which you should do as little as possible) outside of the interrupt handler, data structures can easily be in inconsistent states.除非您在中断处理程序之外锁定中断(您应该尽可能少做),否则数据结构很容易处于不一致状态。

The example in the Arduino Core is good - it just changes a couple of variables. Arduino Core 中的例子很好——它只是改变了几个变量。 Doing more than that - calling functions like Serial.println() or allocating memory or creating objects is not safe.做更多的事情 - 调用Serial.println()类的函数或分配内存或创建对象是不安全的。

When you look at the documentation of attachInterrupt it says that the ISR is a function taking no argument.当您查看attachInterrupt的文档时,它说 ISR 是一个不带参数的函数。 Its type is void(*)() (or most likely actually extern "C" void(*)() but I haven't researched the exact type thoroughly enough).它的类型是void(*)() (或者很可能实际上是extern "C" void(*)()但我还没有足够彻底地研究确切的类型)。 Your memberCallback looks as if it does not take an argument but that is actually not true: in a [non- static ] member function you got an implicit argument: this , the pointer to the object which is need to determine which Button object is used.您的memberCallback看起来好像不带参数,但实际上并非如此:在 [非static ] 成员函数中,您有一个隐式参数: this ,指向需要确定使用哪个Button对象的对象的指针. The type of memberCallback is void (Button::*)() and is incompatible with void(*)() . memberCallback的类型是void (Button::*)()并且与void(*)()不兼容。 Th implication is that you will not be able to use a [non- static ] member function directly.这意味着您将无法直接使用 [非static ] 成员函数。

As was suggest in other answers, you could use a static member function.正如其他答案中所建议的那样,您可以使用static成员函数。 However, that has two problems:但是,这有两个问题:

  1. The function has only access to static members (both function and variables).该函数只能访问static成员(函数和变量)。
  2. The function won't be extern "C"`.该函数不会是extern "C"`。

You will need one function for every Button you want to register.对于要注册的每个Button您都需要一个功能。 For just one Button that would look like that:对于一个看起来像这样的Button

class Button
{
    // ....
public:
    void memberCallback();
    Button(int gpio, Button*& ptr, void(*isr)()) {
        ptr = this;
        attachInterrupt(digitalPinToInterrupt(gpio), isr, FALLING);
    }
};

Button* button1;
extern "C" void button1ISR() {
    button1ISR->memberCallback();
}

// create you Button somewhere, e.g.:
int main() {
    Button b1(gpio, button1, button1ISR);
    // ...
}

Make your memberCallback function static.将您的memberCallback函数设为静态。 Change your code to this:将您的代码更改为:

class Button {
..
..

private:
  static void memberCallback() {
    ...
  }



public:
  Button(const uint8_t gpio ) {
    ..
    attachInterrup(digitalPinToInterrupt(gpio), memberCallback, FALLING);
    ..
  }

..

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM