Bir cümlelik özet
Bir butona bir kez bastığında Arduino bazen birden fazla basış görür; bu yanlış saymayı önlemek için millis() ile küçük bir zaman kontrolü ekleyerek butonu "yumuşatmayı" (debounce) öğreneceğiz.
Neden önemli?
Bir asansör düğmesine bir kez basarsın ve asansör bir kat çağrılır, iki kat değil. Bir oyun kolundaki zıplama tuşuna bir kez basarsın ve karakter bir kez zıplar. Bu doğal görünür, ama arkasında sessiz bir sorun vardır.
Mekanik butonlar metal parçaların birbirine değmesiyle çalışır. Parçalar bastığın anda tam temiz temas etmez; çok kısa bir süre titreşir, minik sıçramalarla açılıp kapanır. Buna bounce (sıçrama/titreme) denir. İnsan gözü bunu göremez çünkü bu titreme yalnızca birkaç milisaniye sürer. Ama Arduino saniyede milyonlarca kez kontrol yaptığı için bu titremenin her sıçramasını ayrı bir basış sanabilir.
Sonuç: Bir sayaç yazarsın, butona bir kez basarsın, ekranda sayı 1 yerine 3 olur. Kod yanlış değildir; buton fiziksel olarak "kekelemiştir". Debounce, bu kekemeliği yazılımla düzeltmenin adıdır ve gerçek dünyayla çalışan her projede karşına çıkar.
Bounce nedir ve neden yanlış sayar?
Butonun içinde ne olur?
Bir buton, iki metal temas noktasını birbirine bağlayan basit bir anahtardır. Bastığında bu iki nokta birleşir, bıraktığında ayrılır. Ama metal esnektir. Bastığın anda temas noktaları bir yayın ucundaki top gibi birkaç kez zıplar:
Basış anı (zaman ->)
Sinyal: 1 0 1 0 1 1 1 1 1 (kararsız titreme, sonra kararlı)
Süre: ~1-10 milisaniye sonra sabit
Gözünle tek bir "bas" görürsün. Arduino ise bu 5–6 değişimin hepsini ayrı bir olay olarak okuyabilir.
Neden basit sayaç şaşırır?
En yalın haliyle bir sayaç şöyle düşünülür: "Buton basılıysa sayacı bir artır." Ama loop() çok hızlı döner. Titreme boyunca buton birçok kez "basılı" görünür, bu yüzden sayaç bir yerine birkaç kez artar. Ayrıca butonu basılı tuttuğun sürece de sayaç sürekli artmaya devam eder. İki farklı sorun var: titreme ve basılı tutma.
Doğru sayım için iki şeye ihtiyacımız var: (1) titremeyi görmezden gelmek, (2) yalnızca "basılı değil → basılı" geçişini bir kez saymak.
Yazılımla debounce: zamanı ölçmek
Neden delay() iyi bir çözüm değil?
İlk akla gelen fikir: butonu görünce delay(50) koyup titremenin geçmesini beklemek. Küçük projelerde işe yarar ama delay() sırasında Arduino başka hiçbir şey yapamaz — LED'i güncelleyemez, başka sensör okuyamaz. Motor kontrolünde veya birden çok işi aynı anda yürüten projelerde bu donma sorun olur. Bu yüzden bloklamayan (non-blocking) bir yöntem tercih ederiz: millis().
millis() ile fikir
millis(), Arduino açıldığından beri geçen milisaniye sayısını verir. Onu bir kronometre gibi kullanırız. Butonun durumu her değiştiğinde zamanı not ederiz. Yeni bir değişiklik gördüğümüzde, "son değişiklikten bu yana en az 50 milisaniye geçti mi?" diye sorarız. Geçmediyse bu muhtemelen titremedir, görmezden geliriz. Geçtiyse gerçek bir değişimdir, kabul ederiz.
50 milisaniye insan için çok kısadır (parmağını bu kadar hızlı iki kez basamazsın), ama titremeden çok daha uzundur. Yani gerçek basışları kaçırmadan titremeyi eleriz.
Filtreleme fikriyle bağ
Bu tam olarak bir filtredir. Sensör derslerinde gürültülü bir ölçümü yumuşatmak için ortalama aldığımızı hatırla. Burada da butondan gelen "gürültülü" sinyali zamanla eleyip sadece gerçek olayı geçiriyoruz. Debounce, dijital dünyanın filtresidir: hızlı ve anlamsız değişimleri atar, yavaş ve gerçek değişimleri geçirir.
Mini uygulama
Amaç: Butona her basışta sayacı tam olarak bir artırmak ve sonucu Seri Ekran'a yazmak.
Bağlantı
- Buton bir bacağı 5V'a, çapraz bacağı hem 2 numaralı dijital pine hem de bir 10 kΩ direnç üzerinden GND'ye. (Bu direnç, pin boştayken sinyali LOW'a çeker; buna pull-down denir.)
- Alternatif: Direnç kullanmadan
INPUT_PULLUPmodu; o zaman butonun diğer bacağı doğrudan GND'ye gider ve basılınca sinyal LOW okunur.
Aşağıdaki sketch harici pull-down direncine göre yazılmıştır (basılınca HIGH).
const int butonPin = 2;
int sayac = 0;
int sonKararliDurum = LOW; // butonun kabul edilmiş durumu
int sonOkuma = LOW; // en son ham okuma
unsigned long sonDegisimZamani = 0;
const unsigned long bekleme = 50; // milisaniye
void setup() {
pinMode(butonPin, INPUT);
Serial.begin(9600);
}
void loop() {
int okuma = digitalRead(butonPin);
if (okuma != sonOkuma) {
sonDegisimZamani = millis(); // titreme başladı, zamanı not et
}
if (millis() - sonDegisimZamani > bekleme) {
if (okuma != sonKararliDurum) {
sonKararliDurum = okuma;
if (sonKararliDurum == HIGH) { // yalnızca basma anında say
sayac++;
Serial.println(sayac);
}
}
}
sonOkuma = okuma;
}
Kodu yükle, Seri Ekran'ı (Serial Monitor) 9600 baud'da aç ve butona bas. Her basışta sayının tam bir arttığını göreceksin. İstersen bekleme değerini 5'e düşür ve titremenin yanlış saymayı nasıl geri getirdiğini gözlemle.
Uygulama laboratuvarı: Buton Debounce Mantığı
Buton Debounce Mantığı konusunu kalıcı hâle getirmenin en iyi yolu, kavramı küçük ve ölçülebilir bir göreve dönüştürmektir. Bu çalışmada Bounce nedir ve neden yanlış sayar? ile Neden basit sayaç şaşırır? arasındaki ilişkiyi kullanarak bağlantı şeması, derlenebilen Arduino kodu, seri monitör kaydı ve test sonucu hazırlayacaksın. Amaç yalnız sonucun çalışması değil; hangi kararı neden verdiğini, neyi test ettiğini ve hangi durumda tasarımı değiştireceğini açıklayabilmektir.
Görev senaryosu
Şu senaryoyu ele al: potansiyometreyle servo açısını sınırlar içinde değiştiren düzenek. Dersin ana hedefi “Buton titremesini (bounce) millis ile yazılımsal debounce yaparak güvenilir saymayı öğren” olduğuna göre önce problemi tek cümleyle tanımla. Ardından sistemin alacağı girdiyi, uygulayacağı işlemi ve üreteceği çıktıyı ayrı ayrı yaz. Bilmediğin bir ayrıntı varsa onu varsayım olarak işaretle; varsayımı gerçek bilgi gibi kullanma.
- Planla: Başlangıç durumunu, beklenen sonucu ve kullanacağın kavramları yaz.
- En küçük sürümü kur: Yalnız temel davranışı çalıştır; süsleme ve ek özellikleri sonraya bırak.
- Üç test hazırla: Normal bir durum, sınırda bir durum ve hatalı ya da beklenmeyen bir durum seç.
- Sonucu kaydet: Beklenen ile gerçekleşeni yan yana yaz; fark varsa olası nedeni belirt.
- Tek değişiklik yap: Aynı anda birçok şeyi değiştirmek yerine bir kararı düzeltip testi yeniden çalıştır.
Başarı ölçütleri
- Kartın 5 V veya 3,3 V çalışma sınırı doğru mu?
- Pin numaraları kod ve devrede eşleşiyor mu?
- Seri monitör ölçümü beklenen aralıkta mı?
- Motor veya servo karttan doğrudan aşırı akım çekiyor mu?
- Güç kesildiğinde sistem güvenli duruma dönüyor mu?
“Buton Debounce Mantığı” çalışmasını bitirdiğinde ürünü bir arkadaşına yalnız bölüm başlıklarıyla anlat. Arkadaşın potansiyometreyle servo açısını sınırlar içinde değiştiren düzenek senaryosundaki adımları ve karar nedenlerini takip edebiliyorsa anlatım yeterince açıktır. Anlaşılmayan noktayı daha fazla terim ekleyerek değil, Bounce nedir ve neden yanlış sayar? ve Neden basit sayaç şaşırır? ilişkisini daha küçük adımlara bölerek düzelt.
Sık yapılan hatalar
Zamanı yanlış yerde not etmek
sonDegisimZamani = millis(); satırı yalnızca okuma değiştiğinde çalışmalı. Her döngüde çağırırsan kronometre sürekli sıfırlanır ve millis() - sonDegisimZamani asla beklemeyi geçemez; buton hiç çalışmaz gibi görünür.
Basma yerine bırakmayı saymak
Eğer if (sonKararliDurum == HIGH) kontrolünü koymazsan, hem basma hem bırakma sayılır ve her basış sayacı iki artırır. Sadece bir geçiş yönünü saymalısın.
Pull-down veya pull-up direncini unutmak
Butonun boştaki bacağı hiçbir yere bağlı değilse pin "havada" kalır (floating) ve rastgele HIGH/LOW okur. Debounce kodu bile bu rastgeleliği düzeltemez. Mutlaka bir pull-down/pull-up direnci veya INPUT_PULLUP kullan.
delay() ile yavaşlatmaya çalışmak
delay(200) koyarak "yavaşlatınca düzelir" düşüncesi hem gerçek hızlı basışları kaçırır hem de diğer işleri durdurur. millis() çözümü hem daha doğru hem de bloklamaz.
Güvenlik notu
- Bu ders yalnızca düşük gerilim ile çalışır: USB veya en fazla küçük pil paketleri. Şebeke elektriğine (prize takılan yüksek gerilim) hiçbir bağlantı yapılmaz.
- Buton, LED gibi bileşenlerde her zaman doğru direnç kullan; kısa devre bileşeni ısıtabilir.
- Devreye motor eklemek istersen, motoru asla doğrudan Arduino pinine bağlama. Motor daha çok akım çeker ve pini yakabilir; bir motor sürücü (motor driver) ve ayrı bir güç kaynağı gerekir.
- Kabloları takıp çıkarırken Arduino'nun USB'den çıkarılı olması iyi bir alışkanlıktır.
- İlk kez devre kurarken bir yetişkinden yardım iste ve bağlantıları o kontrol etsin.
Ders özeti
- Mekanik butonlar basıldığında birkaç milisaniye titrer (bounce); Arduino bu titremeyi çok sayıda basış sanabilir.
- Basit bir sayaç bu yüzden bir basışta birden fazla artabilir; ayrıca basılı tutma da sürekli artışa yol açar.
- Debounce, butonun durumu değiştikten sonra kısa bir süre bekleyip sinyalin kararlı olmasını beklemektir.
millis()ile bloklamayan debounce,delay()gibi diğer işleri durdurmaz ve daha doğru çalışır.- Debounce aslında bir filtredir: hızlı ve anlamsız değişimleri atar, yavaş ve gerçek olayları geçirir.
Kontrol soruları
- Buton "bounce" (titreme) nedir ve neden olur?
- Basit bir sayaç, bir basışta neden bazen 2 veya 3 kez artar?
- Debounce için neden
delay()yerinemillis()tercih ederiz? - Kodda
sonDegisimZamani = millis();satırı ne zaman çalışmalıdır? if (sonKararliDurum == HIGH)kontrolü olmasaydı sayaç nasıl davranırdı?
Cevaplar
- Bounce, butonun metal temas noktalarının basıldığı anda birkaç kez hızlıca açılıp kapanmasıdır; metal esnek olduğu için birkaç milisaniye titrer.
- Çünkü
loop()çok hızlı döner ve titreme boyunca buton birçok kez "basılı" görünür; her görünüş ayrı sayılır. millis()bloklamayan bir zaman ölçümüdür; beklerken Arduino başka işler (LED, sensör, motor) yapmaya devam edebilir,delay()ise her şeyi durdurur.- Yalnızca ham okuma bir önceki okumadan farklı olduğunda; yani sinyal her değiştiğinde kronometre yeniden başlatılır.
- Hem basma hem bırakma "durum değişimi" sayılacağı için her tam basış sayacı iki artırırdı.
Kaynak ve doğrulama notu
“Buton Debounce Mantığı” dersi için doğrulama odağı Bounce nedir ve neden yanlış sayar? ile Neden basit sayaç şaşırır? arasındaki ilişkinin örnekler üzerinde tutarlı çalışmasıdır. Arduino uyumlu kartlarda pin, gerilim ve akım sınırları modele göre değişebilir. Kod derlense bile devre güvenli olmayabileceğinden, motor ve servo gibi yükler uygun sürücü ve harici besleme ile kullanılmalıdır.
Sonraki ders
Servo Kontrolü: Bir servo motoru açıya göre nasıl döndürürüz ve butonla nasıl birleştiririz.