İç içe döngülerde millis () işlevini kullanma

Arduino pro mini'de bazı ışıkları (neopix) yakmak için millis() kullanıyorum. Sorun şu ki ışıkları istenen süre boyunca yakamıyorum. Daha spesifik olarak, ışıklar ~ 250 msn boyunca yanıp sönerken, ben 9 saniye boyunca yanıp sönmek istiyorum! İşte yaptığım şeyin basitleştirilmiş bir versiyonu.

() Döngüsünde 250 ms için çalışması gereken bir süre döngüsüne sahibim (döngü 1 olarak diyelim). 1. döngü içinde, 9 saniye boyunca çalışması gereken bir işlev flaşı() çağırırım! Döngü 1'den sonra başka bir döngü (döngü 2) koydum, bu işlem 3 saniye sürecek.

İşte daha büyük resim ... Aslında bir radyonun 250 ms boyunca uyanmasını istiyorum. Bu zaman aralığında bir şey alırsa, ışıkları 9 saniye boyunca yakacak olan flash() adı verilir. Herhangi bir şey almazsa, sadece 3 saniye uyuyacak, daha sonra 250 ms kadar canlanacak.

void loop() { 
    previousMillis = millis();
    while (millis() - previousMillis < awake_interval) {  //awake_interval = 250 ms    
        flash();  //flash for 9 seconds
    }

    previousMillis = millis();
    while (millis() - previousMillis < sleep_interval);  //sleep_interval = 3 secs
}

Flash() işlevi şöyle görünür,

void flash() {
    unsigned long x = millis();
    while(millis() - x < flash_time){  //flash_time = 9 secs 
        lights_on();
        delay(t1);
        lights_off(); 
        delay(t2); 
    }
}

Buraya döngü 2'yi koydum çünkü ışıklar olmadan sonsuza dek yanıp söner. Bu mantıklı çünkü tüm kod sonsuz bir döngüde - void loop ().

Hata ayıklama için, döngüler girdikten sonra seri baskılar koymak. Her ikisinin de millis() kullandığı iki döngü (benim durumumda flash ()) loop 1) 'in iç içe geçmesi problemin kaynağı gibi görünüyor. Eğer bu doğruysa, bunu başka nasıl yapmalıyım? Sorun bu değilse, herhangi bir fikir takdir edilecektir.

Edit: The suggestions made by @VE7JRO and @ratchet freak are neat but I was still having problems making my thing work. Turns out it was a problem with one my initializations. I am posting my whole code below (that uses LEDs). I guess I should post the correct code as an answer.

const long sleep_interval = 3000; 
const long awake_interval = 250; 
const int ledPin = 3;

const uint8_t flash_time = 9000;  //bug 
const uint16_t t1 = 5; 
const uint16_t t2 = 495;

void flash() {  
    unsigned long x = millis();
    while(millis() - x < flash_time){ 
        digitalWrite(ledPin, HIGH);
        delay(t1);
        digitalWrite(ledPin, LOW);
        delay(t2);
    }   
}   

void setup() {
    Serial.begin(57600);  
    pinMode(ledPin, OUTPUT);
}

void loop() {
    unsigned long x = millis();
    while(millis() - x < awake_interval){
        Serial.println("flash time!");
        flash();  //overflow will occur    
    }

    unsigned long y = millis();; 
    while (millis() - y < sleep_interval) {  //sleep_interval = 3 secs
        Serial.println("Sleep time!");
    }
}
1
Tam kodunuzu gönderin.
katma yazar Hugo, kaynak
senin 'while (millis() - öncekiMillis
katma yazar Juraj, kaynak
@Juraj "Zaman çizelgesi çizelgesi" ile ne demek istediğinizi anlamıyorum. Ve gecikmeyi kullanamıyorum, çünkü engelliyor ve bu (daha büyük) programın geri kalanıyla uyumlu olmayacak
katma yazar Demi, kaynak

5 cevap

Bunun yerine bir durum makinesi kullanın:

bool  flashing;
unsigned long lastflashingChange;

void loop() { 
    unsigned long currentMillis = millis;
    if(flashing){
        flash();  //update flash state and change IO pin
        if (lastflashingChange - currentMillis >= awake_interval) {  //awake_interval = 250 ms    
            flahsing = false;
            //turn off the led
            lastflashingChange = currentMillis;
        }
    } else {
        if (lastflashingChange - currentMillis >= sleep_interval) {  //awake_interval = 250 ms    
            flashing = true; 
            //possible initialize flash state
            lastflashingChange = currentMillis;
        }
    }
}

And a similar bit of code inside flash()

bool flashOn;
unsigned long lastFlashLightChange;
void flash() {
    unsigned long x = millis();

    if(flashOn){
        if (lastFlashLightChange - x >= t1) {
            flashOn = false;
            lights_off();
            lastFlashLightChange = currentMillis;
        }
    } else {
        if (lastFlashLightChange - x >= t2) {
            flashOn = true;
            lights_on();
            lastFlashLightChange = currentMillis;
        }
    }

}
1
katma
@Tahseen hayır gerekli değildir.
katma yazar CTKeane, kaynak
@Tahseen, durumu değiştirmek için geçen zamandan başka bir yol seçebilirsiniz. Sadece durumunda durumu değiştirin. Ayrıca daha fazla devlete sahip olabilir, bool değerini bir enum olarak değiştirebilir ve if/else komutunu bir switch olarak değiştirebilirsiniz.
katma yazar CTKeane, kaynak
Bu gerçekten iyi görünüyor ... denemek için gidiyoruz. Ancak, ilk kod bloğunuzdaki priorMillis = millis (); satırı gerçekten gerekli değil mi?
katma yazar Demi, kaynak
Bu akıllıdır ancak 9 saniye boyunca ışıkları yanıp sönmeyecektir, ancak yalnızca 250 ms (bu bir uyanıklıktır). İşte daha büyük resim ... Aslında bir radyonun 250 ms boyunca uyanmasını istiyorum. Bu zaman aralığında bir şey alırsa, ışıkları 9 saniye boyunca yakacak olan flash() adı verilir. Herhangi bir şey almazsa, sadece 3 saniye uyuyacak, daha sonra 250 ms kadar canlanacak.
katma yazar Demi, kaynak

İşte başlamanız için bir taslak. Gecikme veya kitaplıklar kullanmaz. Herhangi bir NeoPixel cihazım yok, bu yüzden LED'de yerleşik Arduino'yu test amaçlı kullandım. Yanıp sönen LED'i durdurmak için sadece "myTimer4" işlevini ve ilgili kodu/değişkenleri kaldırın. Ne tür bir "radyo" kullandığınızı bilmiyorum bu yüzden alınan radyo sinyalini simüle etmek için seri monitörde girilen bir karakteri kullandım. Test amacıyla da kod boyunca birkaç seri yazdırma deyimi vardır.

const unsigned long delayTime = 3000;
const unsigned long delayTime2 = 250;
const unsigned long delayTime3 = 9000;
const unsigned long delayTime4 = 500;
unsigned long previousMillis = 0;
unsigned long previousMillis2 = 0;
unsigned long previousMillis3 = 0;
unsigned long previousMillis4 = 0;
byte checkForData = 0;
byte dataReceived = 0;
byte flashLED = 0;
byte doOnce = 0;

void setup(){
  Serial.begin(9600);
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);
}

void loop(){

 //Get current time.
  unsigned long currentMillis = millis();

 //First event.
  if(checkForData == 0){
    if(myTimer1(delayTime, currentMillis) == 1){
      Serial.println("3 second timer done");
      checkForData = 1;
      previousMillis2 = currentMillis;
    }
  }

 //Second event.
  if(checkForData == 1){

   //Your radio code here to check for data.

   //I'm simulating data input from your device using the serial monitor.
    if(Serial.read() > 0){dataReceived = 1;}

    if(myTimer2(delayTime2, currentMillis) == 1){
      Serial.println("250 ms timer done");
      checkForData = 0;
      previousMillis = currentMillis;
      if(dataReceived == 1){
        flashLED = 1;
        previousMillis3 = currentMillis;
        dataReceived = 0;
      }
    }
  }

 //Third event.
  if(flashLED == 1){
    if(doOnce == 0){
      flash(1);
      doOnce = 1;
    }
    if(myTimer3(delayTime3, currentMillis) == 1){
      Serial.println("9 second timer done");
      flashLED = 0;
      flash(0);
      doOnce = 0;
      digitalWrite(LED_BUILTIN, LOW);
    }
  }

 //Fourth event. 
  if(myTimer4(delayTime4, currentMillis) == 1 && flashLED == 1){
    //Serial.println("500 ms timer done");
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
    previousMillis4 = currentMillis;
  }

}

// Function to send data to the NeoPixel device.
void flash(byte startStop){
  if(startStop == 1){Serial.println("flash function started");}
  if(startStop == 0){Serial.println("flash function ended");}  
}

// First event timer.
byte myTimer1(unsigned long delayTime, unsigned long currentMillis){
  if(currentMillis - previousMillis >= delayTime){previousMillis = currentMillis;return 1;}
  else{return 0;}
}

// Second event timer.
byte myTimer2(unsigned long delayTime2, unsigned long currentMillis){
  if(currentMillis - previousMillis2 >= delayTime2){previousMillis2 = currentMillis;return 1;}
  else{return 0;}
}

// Third event timer.
byte myTimer3(unsigned long delayTime3, unsigned long currentMillis){
  if(currentMillis - previousMillis3 >= delayTime3){previousMillis3 = currentMillis;return 1;}
  else{return 0;}
}

// Fourth event timer.
byte myTimer4(unsigned long delayTime4, unsigned long currentMillis){
  if(currentMillis - previousMillis4 >= delayTime4){previousMillis4 = currentMillis;return 1;}
  else{return 0;}
}
1
katma

ışıklar ~ 250 msn boyunca yanıp sönerken, 9 saniye boyunca yanıp sönmek istiyorum!

Bu mantık mantıklı değil. Temelde 5 saniye içerisinde 2 saatlik bir öğün yemek istiyorsunuz.

kodunuz bu tür bir mantığı yansıtır: yürütme flash() 'dan çıktığında, döngü() de while döngüsünden çıkmalıdır.

bu yüzden ne yapmak istediğinizi tam olarak düşünün, bir yere yazın ve kodlayın.

0
katma

Tüm kodum konusunda kendime güvendiğim konusunda yanılmışım. İnsanlar kodumun tamamını göndermeyi önerdiğinde, sadece yapmalıydım !!!

Neyse, işte yaptığım hata. Flash_time değişkenini 8 bit tam sayı olarak başlattım. Tabii ki, 8 bit sadece 255'e kadar dayanabilir. 9000'i depolamaya çalışıyorum !!! Aşağıdaki kod bu hatayı düzeltti.

const long sleep_interval = 3000; 
const long awake_interval = 250; 
const int ledPin = 3;

const uint16_t flash_time = 9000;  //bug fixed by using uint16_t instead of uint8_t
const uint16_t t1 = 5; 
const uint16_t t2 = 495;

void flash() {  
    unsigned long x = millis();
    while(millis() - x < flash_time){ 
        digitalWrite(ledPin, HIGH);
        delay(t1);
        digitalWrite(ledPin, LOW);
        delay(t2);
    }   
}   

void setup() {
    Serial.begin(57600);  
    pinMode(ledPin, OUTPUT);
}

void loop() {
    unsigned long x = millis();
    while(millis() - x < awake_interval){
        Serial.println("flash time!");
        flash();    
    }

    unsigned long y = millis();; 
    while (millis() - y < sleep_interval) {  //sleep_interval = 3 secs
        Serial.println("Sleep time!");
    }
}

Bu nedenle, sonlandırma koşullarını belirlemek için millis() kullanan iç içe döngüler, döngü 1 250 ms için çalışıyorsa ve döngü 2 (döngü 1'in içindedir) 9 saniye gibi çok daha uzun sürerse bile gayet iyi çalışır.

Çok fazla zaman harcıyorsam özür dilerim.

0
katma

Mantığın yanlış. ÖncekiMillis'i birinci satırdaki millis() değeriyle güncellerseniz, kodunuzun ilk ve ikinci satırları arasındaki zaman farkını her zaman çıkarırsınız (bu, flash içindeki döngüde yaptığınız aynıdır). İhtiyacınız olan şey, programın çalışmaya başlamasından bu yana geçen süreyi almaksa, bunu her zaman döngüde() güncellemek yerine setup() için sabit bir değişkene sahip olmanız gerekir.

Öyle olsa bile, durumunuzda neden millis() gerekli olduğunu anlamıyorum. Delay() kullanmak çok daha basit olurdu ve hala işi halledebilir. Örnek:

void setup() {
delay(250);
}

void loop(){

    for(int i = 0; i < 9; i++){
        digitalWrite(LED, HIGH);
        delay(time);
        digitalWrite(LED, LOW);
        delay(time);

        delay(1000 - 2*time); //Delays the loop exactly one second per iteraction
    }

}
0
katma
@Tahseen: “ Gecikmeyi kullanamıyorum ” yazdın ve yine de onu flash() 'da kullanıyorsun. Peki anlaşma nedir?
katma yazar Sprogz, kaynak
Gecikmeyi kullanamıyorum, çünkü engelleme yapıyor ve bu (daha büyük) programın geri kalanıyla uyumlu değil. Setup() 'a sabit bir değişken koyma fikrinizi biraz daha genişletebilir misiniz? Benim süre döngülerim içeride kalma zamanını belirlemeye çalışıyor.
katma yazar Demi, kaynak
Flaşın() ışıkların açılıp kapanma etkisine sahip olması için onu kullandım. Ancak bunu gerçek döngü sırasında kullanırsam, gelecekte eklemeyi umduğum diğer şeyler işe yaramaz. Örneğin, 250 ms boyunca uyanan, herhangi bir komutu olup olmadığını gören ve 3 saniye boyunca uyuyan bir radyo kullanmak istiyorum. Bu 250 ms'lik aralıkta bir komut aldıysa, arduino ışıkları 9 saniye boyunca yanıp söner.
katma yazar Demi, kaynak
Kesinlikle ... "Engelliyor" derken ne demek istediğinizi anlamadım, bunu kullanamamanızın nedeni nedir?
katma yazar milton, kaynak