OctoWS2811 with Teensy 4.1 Pinlist only supports 8 strips when using GRBW

I am enjoying the additions of GRBW support and non-default pin lists for the OctoWS2811 library. I believe I have found that when using 4 channel GBRW LED strips and using a non-default pinlist, it appears that the pinlist has a max length of 8 pins. I have tested GRB strips up to the 28 PWM pins on the front of the 4.1 and that works fine. But if I go from 8 to 9 pins in the pinlist with
const int config = WS2811_GRBW | WS2811_800kHz;
then all of a sudden my oscilloscope sees only noise.

Expected: adding one more pin I would see one more pin with signal on my oscilloscope.
Observed: Adding one more pin with a GRBW setup makes all pins stop producing signal. With 3 LED channels the expected behavior is observed.

Other notes and steps to reproduce:
Using non standard pinlists works fine with <=8 strips for both 3 and 4 led channels. They do not have to be default pins, that makes no difference.
I am using Teensyduino 1.54-beta5 and OctoWS2811 on the master branch from https://github.com/PaulStoffregen/OctoWS2811, most recently updated on 12/5. Please see the code examples below:

The following fails to produce a signal on the output pins and is the problematic case:
Code:
#include <OctoWS2811.h>

// NOTE THE 9 digital pins
const int numPins = 9;
byte pinList[numPins] = {2,3,4,5,6,7,8,9,10};
const int ledsPerStrip = 20;

// GRBW memory amounts
DMAMEM int displayMemory[ledsPerStrip*8]; // 8 ints is 32 bytes
int drawingMemory[ledsPerStrip*8]; // RGBW needs 32 bytes, RGB needs only 24
// 4 channels of led color
const int config = WS2811_GRBW | WS2811_800kHz;

OctoWS2811 leds(ledsPerStrip, displayMemory, drawingMemory, config, numPins, pinList);
//OctoWS2811 leds(ledsPerStrip, displayMemory, drawingMemory, config);
void setup() {
  leds.begin();
  leds.show();
}

void loop() {
  int microsec = 2000000 / leds.numPixels();  // change them all in 2 seconds
  # Note that the same thing happens with the standard colorWipe method
  colorCheck(microsec);
}

void colorCheck(int wait)
{
  for (int i=0; i < leds.numPixels(); i++) {
    leds.setPixel(i, 10, 0, 0, 0); //Make then all Red at 10%
    leds.show();
    delayMicroseconds(wait);
  }
  delayMicroseconds(wait);
  for (int i=0; i < leds.numPixels(); i++) {
    leds.setPixel(i, 0, 10, 0, 0); //Make then all Green at 10%
    leds.show();
    delayMicroseconds(wait);
  }
  delayMicroseconds(wait);
  for (int i=0; i < leds.numPixels(); i++) {
    leds.setPixel(i, 0, 0, 10, 0); //Make then all Blue at 10%
    leds.show();
    delayMicroseconds(wait);
  }
  delayMicroseconds(wait);
  for (int i=0; i < leds.numPixels(); i++) {
    leds.setPixel(i, 10, 10, 10, 0); //Make then all Blue at 10%
    leds.show();
    delayMicroseconds(wait);
  }
  delayMicroseconds(wait);
  for (int i=0; i < leds.numPixels(); i++) {
    leds.setPixel(i, 0, 0, 0, 10); //Make then all White at 10%
    leds.show();
    delayMicroseconds(wait);
  }
  delayMicroseconds(wait);
}

Howerver if I reduce the pinlist by 1, down to 8 then everything works as expected.
The following fails to produce a signal on the output pins:
Code:
#include <OctoWS2811.h>

// It works when assigning only 8 digital pins
const int numPins = 8;
byte pinList[numPins] = {3,4,5,6,7,8,9,10};
const int ledsPerStrip = 20;

// GRBW memory amounts
DMAMEM int displayMemory[ledsPerStrip*8]; // 8 ints is 32 bytes
int drawingMemory[ledsPerStrip*8]; // RGBW needs 32 bytes, RGB needs only 24
// 4 channels of led color
const int config = WS2811_GRBW | WS2811_800kHz;

OctoWS2811 leds(ledsPerStrip, displayMemory, drawingMemory, config, numPins, pinList);
//OctoWS2811 leds(ledsPerStrip, displayMemory, drawingMemory, config);
void setup() {
  leds.begin();
  leds.show();
}

void loop() {
  int microsec = 2000000 / leds.numPixels();  // change them all in 2 seconds
  // Note that the same thing happens with the standard colorWipe method
  colorCheck(microsec);
}

void colorCheck(int wait)
{
  for (int i=0; i < leds.numPixels(); i++) {
    leds.setPixel(i, 10, 0, 0, 0); //Make then all Red at 10%
    leds.show();
    delayMicroseconds(wait);
  }
  delayMicroseconds(wait);
  for (int i=0; i < leds.numPixels(); i++) {
    leds.setPixel(i, 0, 10, 0, 0); //Make then all Green at 10%
    leds.show();
    delayMicroseconds(wait);
  }
  delayMicroseconds(wait);
  for (int i=0; i < leds.numPixels(); i++) {
    leds.setPixel(i, 0, 0, 10, 0); //Make then all Blue at 10%
    leds.show();
    delayMicroseconds(wait);
  }
  delayMicroseconds(wait);
  for (int i=0; i < leds.numPixels(); i++) {
    leds.setPixel(i, 10, 10, 10, 0); //Make then all Blue at 10%
    leds.show();
    delayMicroseconds(wait);
  }
  delayMicroseconds(wait);
  for (int i=0; i < leds.numPixels(); i++) {
    leds.setPixel(i, 0, 0, 0, 10); //Make then all White at 10%
    leds.show();
    delayMicroseconds(wait);
  }
  delayMicroseconds(wait);
}

And for completeness, here is working code with GRB AND 28 pins:
Code:
#include <OctoWS2811.h>

// Any group of digital pins may be used
const int numPins = 28;
byte pinList[numPins] = {0,1,2,3,4,5,6,7,8,9,10,11,12,24,25,28,29,13,14,15,18,19,22,23,33,36,37};

const int ledsPerStrip = 120;

// These buffers need to be large enough for all the pixels.
// The total number of pixels is "ledsPerStrip * numPins".
// Each pixel needs 3 bytes, so multiply by 3.  An "int" is
// 4 bytes, so divide by 4.  The array is created using "int"
// so the compiler will align it to 32 bit memory.
DMAMEM int displayMemory[ledsPerStrip * numPins * 3 / 4];
int drawingMemory[ledsPerStrip * numPins * 3 / 4];

const int config = WS2811_GRB | WS2811_800kHz;

OctoWS2811 leds(ledsPerStrip, displayMemory, drawingMemory, config, numPins, pinList);

void setup() {
  leds.begin();
  leds.show();
}

#define RED    0xFF0000
#define GREEN  0x00FF00
#define BLUE   0x0000FF
#define YELLOW 0xFFFF00
#define PINK   0xFF1088
#define ORANGE 0xE05800
#define WHITE  0xFFFFFF

// Less intense...
/*
#define RED    0x160000
#define GREEN  0x001600
#define BLUE   0x000016
#define YELLOW 0x101400
#define PINK   0x120009
#define ORANGE 0x100400
#define WHITE  0x101010
*/

void loop() {
  int microsec = 2000000 / leds.numPixels();  // change them all in 2 seconds

  // uncomment for voltage controlled speed
  // millisec = analogRead(A9) / 40;

  colorWipe(RED, microsec);
  colorWipe(GREEN, microsec);
  colorWipe(BLUE, microsec);
  colorWipe(YELLOW, microsec);
  colorWipe(PINK, microsec);
  colorWipe(ORANGE, microsec);
  colorWipe(WHITE, microsec);
}

void colorWipe(int color, int wait)
{
  for (int i=0; i < leds.numPixels(); i++) {
    leds.setPixel(i, color);
    leds.show();
    delayMicroseconds(wait);
  }
}
 
Update: I would love someone else to review this, but modifying the memory allocations solves the issue in my test cases with 5, 9 and 28 strips and a GRBW configuration. Note that this also matches the comments for 3 LED channel strips, eg RGB in the OctoWS2811 example Teensy4_PinList.ino.

Previously in the examples:

Code:
// GRBW memory amounts
DMAMEM int displayMemory[ledsPerStrip*8]; // 8 ints is 32 bytes
int drawingMemory[ledsPerStrip*8]; // RGBW needs 32 bytes, RGB needs only 24
Proposed modification:

Code:
DMAMEM int displayMemory[ledsPerStrip * numPins]; // Make the memory allocation dynamic based on the number of strips
int drawingMemory[ledsPerStrip * numPins];        // In case of RBG strips these formulas should be multiplied by 3 / 4, i.e. ledsPerStrip * numPins * 3 / 4
 
Back
Top