使用更多像素时 Neopixel 示例代码崩溃

Neopixel sample code crashing when using a higher number of pixels

上下文

我正在重新启动涉及 ESP8266 和 WS2812Bs (Neopixels) 的个人项目。

值得注意的是,我目前没有连接任何 Neopixels;我只是想感受一下我更新像素的速度。

我是 运行 一段非常简单的示例代码,取自 Adafruit 的 Neopixel GitHub 存储库。我对其进行了一些修改,使其更符合我的用例并删除了评论(为了在此处发布)。

详情

示例代码:

#include <Adafruit_NeoPixel.h>

#define PIN 13        
#define NUMPIXELS 300 
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

void setup()
{
  Serial.begin(115200);
  delay(1000);  
  pixels.begin();
}

void loop()
{
  Serial.println("Start");
  for (int i = 0; i < NUMPIXELS; i++)
  {
    Serial.println(i);
    pixels.setPixelColor(i, pixels.Color(0, 150, 0));
    pixels.show();
  }
  Serial.println("End");
}

在调用“End”之前它会崩溃。循环只到达 ~227:

...
227

Soft WDT reset

ctx: cont 
sp: 3ffffd80 end: 3fffffd0 offset: 01b0

>>>stack>>>
3fffff30:  feefef00 feefeffe feefeffe 0000012c  
3fffff40:  3ffee798 00000003 3ffee798 40202a7c  
3fffff50:  3ffee798 3ffee768 3ffee798 40202bc5  
3fffff60:  3ffe894c 000000e3 3ffee798 40202cd7  
3fffff70:  3ffe8940 3ffee810 3ffee798 40202be0  
3fffff80:  3ffee798 3ffee768 0000012b 3ffee768  
3fffff90:  402014f2 3ffee768 000000e4 40202777  
3fffffa0:  feefeffe 00000000 3ffee7b4 3ffee7bc  
3fffffb0:  3fffdad0 00000000 3ffee7b4 40202ed4  
3fffffc0:  feefeffe feefeffe 3ffe85d8 40100739  
<<<stack<<<

 ets Jan  8 2013,rst cause:2, boot mode:(1,6)

疑难解答

如果我将像素数减少到 200 或在 for 循环中添加延迟 (1),此代码将不会崩溃。

或者 - 删除 for 循环并通过简单地使用 loop() 设置 LED 似乎可行。

#include <Adafruit_NeoPixel.h>

#define PIN 13        
#define NUMPIXELS 300 
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

int i = 0;

void setup()
{
  Serial.begin(115200);
  delay(1000);  
  pixels.begin();
}

void loop()
{
    Serial.println(i);
    pixels.setPixelColor(i, pixels.Color(0, 150, 0)); 
    pixels.show();

  if (i == 299) {
    i = 0;
  } else {
    i = i + 1;
  }
}

所以 - 这个问题似乎最终取决于在 loop() 函数内的 for 循环中调用 show() 一定次数(227+)。

问题

许多示例都在 for 循环中包含了 show。我怀疑将 show 移到 for 循环之外是一个适当的解决方法;我在我原来的项目中这样做似乎没有问题。

但我仍然很好奇为什么会这样。事实上,很多示例都将 show() 包含在 for 循环中,这让我认为这应该可行。

有人知道为什么在上面的代码中设置 ~300 个 LED 会导致崩溃,而设置 200 个则不会吗?

这是一个大胆的猜测,但这些条带以串行方式工作:第一个 LED 占用前 24 位,丢弃它们并将剩余的传递给下一个 LED,依此类推。这确实意味着您发送的消息会随着您当前寻址的 LED 数量的增加而增加。

这些 LED IC 在收到下一条消息之前也需要一些时间来重置,可能存在数据冲突,因为条带无法赶上 ESP 全速增加的信号长度。

您的第二个示例包含一些测试,这可能会减慢传输速度以避免冲突。

因此,您可能只需要在 documentation something as small as 50 microsecs might be enough. You can use delayMicrosecond() 中的第一个示例中添加一个小的延迟。

板的输出表明问题所在:

Soft WDT reset

软件看门狗定时器正在启动并重置电路板。参见 https://arduino-esp8266.readthedocs.io/en/latest/faq/a02-my-esp-crashes.html#watchdog

嵌入式系统通常有一个硬件 and/or 软件看门狗。如果在预定义的时间段内未得到服务,看门狗将重置系统。这可以防止系统在软件锁定或过载时变得无响应。

对于有问题的 loop() 代码:

void loop()
{
  Serial.println("Start");
  for (int i = 0; i < NUMPIXELS; i++)
  {
    Serial.println(i);
    pixels.setPixelColor(i, pixels.Color(0, 150, 0));
    pixels.show();
  }
  Serial.println("End");
}

pixels.show()的实现使用忙循环来实现写入LED的时序。见 https://github.com/adafruit/Adafruit_NeoPixel/blob/master/Adafruit_NeoPixel.cpp#L205:

  // Data latch = 300+ microsecond pause in the output stream. Rather than
  // put a delay at the end of the function, the ending time is noted and
  // the function will simply hold off (if needed) on issuing the
  // subsequent round of data until the latch time has elapsed. This
  // allows the mainline code to start generating the next frame of data
  // rather than stalling for the latch.
  while(!canShow());

循环的 227 次迭代足以让看门狗定时器启动。

调用 pixels.show() 更少的次数或 delay()(在内部调用 yield())允许看门狗定时器得到服务。

最简单的解决方案是在 loop() 结束时调用 pixels.show() 一次。

所以你需要记得轻拍 puppy/dog - 否则它会咬人。