Arduino ile Millis kullanımı ve Timer Uygulamaları

Merhabalar…

Bu konuda türkçe kaynağı az bulunan Arduino’ da timer ve millis ile yapılmış bir kaç uygulamaya yer vereceğiz. Öncelikle Arduino’ da Timer kullanımı ile ilgili detaylı yazıya buradan ulaşabilirsiniz.

– > Timer diğer konuda bahsetmiştik. Peki ya millis nedir, ne iş yapar? Bu soruların cevabına gelelim.

Millis bir fonksiyondur. Kullandığınız zaman unsigned long veri tipinde bir sayı döndürür. Arduino’ nun enerjisi kesilmeden bu fonksiyonu art arda çağırdığnız zaman döndürdüğü değerin sürekli artttığını gözlemleyebilirsiniz. Bunun sebebi, bu fonksiyonun her bir milisaniyede artan bir sayaç olarak çalışıyor olmasıdır. Ancak arduino’ nun enerjisini kesildiğinde bu sayaç sıfırlanacak ve enerji aldığında tekrar sıfırdan başlayacaktır. Bunun dışında unsigned long int’ in max değeri olan 4294967295 değeri geçildiğinde de sayaç sıfırlancak ve sıfırdan yeniden saymaya başlyacaktır.

-> Peki ya nasıl kullanabiliriz? Bir kütüphane çağırmamız yada ayar yapmamız gerekir mi?

Hayır. Siz isteseniz de istemesinizde bu sayaç arduino’ ya enerji verildiği anda saymaya başlayacaktır. İhtiyacınız olan yerde “millis()” fonksiyonunu çağırmak yeterlidir. Size sayaç değerini döndürecektir.

-> Kullanırken nelere dikkat etmeliyiz?

Değişkenlere bu değeri atama yaparken değişken tipinin sorunların en aza indirilmesi için unsigned long int tipinde olmalıdır. Elbet bu ihtiyaca göre değiecektir. Örneğin siz saniye yada dakikaya ihtiyacınız varsa ve bölme işleminden sonra atama yapacaksınız char değişkenler bile kullanabilirsiniz. Ama normal kullanımlarda unsigned long int tipinde kullanırsanız arduino 1 aya kadar sorunsuzca sayacaktır. Ondan sonrası için kayıtlı değerlerinizi sıfırlamalısınız yada arduino’ yu self reset atacak şekilde ayarlamalısınız.

Şimdi gelelim uygulamalarımıza.

İlk uygulamamızda genelde en çok örnek bulunan “delay” kullanmadan bir kaç led belli süre aralıkları ile yakıp söndürmek. Tanımlamalarımızı yaparak başlayalım.

//pin tanımları
int ledPin1 = 2, ledPin2 = 3, ledPin3 = 4;

//son millis değerini tutacağımız değişken
unsigned long lastTime1 = 0, lastTime2 = 0, lastTime3 = 0;

Bizim yapacağımız işi bir tane lastTime değişkeni ile de yapabiliriz. Ancak ilk örnek olduğu için ayrı ayrı göstermek istedim. Bu kısımda üstte de bahsettiğimiz gibi millis değerini tutacağımız değişkenleri “unsigned long” tipinde tanımladık.

  //son millis değeri ile arada 1000 fark varsa 
  if (millis() - lastTime1 > 1000)
  {
    digitalWrite(ledPin1, !digitalRead(ledPin1)); //led durumunu değiştir
    lastTime1 = millis();                         //son değere o anki millis 
                                                  //değerini kaydet
  }

Bu kısımda da o anki millis değeri ile bizim son kaydettiğimiz millis değeri arasında 1000 ms geçmiş mi diye kontrol ettik. Eğer geçmişse if içerisindeki işlemleri gerçekleştirdik. Burada dikkat edilmesi gereken nokta işlemler bittiği zaman if içerisinden çıkarken millis in değerini tekrar kaydetmeyi unutmamak. Aksi taktirde bir sefer bekleme yapıp daha sonrasında bekleme meydana gelmeyecektir.

Peki bu bize ne kazandırdı. Herhangi bir bekleme işlemi olmadan yani ana döngüyü durdurmadan bir kaç işlemin belli sıklıklarla gerçeklemesini sağladık. Özellikle hız ölçümü gibi sayılan devrin hassas ve bekleme yapılmaması gereken durumlarda yada newPing gibi delay kullanımın da sorun çıkartan kütüphaneler ile çalışılması durumunda millis yapısı çok kullanışlıdır.

Bu çok basit bir örnek olduğu için diğer örneğe geçmek istiyorum. Üstteki anlatımda kullandığım yazılımın tamamına buradan ulaşabilirsiniz.


 

Sıradaki örnekde analog bir değeri 100 ms ile örnekleyip ortalamasını alalım ve bu ortalamasını aldığımız değeri seri port yardımı ile yine belli aralıkla gönderelim. Eğer ki ortalama belli bir sınırı geçmişsse de bir ledi bir saniye aralıklar ile yakıp söndürelim.

Öncelikle tanımlamarımızı yapalım..

//led tanımlamarı ve millis değerini tutacak değişken tanımları
int ledPin = 13, potPin = A0;   
unsigned long lastTime = 0, lastTimeSeriPort = 0, ledLastTime = 0;
int sayac = 0;                //3 örneği sayacak sayaç
double ortalama, deger = 0;   //ortalama için değişkenler

void setup() {
  // put your setup code here, to run once:

  //ledi çıkış potu giriş olarak atadık ve haberleşmeyi
  //başlattık
  pinMode(ledPin, OUTPUT);
  pinMode(potPin, INPUT);
  Serial.begin(9600);
}

Şimdi sıra örneklerı alıp ortalamayı alalım.

//3 örneği 100 ms arlıklarla alalım
  if (millis() - lastTime > 100)
  {
    deger += analogRead(potPin);
    sayac++;
    if (sayac == 3)   //sayaç değeri 3 olmuşsa ortalamayı al
    {
      ortalama = (double)deger / 3.0;
      sayac = 0;    //değerleri sıfırla
      deger = 0;
    }
    lastTime = millis();    
  }

Gördüğünüz gibi herhangi bir geciktirme işlemi kullanmadık. Bu da bize veriyi gönderirken yada ledi yakıp söndürürken bize ekstra gecikmeye sebep olmamasını ve tüm işlemlerin bir nevi asenkronmuş gibi gerçeklenmesini sağladık. Son olarak da veriyi gönderip ledi yakıp söndürelim.

//500 ms de bir ortalama değerini gönder
  if (millis() - lastTimeSeriPort > 500)
  {
    Serial.println(ortalama);
    lastTimeSeriPort = millis();
  }

  //eğer ortalama 512 den büyükse ledi yakıp söndür
  if (ortalama > 512)
  {
    if (millis() - ledLastTime > 1000)
    {
      digitalWrite(ledPin, !digitalRead(ledPin));
      ledLastTime = millis();
    }
  }

Bu örneğin tamamına buradan ulaşabilirsiniz.


 

Şimdi timer kullanarak karaşimşek devresini delay kullanmadan yapalım. Led ‘ler 100ms aralıkla değişecek şekilde ayar yapalım. Bekleme süresini değişmek için interrupt ların durdurulup OCRxA registerinde değerin o şekilde değiştirilmesi gerektiğini unutmayınız.

// timer 100 ms olacak şekilde ayarlanır
  cli();
  TCCR1A = 0;// TCCR1A register 0'lanıyor
  TCCR1B = 0;// TCCR1B register 0'lanıyor
  TCNT1  = 0;//sayac değeri  0'la
  // OCRxA karşılaştırma registeri 1Hz değer için ayarlanıyor
  //16 MHz osilatör,10Hz timer1 ın çalışma frekansı,1024 prescalar
  OCR1A = 1562;// = (16*10^6) / (1*1024) - 1 (değer 65536 dan küçük)
  //   CTC mod açılıyor.
  TCCR1B |= (1 << WGM12);
  //   CS10 ve CS12 bitleri 1024 prescaler için ayarlanıyor
  TCCR1B |= (1 << CS12) | (1 << CS10);
  // timer karşılaştırma interruptı aktifleştiriliyor
  TIMSK1 |= (1 << OCIE1A);
  sei();

Timer ın ayarını yaptık. Şimdi sıra timer taşma interrupt ı geldiğinde yapılacakları ayarlayalım. Bu kısımda hem led lerin sağa hem de sola gidebilmesi için bool bir değişken ile sayacın arta ve azalmasını kontrol ettik.

ISR(TIMER1_COMPA_vect) {  //timer1 interrupt ı 10Hz de tetikleniyor.
  if (arttir)
  {
    sayac++;
  } else {
    sayac--;
  }

  if (sayac == 9) arttir = false;
  if (sayac == 0) arttir = true;
}

Ana döngümüzde de yanması gereken ledi yakıp diğerleri sönük bıraktık. Led lerde yanıp sönmeyi engellemek için son led değerinin değişimini kontrol ettik. Eğer değişmişse tüm led leri söndürüp diğer ledi o şekilde yaktık. Böylece led değişme işlemi tamamen 100 ms de bir gerçekleşecek şekilde yapmış olduk.

void loop() {
  // put your main code here, to run repeatedly:
  if (sayac != sonSayac)
  {
    //tüm ledleri söndür
    for (int i = 0; i < 9; i++)
    {
      digitalWrite(ledPin[i], LOW);
    }

    //sırası gelen ledi yak
    digitalWrite(ledPin[sayac], HIGH);

    //son sayac değerini bir sonraki değişime kadar hafızada tut
    sonSayac = sayac;
  }
}

Yazılımın tamamına buradan ulaşabilirsiniz. Arduino gibi tek işlemcili donanımlarda asenkron yapılar son derece kullanışlıdır. Bu yüzden üzerinde durulmasında fayda olduğu düşüncesindeyim. Daha fazla örnek vermek isterdim aklıma şu anlık bunlar geldi ki bu tür sorular sorulmuştu genelde. Umarım biraz da olsun faydası dokunmuştur.

İyi çalışmalar..

 

 

33 Comments

Add a Comment

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir