Skip to Content
Arduino Eğitimlerimillis() ve delay() Farkı

millis() ve delay() Farkı

delay() öğrenmesi en kolay, ama projeleriniz büyüdükçe önünüze en çok engel çıkaran fonksiyondur. Bu yazı, ondan nasıl kurtulacağınızı anlatır.

Konu ileri seviye görünür ama değildir. Aksine, ne kadar erken öğrenirseniz o kadar çok zaman kazanırsınız.

delay() Ne Yapar?

delay(1000); // 1 saniye bekle

delay() programı tamamen durdurur. Bu süre boyunca Arduino:

  • Buton okuyamaz
  • Sensör kontrol edemez
  • Seri porttan gelen komutu göremez
  • Başka hiçbir iş yapamaz

Sadece bekler.

Sorunun somut hali

Bir LED’i saniyede bir yanıp söndüren ve aynı anda bir butonu dinleyen program yazmak istiyorsunuz:

void loop() { digitalWrite(LED_PIN, HIGH); delay(1000); digitalWrite(LED_PIN, LOW); delay(1000); if (digitalRead(BUTON_PIN) == LOW) { // neredeyse hic calismaz // ... } }

Buton kontrolü döngüde saniyede yalnızca bir kez çalışır. Butona 1,5 saniye boyunca basılı tutmazsanız program bunu kaçırır.

millis() Ne Yapar?

millis(), Arduino açıldığından beri geçen süreyi milisaniye cinsinden döndürür. Programı durdurmaz, sadece “şu an saat kaç?” sorusuna cevap verir.

unsigned long simdi = millis();

Fikir şudur: beklemek yerine, belirli bir sürenin geçip geçmediğini kontrol edin.

Temel Kalıp

const int LED_PIN = 13; const unsigned long ARALIK = 1000; unsigned long oncekiZaman = 0; bool ledDurumu = LOW; void setup() { pinMode(LED_PIN, OUTPUT); } void loop() { unsigned long simdi = millis(); if (simdi - oncekiZaman >= ARALIK) { oncekiZaman = simdi; ledDurumu = !ledDurumu; digitalWrite(LED_PIN, ledDurumu); } // Program burada calismaya devam eder! // Buton, sensor, seri port... hepsi kontrol edilebilir. }

loop() saniyede binlerce kez döner. Her turda “1000 ms geçti mi?” diye bakılır; geçmediyse hiçbir şey yapılmadan devam edilir.

Neden unsigned long?

millis() değeri sürekli büyür. int (maksimum 32.767) sadece 33 saniyede taşar.

unsigned long (maksimum 4.294.967.295) ise yaklaşık 49,7 gün dayanır.

Zaman değişkenlerini her zaman unsigned long olarak tanımlayın.

Taşma (Overflow) Sorunu ve Doğru Çözüm

49,7 gün sonra millis() sıfıra döner. Bu, uzun süre çalışan projelerde gerçek bir sorundur.

Kritik nokta şudur: simdi - oncekiZaman >= ARALIK yazımı taşmaya karşı doğal olarak güvenlidir. unsigned long aritmetiğinde çıkarma işlemi taşma anında bile doğru sonucu verir.

Buna karşılık şu yazım hatalıdır ve taşma anında kilitlenir:

// YANLIS - tasma aninda calismaz if (millis() >= oncekiZaman + ARALIK) { // ... }

Her zaman fark alma biçimini kullanın.

Birden Fazla Zamanlayıcı

millis() yaklaşımının asıl gücü buradadır. Farklı hızlarda çalışan işleri aynı anda yönetebilirsiniz:

const int LED1 = 8; const int LED2 = 9; unsigned long led1Zaman = 0; unsigned long led2Zaman = 0; unsigned long sensorZaman = 0; const unsigned long LED1_ARALIK = 500; const unsigned long LED2_ARALIK = 1300; const unsigned long SENSOR_ARALIK = 2000; bool led1Durum = LOW; bool led2Durum = LOW; void setup() { pinMode(LED1, OUTPUT); pinMode(LED2, OUTPUT); Serial.begin(9600); } void loop() { unsigned long simdi = millis(); // Gorev 1: LED1 her 500 ms if (simdi - led1Zaman >= LED1_ARALIK) { led1Zaman = simdi; led1Durum = !led1Durum; digitalWrite(LED1, led1Durum); } // Gorev 2: LED2 her 1300 ms if (simdi - led2Zaman >= LED2_ARALIK) { led2Zaman = simdi; led2Durum = !led2Durum; digitalWrite(LED2, led2Durum); } // Gorev 3: Sensor her 2 saniye if (simdi - sensorZaman >= SENSOR_ARALIK) { sensorZaman = simdi; Serial.print("Sensor: "); Serial.println(analogRead(A0)); } // Gorev 4: Buton her dongude kontrol edilir if (digitalRead(2) == LOW) { Serial.println("Butona basildi"); } }

Bu üç iş farklı ritimlerde, birbirini engellemeden çalışır. delay() ile bunu yapmak mümkün değildir.

Buton Debounce (Bloke Etmeyen)

const int BUTON_PIN = 2; const unsigned long DEBOUNCE = 50; int sonOkuma = HIGH; int kararliDurum = HIGH; unsigned long sonDegisim = 0; void loop() { int okuma = digitalRead(BUTON_PIN); if (okuma != sonOkuma) { sonDegisim = millis(); } if (millis() - sonDegisim > DEBOUNCE) { if (okuma != kararliDurum) { kararliDurum = okuma; if (kararliDurum == LOW) { Serial.println("Yeni basis"); } } } sonOkuma = okuma; }

Zaman Aşımı (Timeout) Kontrolü

millis() yalnızca periyodik işler için değil, “belirli bir sürede olmazsa vazgeç” mantığı için de kullanılır:

unsigned long baslangic = millis(); bool basarili = false; while (millis() - baslangic < 5000) { // en fazla 5 saniye dene if (sensorHazirMi()) { basarili = true; break; } } if (!basarili) { Serial.println("Sensor 5 saniyede yanit vermedi"); }

Durum Makinesi (State Machine)

Karmaşık sıralı işler için en temiz yapı budur. Program hangi aşamada olduğunu bir değişkende tutar.

enum Durum { BEKLIYOR, ISINIYOR, OLCUYOR, GONDERIYOR }; Durum mevcutDurum = BEKLIYOR; unsigned long durumZamani = 0; void durumDegistir(Durum yeni) { mevcutDurum = yeni; durumZamani = millis(); } void loop() { unsigned long gecen = millis() - durumZamani; switch (mevcutDurum) { case BEKLIYOR: if (gecen >= 10000) { // 10 saniyede bir baslat Serial.println("Isitiliyor..."); durumDegistir(ISINIYOR); } break; case ISINIYOR: if (gecen >= 3000) { // 3 saniye isinma Serial.println("Olculuyor..."); durumDegistir(OLCUYOR); } break; case OLCUYOR: if (gecen >= 500) { int deger = analogRead(A0); Serial.print("Deger: "); Serial.println(deger); durumDegistir(GONDERIYOR); } break; case GONDERIYOR: if (gecen >= 1000) { Serial.println("Gonderildi. Bekleme moduna gecildi."); durumDegistir(BEKLIYOR); } break; } // Bu satir her durumda calisir if (digitalRead(2) == LOW) { Serial.println("Acil durdurma!"); durumDegistir(BEKLIYOR); } }

Aynı işi delay() ile yazsaydınız, acil durdurma butonu hiçbir zaman çalışmazdı.

delay() Ne Zaman Kullanılabilir?

delay() tamamen yasak değildir. Şu durumlarda kullanmakta sakınca yoktur:

  • setup() içinde: Sensör ısınması, modül başlatma.
  • Çok kısa gecikmeler: Bir sensör protokolünde gereken 10 µs’lik darbe (delayMicroseconds()).
  • Tek işi olan basit projeler: Sadece bir LED yakıp söndüren bir test kodu.
  • Öğrenirken: İlk örnekleri anlamak için.

Kural şudur: Programınız aynı anda birden fazla iş yapacaksa delay()’den vazgeçin.

micros() ve Diğer Zaman Fonksiyonları

FonksiyonBirimTaşma süresi
millis()milisaniye~49,7 gün
micros()mikrosaniye~70 dakika
delay(ms)milisaniye
delayMicroseconds(us)mikrosaniye

micros() çözünürlüğü 4 mikrosaniyedir (Uno’da); daha hassas değil.

delayMicroseconds() 16.383 µs’ye kadar güvenilirdir; daha uzun beklemeler için delay() kullanın.

Kesme (Interrupt) İçinde millis()

Kesme fonksiyonları (attachInterrupt ile bağlananlar) içinde millis() artmaz — çünkü millis() de bir kesmeyle çalışır ve kesmeler iç içe çalışmaz.

Kesme içinde millis() okuyabilirsiniz (değer doğrudur) ama uzun süre beklerseniz zaman donar. Kesme fonksiyonlarını çok kısa tutun: bir bayrak set edin, işi loop() içinde yapın.

volatile bool olayVar = false; void kesmeFonksiyonu() { olayVar = true; // sadece isaretle } void loop() { if (olayVar) { olayVar = false; // asil isi burada yap } }

Özet

delay() programı tamamen durdurur; millis() ise geçen süreyi ölçerek programın akmaya devam etmesini sağlar. Birden fazla iş yapan her projede millis() kullanılmalıdır.

Bu derste şunları öğrendik:

  • delay()’in neden diğer işleri engellediğini
  • if (simdi - onceki >= ARALIK) kalıbını
  • Zaman değişkenlerinin neden unsigned long olması gerektiğini
  • Taşmaya karşı güvenli yazım biçimini
  • Birden fazla zamanlayıcıyı aynı anda yönetmeyi
  • Zaman aşımı kontrolünü ve durum makinesi yapısını
  • Kesme içinde millis() davranışını