Немедленный выход из блокирующих циклов

Доброе утро господа! Подскажите пожалуйста, есть простенький проект, ардуинка, кнопка, LED лента, задача кнопкой переключать эффекты. Все эффекты в процедурах, в основном цикле в зависимости от переменной выбирается эффект… И всё вроде работает, но с костылём! Дело в том, что при нажатии на кнопку, хочется сразу видеть новый эффект, но из за блокирующих циклов эффект продолжает работать пока не закончит работу и только потом перепрыгивает на новый.
Вопрос, как в данном коде грамотно реализовать моментальный выход из эффекта?
Пока это сделано путям помещения переменной в память и перезагрузки контроллера, что в принципе работает но я считаю это костыль… Быть может есть что то более изящное?

#define BUTTON 2
volatile byte counter = 1;
//=============================
#include <EEPROM.h>
#include "Adafruit_NeoPixel.h"
#define PIN 5
#define NUM_LEDS 28
// Parameter 1 = number of pixels in strip
// Parameter 2 = pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, PIN, NEO_GRB + NEO_KHZ800);
int yarkost = 96;

void setup() {
  Serial.begin(115200);
  pinMode(BUTTON, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(BUTTON), buttonPressed, CHANGE);

  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
  strip.setBrightness(yarkost);
  EEPROM.get(0, counter);
  
}

void loop() {
//      Serial.println(counter);
switch(counter) {
      case 1: {
        strip.setBrightness(yarkost);
        for(int i=0; i< 5; i++) {
        colorWipe ( 0x00 , 0xff , 0x00 , 5 ) ;
        colorWipe ( 0x00 , 0x00 , 0xff , 5 ) ;
        colorWipe ( 0xff , 0x00 , 0x00 , 5 ) ;
        }
        FadeOUT();
        
        strip.setBrightness(yarkost);
        for(int i=0; i< 10; i++) Rpulse();
        
        strip.setBrightness(yarkost);
        rainbowCycle(7);
        FadeOUT();
        
        strip.setBrightness(yarkost);
        for(int i=0; i< 10; i++) Bpulse();
        
        strip.setBrightness(yarkost);
        for(int i=0; i< 8; i++) {
        meteorRain(0xff,0xff,0xff, 20, 32, false, 50);           
        }
        FadeOUT();
        setAll(0,0,0);
      break;
      }
      case 2: {
        strip.setBrightness(yarkost);
        Bpulse();     
      break;
      }
      case 3: {
        strip.setBrightness(yarkost);
        Rpulse();
      break;
      }
      case 4: {
        strip.setBrightness(yarkost);
        rainbowCycle(7);
        FadeOUT();
      break;
      }
      case 5: {
        strip.setBrightness(yarkost);
        meteorRain(0xff,0xff,0xff, 20, 32, false, 50);        
        FadeOUT();
        setAll(0,0,0);
      break;
      }
      case 6: {
        FadeOUT();
        setAll(0,0,0);
      break;
      }
  }
}
//===================

void buttonPressed() {
    if ( digitalRead ( BUTTON ) == LOW ) {
        counter ++;
        if (counter > 6)counter = 1;
      EEPROM.put(0, counter);
      asm volatile ("jmp 0");
      }
}

void meteorRain(byte red, byte green, byte blue, byte meteorSize, byte meteorTrailDecay, boolean meteorRandomDecay, int SpeedDelay) {  
  setAll(0,0,0);
 
  for(int i = 0; i < NUM_LEDS+NUM_LEDS; i++) {
   
   
    // fade brightness all LEDs one step
    for(int j=0; j<NUM_LEDS; j++) {
      if( (!meteorRandomDecay) || (random(10)>5) ) {
        fadeToBlack(j, meteorTrailDecay );        
      }
    }
   
    // draw meteor
    for(int j = 0; j < meteorSize; j++) {
      if( ( i-j <NUM_LEDS) && (i-j>=0) ) {
        setPixel(i-j, red, green, blue);
      }
    }
   
    showStrip();
    delay(SpeedDelay);
  }
}

void fadeToBlack(int ledNo, byte fadeValue) {
    // NeoPixel
    uint32_t oldColor;
    uint8_t r, g, b;
    int value;
   
    oldColor = strip.getPixelColor(ledNo);
    r = (oldColor & 0x00ff0000UL) >> 16;
    g = (oldColor & 0x0000ff00UL) >> 8;
    b = (oldColor & 0x000000ffUL);

    r=(r<=10)? 0 : (int) r-(r*fadeValue/256);
    g=(g<=10)? 0 : (int) g-(g*fadeValue/256);
    b=(b<=10)? 0 : (int) b-(b*fadeValue/256);
   
    strip.setPixelColor(ledNo, r,g,b);
}

void rainbowCycle(int SpeedDelay) {
  byte *c;
  uint16_t i, j;

  for(j=0; j<256*10; j++) { // 5 cycles of all colors on wheel
    for(i=0; i< NUM_LEDS; i++) {
      c=Wheel(((i * 256 / NUM_LEDS) + j) & 255);
      setPixel(i, *c, *(c+1), *(c+2));
    }
    showStrip();
    delay(SpeedDelay);
  }
}

byte * Wheel(byte WheelPos) {
  static byte c[3];
 
  if(WheelPos < 85) {
   c[0]=WheelPos * 3;
   c[1]=255 - WheelPos * 3;
   c[2]=0;
  } else if(WheelPos < 170) {
   WheelPos -= 85;
   c[0]=255 - WheelPos * 3;
   c[1]=0;
   c[2]=WheelPos * 3;
  } else {
   WheelPos -= 170;
   c[0]=0;
   c[1]=WheelPos * 3;
   c[2]=255 - WheelPos * 3;
  }

  return c;
}
//=====================
void colorWipe ( byte red , byte green , byte blue , int SpeedDelay ) {
  for ( uint16_t i = 0 ; i < NUM_LEDS ; i ++ ) {
      setPixel ( i , red , green , blue ) ;
      showStrip ( ) ;
      delay ( SpeedDelay ) ;
  }
}
void FadeOUT() {
    // Fade OUT
    for(int k = yarkost; k > 0; k--) {
      strip.setBrightness(k);
      showStrip();
      delay(5);
  }
}
void Rpulse() {
    // Fade IN
    for(int k = 50; k < 256; k=k+15) {
      setAll(k,0,0);
      showStrip();
      delay(5);
    }
    // Fade OUT
    for(int k = 255; k >= 180; k=k-3) {
      setAll(k,0,0);
      showStrip();
      delay(3);
    }
    for(int k = 179; k >= 10; k--) {
      setAll(k,0,0);
      showStrip();
      delay(15);
  }
}
void Bpulse() {
    // Fade IN
    for(int k = 50; k < 256; k=k+15) {
      setAll(0,k,k);
      showStrip();
      delay(5);
    }
    // Fade OUT
    for(int k = 255; k >= 180; k=k-3) {
      setAll(0,k,k);
      showStrip();
      delay(3);
    }
    for(int k = 179; k >= 10; k--) {
      setAll(0,k,k);
      showStrip();
      delay(15);
  }
}
//=====================
void RGBpulse(){
  for(int j = 0; j < 3; j++ ) {
    // Fade IN
    for(int k = 0; k < 256; k++) {
   
      switch(j) {
        case 0: setAll(k,0,0); break;
        case 1: setAll(0,k,0); break;
        case 2: setAll(0,0,k); break;
      }
      showStrip();
      delay(5);
    }
    // Fade OUT
    for(int k = 255; k >= 0; k--) {
   
      switch(j) {
        case 0: setAll(k,0,0); break;
        case 1: setAll(0,k,0); break;
        case 2: setAll(0,0,k); break;
      }
      showStrip();
      delay(5);
    }
  }
}

void showStrip() {
   strip.show();
}

void setPixel(int Pixel, byte red, byte green, byte blue) {
   strip.setPixelColor(Pixel, strip.Color(red, green, blue));
}

void setAll(byte red, byte green, byte blue) {
  for(int i = 0; i < NUM_LEDS; i++ ) {
    setPixel(i, red, green, blue);
  }
  showStrip();
}

При помощи пары функций: setjmp и longjmp. Они специально для этого и созданы.

2 лайка

Спасибо, почитаю! Одним глазом глянул, с наскоку сложно для понимания как оно работает :slight_smile:

А чего там сложного? setjmp – это просто указание “запомни это место”. А longjmp – команда “вернись в ранее запомненное место”. Идейно это всё. Больше там ничего нет.

1 лайк

Можно попробовать ещё

void setup() {
}
void loop() {
 metka: 
 goto metka; 
}

Но как то коряво о нём написано тут:

Скажем так уже не компилируется :slight_smile:

void setup() {
  metka: 
}
void loop() {
 goto metka; 
}

Хотя какая разница к какой строке кода переходить?

Метка в другой функции? Ну-ну!

Ну, не Вам же переходить, вот Вам и нет разницы.

Впрочем, так говнокодить тоже можно, только такие переходы надо уметь готовить. Пример №1 и пример №2.

Видимо есть ограничения в применении, о которых явно не пишут. Язык абстракция, что тут сделаешь!

Почему не пишут? Это Вы не читаете.

дядя, тебе надо:

  1. объявить volatile uint8_t переменную = 0
  2. при нажатии на кнопку устанавливать ее значение = 1
  3. в цикле опрашивать значение, если оно == 1, установить его = 0 и выйти из цикла

одним глазом лучше в другое место смотреть, тут приличные люди я не буду говорить куда именно.

Не тупи. В разных функциях на входе сохраняются в стеке разное количество регистров, в зав-ти от сложности функции. На выходе они восттанавливаются из стека, это общепринятая практика всех компиляторов. И если ты делаешь goto из функции которая сохраняет 5 регистров прямо в функцию, которая на выходе восстанавливает 7 регистров, наерна можешь себе представить, что происходит со стеком, тем более, что одна функция пхает одно значение в стек, а вторая восстанавливает совершенно другое.

setjmp

Ёпрст… Ардуино познавательный. Обогатил словарь.

Стараюсь избегать блокирующих операций и бить временную диаграмму на фрагменты без пауз. Фрагмент возвращает “какой следующий шаг” и “когда/через сколько этот шаг выполнить”, как в Quake1 thinktime+nextthink. Периодически пробегаемся по списку, смотрим, чьё время подошло, вызываем фрагмент, получаем от него следующий шаг и сколько не беспокоить.

Наверное, это какой-то шаблон, и у него есть название.