C++ 回调 ESP32 / Arduino 上的成员函数指针

问题描述 投票:0回答:1

在 C++ 中这似乎是一件很难的事情。对 C 风格的类中的成员函数的回调,从而使其成为 void(*)() 以便能够在 C 风格的回调函数中使用 this。 在本例中,我想在 ESP32/Arduino 上使用它来将中断附加到成员函数。

(我研究了互联网并花了几天时间寻找解决方案,但无济于事)。

好消息,我自己找到了解决方案,我认为这很棘手,但我以前没有见过这样的解决方案。它可以在我的 Mac 上本地运行,但坏消息是,它不能在 ESP32 上运行。

PlatformIO 上的行:

attachInterrupt(pin,_lambdas.back(),RISING) ; 

给出编译错误:

"no suitable conversion function from "std::function<void ()>" to "void (*)()" exists"

而attachInterrupt函数的定义是完全相同的。

代码非常简单易懂。我做的最棘手的事情是将 Lambda 函数存储在向量中。事实上,我很惊讶它竟然能运行,而且在我的 Mac 上也能运行。 我有点菜鸟,但是任何帮助让它在 EPS32 上工作的帮助都是值得赞赏的!!!

参见下面的代码:

主要.cpp:

#include "button.hpp"

void pushedA() { printf("Pushed A\n") ; }
void pushedB() { printf("Pushed B\n") ; }
void pushedC() { printf("Pushed C\n") ; }

int main() {
    printf("Start\n") ;
    Button a(27,pushedA) ;
    Button b(25,pushedB) ;
    Button c(23,pushedC) ;

    printf("Execute\n") ;
    // So in the interruptStore we have functionpointers to class members. Now execute!
    for (auto interruptRoutine : interruptStore) {
        interruptRoutine() ;
    }
    return 0 ;
}

按钮.hpp:

#ifndef BUTTON_H
#define BUTTON_H
#include <vector>
#include <functional>
/* Helper routines to simulate Arduino "attachInterrupt" */
extern std::vector<std::function<void(void)>>interruptStore ;
#define RISING 1
// Same function definition as the Arduino definition, but on the Arduino this doesn't seem to work :-(
inline void attachInterrupt(uint8_t pin, std::function<void(void)> intRoutine, int mode) { interruptStore.push_back(intRoutine) ; }
inline void detachInterrupt(uint8_t pin) { ; }
/**************************************************/

class Button {
public:
    Button(uint8_t pin, void (*callbackFunction)()) ;
  ~Button() ;
private:
  inline void pressed() ;
  uint32_t _pin ;
  void    (*_func)() ;
  uint32_t _debounce ;
  static std::vector<std::function<void(void)>>_lambdas ;
} ;
#endif

按钮.cpp:

#include "button.hpp"

std::vector<std::function<void(void)>>interruptStore ;

Button::Button(uint8_t pin, void (*callbackFunction)()) {
    _func = callbackFunction ;
    auto pressedFunction =  [this]() {this->pressed() ;} ;
    _lambdas.push_back(pressedFunction) ;
    attachInterrupt(pin,_lambdas.back(),RISING) ;
    _pin = pin ;
}

Button::~Button() {
    detachInterrupt(_pin) ;
}

inline void Button::pressed() {
//  if (millis()-_debounce>60) { // Here we would normally handle the debounce
        printf("Button on pin %d pressed\n",(int)_pin) ; 
        _func() ;
//      _debounce = millis() ;
//  }
}

std::vector<std::function<void(void)>>Button::_lambdas ; // We need to define a static here, declaration is done in button.hpp

或在 Github 上:https://github.com/MatersM/Arduino-Button

ps。我也不敢相信,但它确实可以在 Mac 上运行: enter image description here

在终端上输出,以显示其工作原理:

Launching: '/Users/mmaters/Documents/code/BUTTON-TEST/output/main'
Working directory: '/Users/mmaters/Documents/code/BUTTON-TEST'
1 arguments:
argv[0] = '/Users/mmaters/Documents/code/BUTTON-TEST/output/main'
Start
Execute
Button on pin 27 pressed
Pushed A
Button on pin 25 pressed
Pushed B
Button on pin 23 pressed
Pushed C
Process exited with status 0
logout

Saving session...
...copying shared history...
...saving history...truncating history files...
...completed.

[Proces voltooid]
c++ arduino function-pointers esp32 pointer-to-member
1个回答
0
投票

使用这种形式的中断回调的能力是 ESP32 Arduino/PlatformIO 库上提供的扩展,在原始 AVR Arduino 中不可用。它在标头中定义,您必须包含它才能获得

attachInterrupt()
的额外重载定义。

#include <FunctionalInterrupt.h>

它适用于您编写的用于测试的其他代码,因为您自己编写了

attachInterrupt()
定义并且使用了来自FunctionalInterrupt.h 的定义。

这可以工作的原因是因为在 ESP32 上,ESP-IDF 核心运行时包含中断处理程序,这些中断处理程序具有传递给它们的参数,该参数是在注册处理程序时提供的。此参数可以是任何内容,但对于 C++ 来说,常见用途是用作类的

this
指针。

这解决了非静态类方法作为中断处理程序的根本问题:调用方法时无法将

this
指针传递给该方法。

std::function<>
内部是一个称为函子的类。它有一个名为
operator()
的方法,当使用函数语法调用类对象时执行该方法。由于我们现在可以调用类方法,因此它可以用作中断处理程序。

© www.soinside.com 2019 - 2024. All rights reserved.