ATMEL PROGRAMLAMA 12 - Buton/Anahtar arkı (bounce) ve önlemi (debounce)

       Buton ve anahtar uygulamalarında göz önünde bulundurulması gereken bir konu da buton veya anahtar kontaklarının istenilen konuma gelmesi sırasında kontaklar arasında oluşan   ark 'dır. Kontaklar çok kısa sürede istenilen konuma gelirler, bu kısa süre içersinde ark oluşur ve insanın bunu algılaması zordur.

       Aşağıdaki gibi  bir pull-up buton bağlantısında butona basılmadığı sürece  mikrodenetleyici pini   lojik 1 seviyesindedir. Butona basılınca pin  lojik 0 seviyesinde olacaktır. Her butona basılışta böyle devam edecektir.

  






 Biz bu 1-0-1 geçişlerini resimdeki gibi zannederiz.  Gerçekte ise aşağıdaki gibidir.



yaptığımız deney

Lojik 1 seviyesinden lojik  0 seviyesine geçiş hemen olmaz. Çok kısa bir süreliğine (yukarıdaki şekilde 250mikro saniye ) ark oluşur. Oluşan bu arkları mikrodenetleyici fark eder ve yanılır. Yukarıdaki şekilleri ve deney videomuzu incelerseniz sinyalin ark kısımlarında bir kaç tane 1-0-1-0-1 geçişleri yaşanır. Mikrodenetleyici bu durumları butona basıldı olarak algılar ve ona göre tepki verir. Yaptığı işlem hatalı olur.

Bu sorunun çözümü iki şekilde olur.

1- Donanımsal  çözüm
2- Yazılımsal çözüm .

1- Donanımsal Çözüm 

Ark  istenmeyen bir sinyaldir. Elektronik devrelerde istenmeyen sinyaller donanımsal olarak filtreleme ile bastırılır. Biz de donanımımızda gerekli filtrelemeyi yaparsak ark'ı etkisiz hale getirebiliriz.  Sinyal içindeki ark kısmında değişim sinyale göre fazladır. Yani sinyale göre frekansı yüksektir. Alçak geçiren bir filtre yaparsak yüksek frekanstaki ark sinyalini bastırmış , istenilen sinyali geçirmiş oluruz. Bunun en basit yolu  buton uçlarına paralel kondansatör bağlamaktır.

 
       Botuna basılmadığı anlarda   kondansatör (C),  R pull-up direnci üzerinden şarj olur. Mikrodenetleyici pini girişinde kondansatör üzerindeki gerilim değeri görülecektir. ve lojik 1 seviyesindedir .
       Butona basılınca  kondansatörün iki ucu kısa devre olarak GND'ye bağlanmış olur ve kondansatör hemen  deşarj olur. Giriş gerilimi kondansatör üzerindeki gerilim değeri olduğundan giriş anında 0V olur. Elimizi butondan çekitiğimizde kondansatör şarj olmaya başlayacaktır. Kondansatörün üzerindeki gerilim Atmega 328p'yi lojik 0 seviyesinde tutacak gerilim değerine ulaşana kadar giriş pini lojik 0 seviyesinde kalır. Bu gerilim değerine ulaşıncaya kadar geçen süre içersinde  ark oluştuğu anda arkın 1 seviyeleri  kondansatör şarjını fazla etkilemeyeceğinden   mikrodenetleyici pini lojik 0 seviyesini algılar.   Böylece giriş pini ark durumundan fazla etkilenmemiş olur.
  Atmega 328p'nin  lojik 0 ve lojik 1 gerilim değeri aralıkları datasheet'te sayfa 303'te  DC Characteristics başlığı altında tablo 29-1de belirtilmiştir.. Burada 

     

Vcc= 5V kullandığımızı farz edersek tabloya göre;

 Lojik 0 aralığı -0.5V--------0.3Vcc aralığıdır.  bu durumda
0.3Vcc =0.3*5V=1.5V olur  ve    -0.5V- ------ 1.5V arası Atmega 328p için lojik 0 kabul edilir.

Lojik 1 aralığı ise   0.6Vcc-------Vcc+0.5V'tur. bu durumda Vcc=5V oduğundan
3V----------5.5V arası atmega 328p için lojik 1 kabul edilir.

       Giriş pinindeki gerilim değeri kondansatör üzerindeki gerilim değeri olduğuna göre, kondansatör üzerindeki gerilim 1.5V'tu geçmediği sürece giriş Lojik 0 olacaktır.  Kondansatörün belli bir gerilim değerine ulaşması için geçen süre  şu formülden elde edilir

  

         buradan gerekli değerleri girerek istediğiniz hesaplamayı yaptırabilirsiniz. Örneğin yukarıdaki grafikte ark süresi 25mikrosaniyeydi. isteğimiz bu süre içinde kondansatör üzerindeki gerilim 1.5V seviyesine ulaşmalı. Direnç değerimiz zaten belli 10Kohm pull-up direnci. Bu değerlere göre hesaplama yaparsanız yaklaşık 8 nf'lık bir kondansaör değeri elde edilir. Bu teorik olanı.
         Peki pratikte  ne yapacağız. 25 mikrosaniye  bizim için az bir süre . işimizi garantiye almak için biz bu süreyi daha yüksek tutatabiliriz. örneğin 250 mikro saniye. tekrar hesaplarsak bu sefer kondansatör değeri yaklaşık 70nf olur ki biz kolayca bulunabilen 100nf kullansak bu değer bize oldukça yeterli olacaktır.

100nf kullandığımız  zaman sinyalimiz. 

 

      Şekilde de gördümüz gibi arkları önledik ancak şimdi de bazı salınımlar meyadana gelebilir. Bunlar kondansatörün ve direncin toplam empedansı nedeyiyle oluşuyor. Ve deşarj işlemi hemen olmuyor. Her elemanın ve her bağlantı noktasının bir endüktansı vardır. Bu endüktans ve kondansatör bir araya gelince salınım hareketi başlayacak ve   - (negatif) yönde gerilim oluşacaktır. Bu negatif gerilim değeri  Atmega 328p'nin pinleri için minimum değer olan -0.5V sınırını aşarsa  mikrodenetleyiciye zarar verebilir. Bu nedenle  salınımların engellenmesi gerekir. Bunun için en iyi yöntem direnç eklemektir. Yeni devremiz aşağıdaki gibi olacaktır.

       Salınımları en aza indirmek için kondansatörün çık hızlı bir şekilde R2 direnci üzerinden deşarj olması gerekir. Bu nedenle R2 direncinin düşük değerde olmalıdır. Örneğin 100 ohm.

 alacağımız sonuç aşağıdaki gibidir.



       Şekilde de görüldüğü gibi osilasyon ve ark engellenmiş oldu. ancak Lojik 1 seviyesinden Lojik 0 seviyesine geçiş hala belli bir süre almakta .Bunun çözümü için  74HC14 schmitt triger buffer kullanmamız gerekir. Devremiz  aşağıdaki gibi olur.



        74HC14 lojik seviye geçişlerini daha hızlı bir hale getirir ve istenmeyen sinyalleri engeller. 74HC14 aynı zamanda bir inverter'dır. Pull-up bağlantıda butona basınca giriş pinine lojik 0 bilgisi geliyordu. 74HC14kullandığımız zaman bu lojik 0 terslenecek ve giriş pini  butona basılınca Lojik 1 olacak. Bu nedenle programda buton kontrolu Lojik 1 ' e göre tekrar düzenlenmelidir.

      Donanımsal olarak yapabileceklerimiz bunlar. ekleyeceğimiz her eleman devremizde fazladan yer , iletim yolu ve maliyet olarak karşımıza çıkacaktır.  Devremiz çok hassas olamayacaksa donanım yerine yazılımımızı düzelterek de bu sorunu çözebiliriz.


devremize kondansatör ve 74hc14 ekledikten sonra


Deneyimizde kondansatör ve 100 ohm'luk direnci çıkratıp sadece 10kohm pull-up ve 74hc14 kullandık sonuç yine temiz bir giriş puls'i oldu.



2- Yazılımsal  Çözüm

buton programımız

#include <avr/io.h>
#define F_CPU 1000000ul
#include "util/delay.h"


int main(void)
{
    DDRB |=(1<<PINB0);
    DDRD &=~(1<<PIND0);
   
    while (1)
    {
         if(!(PIND & 0X01))
            {

               PORTB|=(1<<PINB0);
            }
         else
           {
             PORTB&=~(1<<PINB0);
           }
    }
}


 1.yöntem buton arkını önlemek için birkaç tane yazılımsal çözüm vardır.  Bunlardan en basit olanı buton kontrolünden sonra bir süre gecikme sağlamaktır. Bu şekilde sinyalin ark ksımları program tarafından okunmaz.  Ancak gecikme algılamanın hızlı oması istenilen durumlarda kullanışlı olmaz.


#include <avr/io.h>
#define F_CPU 1000000ul
#include "util/delay.h"


int main(void)
{
    DDRB |=(1<<PINB0);
    DDRD &=~(1<<PIND0);
   
    while (1)
    {
         if(!(PIND & 0X01))
            { 

               _delay_ms(30);
               PORTB|=(1<<PINB0);
            }
         else
           {
             PORTB&=~(1<<PINB0);
           }
    }
}


programda şöyle bir değişiklik daha yapabiliriz. Bekleme sonrası tekrar butonun durumunu kontrol edbiliriz.

while (1)
    {
         if(!(PIND & 0X01))
            { 

                 _delay_ms(30);
                   if(!(PIND & 0X01))
                    {
                     PORTB|=(1<<PINB0);
                   }

           } 
         else
           {
             PORTB&=~(1<<PINB0);
           }
    }


 2.yöntem butona basıldığı anlaşıldığında, bir de bir önceki periyotta butona basılmamış olması kontrol edilir. Eğer bir önceki periyotta da butona basıldı uyarısı varsa bu ark olarak algılanır ve işlenmez.

int butonbasildi=0;
while (1)
    {
         if(!(PIND & 0X01))
            { 

                if(butonbasildi==0)
                    {
                     PORTB|=(1<<PINB0); 
                     butonbasildi= 1;                 
                    }
           } 
         else
           {
             PORTB&=~(1<<PINB0);  

             butonbasildi=0;
            }
    }



 3.yöntem butona basildiğinda veya basilmadiğinda her iki durum için de ayrı ayrı bir sayaç tutulur.. Bu sayaç değerleri belli bir sayıya ulaştığında butonun durumuna karar verilir. Örneğin sayacımızın sınırını 500 olarak ayarladığımızda, butona  basıldığı veya basılmadığı değeri 500 'e ulaşan sayaç ile belirlenir.  Sayaçlardan herhangi biri artarken diğer durum oluşursa  sayaç sıfırlanır. Böylece ark durumları sırasındaki hızlı geçişler algılanır ama en son butonun durumuna karar verlir. Kullanılabilecek en güvenli yöntemlerden biridir.

#define F_CPU 1000000ul
#include <util/delay.h>

#define buton PINB1
#define led   PINB0

int buton_basili=0;
int buton_basilidegil=0;
char basildi=0;

int main(void)
{
   DDRB&=~(1<<buton);   // BUTON PINB1
   PORTB|=(1<<buton);   // PINB1 PULL UP
  
   DDRB|=(1<<led);       // LED PINB0 
    while (1)
    {
        if(!(PINB & 0x02))                        // butona basıldığı anlaşılıyor.
         {
           
                 buton_basili++;                  // butona basildiğini gösteren sayaç
                 buton_basilidegil=0;          //
butona basilmadiğini gösteren sayaç  sıfırlanır               
     
          if(buton_basili>500)                  // buton_basili sayacı 500'e ulaşırsa butona basıldığı          
                                                              //anlaşılır
                 {
                     if (basildi==0)                 // bir önceki durumda basılı değil ise
                     {
                          PORTB^=(1<<led);  // Led toggle yapılıyor
                        _delay_ms(200);  
                          basildi=1;                  // bir sonraki periyot için basıldı uyarısı
                          buton_basili=0;        // 
buton_basili  sayaci sıfırlanmış
                    }
                 }
         }
           else                                           // butona basılı değil ise
            {
               
                buton_basili=0;                  // buton _basili sayacı sıfırlanır.
                buton_basilidegil++;         // buton basilidegil sayacı artıyor
                if (buton_basilidegil>500)   // buton_basilidegil sayacı 500 sınırını aşarsa
                {
                basildi=0;                            // bir sonraki periyot için basilmadı uyarısı
                buton_basilidegil=0;          //
buton basilidegil sayacı artıyor               
                }
              
           }
    }
}
 Bu konu ile ilgili çeşitli yazılımsal yöntemler geliştirilebilir.  Yöntem yapılan uygulamaya göre değişkenlik gösterebilir. 
  
Donanımsal çözüm,  çok hassas ve algılama hızının yüksek olduğu durumlarda  daha kullanışlıdır.  Ancak devrede daha fazla  elaman , daha fazla iletim yolu ve maliyet artışı demektir. Yazılımsal çözümler hassasiyet az olduğu ve algılama hızının önemsenmediği uygulamalarda daha kullanışlı olur.

Kaynak :  http://www.gooligum.com.au/tutorials.html

Ayrıca yardımlarından dolayı Düzce Üniversitesi Teknoloji Fakültesi Elektrik-Elektronik Mühendisliği bölümü 3. sınıf öğrencilerim  Mahmut ÖZBAY,  Engin MERT,  Yasin UYSAL  ve  Yasin BİLGİ'ye teşekkür ederim

Yorumlar

  1. kodları, yorum satırı ile acıklarsanız daha iyi olabilir. gene gayet faydalı bir içerik olmuş teşekkür ederim

    YanıtlaSil

Yorum Gönder