Arduino的attachInterrupt
需要一个void(*)()
类型的回调函数,但是我想将其传递给成员函数。由于其隐式this
参数,我不能在这里使用C ++成员函数。
我知道可以将C ++成员函数用作回调。例如,FreeRTOS的xTaskCreate(...)
采用类型为void(*)(*)
的回调函数。
isocpp.org has a nice FAQ关于将成员函数用作回调。
在此related question用户中,thiton写道:
大多数理智的回调库允许您将此void *参数传递给函数,以在其中包含用户定义的数据
也许Arduino库不是“理智的”?还是为了简化Arduino API而做出的设计决定?
attachInterrupt
里面我正在为ESP32编程。 In the arduino-esp32
implementation of attachInterrupt
,有一个名为__attachInterruptFunctionalArg(...)
的函数似乎完全可以实现我想要的功能,但是由于它不是Arduino API的一部分,因此我很犹豫将其包含在一个供公众使用的项目中可能会破裂。
// An attempt to summarize https://github.com/pierremolinaro/acan2517/issues/4
#include <stdio.h>
#include <stdint.h>
#include <functional>
#define IRAM_ATTR __attribute__((section(".iram1")))
// from `esp32-hal-gpio.c`
typedef void (*voidFuncPtrArg)(void*);
extern void __attachInterruptFunctionalArg(uint8_t pin, voidFuncPtrArg userFunc, void * arg, int intr_type, bool functional);
// from Arduino `FunctionalInterrupt.cpp`
void attachInterrupt(uint8_t pin, std::function<void(void)> intRoutine, int mode);
void IRAM_ATTR interruptFunctional(void* arg);
// from Arduino `FunctionalInterrupt.h`
struct InterruptArgStructure {
std::function<void(void)> interruptFunction;
};
// from ACAN2517
class ACAN2517
{
public: ACAN2517 (const int interrupt_pin);
public: void begin (void (* inInterruptServiceRoutine) (void));
public: void begin_functional (void (* inInterruptServiceRoutine) (void *), void *);
public: void isr(void);
private: const int interrupt_pin;
};
ACAN2517::ACAN2517 (const int interrupt_pin):
interrupt_pin(interrupt_pin)
{};
#define FALLING 0
// This won't work with a member function
void ACAN2517::begin (void (* inInterruptServiceRoutine) (void)) {
attachInterrupt(interrupt_pin, inInterruptServiceRoutine, FALLING);
}
// This will, but is prone to breakage when the Arduino internals change
void ACAN2517::begin_functional (void (* inInterruptServiceRoutine) (void *), void *arg)
{
__attachInterruptFunctionalArg(interrupt_pin, inInterruptServiceRoutine, arg, FALLING, true);
}
void ACAN2517::isr(void)
{
printf("fhtagn");
}
//===
// User code begin
//===
#define N_DRIVERS 3
ACAN2517 g_driver(23); // Initializing a driver instance statically
ACAN2517 *drivers[N_DRIVERS];
void call_ACAN_isr(void *arg)
{
ACAN2517 *driver = (ACAN2517 *)arg;
driver->isr();
}
int main()
{
g_driver.begin( []{g_driver.isr();} ); // No problem
for (int i = 0; i < N_DRIVERS; i++)
{
drivers[i] = &ACAN2517(i);
drivers[i]->begin( []{drivers[i]->isr();} );
// ERROR
// static void lambda []void ()->void::_FUN()
// an enclosing-function local variable cannot be referenced in a lambda body unless it is in the capture list
}
for (int i = 0; i < N_DRIVERS; i++)
{
drivers[i] = &ACAN2517(i);
drivers[i]->begin( [i]{drivers[i]->isr();} );
// ERROR
// no suitable conversion function from "lambda []void ()->void" to "void (*)()" exists
}
for (int i = 0; i < N_DRIVERS; i++)
{
drivers[i] = &ACAN2517(i);
ACAN2517 *driver = drivers[i];
drivers[i]->begin_functional( [driver]{driver->isr();}, driver);
// Not sure how to get this to work in a lambda...
}
for (int i = 0; i < N_DRIVERS; i++)
{
drivers[i] = &ACAN2517(i);
ACAN2517 *driver = drivers[i];
drivers[i]->begin_functional( call_ACAN_isr, driver);
// OK
}
}
//===
// User code end
//===
// from esp32-hal-gpio.c
extern void __attachInterruptFunctionalArg(uint8_t pin, voidFuncPtrArg userFunc, void * arg, int intr_type, bool functional)
{
// ...
}
// from Arduino `FunctionalInterrupt.cpp`
void attachInterrupt(uint8_t pin, std::function<void(void)> intRoutine, int mode)
{
// use the local interrupt routine which takes the ArgStructure as argument
__attachInterruptFunctionalArg (pin, (voidFuncPtrArg)interruptFunctional, new InterruptArgStructure{intRoutine}, mode, true);
}
void IRAM_ATTR interruptFunctional(void* arg)
{
InterruptArgStructure* localArg = (InterruptArgStructure*)arg;
if (localArg->interruptFunction)
{
localArg->interruptFunction();
}
}
答案 0 :(得分:0)
由于这里隐含了this参数,因此我不能在这里使用C ++成员函数。
是的,这就是问题所在,如果您的API没有提供使您存储一些其他数据(例如this指针)的东西,那么没有额外的代码就无法解决这个问题。
您可以做的只是:
编写自己的wrapper
并将回调注册到原始处理程序。但这会产生另一个间接性,从而增加延迟。
另一种方法不是那么简单,但是慢了一点:
编写您的own interrupt handler
和回调注册。有了arduino库的原始资源后,您只需替换attachInterrupt
函数周围的内容即可。
对不起,但是没有任何其他软件,没有办法为this
生成数据存储。