使用Raspberry PI GPIO - 我想要虚拟/抽象/接口类吗?

use*_*501 6 c++ electronics class gpio raspberry-pi

介绍

我正在使用Raspberry PI GPIO做一些工作.到目前为止,我正在编写代码,就像在C中一样,使用函数将代码段组合在一起.

我的工作已经到了我很高兴一切正常,但现在事情开始变得混乱,所以我想转向面向对象的方法.

问题

这是我面临的问题.

目前我有一个代表我的"设备"的课程.(我构建的硬件连接到GPIO端口.)硬件有2个不同的部分.一部分是"输入"部分,另一部分是"输出"部分.

为了帮助您更好地理解这一点,"输入"部分是一个ADC.(模数转换器.)这是一种将模拟信号转换为10位二进制表示的设备.(如果你不熟悉电子产品.)

"输出"部分只是一个开关一组LED的晶体管.

我想要一个代表ADC板的类,以及一个代表LED板的类,因为它们是两个概念上不同的器件,因为它们没有以任何方式"链接".

这会导致问题,因为在设置模式之前必须将GPIO引脚设置为特定值.值我的意思是"高"或"低",1或0.模式我的意思是"输入"或"输出".这听起来很奇怪,但基本上,如果控制线在上电之前没有设置为正确的LOGIC HIGH和LOGIC LOW值,ADC将变为去同步.(这是一个非常奇怪的设备,即使它被连接到电源也不会通电.(VCC或VDD 5.0V)其中一条控制线发送信号给设备上电.

为了实现上述目的,请考虑GPIO引脚最初处于INPUT模式.为了"使ADC正常工作",我们首先设置引脚上的数据值(HIGH/LOW),然后再将它们更改为OUTPUT模式.这样,当模式从INPUT变为OUTPUT时,数据存在正确的值,我们不会扰乱ADC.

我最初的想法是为ADC提供一个构造函数,它首先设置输出数据的值,然后将需要的引脚从INPUT模式更改为OUTPUT模式.但这迫使我们在LED板级之前构建ADC板级.

这可以通过执行相同代码来设置输出模式的两个构造函数来解决,但这似乎是一个坏主意,因为我们两次调用2位代码 - 这不是一个非常优雅的解决方案.

另一种解决方案是使用GPIOPort类将输入和输出设备组合在一起,但这也不是很优雅,如果我们要添加第二个相同的LED板,则很难修改.(例如.)

我想我想要但我不确定......

我想我想要的是另一个代表GPIOPort本身的类.(我猜是一种抽象的想法?)然后我想我想要"一个班级中的一个班级"来代表ADC板和"一个班级中的一个班级"来代表LED板.我不记得调用这种技术是什么,但通常"外部类"就像一个shell,指向一个类型为"内部类"的对象,以及一个create方法和一个destroy方法.外部类pointer = new type;在create方法和delete pointerdestroy方法中执行类似操作.这允许在需要时调用构造函数,并在需要时调用类析构函数.

关键是GPIOPort类构造函数处理这些对象的创建顺序,它隐藏了main()中的所有对象.在main中,程序员只是做了类似的事情GPIOPort myGPIOPort;,并且处理了你需要的所有东西,所以你不必在main()中包含20行代码来设置输出引脚的数据,这是唯一的其他解决方案.(我上面没有提到.)

问题

所以我的第一个问题是这种技术被称为什么?我认为它被称为包装类,但我的理解是包装类是用于使用基本类型doubleint作为对象.(并添加类似clear()reset()类似的方法.)这是我真正想要做的,还是有更好的方法?(我想这归结为"如何解决我的问题".)

我的第二个问题是,据我所知,我必须制作一些方法(析构函数?)虚拟方法,但我不记得为什么.(或许我不这样做,我只是感到困惑.)

我的第三个问题是,我可以用它来帮助自己理解它,或者我可以在哪里提高我的理解.(参考资料).

谢谢,显然这是一个很长的问题.我试图尽可能多地提供信息来帮助解释这种情况.如果你想要澄清,那么我会尝试改进我说的话.

编辑:有关设备的更多信息

在将模式从输入更改为输出之前,必须将数据发送到GPIO引脚.

GPIO引脚看起来像全零,因为引脚上有下拉电阻,它们仍然设置为输入.发送的数据在模式更改之前不会出现.

然后将引脚设置为输出模式.(或者其中一些是无论如何.)现在发送的数据出现在引脚上.

如果在发送数据之前将引脚设置为输出模式,则无法阻止ADC上电,因为控制ADC上电的数据引脚可能设置为高电平.它可能被设置为LOW,但没有办法说明,它的状态是未定义的,直到我们在设置模式输出之前告诉GPIO我们想要什么值.幸运的是,我们可以保证所有引脚都处于输入模式.

Hol*_*olt 1

编辑:我对这篇文章进行了重大编辑,因为我认为这目前不是最好的解决方案。这个编辑与 @MarkusMayer 的答案非常相似,但我以一种非常不同的思维方式(我认为)来实现它,所以也许它会对你有所帮助。

首先,让我们定义一个 GPIO 引脚,它可以是您想要的任何内容(一个类就很好,然后您可以执行pin.setOutput()orpin.set()等​​操作。我让您按照您想要的方式定义它,让我们假设我们有一个GPIOPin类。

首先,我将抽象板定义为一组引脚,这对我来说看起来非常正确:

template <int N>
class Board {
protected:
    Board (std::array <GPIOPin, N> const& pins) : _pins(pins) { }
    std::array <GPIOPin, N> _pins ;
};
Run Code Online (Sandbox Code Playgroud)

然后我定义接口 和ADCLEDs它也是抽象的:

class ADC {
public:
    ADC () { }
    float read () { }
} ;

class LEDs {
public:
    LEDs () { }
    void set (int) { }
} ;
Run Code Online (Sandbox Code Playgroud)

ADC现在我可以使用和创建代表真实板的内容LED

class MyBoard : public Board <5> { // Let's assume it's connect to 5 bits
public:
    MyBoard (std::array <GPIOPin, N> const& pins) : Board<5>(pins) {
        // Here you can initialize what you want
    }
} ;
Run Code Online (Sandbox Code Playgroud)

然后你创建自己的ADC并且LED

class AD7813 : public ADC {
    Board <5> _board ;
public:
    AD7813 (Board <5> *board) : ADC(), _board(board) { }
} ;

// Same for the LED
Run Code Online (Sandbox Code Playgroud)

最后,您可以简单地使用它,如下所示:

Board <5> *board = new MyBoard(/* The corresponding GPIO pins. */) ;
ADC *adc = new AD7813(board) ;
LEDs *led = new MyLEDs(board) ;
Run Code Online (Sandbox Code Playgroud)

我没有为MyBoardor定义析构函数Board,但你当然可以。您也可以shared_ptr像@MarkusMayer 一样使用。

编辑结束。

我认为解决这个问题有不同的方法,我将在这里介绍我会做的事情。在嵌入式系统上使用面向对象设计通常很困难,第一件事是你应该几乎到处都有单例类,因为你只有一个ADC(你不能实例化多个 ADC),所以你的 ADC 类(和 LEDBoard 类)应该如下所示:

class ADC {
public:
    static ADC *getInstance () {
        if (_instance == nullptr) {
            _instance = new ADC () ;
        }
        return _instance ;
    }
private:
    ADC () ;
};
Run Code Online (Sandbox Code Playgroud)

为了回答您的问题,我将创建一个基类来执行初始化,并且仅执行一次(使用静态成员来了解端口是否已初始化)。

class GPIOs {
protected:
    GPIOs () {
        if (!GPIOs::_init) {
            /* Do what you want. */
            GPIOs::_init = true ;
        }
    }
private:
    static bool _init ;
} ;

bool GPIOs::_init = false ;
Run Code Online (Sandbox Code Playgroud)

然后你的ADCLEDBoard继承自GPIOs

class ADC : public GPIOs {
public:
    ADC *getInstance () { /* ... */ }
private:
    ADC () : GPIOs () { } // Call constructor
} ;
Run Code Online (Sandbox Code Playgroud)

然后在您的代码中,您只需执行以下操作:

ADC *adc = ADC::getInstance () ;
Run Code Online (Sandbox Code Playgroud)

您还可以为该类使用单例GPIOs,但由于它是一个抽象类,仅被使用ADC并且LEDBoard已经是单例,因此它不是最有用的。

我确信还有很多其他方法可以解决您的问题,但我想展示的主要思想是使用init可以多次调用的方法/类,而无需因为_init布尔值而进行多次初始化。