【问题标题】:Arduino NeoPixel code behaves oddly when code is moved to class将代码移至类时,Arduino NeoPixel 代码行为异常
【发布时间】:2021-09-02 17:24:04
【问题描述】:

所以我在尝试划分我为 Arduino/NeoPixel 应用程序编写的一些测试代码时遇到了一个奇怪的问题。有两个场景:场景 A,我的移动前的代码,场景 B,我的移动后的代码。测试代码在场景 A 中按预期工作(红灯穿过我的 8x8 LED 矩阵)。同样的代码,当移动到容器类(场景 B)时会导致奇怪的行为(出现随机颜色的 LED 斑点并且不移动)。不过,将功能从一个地方简单地转移到另一个地方似乎不会引起这些症状,所以我有点迷茫。

这里有一些图片。 Scenario A Scenario B

我附上了以下两种不同场景的代码。为了清楚起见,我删除了部分代码并包含尚未引用的部分。

在谈到 Arduino/C++ 时,我或多或少还是个爱好者,所以也请随意指出一些小事。

场景 A

Program.ino

#include <Arduino.h>
#include "Hardware.h"

Hardware* hardware = new Hardware();

void setup()
{
  hardware->Setup();
}

uint8_t i = 0;

void loop()
{
  auto screen = hardware->GetScreen();
  screen->Clear();
  screen->SetLedHSV(i++ % screen->Count(), 0, 255, 255);
  screen->Show();

  delay(100);
}

硬件.h

#pragma once
#include "Screen.h"

class Hardware
{
private:
    Screen screen = Screen(8, 8, 14);

public:
    void Setup()
    {
        screen.Setup();
    }

    Screen* GetScreen() { return &screen; }
};

屏幕.h

#pragma once
#include <Adafruit_NeoPixel.h>

class Screen
{
private:
    uint8_t width, height;
    uint8_t pin;

    Adafruit_NeoPixel pixels;

public:
    Screen(uint8_t width, uint8_t height, uint8_t pin) :
        width(width), height(height), pin(pin)
    {
        pixels = Adafruit_NeoPixel(width * height, pin, NEO_GRB + NEO_KHZ800);
    }

    void Setup()
    {
        pixels.begin();
        pixels.show();
        pixels.setBrightness(32);
    }

    uint16_t Count() { return width * height; }
    uint8_t GetWidth() { return width; }
    uint8_t GetHeight() { return height; }

    void Show()
    {
        pixels.show();
    }

    void Clear()
    {
        pixels.clear();
    }

    void SetLedHSV(uint16_t i, uint16_t h, uint8_t s, uint8_t v)
    {
        pixels.setPixelColor(i, pixels.ColorHSV(h, s, v));
    }

    void SetLedHSV(uint8_t x, uint8_t y, uint16_t h, uint8_t s, uint8_t v)
    {
        if (x < 0 || x >= width)
            return;
        if (y < 0 || y >= height)
            return;

        auto i = x + y * width;
        pixels.setPixelColor(i, pixels.ColorHSV(h, s, v));
    }
};

场景 B

Program.ino

#include <Arduino.h>
#include "Hardware.h"
#include "TestApp.h"

unsigned long timestamp;
Hardware* hardware = new Hardware();
TestApp* app = new TestApp(hardware);

void setup()
{
  hardware->Setup();
}

void loop()
{
  app->Update();
  delay(100);
}

硬件.h

同上。

屏幕.h

同上。

TestApp.h

#pragma once
#include "Hardware.h"

class TestApp
{
    Hardware* hardware = 0;
    uint8_t i = 0;

public:
    TestApp(Hardware* hardware) : hardware(hardware) {}

    void Update()
    {
        auto screen = hardware->GetScreen();
        screen->Clear();
        screen->SetLedHSV(i++ % screen->Count(), 0, 255, 255);
        screen->Show();
    }
};

【问题讨论】:

  • 我的猜测:pixels = Adafruit_NeoPixel(width * height, pin, NEO_GRB + NEO_KHZ800); 它使用默认的复制赋值运算符,但它具有显式析构函数,因此副本获得与该临时实例相同的指针,然后其空间被析构函数释放。您可能想使用另一个指针和 new
  • 再看一下 - 它在构造函数中,所以对像素变量使用构造函数。现在你有了默认构造函数,从临时创建的实例中复制分配(它正在做浅拷贝并释放内存)
  • 哇哦!看起来你的猜测是正确的。我将“像素”字段更新为指针,并使用 new 关键字创建实例。它现在工作正常。我仍然有点不确定出了什么问题。你是说像素以某种方式调用了它的析构函数?
  • 对于为什么移动代码会导致问题,我仍然有些困惑。在这两种情况下 Screen.h 和 Hardware.h 保持不变。我会假设问题会在两者中出现或不同。

标签: c++ matrix arduino led neopixel


【解决方案1】:

问题显然出在 Screen 构造函数中:

Screen(uint8_t width, uint8_t height, uint8_t pin) :
    width(width), height(height), pin(pin)
{
    pixels = Adafruit_NeoPixel(width * height, pin, NEO_GRB + NEO_KHZ800);
}

现在它使用默认构造函数创建“空”像素,并在代码块中创建另一个实例(现已正确初始化)并复制到像素中。由于没有提供复制分配操作符,所以使用默认操作符,它对像素存储进行浅拷贝。在第二个实例超出范围并删除分配的指针之后。

现在你有了悬空指针,如果你分配任何东西,它将被分配到悬空指针所指向的空间中。

这两个测试用例的不同之处在于您在第二个程序中进行了另一次分配:

Hardware* hardware = new Hardware();  // dangling pointer is created here
TestApp* app = new TestApp(hardware); // <<-- allocated in the same space as that dangling pointer

无论如何,像素类成员应该在构造函数的初始化列表中初始化(与其余值完全相同):

Screen(uint8_t width, uint8_t height, uint8_t pin) :
    width(width), height(height), pin(pin), pixels(width * height, pin, NEO_GRB + NEO_KHZ800)
{
    // btw: do you really have to store a "pin" value?
}

为什么你这么多使用指针?可以是这样(或者也可以作为引用传递到应用程序中):

#include <Arduino.h>
#include "Hardware.h"
#include "TestApp.h"

unsigned long timestamp;
Hardware hardware;
TestApp app { &hardware };

void setup()
{
  hardware.Setup();
}

void loop()
{
  app.Update();
  delay(100);
}

顺便说一句:这是由 Adafruit 库引起的,它不遵循“三规则”/“五规则”准则。它根本不允许复制对象(因为深拷贝在小型设备上可能更糟糕)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-01-09
    • 1970-01-01
    • 2012-10-26
    • 2013-07-17
    • 1970-01-01
    • 2018-02-23
    • 1970-01-01
    相关资源
    最近更新 更多