当代码移动到 class 时,Arduino NeoPixel 代码表现异常
Arduino NeoPixel code behaves oddly when code is moved to class
所以我 运行 在尝试划分我为 Arduino/NeoPixel 应用程序编写的一些测试代码时遇到了一个奇怪的问题。有两种场景:场景 A,我的代码在移动之前,场景 B,我的代码在移动之后。测试代码在场景 A 中按预期工作(红灯穿过我的 8x8 LED 矩阵)。完全相同的代码,当移动到容器 class 时(场景 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);
}
Hardware.h
#pragma once
#include "Screen.h"
class Hardware
{
private:
Screen screen = Screen(8, 8, 14);
public:
void Setup()
{
screen.Setup();
}
Screen* GetScreen() { return &screen; }
};
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);
}
Hardware.h
同上
Screen.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();
}
};
问题显然出在 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);
}
现在它使用默认构造函数创建“空”像素,并在代码块中创建另一个实例(现在已正确初始化)并复制到像素中。由于未提供复制赋值运算符,因此使用默认值,它对像素存储进行浅表复制。在第二个实例超出范围并删除分配的指针之后。
现在你有了悬垂指针,如果你分配任何东西,它将被分配到与悬垂指针指向的相同 space 中。
这两个测试用例的区别在于您在第二个程序中进行了另一次分配:
Hardware* hardware = new Hardware(); // dangling pointer is created here
TestApp* app = new TestApp(hardware); // <<-- allocated in the same space as that dangling pointer
无论如何像素 class 成员应该在构造函数的初始化列表中初始化(与其余值完全相同):
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 库引起的,它不遵循“三规则”/“五规则”准则。它根本不允许复制对象(因为深拷贝在小型设备上可能更糟糕)
所以我 运行 在尝试划分我为 Arduino/NeoPixel 应用程序编写的一些测试代码时遇到了一个奇怪的问题。有两种场景:场景 A,我的代码在移动之前,场景 B,我的代码在移动之后。测试代码在场景 A 中按预期工作(红灯穿过我的 8x8 LED 矩阵)。完全相同的代码,当移动到容器 class 时(场景 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);
}
Hardware.h
#pragma once
#include "Screen.h"
class Hardware
{
private:
Screen screen = Screen(8, 8, 14);
public:
void Setup()
{
screen.Setup();
}
Screen* GetScreen() { return &screen; }
};
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);
}
Hardware.h
同上
Screen.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();
}
};
问题显然出在 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);
}
现在它使用默认构造函数创建“空”像素,并在代码块中创建另一个实例(现在已正确初始化)并复制到像素中。由于未提供复制赋值运算符,因此使用默认值,它对像素存储进行浅表复制。在第二个实例超出范围并删除分配的指针之后。
现在你有了悬垂指针,如果你分配任何东西,它将被分配到与悬垂指针指向的相同 space 中。
这两个测试用例的区别在于您在第二个程序中进行了另一次分配:
Hardware* hardware = new Hardware(); // dangling pointer is created here
TestApp* app = new TestApp(hardware); // <<-- allocated in the same space as that dangling pointer
无论如何像素 class 成员应该在构造函数的初始化列表中初始化(与其余值完全相同):
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 库引起的,它不遵循“三规则”/“五规则”准则。它根本不允许复制对象(因为深拷贝在小型设备上可能更糟糕)