Tuşlu Rotary Encoder Modül Yapımı

Şu migrenimi bir kontrol altına alayım da sonra bu adımları uygulayacağım. Şuan odaklanamıyorum. Okuyorum, anlamıyorum.

Allah kolaylık, şifa versin...

Redmi Note 8 Pro cihazımdan Tapatalk kullanılarak gönderildi
 
void debounce() konusunda:
fonksiyon ve yapı hatalı çünkü
1- Bu fonksiyon PinSw'nin FALLING hâlinde yani *0'a çekildiğinde çağrılıyor. Dolayısıyla PinSw durumu zaten 0 demek ve siz if kontrolünde buton durumu 0 ise işlem yaptırmıyorsunuz. Yani fonksiyonu bypass ediyorsunuz. Bunun yerine fonksiyonu kullanmayacaksanız kesme hiç aktive etmeyin.
2- Kesmeler içerisinde değişken tanımlaması yapmamak gerekiyor,
3- boolean yerine bool değişkeninin daha kararlı çalıştığı Arduino referanslarında bahsediliyor.
4- Kesmeyi bu fonksiyondan çıkarken değil, bu fonksiyon yoluyla ürettiğiniz veriyi işledikten sonra yeniden aktive ediniz.

debounce:
void debounce() {
  bool butonDurum = digitalRead(PinSw);    // 2.Madde, Değişken kesme dışına alındı. 3.Madde "boolean" "bool" ile değiştirildi.
  detachInterrupt(PinSw);     // 1.Madde, Kesme Kapatıldı
  if (butonDurum == 0) {
  }
  delayMicroseconds(5000);
}
1.Madde, Kesme Kapatıldı
2.Madde, Değişken kesme dışına alındı.
3.Madde "boolean" "bool" ile değiştirildi.
4.Madde bu kod buton kullanıldığı yerde, işlem bittikten sonra eklenecek.
C++:
attachInterrupt(digitalPinToInterrupt(PinSw) , debounce , FALLING );

void encoder() konusunda:
1- delayMicroseconds() ile zaman kontrolü sağlıyorsunuz. Bu nedenle millis() fonksiyonu üzerinden ikinci bir kontrole gerek yok. Kesme içerisinde fazladan meşguliyet oluşturuyor.
2- Sayac değişkenini bu fonksiyon içerisinde işliyorsunuz, ancak bu değeri bu fonksiyondan çıktıktan sonra loop() fonksiyonu içerisinde sırası geldiğinde yazdırınız, çünkü kesmenin işleme hızı bu sebeple Serial.print kodunun çalışma hızına düşmekte bu nedenle sizin encoder hareketinizle oluşturulan senkronizasyon bozulmaktadır.
3- Kesmeyi bu fonksiyondan çıkarken değil, bu fonksiyon yoluyla ürettiğiniz veriyi işledikten sonra yeniden aktive ediniz.

encoder:
void encoder() {
  PinDT = digitalRead(PinDt);
  detachInterrupt(PinCLK);
    if (PinDT == HIGH) {
      sayac++;
      Serial.print(sayac);
      Serial.println(" : sag");
    } else {
      sayac--;
      Serial.print(sayac);
      Serial.println(" : sol");
    }
  delayMicroseconds(10000); //Burası Debounce süresi için 10 milisaniye gecikme vererek encoder pals okuma sıklığını saniyede en falza 100'e ayaralar.
  oncekiZaman = millis();
}
1.Madde millis() fonksiyonu kaldırıldı.
2.Maddeyi tam anlayamadım. sayac değişkeni burada tanımlanıyor, daha sonra canım nerede isterse orada çağırıyorum zaten. Burada demek istediğiniz "fonksiyon içinde serial.print kullanma" mı? Eğer öyleyse işim bittiğinde bütün serial.print kodlarını temizleyeceğim zaten. Şimdilik bilgi almak amaçlı tutuyorum.

3.Madde debounce daki ile aynı mantık. sayac değişkenini kullan, işin bitince bu kod ile kesmeyi aktif et.
C++:
attachInterrupt(digitalPinToInterrupt(PinCLK), encoder, FALLING);

void loop() konusunda:
1- İlk satırda rigth_btn ve left_btn pinlerinden okuduğunuz değerleri birer değişkene atayınız. digitalRead() fonksiyonu pinin bu fonksiyon ile kontrol edildiği andaki değerini size verir. Her okuduğunuzda yeni bir değer döndürebilir. Özellikle birbiriyle karşılaştıracağınız pinleri aynı anda kaydediniz ki if-else bloğu içerisinde mantık hatasına yol açacak durumlara mahal kalmasın. Sonra bu değişken üzerinden if-else blokları içerisinde bu değişkenler üzerinden kontrol gerçekleştiriniz. Böylece bu butonların ilk okunduğu andaki değerlerine göre doğru ve öngörülebilir kesin işlemler yapabilirsiniz, diğer türlü her okumada yeni durumları üzerinden kararsız okumalar meydana gelecektir. Yukarılarda @semih_s pin durumlarını ilk satırdaki gibi aynı anda bir değişkene aktarmıştı, bu if-else içerisinde birbiriyle karşılaştırılacak pinleri bir değişkene kaydetmek için güzel bir yöntem.
2- Bir de right_btn basılı ise sayac değişkenini sıfırlıyorsunuz, ancak left_btn basılmış ise bunu yapmıyorsunuz neden?
Bu durumda bu kısımdaki kodlar şöyle olacaktır:

C++:
byte butonlar = digitalRead(right_btn) + digitalRead(left_btn) << 1;
  if (butonlar == 0b01) {//right_btn basılı durumu
    sayac = 0;
    kursor_konum++;
    if (kursor_konum > 3) {kursor_konum = 0;}
  } else if (butonlar == 0b10) { //left_btn basılı durumu
    kursor_konum--;
    if (kursor_konum < 1) {kursor_konum = 3;}
  }

1.Maddeyi gayet iyi anladım. Sizin tanımladığınız şekilde kullanacağım.
2.Madde: Projede Maksimum akım değeimiz 5A olacak. Kürsor basamağımız 4 konumdan oluştuğu için ilk basamağı atlamak durumundaydım. Yani 00.00 da ilk basamağı atlayınca 05.00 A olacak şekilde yazdırmam gerekiyordu. O yüzeden sayacı ilk basamakta sıfırlayıp bu değeri, ilk basamağa yazdırıp direk ikinci basamağa geçirmek için o olay.
En son hatırladığım böyleydi. Ama başka bir şey içinde yapmış olabilirim. Şuan emin olamadım.

Bu durumda kodların en son düzenlenmiş hali şöyledir;

C++:
#include <Arduino.h>
#include <U8g2lib.h>
#include "Wire.h"
#include "I2CKeyPad.h"
#include "hardware/watchdog.h"

/// 128x64 Ekran Tanımlaması ///////////////
#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif
//U8G2_ST7920_128X64_F_SW_SPI u8g2(U8G2_R0, /* clock=*/ 18 /* A4 */ , /* data=*/ 17 /* A3 */, /* CS=*/ 16 /* A2 */, /* reset=*/ U8X8_PIN_NONE);
//U8G2_ST7920_128X64_F_SW_SPI u8g2(U8G2_R0, /* clock=*/ 0, /* data=*/ 1, /* CS=*/ 10, /* reset=*/ 21);
U8G2_ST7920_128X64_F_SW_SPI u8g2(U8G2_R0, /*PICO SPI CLOCK PİN clock=*/ 10 /* A4 */ , /*PICO SPI TX data=*/ 11 /* A3 */, /*PICO CHIP SELECT CS=*/ 13 /* A2 */, /* reset=*/ U8X8_PIN_NONE);

//Timur abinin kodları
#define MAX_VOLTAJ 30.0
#define MIN_VOLTAJ 0.0

unsigned int kursor_konum; // 0 .. 3 arasinda degisiyor. 0 en soldaki hane
float katsayilar[] = {10, 1, 0.1, 0.01};
float voltaj = 0;
volatile int sayac = 0;

// Encoder Pinleri /////////////////////
#define PinCLK 7
#define PinDt 8
#define PinSw 6

/// 4x Yön Tuşları ////////////////////////
#define up_btn 2 //19
#define right_btn 3 //18
#define down_btn 4 //17
#define left_btn 5 //16

volatile bool PinDT = LOW;
long oncekiZaman;
int ival;
static char buffer[50];

void setup(void) {
  Serial.begin(115200);
  Wire.setSDA(0);
  Wire.setSCL(1);
  Wire.begin();
  u8g2.begin();
  pinMode(PinCLK, INPUT);
  pinMode(PinDt, INPUT);
  pinMode(PinSw, INPUT_PULLUP);
  pinMode(up_btn, INPUT);
  pinMode(right_btn, INPUT);
  pinMode(down_btn, INPUT);
  pinMode(left_btn, INPUT);

  attachInterrupt(digitalPinToInterrupt(PinCLK) , encoder , FALLING );//BAĞLI PIN LOW OLURSA KES
  attachInterrupt(digitalPinToInterrupt(PinSw) , debounce , FALLING );//BAĞLI PIN LOW OLURSA KES
}

//// ÇERÇEVE OLUŞTURMA FONKSİYONU/////////////
void drawF(int x, int y, int width, int height, int x2, int y2, int width2, int height2) {
  u8g2.drawFrame(x, y, width, height);
  u8g2.drawFrame(x2, y2, width2, height2);
}

/// Rotary Encoder Buton Fonksiyonu ///////
void debounce() {
  bool butonDurum = digitalRead(PinSw);
  detachInterrupt(PinSw);
  detachInterrupt(PinCLK);
  if (butonDurum == 0) {
  }
  delayMicroseconds(5000);
}

void encoder() {
  PinDT = digitalRead(PinDt);
  detachInterrupt(PinCLK);
  detachInterrupt(PinSw);
  if (PinDT == HIGH) {
    sayac++;
    Serial.print(sayac);
    Serial.println(" : sag");
  } else {
    sayac--;
    Serial.print(sayac);
    Serial.println(" : sol");
  }
  delayMicroseconds(10000); //Burası Debounce süresi için 10 milisaniye gecikme vererek encoder pals okuma sıklığını saniyede en falza 100'e ayaralar.
  oncekiZaman = millis();
}

void loop(void) {

  u8g2.clearBuffer();
  byte butonlar = digitalRead(right_btn) + digitalRead(left_btn) << 1;
  if (butonlar == 0b01) {//right_btn basılı durumu
    sayac = 0;
    kursor_konum++;
    if (kursor_konum > 3) {
      kursor_konum = 0;
    }
  } else if (butonlar == 0b10) { //left_btn basılı durumu
    kursor_konum--;
    if (kursor_konum < 1) {
      kursor_konum = 3;
    }
  }

  if (sayac) {
    float yeni_voltaj = voltaj + sayac * katsayilar[kursor_konum];

    if ((yeni_voltaj <= MAX_VOLTAJ) && (yeni_voltaj >= MIN_VOLTAJ))
    {
      voltaj = yeni_voltaj;
      //ekrani_tazele();
    }
    sayac = 0;
    attachInterrupt(digitalPinToInterrupt(PinCLK), encoder, FALLING);
    attachInterrupt(digitalPinToInterrupt(PinSw) , debounce , FALLING );
  }
 
Bu durumda bu kısımdaki kodlar şöyle olacaktır:
C++:
byte butonlar = digitalRead(right_btn) + digitalRead(left_btn) << 1;
if (butonlar == 0b01) {//right_btn basılı durumu
sayac = 0;
kursor_konum++;
if (kursor_konum > 3) {kursor_konum = 0;}
} else if (butonlar == 0b10) { //left_btn basılı durumu
kursor_konum--;
if (kursor_konum < 1) {kursor_konum = 3;}
}
Abi bu buton kodlarında hata var. Sağlıklı işlem yaptıramıyorum bu bloğu kullanınca.
 
@taydin abi bak yine aynısı oldu. Eski kolarla da, senin verdiğin son kodlarla da hesaplamada bir sıkıntı oluyor ama nedenini anlayamıyorum.
Eksiltme - artırma sırasında mı bir çarpım hatası oluşuyor acaba? :dusun1:

 
Buradaki senaryo nedir? Ne yapıyorsun da böyle oluyor?
 
Buradaki senaryo nedir? Ne yapıyorsun da böyle oluyor?
Ne yaptığımı bilsem cevabıda bulacağım.
Misal amperi, 4.5 ampere ayarlayacağım. Bunun için 00.00'a dayanarak 1.basamağı kürsör atlatarak boş geçiyorum, 2.basamağı 4 yapıp yine kürsör ile 3.basamağa atlıyorum. Bu basamağıda ilk artırmada 1 yapıyorum, ikinci artırmada 2 olması gerekirken matematiksel bir hata oluyor ve 0.01 azaltarak 4.basamaktan 1 eksiltiyor. Bu durumda 04.20 olması gereken rakam 04.19 oluyor.

Kullandığım son işlem bloğu;
C++:
 if (sayac) {
    float yeni_voltaj = voltaj + sayac * katsayilar[kursor_konum];

    if ((yeni_voltaj <= MAX_AMPER) && (yeni_voltaj >= MIN_VOLTAJ))
    {
      voltaj = yeni_voltaj;
      //ekrani_tazele();
    }
    sayac = 0;
    attachInterrupt(digitalPinToInterrupt(PinCLK), encoder, FALLING);
    attachInterrupt(digitalPinToInterrupt(PinSw) , debounce , FALLING );
  }
 
Şu enkoder bounce olayını bir görelim bakalım. Bir konu açayım ben en iyisi.
 
Şu enkoder bounce olayını bir görelim bakalım. Bir konu açayım ben en iyisi.
Abi senin encoder kodlarını gömdüm benim sisteme.
Aşağıdaki gibi detachInterrupt(encA); ve detachInterrupt(encA); yi aktif edince encoder çalışmıyor.
Pasif yaptığımda gayet sağlıklı ama bu seferde kesme devreye giriyor mu bilemiyorum.
C++:
void encoder(void)
{
  detachInterrupt(encA);
  detachInterrupt(encB);
  enc = digitalRead(encB) + (digitalRead(encA) << 1);
...........................

İşin acı tarafı sayacı artırırken hala hata var. Enkoder pals atlamamasına rağmen hata aynen devam ediyor.

EK: Sorunu biraz daralttım. Şöyleki;
Matematiksel işlemin sonucunu Serial Monitöre yazdırdım.
Serial Mönitörde "voltaj" değişkeninin değeri tam 04.20 olarak doğru görünüyor.
Ama GLCD ye giderken yolda ne oluyorsa 04.19'a düşüyor.
Yani serial monitörde gördüğüm 04.20 iken, GLCD de gördüğüm 04.19

Buradaki en büyük şüpheli aşağıdaki fonksiyon olmalı. Çünkü "voltaj" değişkeni GLCD ye giderken başka hiç bir yerde matematiksel işleme uğramıyor. (GLCD Kütüphanesinden kaynaklı bir durum var mıdır bilemiyorum ama)

C++:
ival = voltaj * 100;
  sprintf(buffer, "%02d.%02d", ival / 100, ival % 100);

EK2: Sorunu Kesinleştirdim. Kesinlikle yukarıdaki sprintf fonksiyonundan kaynaklanıyor.
O fonksiyonu kaldırıp "voltaj" değişkenini u8g2.print(voltaj); olarak ekrana yazdırınca tam olarak 4.20 sayısını görüyorum.
Bu şekilde sayının en başındaki "0" olmadığı için sprintf fonksiyonunun doğru çalışan bir biçimine muhtacım şuan.

sorun.png


 
Son düzenleme:
Evet aşağıdaki satırda yuvarlama hatası oluyor
C++:
ival = voltaj * 100;

Onu şöyle değiştirmen lazım:
C++:
ival = round(voltaj * 100);
 

Forum istatistikleri

Konular
5,848
Mesajlar
99,747
Üyeler
2,482
Son üye
ilker29

Son kaynaklar

Son profil mesajları

gruptaki arkadaşlara selamlar. sıteyi bu gün fark ettim. Asansör için 2x7 segment LCD gösterge üretmek istiyorum. acaba bu sayfadaki arkadaşlardan destek alabilirmiyim. LCD nin mantık açılımı ektedir.
deneyci wrote on TA3UIS's profile.
Selam.
Amatör telsiz lisansı nasıl alınıyor?
Lisansı olmayanı forumlarına almıyorlar. :)
Bilgi alamıyoruz.
cemalettin keçeci wrote on HaydarBaris's profile.
barış kardeşim bende bu sene akıllı denizaltı projesine girdim ve sensörleri arastırıyorum tam olarak hangi sensör ve markaları kullandınız yardımcı olabilir misin?
m.white wrote on Altair's profile.
İyi akşamlar.Arabanız ne marka ve sorunu nedir.Ben araba tamircisi değilim ama tamirden anlarım.
* En mühim ve feyizli vazifelerimiz millî eğitim işleridir. Millî eğitim işlerinde mutlaka muzaffer olmak lâzımdır. Bir milletin hakikî kurtuluşu ancak bu suretle olur. (1922)
Back
Top