Go Down

Topic: Initializing FastLED with non-constant pin values (C++ templates?) (Read 2021 times) previous topic - next topic


I'm working on converting a chunk of code to use the FastLED (https://github.com/FastLED/FastLED) library rather than the Adafruit WS2801 library, however I've hit a problem that may be due to my limited experience with C++ templating (I've got many years of professional straight C experience, but very little C++ specific experience).

In my code I'm storing the pins used to control the WS2801 LED strings in an EEPROM config structure, so the pin values are not constants.  The method of initializing the LEDs showing in the examples basically goes like this (this works in my code):

void PixelUtil::init(uint16_t num_pixels, uint8_t data_pin, uint8_t clock_pin) {
  #define DATA_PIN 8
  #define DATA_PIN 12
  CRGB leds = new CRGB[num_pixels];
  FastLED.addLeds<WS2801, DATA_PIN, CLOCK_PIN, RGB>(leds, num_pixels);

However if you replace that line with one using the variables:

    FastLED.addLeds<WS2801, dataPin, clockPin, RGB>(leds, num_pixels);

it will throw these compile errors:
/Users/amp/Dropbox/Arduino/libraries/PixelUtil/PixelUtil.cpp:38: error: 'dataPin' cannot appear in a constant-expression
/Users/amp/Dropbox/Arduino/libraries/PixelUtil/PixelUtil.cpp:38: error: 'clockPin' cannot appear in a constant-expression

Does anyone have a good idea for getting around this problem?


Templates were used for performance reasons, and they are a compile time 'thing' which basically means you cannot instantiate a new class of a template using a runtime variable. The library you are using is most probably based off a slower non template version that can do what you want. EDIT: it seems there was FastSPI_LED

The physical connection isn't going to change is it? If not, the values should be constant, no need to save in the EEPROM.

Just make the function in your lib a template too, this can pass in the values. You can also use defaults on function templates so you do not need to use the template brackets on a call '<...>'

Code: [Select]
enum Pins{
  DataPin = 8,
  ClockPin = 12,

struct PixelUtil{

  template< uint8_t DATA_PIN = DataPin, uint8_t CLOCK_PIN = ClockPin >
    void init(uint16_t num_pixels) {
      FastLED.addLeds< WS2801, DATA_PIN, CLOCK_PIN, RGB >( new CRGB[num_pixels], num_pixels);

PixelUtil p;

void setup() {

  //Using the defaults:
  p.init( 40 );

  //Or the equivalent:
  p.init< 8, 12 >( 40 );


void loop() {}


In my case, the pins do change and thus they're saved in EEPROM (I have a couple versions of a PCB I'm working on, and the pin layout varies between the versions).

I'm just not particularly familiar with C++ templates and couldn't easily trace what these are doing underneath in order to guess if there was a way to initialize without the templates.


In general, on AVR, most of the "faster" pin-related functions will require that the pins be constant.
The fastest pin-set instruction is 2 cycles (sbi port, bit) for a constant pin, and at least 5 (ld r,X / or r, r2 / st x, r) (and that's if you have the port address and bit in convenient registers, which will usually not be the case.)
You could write functions that are faster than digitalWrite() but slower than the full constant cases, but it's more difficult and the pay-back isn't as great.  (Hmm.  And the adafruit library already does at least some of this.)

Go Up