Controlling RGB Strips with Timer

Hello Everyone. I'm trying to replicate Adafruits NeoPixel Cyber Wig with an Arduino Uno Board instead of a trinket. Most of the code compiles just fine, but the Void Setup give a bunch of errors. I am aware that Trinket code is different that Uno code but I'm not sure how to change the below code to make it compatible. Any help would be greatly appreciated.

Preformatted textvoid setup() {

  // Set up Timer/Counter1 for ~30 Hz interrupt
#if F_CPU == 16000000L
  clock_prescale_set(clock_div_1);
  TCCR1A  = _BV(PWM1A) |_BV(CS13) | _BV(CS12);             // 1:2048 prescale
 
#else
  TCCR1A  = _BV(PWM1A) |_BV(CS13) | _BV(CS11) | _BV(CS10); // 1:1024 prescale
 
#endif

  // Turn strips off ASAP (must follow clock_prescale_set)
  strip.begin();
  for(uint8_t s=0; s<N_STRIPS; s++) {
    strip.setPin(s);
    strip.show();
  }

  // Finish timer/counter setup
  OCR1C  = 255;        // ~30 Hz
  GTCCR  = 0;          // No PWM out
  TIMSK |= _BV(TOIE1); // Enable overflow interrupt

It is better if you post all the code. The missing parts may have required information like variable data types.

Please include the entire error message. It is easy to do. There is a button (lower right of the IDE window) called "copy error message". Copy the error and paste into a post in code tags. Paraphrasing the error message leaves out important information.

This is the full code I’m trying to modify from Adafruit

// SPDX-FileCopyrightText: 2018 Mikey Sklar for Adafruit Industries
//
// SPDX-License-Identifier: MIT

// 'Cyber falls' sketch, adapted from code for Firewalker sneakers.
// Creates a fiery rain-like effect on multiple NeoPixel strips.
// Requires Adafruit Trinket and NeoPixel strips.  Strip length is
// inherently limited by Trinket RAM and processing power; this is
// written for five 15-pixel strands, which are paired up per pin
// for ten 15-pixel strips total.

#include <Adafruit_NeoPixel.h>
#include <avr/power.h>

const uint8_t gamma[] PROGMEM = { // Gamma correction table for LED brightness
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  1,  1,  1,
    1,  1,  1,  1,  1,  1,  1,  1,  1,  2,  2,  2,  2,  2,  2,  2,
    2,  3,  3,  3,  3,  3,  3,  3,  4,  4,  4,  4,  4,  5,  5,  5,
    5,  6,  6,  6,  6,  7,  7,  7,  7,  8,  8,  8,  9,  9,  9, 10,
   10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
   17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
   25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
   37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
   51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
   69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
   90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114,
  115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142,
  144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175,
  177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213,
  215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 };

#define STRIPLEN 15 // Length of LED strips
#define MAXDROPS  5 // Max concurrent 'raindrops'
#define N_STRIPS  5 // Connect strips to pins 0 to (N_STRIPS-1)

Adafruit_NeoPixel strip = Adafruit_NeoPixel(STRIPLEN, 0);

// Everything's declared volatile because state changes inside
// an interrupt routine.
volatile struct {
  uint8_t  strip;
  int16_t  pos;
  uint8_t  speed;
  uint16_t brightness;
} drop[MAXDROPS];
volatile uint8_t
  nDrops    = 0,   // Number of 'active' raindrops
  prevStrip = 255; // Last selected strip
volatile uint16_t
  countdown = 0;   // Time to next raindrop

void setup() {

  // Set up Timer/Counter1 for ~30 Hz interrupt
#if F_CPU == 16000000L
  clock_prescale_set(clock_div_1);
  TCCR1  = _BV(PWM1A) |_BV(CS13) | _BV(CS12);             // 1:2048 prescale
#else
  TCCR1  = _BV(PWM1A) |_BV(CS13) | _BV(CS11) | _BV(CS10); // 1:1024 prescale
#endif

  // Turn strips off ASAP (must follow clock_prescale_set)
  strip.begin();
  for(uint8_t s=0; s<N_STRIPS; s++) {
    strip.setPin(s);
    strip.show();
  }

  // Finish timer/counter setup
  OCR1C  = 255;        // ~30 Hz
  GTCCR  = 0;          // No PWM out
  TIMSK |= _BV(TOIE1); // Enable overflow interrupt
}

void loop() { } // Not used -- everything's in interrupt below

// A timer interrupt is used so that everything runs at regular
// intervals, regardless of current amount of motion.
ISR(TIMER1_OVF_vect) {

  uint16_t mag[STRIPLEN];
  uint8_t  s, d, p, r, g, b;
  int      mx1, m, level;

  if(countdown == 0) {         // Time to launch new drop?
    if(nDrops < MAXDROPS-1) {  // Is there space for one in the list?
      do {
        s = random(N_STRIPS);
      } while(s == prevStrip); // Don't repeat prior strip
      drop[nDrops].strip      = prevStrip = s;
      drop[nDrops].pos        = -32; // Start off top of strip
      drop[nDrops].speed      = 1 + random(3);
      drop[nDrops].brightness = 250 + random(520);
      nDrops++;
      countdown = 9 + random(28); // Time to launch next one
    }
  } else countdown--;


  for(s=0; s<N_STRIPS; s++) { // For each strip...
    memset(mag, 0, sizeof(mag)); // Clear magnitude table

    // Render active drops for this strip into magnitude table
    for(d=0; d<nDrops; d++) {
      if(drop[d].strip == s) {
        for(p=0; p<STRIPLEN; p++) { // For each pixel...
          mx1 = (p << 2) - drop[d].pos; // Position of LED along wave
          if((mx1 <= 0) || (mx1 >= 32)) continue; // Out of range
          if(mx1 > 24) { // Rising edge of wave; ramp up fast (2 px)
            m = ((long)drop[d].brightness * (long)(32 - mx1)) >> 4;
          } else { // Falling edge of wave; fade slow (6 px)
            m = ((long)drop[d].brightness * (long)mx1) / 24;
          }
          mag[p] += m; // Accumulate result in magnitude buffer
        }
      }
    }

    // Remap magnitude table to RGB for strand
    for(p=0; p<STRIPLEN; p++) {      // For each pixel in strip
      level = mag[p];                // Pixel magnitude (brightness)
      if(level < 255) {              // 0-254 = black to green-1
        r = b = 0;
        g = pgm_read_byte(&gamma[level]);
      } else if(level < 510) {       // 255-509 = green to yellow-1
        r = pgm_read_byte(&gamma[level - 255]);
        g = 255;
        b = 0;
      } else if(level < 765) {       // 510-764 = yellow to white-1
        r = g = 255;
        b = pgm_read_byte(&gamma[level - 510]);
      } else {                       // 765+ = white
        r = g = b = 255;
      }
      strip.setPixelColor(p, r, g, b);
    }

    strip.setPin(s); // Select output pin
    strip.show();    // Strips don't need to refresh in sync
  }

  // Move 'active' drops
  for(d=0; d<nDrops; d++) {
    drop[d].pos += drop[d].speed;
    if(drop[d].pos >= (STRIPLEN * 4)) { // Off end?
      // Remove drop from list (move last one to this place)
      memcpy((void *)&drop[d], (void *)&drop[nDrops-1], sizeof(drop[0]));
      nDrops--;
    }
  }
}

I am not familiar with the Trinket, but doesn't it use a different processor than the Uno? That code contains a lot of hardware timer code, it's not portable between processors with different hardware timers.

On the UNO, the Clock Select bits (CS10, CS11, CS12, C13) are in register TCCR1B, not TCCR1A.

The UNO doesn't have an OCR1C.

I am fine with scrapping most of the code. The point is i want to replicate the effect for UNO and i dont know how to do that.

It looks like all you need is for the Timer1 overflow interrupt to happen about thirty times a second.

16 MHz / 30 Hz = 533333+1/3
You can't fit 533k in a 16-bit counter so you will need a pre-scale of at least 8.1. The next available prescale on Timer1 is 64.

16 MHz / 64 / 30 Hz = 8333.33... so set TOP to 8332 to get 30.00120004800192 Hz. Probably close enough.

Let's use Waveform Generation Mode 4: CTC with TOP in OCR1.

  TCCR1B = 0;
  TCCR1A = 0;
  TIMSK1 = _BV(TOIE1);
  // Set WGM to 4
  TCCR1B |= _BV(WGM12);
  OCR1A = 8332;  // 30.00120004800192 Hz
  // Set prescale to 64
  TCCR1B |= _BV(CS11) | _BV(CS10);
1 Like

So something like this for the Void Setup?

void setup() {

  // Set up Timer/Counter1 for ~30 Hz interrupt
  TCCR1B = 0;
  TCCR1A = 0;
  TIMSK1 = _BV(TOIE1);
  // Set WGM to 4
  // Turn strips off ASAP (must follow clock_prescale_set)
  strip.begin();
  for(uint8_t s=0; s<N_STRIPS; s++) {
    strip.setPin(s);
    strip.show();
  }

  // Finish timer/counter setup
  TCCR1B |= _BV(WGM12);
  OCR1A = 8332;  // 30.00120004800192 Hz
  // Set prescale to 64
  TCCR1B |= _BV(CS11) | _BV(CS10);
}

What happens when you try?

Rgb strips do not turn on. No animations at all.

Don't try to trouble shoot the timer code when it's embedded in the LED code. Write only sketches to generate your time base correctly first. Then add the LED code when it is tested and confirmed.

I am assuming that you have already tested LED code separately from the timer code. If you have not, the same thing applies to it. Write sketches that only light up an array of test values on the strip - no more. Then combine it with the timer code that you tested as above.

Do I understand right that this code is based on a timer-interrupt that occurs with a frequency of 30 Hz?
All "activity" is done inside the ISR?

Here is a code that is based on timer2. I don't know if the Adafruit_NeoPixel-library used timer2. The code does compile.

// A T T E N T I O N !    R E M A R K
// some other libraries that make use of timer2 may conflict with this

#include <Adafruit_NeoPixel.h>

const int BaseFrequency = 30;

const uint8_t gamma[] PROGMEM = { // Gamma correction table for LED brightness
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  1,  1,  1,
  1,  1,  1,  1,  1,  1,  1,  1,  1,  2,  2,  2,  2,  2,  2,  2,
  2,  3,  3,  3,  3,  3,  3,  3,  4,  4,  4,  4,  4,  5,  5,  5,
  5,  6,  6,  6,  6,  7,  7,  7,  7,  8,  8,  8,  9,  9,  9, 10,
  10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
  17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
  25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
  37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
  51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
  69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
  90, 92, 93, 95, 96, 98, 99, 101, 102, 104, 105, 107, 109, 110, 112, 114,
  115, 117, 119, 120, 122, 124, 126, 127, 129, 131, 133, 135, 137, 138, 140, 142,
  144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 167, 169, 171, 173, 175,
  177, 180, 182, 184, 186, 189, 191, 193, 196, 198, 200, 203, 205, 208, 210, 213,
  215, 218, 220, 223, 225, 228, 231, 233, 236, 239, 241, 244, 247, 249, 252, 255
};

#define STRIPLEN 15 // Length of LED strips
#define MAXDROPS  5 // Max concurrent 'raindrops'
#define N_STRIPS  5 // Connect strips to pins 0 to (N_STRIPS-1)

Adafruit_NeoPixel strip = Adafruit_NeoPixel(STRIPLEN, 0);

// Everything's declared volatile because state changes inside
// an interrupt routine.
volatile struct {
  uint8_t  strip;
  int16_t  pos;
  uint8_t  speed;
  uint16_t brightness;
} drop[MAXDROPS];
volatile uint8_t
nDrops    = 0,   // Number of 'active' raindrops
prevStrip = 255; // Last selected strip
volatile uint16_t
countdown = 0;   // Time to next raindrop


void setupTimerInterrupt(unsigned long ISR_call_frequency) {
  const byte Prescaler___8 = (1 << CS21);
  const byte Prescaler__32 = (1 << CS21) + (1 << CS20);
  const byte Prescaler__64 = (1 << CS22);
  const byte Prescaler_128 = (1 << CS22) + (1 << CS20);
  const byte Prescaler_256 = (1 << CS22) + (1 << CS21);
  const byte Prescaler1024 = (1 << CS22) + (1 << CS21) + (1 << CS20);

  const unsigned long CPU_Clock = 16000000;
  const byte toggleFactor = 1;

  unsigned long OCR2A_value;

  cli();//stop interrupts

  TCCR2A = 0;// set entire TCCR2A register to 0
  TCCR2B = 0;// same for TCCR2B
  TCNT2  = 0;//initialize counter value to 0

  TCCR2A |= (1 << WGM21); // turn on CTC mode
  TIMSK2 |= (1 << OCIE2A); // enable timer compare interrupt

  // the prescaler must be setup to a value that the calculation
  // of the value for OCR2A is below 256
  TCCR2B = Prescaler___8;
  OCR2A_value = (CPU_Clock / ( 8 * ISR_call_frequency * toggleFactor) )  - 1;


  if (OCR2A_value > 256) {  // if value too big
    TCCR2B = Prescaler__32; // set higher prescaler
    OCR2A_value = (CPU_Clock / ( 32 * ISR_call_frequency * toggleFactor) )  - 1;
  }

  if (OCR2A_value > 256) { // if value too big
    TCCR2B = Prescaler__64;// set higher prescaler
    OCR2A_value = (CPU_Clock / ( 64 * ISR_call_frequency * toggleFactor) )  - 1;
  }

  if (OCR2A_value > 256) { // if value too big
    TCCR2B = Prescaler_128;// set higher prescaler
    OCR2A_value = (CPU_Clock / ( 128 * ISR_call_frequency * toggleFactor) )  - 1;
  }

  if (OCR2A_value > 256) {  // if value too big
    TCCR2B = Prescaler_256; // set higher prescaler
    OCR2A_value = (CPU_Clock / ( 256 * ISR_call_frequency * toggleFactor) )  - 1;
  }

  if (OCR2A_value > 256) {   // if value too big
    TCCR2B = Prescaler1024;  // set higher prescaler
    OCR2A_value = (CPU_Clock / ( 1024 * ISR_call_frequency * toggleFactor) )  - 1;
  }

  OCR2A = OCR2A_value; // finally set the value of OCR2A

  sei();//allow interrupts

}


void setup() {
  Serial.begin(115200);
  Serial.println( F("Setup-Start") );

  setupTimerInterrupt(BaseFrequency);
  // Turn strips off ASAP (must follow clock_prescale_set)
  strip.begin();
  for (uint8_t s = 0; s < N_STRIPS; s++) {
    strip.setPin(s);
    strip.show();
  }

}


ISR(TIMER2_COMPA_vect) {

  uint16_t mag[STRIPLEN];
  uint8_t  s, d, p, r, g, b;
  int      mx1, m, level;

  if (countdown == 0) {        // Time to launch new drop?
    if (nDrops < MAXDROPS - 1) { // Is there space for one in the list?
      do {
        s = random(N_STRIPS);
      } while (s == prevStrip); // Don't repeat prior strip
      drop[nDrops].strip      = prevStrip = s;
      drop[nDrops].pos        = -32; // Start off top of strip
      drop[nDrops].speed      = 1 + random(3);
      drop[nDrops].brightness = 250 + random(520);
      nDrops++;
      countdown = 9 + random(28); // Time to launch next one
    }
  } else countdown--;


  for (s = 0; s < N_STRIPS; s++) { // For each strip...
    memset(mag, 0, sizeof(mag)); // Clear magnitude table

    // Render active drops for this strip into magnitude table
    for (d = 0; d < nDrops; d++) {
      if (drop[d].strip == s) {
        for (p = 0; p < STRIPLEN; p++) { // For each pixel...
          mx1 = (p << 2) - drop[d].pos; // Position of LED along wave
          if ((mx1 <= 0) || (mx1 >= 32)) continue; // Out of range
          if (mx1 > 24) { // Rising edge of wave; ramp up fast (2 px)
            m = ((long)drop[d].brightness * (long)(32 - mx1)) >> 4;
          } else { // Falling edge of wave; fade slow (6 px)
            m = ((long)drop[d].brightness * (long)mx1) / 24;
          }
          mag[p] += m; // Accumulate result in magnitude buffer
        }
      }
    }

    // Remap magnitude table to RGB for strand
    for (p = 0; p < STRIPLEN; p++) { // For each pixel in strip
      level = mag[p];                // Pixel magnitude (brightness)
      if (level < 255) {             // 0-254 = black to green-1
        r = b = 0;
        g = pgm_read_byte(&gamma[level]);
      } else if (level < 510) {      // 255-509 = green to yellow-1
        r = pgm_read_byte(&gamma[level - 255]);
        g = 255;
        b = 0;
      } else if (level < 765) {      // 510-764 = yellow to white-1
        r = g = 255;
        b = pgm_read_byte(&gamma[level - 510]);
      } else {                       // 765+ = white
        r = g = b = 255;
      }
      strip.setPixelColor(p, r, g, b);
    }

    strip.setPin(s); // Select output pin
    strip.show();    // Strips don't need to refresh in sync
  }

  // Move 'active' drops
  for (d = 0; d < nDrops; d++) {
    drop[d].pos += drop[d].speed;
    if (drop[d].pos >= (STRIPLEN * 4)) { // Off end?
      // Remove drop from list (move last one to this place)
      memcpy((void *)&drop[d], (void *)&drop[nDrops - 1], sizeof(drop[0]));
      nDrops--;
    }
  }
}

void loop() {
}

best regards Stefan

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.