传递外围对象以用作嵌入式系统中的 class 成员

Passing peripheral objects to be used as class members in embedded systems

在使用 C++(例如,使用 MBED)进行嵌入式编程时,我经常为 I2C 或串行等设备外围设备全局声明对象,它们通常为您为特定设备使用的引脚获取参数申请:

I2C i2c(SDA_1, SCL_1);
Serial my_serial(D7, D8);

如何正确地将这些对象传递给其他 classes?我想在我的平台级代码中保留所有特定于应用程序的信息,理想情况下应用程序中的每个 class 都应该使用这些对象的相同实例,但我似乎无法正确设置构造函数语法这样外围对象就可以在声明时传递到 class 中,然后用作该 class 中的成员对象。

示例: 我在 main.cpp 文件中全局声明了一个设备外围设备:

I2C i2c(SDA_1, SCL_1);

我需要在我的应用程序中使用的几个对象中使用这个 i2c 外设,例如 ThingController。这是我用指针传递这些对象的尝试:

ThingController.h

class ThingController {
public:
    ThingController(I2C *i2c);
    void init();
    void run();
private:
    I2C my_i2c;

ThingController.cpp

Editor::Editor(I2C *i2c)
{
    my_i2c = *i2c;

为此,我得到了头文件 "no default constructor exists for class I2C",这意味着它只是试图创建 class 的新实例。如果我尝试按值传递,我会得到同样的结果。

这样做的语法是什么?或者,就我的想法而言,我是否偏离了?我无法在网上找到与我遇到的问题类似的任何其他内容,考虑到如果我想在我的应用程序中使用 classes,我需要多频繁地执行此确切任务,这让我觉得很奇怪。是否每个人都只是通过大量特定于应用程序的引脚列表,然后为每个 class 创建这些硬件外围设备的新实例?或者其他方法?

您已将成员 my_i2c 声明为 I2C 实例 ,这意味着其默认构造函数将在 [=13] 的构造函数中调用=] 如果你不自己调用它。由于它没有 default constructor,您会收到此错误。

您需要做的就是使实例成员成为 reference(或指针)而不是值,如下所示:

class ThingController {
public:
    ThingController(I2C& i2c);
    void init();
    void run();
private:
    I2C& my_i2c;
}

Editor::Editor(I2C& i2c) :
    my_i2c(i2c)
{
}

您可以使用指针而不是引用,但在这种情况下,引用的语义更适合您的情况 - 您希望对其进行一次初始化而不更改它。

阅读涵盖所有这些内容的 constructors and member initialiser syntax 是个好主意。

Does everyone just pass through massive lists of application specific pins and then make new instances of these hardware peripherals for every class? Or another approach?

不,那将是一个非常糟糕的主意。 :) I2C 和 SPI 等外设应该有一个单例包装器 class,它允许您序列化访问。如果您传递了引脚定义并创建了 I2C class 的多个实例(例如),您最终可能会有代码的两个不同部分同时尝试访问外围设备,并导致所有各种奇怪且难以调试的问题。上面的方法很好,你还想看看在 I2C class.

上添加一个互斥锁并使用 lock/unlock 方法

首先,您不应该声明任何全局的东西——至少在所有 C++ classes 中。这会给您带来各种初始化顺序错误。在大多数嵌入式系统中,初始化顺序很重要。

您应该做的是将所有此类对象声明为主要 loop/application 代码的本地对象。如果您的代码的多个部分需要访问 I2C,则将对象传递给它们。

但是,如果您的程序的多个部分需要访问原始 I2C,那么您的设计已经是错误的。让你的程序的一些随机部分知道硬件特定的东西是糟糕的 OO 设计,比如 "SDA_1"、"SCL_1".

只有 I2C 驱动程序应该直接与外围设备对话,并且 "know" 关于 I2C 硬件。你需要在那个之上实现一个 HAL。 (可选地,HAL 可以是一个抽象基础 class 并且驱动程序可以继承它。)并且您应该只允许与可用的硬件外设一样多的 I2C 对象实例。