在嵌入式系统上使用带参数的最小化C++回调函数

3

我正在开发一个嵌入式系统的c++应用程序。由于资源限制,我不能在我的回调函数中使用std::function

我想从软件的上层注册一个回调函数,并在非常低级别的驱动程序中执行它。问题是这个回调函数还需要作为数组传递参数,而我不想使用全局变量。

低级别驱动程序:

using callback = void (*)(uint32_t data[2]);

// extern C because it is executed inside an c style interrupt, don't ask
extern "C" {
static callback global_cb = nullptr;

void ExecuteCallback(uint32_t data[2]) {
    if (global_cb != nullptr) {
        global_cb(data);
    }
}

void Interrupt() {
 // Error occured
    uint32_t logData[2] = { 0, 1}; // Dummy data for stackoverflow
    ExecuteCallback(logData);
} 
}

void low_level_driver::RegisterCallback(callback cb) {
    global_cb = cb;
}



应用层看起来像这样:
#include "low_level_driver.h"

ResetHandler::ResetHandler() {
    low_level_driver.RegisterCallback(MyCallback);
}

void ResetHandler::MyCallback(uint32_t logData[2]) {
  // log the Data
}

目前我无法以任何方式注册我的回调函数。 我知道我的回调函数或者我尝试在应用程序层中注册它的方式是错误的。需要帮助。

编辑: 无论这个设计有多糟糕,我都在寻找以下内容: 基本上只需要一种具有参数的回调方法。


1
void (*)(uint32_t data[2]); 不是成员函数指针。您可以尝试传递不捕获任何内容的lambda表达式或自由函数,甚至将 MyCallback 设为 static - George
1
一个免费的函数可以是void MyCallback(uint32_t logData[2]) { // log the Data },如果你喜欢,你也可以在头文件中声明它,只要它不在任何类或结构体的范围内。一个lambda可能看起来像这样 low_level_driver.RegisterCallback([] (uint32_t logData[2]) -> void { // log the Data }); - George
1
@d3rdon 想一想。SP指向受保护的区域。ISR发生了。CPU为ISR堆栈调用参数。但这从未发生过,因为(希望如此)CPU无法写入受保护的区域。然后它尝试分配2个32位变量,可以在堆栈上或者寄存器中,如果调用约定尝试将寄存器推到堆栈上。在任何情况下,这些变量都不会被分配。然后你调用一个函数,函数调用开销被堆栈化。但是现在SP已经深入到lala land中。然后函数返回。返回地址是来自受保护内存的垃圾。 - Lundin
1
@d3rdon 不,获取访问受保护内存中断后的下一步将是访问受保护内存的中断。您在这种设计中陷入了循环,只有看门狗才能拯救局面。 - Lundin
1
通常情况下,您可以尝试记录错误并进行MCU重置。但实际上,该日志不会太有用,因为您需要知道哪个代码写入了错误的地址。如果您可以获取由ISR推送到堆栈上的返回地址,然后将其保存到日志中,代码就会变得有意义。 - Lundin
显示剩余10条评论
2个回答

0

std::function有一些替代品,既不进行类型擦除也不分配内存。

我个人最喜欢的是IncludeOSdelegate implementation。它在函数签名后面有两个额外的模板参数,允许自定义回调大小,并根据是否为纯函数(基本上是一个自由函数)、可平凡复制或两者都不是进行一些可选的优化。这些附加参数的缺点是,如果您使用了很多不同的变体,它可能会使您的代码膨胀。

另一个选择可能是MBed所做的。他们有一个叫做CThunk的类,甚至在其描述中声明其目的是将回调重定向到非静态成员函数。我还没有使用过它,而且API似乎远不如现代和花哨,但它可能适合您的需求。


但是你不能仅仅从低级ISR中调用所有这些膨胀软件。 - Lundin
但是MBed会为您处理所有的驱动程序,对吧?因此,在其上运行的应用程序处于如此高的级别,以至于它甚至从未听说过ISR这个术语。 - Lundin
ARM特别提到CThunk(我直接引用)“非常适合用于接收中断的类对象(NVIC_SetVector)”。 - Vinci

0
回调函数应该是静态的,尝试将MyCallback设为静态。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接