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

@hafy 'nin kodları ile devam etmeye karar verdim.
Çünkü şimdiye kadar benim kodların arasına en iyi gömebildiğim kodlar onlar.
Burada şöyle bir durum olacak;
Serial ekrana baktığım zaman hafy'nin kodlarında pals kaçırma görüyorum.
Ama serial ekrana değilde GLCD ye odaklanırsam bu pals kaçırmaları görmeden tolere edebiliyorum.
Misal encoderi çevirdim 3 olmadımı? bir tık daha çeviriyorum 3 oluyor.
GLCD de kaçan palsi görmediğim için beni rahatsız etmiyor.

Benim 3D yazıcıda seçeceğim baskıya gelmek için resmen encoder ile savaşıyorum. Ona bile alıştım artık. Bu vız gelir. :D

Kod:
void encoder() {
  PinDT = digitalRead(PinDt);
  detachInterrupt(PinCLK);
  if (millis() - oncekiZaman > 1) {
    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.
  attachInterrupt(digitalPinToInterrupt(PinCLK), encoder, FALLING);
  oncekiZaman = millis();
}
eğer dediğimi denersen sorunu anlamış oluruz abi puls görmüyorda mı böyle davranıyor görüyorda değişkene yazamıyor mu onu tespit etmiş oluruz
 
Sayın @Gokrtl'ın yoğun emeklerine dair bu başlıkta parça parça ürettiğim bazen neticesiz kalmış denemeler ve bunlara dair düşüncelerimi biraz daha elle tutulur hale getirmek üzere burada toparlamak istedim. Sayın @Gokrtl'a ve katkısı bulunan diğer forumdaşlara saygı ve teşekkürlerimle...

Amaç
2si tam ve 2si ondalık olmak üzere 4 basamaklı bir sayıyı Rotary Encoder ile üreterek Serial monitörde yazdırmak.

Araçlar
1- Arduino UNO / Raspberry Pi Pico
2- Arduino IDE Serial Monitör
3- Rotary Encoder (with Push Button)
4- Arduino IDE Kodlama Ortamı

Araçların İncelenmesi
Arduino UNO / Raspberry Pi Pico
C dili ile kodlanabilen ve Arduino IDE üzerinden programlanabilen, doğrudan ve kod vasıtasıyla kontrol edilebilen Giriş-Çıkış pinleri ve pek çok işlemsel özellik barındıran geliştirme kartlarıdır. Zaten tanınıyor ve yaygın biçimde kullanılıyor olduğu için burada detaylandırmaya ihtiyaç görülmemiştir.

Arduino IDE Serial Monitör
USB üzerinden bağlı bulunan geliştirme kartı ile yapılan serial haberleşmesinin kullanıcı arayüzü ile görüntülenmesini sağlayan araçtır.

Rotary Encoder (with Push Button)
Rotary Encoder, üzerinde bulunan milin kendi ekseni etrafında dönüşü ile bu mile temaslı (A ve B isimli) iki butonun aralarında 90° faz farkı bulunacak biçimde sıralı olarak tetiklenmesini sağlayan bir elektro-mekanik araçtır. Milin ekseni etrafında dönüşünde tick adını verdiğimiz hareket meydana gelir. Ayrıca milin eksenine dik olarak yerleştirilmiş diğer bir push button daha vardır.

a- Butonlar: A ve B butonu birer uçları birbirine ve Common isimli bir pine bağlıdır. A butonunun diğer ucu A Pini ve B butonunun diğer ucu B Pini olarak adlandırılır. Bu butonlar normal hallerinde açık-açık pozisyondadır.
Common pini A ve B pinlerinin geliştirme kartı üzerinde bağlanacağı pinlerin Pull-up yahut Pull-down yapılmasına bağlı olarak ters polarlanır. Yani A-B pinleri +5/3.3volta (HIGH/1) çekilirse Common 0volta (LOW/0); A-B pinleri 0volta (LOW/0) çekilirse Common +5/3.3volta (HIGH/1) çekilir. Böylece pinlerin tetiklenmeleri esnasında geliştirme kartında bağlı bulundukları pinlerdeki lojik durum terslendiğinden Encoder’ın hareketi geliştirme kartı tarafından algılanır.

b- Tick: Rotary Encoderler, üzerlerindeki milin 360°lik bir turu farklı sayıdaki eşit açısal aralıklarda tamamlanacak şekilde üretilebilmektedir. Bu projede kullanılacak Encoder ise 360°lik bir turu 20 adımda tamamlayabilmektedir. Bu adımların her birisi “tick” olarak isimlendirilir. Her tick, ilk hallerinde açık-açık pozisyonda bulunan A ve B butonlarında sırasıyla kapalı-açık, kapalı-kapalı, açık-kapalı ve açık-açık durumlarını üreterek tamamlanır. Görüldüğü üzere bir tick 4 farklı durum üretmektedir. Demek ki Encoder’ımız 360°lik bir turda toplam 80 farklı pin durumu üretebilmektedir. Bu da bir tick 18° ve her bir buton hareketi 4,5° açı bilgisi üretildiğini gösterir.

c- Push Button: Encoder üzerinde bulunan milin dikey eksende bastırılmasıyla temasa geçen diğer bir button daha vardır. Bu buton ise bir pini geliştirme kartına bağlanarak Pull-up yahut Pull-down yapılır. Diğer pini ise bu duruma ters şekilde polarlanarak butonun tetiklenmesine bağlı olarak geliştirme kartını pini lojik olarak tersleneceğinden hareket algılanır.

d- Rotary Encoder Modülü: Bu modülün hazırlanmasında bilhassa A ve B pinlerinin bir kondansatör ile debounce edilmesi hızlı dönüşlerde sorun meydana getirmekte. Çünkü kondansatörler sıklaşan palslerin arasında LOW-HIGH geçişini zamana yayarak olumsuz bir etki gösteriyor. Bunun kondansatörlerin t süresi üzerinden hesaplanmasıyla nasıl etkileri olduğu görülebilir. Nitekim Sayın @Gokrtl tarafından paylaşılan osiloskop incelemesine dair videonun 15. saniyesinden itibaren bu etkinin palsler arasındaki 90°lik faz farkını etkilediği görülecektir. Ancak Buton pininin böyle bir kondansatörle debounce edilmesine ihtiyaç görülebilir. Diğer taraftan Modülde kullanılacak dirençler hakkında da bazı durtumları değerlendirmek icap ediyor. Modülde 10Kohm Pull-up direnci kullanıldığında Pico pinleri 50Kohm ile internal Pullup yapılırsa eşdeğer direncin 1/50+1/10 = 6/50 = 8,33 Kohm'a karşılık geldiğini görürüz. Arduino UNO'nun internal pullup dirençlerinin 20Kohm ile 50Kohm arasında olduğu belirtiliyor. Bu durumda 1/20+1/10 = 3/20 = 6,66Kohm ile 8,33 Kohm eşdeğer direnç gibi davranacaktır. Bu kadar düşük pullup direncinin modülün 1 tick hareketi esnasında meydana gelen gürültüye sebep olması mümkün gibi görünüyor.

Arduino UNO IDE Kodlama Ortamı
Bilindiği gibi bu ortam C ve C++ dilinde ve kendine özgü metotlarla üretilen kodun, bir derleyici vasıtasıyla makine diline çevirerek geliştirme kartına yüklenmesini sağlar.

Sistematik Çözümleme
Bilindiği gibi elektronik devreler harici bir müdahaleye ihtiyaç duymayacak biçimde tasarlandıklarında, içerdikleri komponentlerin yapılarına bağlı bir senkronizasyon ile çalışırlar. Burada kullandığımız Arduino UNO / Raspberry Pi Pico gibi geliştirme kartları ise temel komponent olan işlemci bakımından işlemlerini üretmek ve yürütebilmek adına diğer unsurlarla senkronizasyonu sağlamak üzere kendi saatlerini kullanırlar.
Ancak dışarıdan işlemcilerin bu zamanlamasına senkronize yanıt veremeyecek müdahaleler öngörülüyor ise işlemcinin kendi zamanlamasını bu müdahaleye uydurması sağlanmaktadır. Bu müdahaleler neler olabilir? Bir kullanıcın sisteme işleme ihtiyacı duyacağı veriler, işlemci tarafından kontrol edilen çeşitli elektronik ve mekanik araçların durumlarına dair veriler olabilir. Ancak SPI, I2C, UART, Serial gibi haberleşme biçimleri işlemcilerin iletişime geçtikleri diğer cihazlarla aralarında senkronizasyon temin ederek gerçekleştiğinden burada çözümlenmesi gereken konular değildir. Bu bahiste ana mesele şöylece şekillenmektedir:
1- Rotary Encoder ile kullanıcı tarafından üretilen hareketin işlemci tarafından doğru algılanması,
2- Bu hareketin sonucunun görüntülenmesi yoluyla kullanıcı tarafından doğruluğunun teyit edilebilmesi,
3- Hareketle üretilen veriler üzerinden amaçlanan işlemlerin gerçekleştirilmesi.
Bu üç meselenin temelinde yatan nokta şudur. Biz insanlar, saniyenin 1/6’sından daha kısa sürede meydana gelen olayları algılayamıyor ve tepki üretemiyoruz. Her ne kadar bazı ritmik durumlarda bu türden kısa süreleri yakalayabilsek de saniyenin 1/100’ünden 1/1000000’una kadar kısa zamanlarda işlem gerçekleştiren elektronik cihazlarla doğrudan iletişim kurabilmemiz imkânsız. Dolayısıyla bu cihazlar insanlar tarafından kullanıma elverişli arayüzlerle donatılır ve cihazların, bu arayüzler üzerinden aldıkları tepkilere zamansal olarak hizalanması temin edilir. Burada söz, insan-işlemci senkronizasyon vasıtası olan Hardware Interrupt’a gelmektedir. Ki bu vasıta işlemciler tarafından pek çok çeşit cihazın kontrolünde de kullanılmaktadır.

Esasında Arduino gibi geliştirme ortamlarında bir Interrupt’a bağlı olmadan da pek çok buton ve encoder uygulaması tasarlanıp çalıştırılabilmektedir. Öyleyse neden Interrupt gibi karmaşık olaylar silsilesi bir konuya girmek durumunda kalıyoruz? Şöyle izah edelim:

1- Ana kod bloğunu oluşturan Main Loop içerisinde bir butonun durumunu kontrol etmek için bazı yöntemler bulunmakta: Maksat bunlardan birisi olan IF-ELSE yapısıyla Main Loop’un her döngüsünde bir defa kontrol ederek; diğeri olan WHILE yapısıyla da butonda bir hareket meydana geldiğini kaçırmamak üzere Main Loop döngüsünü bekleterek gerçekleşir.

Aşağıda iki yöntem üzerinden örnek verilecek olup önce ikisinde de kullanılması muhtemel değişken tanımlamalarını yapalım:
Kod:
int Button = 3;
int Led = 4;
void setup() {
pinMode(Button, INPUT_PULLUP);
pinMode(Led, OUTPUT);
digitalWrite(Led, LOW);
}


a- IF-ELSE ile buton kontrolü: Burada Main Loop’a ait diğer kod örneklerine değinilmeyecektir.
Kod:
void loop() {
if (Button == LOW) {
digitalWrite(Led, HIGH);
}
//Diğer kodlar
}


Burada örneği verildiği şekliyle butonun basılı olup-olmadığı, Main Loop’un her döngüsünde bir anlığına kontrol edilir. Main Loop’un her döngüsü ne kadar zamanda tamamlanırsa butondaki hareket de ancak o kadar sürede bir kontrol edilebilir. Yani main loop 1 saniye sürerse butona basıldığı saniyede 1 defa kontrol edilebilir. Bizim ise butona bastığımızda 1 saniye kaybetmek istemiyorsak main loop döngüsünün tam da bu kontrol koduna geldiği ânı yakalamamız gerekir.

b- WHILE ile gerçekleştirilen diğer çözüm, IF-ELSE bloğu ile yapılan kontrolünde temeli oluşturan “zamanını yakalama” problemini aşıyor gibi. Bu kod ise şöyledir:
Kod:
void loop() {
WHILE (Button == HIGH) {
delay(100);
}
pinMode(Led, OUTPUT);
//Diğer Kodlar
}

Burada görüleceği üzere butonun basıldığını anlamak üzere Main Loop döngüsü bekletilmektedir. Ancak buton basıldığında gerekli işlem yapıldıktan sonra Main Loop’un döngüsünü devam ettirmesine izin verilir.

Bu iki yöntemin de temelinde yatan problem bir TRIGGER problemidir. Yani bize butonun basıldığını takip ederek haber verecek bir tetikleyici eksikliğidir. Yazılım ve özellikle elektronik cihaz haberleşmelerine aşina olanlar TRIGGER kavramını da biliyorlardır. İşte Hardware Interrupt, bu türden bir TRIGGER görevi görmekte, programın ana döngüsündeki akışı herhangi bir aksamaya uğratmadan buton vesair olaylarda işlemci zamanlamasını bu olayların zamanına hizalamaktadır.

2- Interrupt karmaşasına girmek üzere ikinci nedenimiz ise, Main Loop gibi yoğun kodlar arasında yaşanacak aykırı durumlarda bizim buton kontrolümüze ne şekilde sıra geleceğinin çoğu kere öngörülebilir olmamasıdır. Oysa işleyişi doğru kavrandığında Hardware Interrupt, buton, Rotary Encoder vb. ihtiyaçların hemen hepsinde öngörülebilir ve sistem kaynaklarını doğru kullanabilen bir vasıtadır. Bu karmaşayı aşmanın yolu ise Interrupt işleyişi ile buna bağlı kullanılacak komponentlerin iyi kavranmasıdır.

Gelin Interrupt-Rotary Encoder entegrasyonunu bu bağlamda değerlendirelim.

Interrupt-Rotary Encoder Entegrasyonunu

Rotary Encoder Çalışma Mantığı ve Vazifesi

A- Dönüş Hareketinin (Tick) Kullanımı:

Daha yukarıda anlattığımız gibi Rotary Encoder bir tick esnasında A ve B pinlerinin durumu itibariyle şu çıktıları verir. A-B pinleri Pull-up yapıldığı ve Encoder Saat yönünde döndürüldüğünde:
Ân
A Pini (CLK)
B Pini (DATA)
BINARY
01111
10101
20000
31010

A-B pinleri Pull-up yapıldığı ve Encoder Saatin tersi yönde döndürüldüğünde:
Ân
A Pini (CLK)
B Pini (DATA)
BINARY
01111
11010
20000
30101

Görüleceği üzere bir tick sürecinde Encoder’dan 4 defa veri alınabilmekte ve buna binaen bir Tick’te 4 işlem yapma imkânı bulunmaktadır.

2si tam ve 2si ondalık olmak üzere 4 basamaklı bir sayının her basamağını ayrı ayrı ayarlamak söz konusu olduğuna göre:
1- Tamamlanması neredeyse 1 saniye dahi sürmeyen 1 tick ile basamak rakamlarında 4 artış gerçekleştirmek pek de kullanışlı değildir.

2- Saniyenin ¼’lük kısmında ürettiğimiz her bir rakamın ekranda yazdırılmasıyla kaybolması aynı kısa sürede gerçekleşeceğinden Encoder ile elde edilen etkinin gözlenmesi zorlaşacaktır.

3- Bir basamak değeri 3’e ayarlanacak olursa Encoder milinin, bir Tick’in ¾’lük kısmında sabitlenmesi gerekecektir. Ancak Encoder mili bir tick tamamlanacak şekilde tasarlanmış olduğundan bu da mekanik olarak imkân dışı bulunacaktır.

4- Diğer taraftan biz bu dört basamaklı sayıyı basamak basamak ayırmadan sürekli encoder çevirerek oluşturmak isteseydik, bir Tick'te 4 işlem yapma imkânı bize büyük katkı sunardı. 00.00-39.99 arasındaki 4000 farklı sayıyı bir turda 80 (1 tur 20 Tick, 1 Tick 4 defa işlem yapma) veri üreten encoder ile 50 turda oluşturabilirdik. Ancak yapmaya çalıştığımız şey her basamakta 0-9 arasında toplamda 10 farklı değer döndürmek olduğuna göre bu kadar baş döndürücü bir hıza zaten ihtiyaç da yok.


Görüldüğü üzere hem döndürme hareketinde kendi kontrolümüzü sağlamak hem de Rotary Encoder üzerinden çalıştıracağımız Interrupt kesmelerini sağlıklı kullanabilmek önemlidir. Bu nedenle Interrupt Mode (yani tetiklenme biçimi) ayarında, bir Tick’te 1 rakam ilerleme çözünürlüğü sağlayacak tercihte bulunmamız gerekiyor. CLK görevi üstelenen A pinimizi geliştirme kartımızın 0 nolu Interrupt pini olan 2 nolu dijital pine bağlayacağız. Ve bu pini de Pull-up yapacağız.

Yukarıdaki iki tabloda vurgulandığı üzere, CLK pini FALLING hâlinde iken DATA pini 1 ise Saat Yönünde; DATA pini 0 ise Saatin Tersi Yönde bir dönme hareketi tespit edilmektedir. Bu nedenle CLK pininin FALLLING hâlinde DATA pininin durumunun okunabilmesi gerektiğinden onu da geliştirme kartımızın 7 nolu dijital pinine bağlıyoruz.

Bu da A pininin 0’dan 1’e geçtiği 1. ânı bize gösteren FALLING hâlidir. A pini FALLING durumuna uğradığında çalışacak fonksiyonumuz ise Encoding() olacaktır.

B- Push Button Kullanımı

Rotary Encoder’ın dönüş hareketiyle basamaklara gelen sayıları değiştireceğiz. Ancak hangi basamakta işlem yaptığımızı ise Encoder’ın butonuna basarak seçeceğiz. Bu nedenle Push Button pinlerinden birisini geliştirmek kartımızın 1 nolu Interrupt pini olan 3 nolu dijital pine bağlayacağız. Bu pin Pull-up yapılacaktır. Diğeri ise GND’ye bağlanarak butona tıklanması durumunda bağlı bulunduğu Interrupt pinini terslemesi sağlanacaktır. Böylece buton hareketi algılanacaktır. Butona dair Interrupt ise Bt_Pushed fonksiyonunu çağıracaktır.

Buraya kadar elde ettiğimiz veriler üzerinden pin tanımlamaları ve Interrupt yapılandırmasına ait kodları şöylece düzenleyebiliriz:

C- Rotary Encoder Pinlerine Dair Kodlar

Encoder Pinlerine Dair Değişkenler
Kod:
int RE_A = 2;
int RE_Bt = 3;
int RE_B = 7;

Encoding() fonksiyonu içerisinde B pininin durumunu tutacak olan PinB değişkeni:
Kod:
volatile byte PinB = 0;

Dönüş yönü bilgisini taşıyacak olan değişken tanımlanıyor:
Kod:
String Yon = “BOS”;

Tick Gerçekleştiği Bilgisini taşıyacak değişken:
Kod:
volatile bool ticked = 0;

Butona Basıldığı Bilgisini taşıyacak değişken:
Kod:
volatile bool pushed = 0;

Buton ile işlenecek basamak verisine tutacak değişken tanımlanıyor:
Kod:
volatile int BasamakNo = 0;

Basamaklara ait rakam verisini taşıyacak değişkenler tanımlanıyor:
Kod:
int basamak0 = 0, basamak1 = 0, basamak2 = 0, basamak3 = 0;

Encoder ile üretilerek Serial monitöre yazdırılacak sayıyı tutacak değişken tanımlanıyor:
Kod:
float sayi = 00.00;

setup() fonksiyonu:
Kod:
void setup() {

Encoder Pinlerinin yapılandırılması:
Kod:
pinMode(RE_A, INPUT_PULLUP);
pinMode(RE_Bt, INPUT_PULLUP);
pinMode(RE_B, INPUT_PULLUP);

Interrupt Kesmelerinin yapılandırılması:
Kod:
attachInterrupt(digitalPinToInterrupt(RE_A), Encoding, FALLING);
attachInterrupt(digitalPinToInterrupt(RE_Bt), BT_Pushed, FALLING);

Serial Monitör kurulumu:
Kod:
Serial.begin(115200);
}

Encoding ve Bt_Pushed ISR Fonksiyonları

A- Encoding()
Encoder mili dönme hareketine başladığı anda CLK pini LOW’a çekilecek ve Encoding() isimli fonksiyon çağırılacak. Bu nedenle Encoding() fonksiyonu yazılıyor:
Kod:
void Encoding(){

RE_A şeklinde isimlendirilen CLK pininin FALLING hâlinde fonksiyon çağırıldığına göre RE_B ile isimlendirilen DATA (B) pininin durumunu kontrol ederek dönüş yönünü tespit edilecek bu nedenle önce bu pinin durumu okunarak PinB değişkenine atanıyor:
Kod:
PinB = digitalRead(RE_B);

Interrupt kesmesi meydana geldiğinde işlemler tamamlanana kadar başka kesme meydana gelmemesi amacıyla tüm kesmeler deaktive ediliyor. Kesmeler, Interruptlar ile amaçlanan işlemler gerçekleştirildiğinde yeniden aktive edilecek:
Kod:
IrqStop();

RE_A şeklinde isimlendirilen CLK pininin FALLING hâlinde fonksiyon çağırıldığına göre RE_B ile isimlendirilen DATA (B) pininin PinB değişkenine aktarılan durumu kontrol edilerek dönüş yönü tespit ediliyor:
Kod:
if (PinB == 1) {Yon = “SAG”;} {Yon = “SOL”;}

Tick gerçekleştiği bilgisi ilgili değişkene işleniyor ve Encoding() fonksiyonundan çıkılıyor:
Kod:
ticked = 1;
}


B- Bt_Pushed()
Encoder butonuna basılmasıyla RE_Bt pini LOW’a çekileceğinden Bt_Pushed() isimli fonksiyon çağırılacak. Bu nedenle Bt_Pushed() fonksiyonu yazılıyor.
Kod:
void Bt_Pushed(){

Interrupt kesmesi meydana geldiğinde işlemler tamamlanana kadar başka kesme meydana gelmemesi amacıyla tüm kesmeler deaktive ediliyor. Kesmeler, Interruptlar ile amaçlanan işlemler gerçekleştirildiğinde yeniden aktive edilecek:
Kod:
IrqStop();

Butona basıldığı bilgisi pushed değişkenine ve BasamakNo değişkenine işleniyor:
Kod:
pushed = 1;
BasamakNo ++;

BasamakNo değişkeni proje amacında bahsedilen 4 basamaklı sayının basamaklarını seçmek üzere kullanılacağından 0 ile 3 arasında 4 farklı değer alacak şekilde IF-ELSE bloğu ile düzenleniyor ve Bt_Pushed() fonksiyonundan çıkılıyor:
Kod:
if (BasamakNo >=3) { BasamakNo = 0}
}

Diğer Fonksiyonlar

Burada Interruptlarla ilgili zaman gecikmelerine ihtiyaç duyulması hâlinde kullanılacak bir Gecikme() fonksiyonu tanımlanıyor. Buna gerek duyulmasının sebebi Interruptlar içerisinde delay(), millis(), micros() gibi bilinen diğer zaman fonksiyonlarının çalışmaması. Bu fonksiyonda Interrupt esnasında çalışabilen tek zaman fonksiyonu olan delayMicroseconds() kullanılacaktır.

Interrupt kesmelerinin aktive ve deaktive edilmesi birkaç yerde tekrar edeceğinden kod karmaşasına yol açmamak üzere bunların birer fonksiyonla çağrılması amaçlandı.

A- Gecikme()

Fonksiyon içerisinde, çağrıldığı zaman ne kadar süre gecikme sağlanmasını hesaplamakta kullanılacak bir de değişken tanımlaması yapılıyor.
Kod:
void Gecikme(volatile int Sure){

Gecikmeyi sağlayacak olan for döngüsü kurularak, delayMicroseconds() fonksiyonu ile 1 milisaniye karşılığı 1000 mikrosaniye gecikme ayarlanıyor. Bu sayede fonksiyon çağrıldığında belirtilecek Sure değişkeni kadar milisaniye gecikme temin edilecektir. Sonra da fonksiyondan çıkılıyor:
Kod:
for (int a = 0; a <= Sure; a++) {delayMicroseconds(1000);}
}

B- IrqStart() ve IrqStop()

IrqStart() fonksiyonu tanımlanıyor ve Interrupt kesmelerini yapılandıran ve aktive eden kodlar eklenerek fonksiyondan çıkılıyor:
Kod:
void IrqStart(){
attachInterrupt(digitalPinToInterrupt(RE_A), Encoding, FALLING);
attachInterrupt(digitalPinToInterrupt(RE_Bt), BT_Pushed, FALLING);
}

IrqStop() fonksiyonu tanımlanıyor ve Interrupt kesmelerini deaktive eden kodlar eklenerek fonksiyondan çıkılıyor:
Kod:
void IrqStop(){
detachInterrupt(digitalPinToInterrupt(RE_A));
detachInterrupt(digitalPinToInterrupt(RE_Bt));
}

Button_A-B.jpg
 
Last edited by a moderator:
Devamı...
Main Fonksiyonu

Bu kısımda artık buton ve tick yoluyla elde edilen veriler Serial monitöre yazdırılacak.

loop() fonksiyonu tanımlanıyor
Kod:
void loop(){
Serial monitöre yazdırılacak verilerin işlenmiş olduğu herhangi bir kesmenin gerçekleşmesi üzerinden kontrol ediliyor
Kod:
if (ticked + pushed >= 0) {


Herhangi bir kesmenin gerçekleşmiş olması durumuna binaen gerçekleşen kesme tespiti ile uygun yazdırma işlemleri yapılıyor:
Kod:
if (pushed == 1) {
Serial.print(BasamakNo);
Serial.println(“. Basamak”);

Gerekli işlemleri gerçekleştirildiği için buton basıldığı durumunu taşıyan değişken sıfırlanıyor:
Kod:
pushed = 0;

Hangi kesme gerçekleştiğinin kontrolüne dair IF-ELSE bloğunun ikinci kısmına geçiliyor
Kod:
} else if (ticked == 1) {

Tick yönü ve BasamakNo değerine göre ilgili basamak değeri işlenecek
Kod:
if (Yon == “SAG”){
Switch (BasamakNo)
Case 0:
basamak0++;
break;
Case 1:
basamak1++;
break;
Case 2:
basamak2++;
break;
Case 3:
basamak3++;
if (basamak3 >= 3) {basamak3 = 0;}
break;
} else if (Yon == “SOL”){
Switch (BasamakNo)
Case 0:
basamak0--;
break;
Case 1:
basamak1--;
break;
Case 2:
basamak2--;
break;
Case 3:
basamak3--;
if (basamak3 <= 0) {basamak3 = 3;}
break;
}

Gerekli işlemleri yapıldığı için tick gerçekleşme bilgisini taşıyan değişken sıfırlanarak ilgili IF-ELSE kontrolünden çıkılıyor:
Kod:
ticked = 0;
}

Sayı değeri üretilerek son yazdırma işlemleri yapılıyor:
Kod:
sayi = (basamak3*10.0)+(basamak2*1.0)+(basamak1*0.1)+(basamak0*0.01);

Sayı değişkeninin onlar basamağı boş ise soluna “0” eklenerek Serial monitöre yazdırılıyor:
Kod:
if (length(sayi) <= 5) {
  Serial.print("0");
  Serial.println(sayi);
} {
  Serial.print(“Sayi: ”);
  Serial.println(sayi);
}
if (ticked + pushed == 0) {IrqStart();}
}
 
Son düzenleme:
@hafy çok güzel toparlanmış. Eline sağlık.
Bunu pdf yapıp kaynaklara ekleyeyim ben.

eğer dediğimi denersen sorunu anlamış oluruz abi puls görmüyorda mı böyle davranıyor görüyorda değişkene yazamıyor mu onu tespit etmiş oluruz
Senin kodları tekrar ekleyip test yapmak şuan ölüm gibi geliyor bana. Kopyala-yapıştır yapıp pin, değişken adı düzeltmekten helak oldum. Artık tek harf yazasım gelmiyor inanki. En son hafy'nin kodları ekledim. Artık ya bu kodlar ile devam edeceğim yada komple encoderi kaldıracağım.
Proje tıkandı yav. İlerlemiyor bunun yüzünden.
 
@hafy çok güzel toparlanmış. Eline sağlık.
Bunu pdf yapıp kaynaklara ekleyeyim ben.


Senin kodları tekrar ekleyip test yapmak şuan ölüm gibi geliyor bana. Kopyala-yapıştır yapıp pin, değişken adı düzeltmekten helak oldum. Artık tek harf yazasım gelmiyor inanki. En son hafy'nin kodları ekledim. Artık ya bu kodlar ile devam edeceğim yada komple encoderi kaldıracağım.
Proje tıkandı yav. İlerlemiyor bunun yüzünden.
Sanırım sonlarda bir iki satır kodda hata var onu düzelteyim akşam... PDF olarak öyle paylaşalım. Hem ustaların ekleyip, çıkarıp değiştireceği yerler olur diye bir süre beklesin bence...

Redmi Note 8 Pro cihazımdan Tapatalk kullanılarak gönderildi
 
Şimdi ilerlemeye çalışıyorum.
Voltajı veya Akımı ayarladıktan sonra Encoder butonuna bastığımda keyPress(); fonksiyonuna dışarıdan değişken göndermek zorundayım.
Bu değişken keypaddeki enter butonuna basılmış gibi gösterecek ve ayarlanan voltaj veya akım set edilecek.

Bu işlemi keyPress() fonksiyonuna eleman göndermeden de yapabilirim ama encoder butonu için aynı kodları 2.defa yazmam gerekir.
keyPress fonksiyonu dışarıdan değişken almıyor ama fonksiyon alır mı acaba?
Eğer alırsa "set" adında bir fonksiyon yazarım. Bunu da hem keyPress fonksiyonundan çağırırım, hemde encoder butonundan. En mantıklısı böyle olur.
 
keyPress fonksiyonu dışarıdan değişken almıyor ama fonksiyon alır mı acaba?
Eğer alırsa "set" adında bir fonksiyon yazarım. Bunu da hem keyPress fonksiyonundan çağırırım, hemde encoder butonundan. En mantıklısı böyle olur.
Alıyormuş. fonksiyon oluşturarak hallettim.
 
Şimdi ilerlemeye çalışıyorum.
Voltajı veya Akımı ayarladıktan sonra Encoder butonuna bastığımda keyPress(); fonksiyonuna dışarıdan değişken göndermek zorundayım.
Bu değişken keypaddeki enter butonuna basılmış gibi gösterecek ve ayarlanan voltaj veya akım set edilecek.

Bu işlemi keyPress() fonksiyonuna eleman göndermeden de yapabilirim ama encoder butonu için aynı kodları 2.defa yazmam gerekir.
keyPress fonksiyonu dışarıdan değişken almıyor ama fonksiyon alır mı acaba?
Eğer alırsa "set" adında bir fonksiyon yazarım. Bunu da hem keyPress fonksiyonundan çağırırım, hemde encoder butonundan. En mantıklısı böyle olur.
Ayarlanan voltaj ve akımın set edilmesini Encoder Butonu'nun uzun basılmasıyla halledebilirsiniz. Tekrar uzun basıldığında da yeniden ayarlama ortamına dönersiniz. Butona tek ve kısa basmakla basamaklar arasında gezineceksiniz; 4. basışta ayarlanan voltaj ve akım set edildiğinde ola ki basamaklardan birindeki bir yanlışı düzeltemeden set etmiş olursunuz!
 
Ayarlanan voltaj ve akımın set edilmesini Encoder Butonu'nun uzun basılmasıyla halledebilirsiniz. Tekrar uzun basıldığında da yeniden ayarlama ortamına dönersiniz. Butona tek ve kısa basmakla basamaklar arasında gezineceksiniz; 4. basışta ayarlanan voltaj ve akım set edildiğinde ola ki basamaklardan birindeki bir yanlışı düzeltemeden set etmiş olursunuz!
Basamaklar için Sağ ve Sol yön butonları var. Ayrıca henüz nerede kullanacağımı bilmediğim Yukarı, Aşağı butonlarıda var.
En başından beri encoder butonunu "Enter" olarak planladım. Bundan cayarsam yine kodlama ile uğraşmak zorunda kalırım. Üstelik yön tuşlarıda boşa çıkar.
 
Basamaklar için Sağ ve Sol yön butonları var. Ayrıca henüz nerede kullanacağımı bilmediğim Yukarı, Aşağı butonlarıda var.
En başından beri encoder butonunu "Enter" olarak planladım. Bundan cayarsam yine kodlama ile uğraşmak zorunda kalırım. Üstelik yön tuşlarıda boşa çıkar.
Haklısınız, epey bi buton bolluğu var tasarımınızda. Bu arada yukarıda paylaştığım kod Encoder butonu ile basamaklar arasında gezinmek üzerine... Bir hatırlatayım dedim.
 
Haklısınız, epey bi buton bolluğu var tasarımınızda. Bu arada yukarıda paylaştığım kod Encoder butonu ile basamaklar arasında gezinmek üzerine... Bir hatırlatayım dedim.
Tamam. Ben aştım oraları. Şuan ekrana değer yazdırma üzerine çalışıyorum. Voltaj 30v dan yukarı çıkmayacak. Bunun için ilk basamak 3'e eşitse diğer basamaklar artırılmayacak filan gibi. işin gıcık tarafı birde bu rakamları dijital potlara uyduracağız. Öyle 12.00v ayarlayıp göndermekle dijital pot ayarlanmaz herhalde. Onun için de 12.00 geldiyse potu 50 adım artır gibi birşeyler yapacağız. Daha çekeceğimiz çile çok :)
 
Şurada bir sorun yaşıyorum.
Matematiksel bir hesaplama hatası var sanki.

4 basamaklı sayımın 2.basamağı 1 olursa, son basamağı 6 olmuyor. 12345 789 şeklinde gidiyor.


4 basamaklı sayımın 2.basamağı 2 olursa, 3.basamak 1 olmuyor ve 4.basamak 9 oluyor.


Kod:
    switch (kursor_konum) {
      case 0:
        kursor_konum++;
        sayi1 = 0;
        break;
      case 1:
        u8g2.drawLine(64, 41, 71, 41);
        u8g2.drawLine(64, 42, 71, 42);
        if (sayac >= 6){
          sayac = 0;
        } else if (sayi2 == 5) {
          sayi3 = 0;
          sayi4 = 0;
        }
        sayi2 = sayac;
        break;
      case 2:
        u8g2.drawLine(88, 41, 95, 41);
        u8g2.drawLine(88, 42, 95, 42);
        if (sayi2 >= 5 || sayac >=10) {
          sayac = 0;
        }
        sayi3 = sayac;
        break;
      case 3:
        u8g2.drawLine(100, 41, 107, 41);
        u8g2.drawLine(100, 42, 107, 42);
        if (sayi2 >= 5 || sayac >=10) {
          sayac = 0;
        }
        sayi4 = sayac;
        break;
    }
    voltaj_akim = sayi1 * 10.0 + sayi2 * 1.0 + sayi3 * 0.1 + sayi4 * 0.01;
 
Şurada bir sorun yaşıyorum.
Matematiksel bir hesaplama hatası var sanki.

4 basamaklı sayımın 2.basamağı 1 olursa, son basamağı 6 olmuyor. 12345 789 şeklinde gidiyor.
16612 eklentisine bak

4 basamaklı sayımın 2.basamağı 2 olursa, 3.basamak 1 olmuyor ve 4.basamak 9 oluyor.
16611 eklentisine bak

Kod:
    switch (kursor_konum) {
      case 0:
        kursor_konum++;
        sayi1 = 0;
        break;
      case 1:
        u8g2.drawLine(64, 41, 71, 41);
        u8g2.drawLine(64, 42, 71, 42);
        if (sayac >= 6){
          sayac = 0;
        } else if (sayi2 == 5) {
          sayi3 = 0;
          sayi4 = 0;
        }
        sayi2 = sayac;
        break;
      case 2:
        u8g2.drawLine(88, 41, 95, 41);
        u8g2.drawLine(88, 42, 95, 42);
        if (sayi2 >= 5 || sayac >=10) {
          sayac = 0;
        }
        sayi3 = sayac;
        break;
      case 3:
        u8g2.drawLine(100, 41, 107, 41);
        u8g2.drawLine(100, 42, 107, 42);
        if (sayi2 >= 5 || sayac >=10) {
          sayac = 0;
        }
        sayi4 = sayac;
        break;
    }
    voltaj_akim = sayi1 * 10.0 + sayi2 * 1.0 + sayi3 * 0.1 + sayi4 * 0.01;
abi bir sorun daha var.
sen burda dijit dijit sayı giriyorsun ama bu sayının toplamını da alman lazımki ona göre dijital potlaradan direnç çıkaracaksın bu haliyle nası alıcaz
 
abi bir sorun daha var.
sen burda dijit dijit sayı giriyorsun ama bu sayının toplamını da alman lazımki ona göre dijital potlaradan direnç çıkaracaksın bu haliyle nası alıcaz
voltaj_akim değişkeni toplamı tutuyor.
 
Kod çok karışık Gökhan, hatanın nerede olduğu kodu inceleyerek görülmüyor.

Bir değişkende voltaj değerini tut. Sonra da o voltaj değerini kürsör pozisyonuna göre ya 10'ar 10'ar veya 1'er 1'er veya 0.1 veya 0.01 artıracaksın veya azaltacaksın. Sonra da elde ettiğin sayı sınırların dahilinde ise uygulayacaksın, yani ekranı yeni değer ile güncelleyeceksin. Sen her kürsör pozisyonu için ayrı kod yazmışsın, bir sürü if var.
 
Kod çok karışık Gökhan, hatanın nerede olduğu kodu inceleyerek görülmüyor.

Bir değişkende voltaj değerini tut. Sonra da o voltaj değerini kürsör pozisyonuna göre ya 10'ar 10'ar veya 1'er 1'er veya 0.1 veya 0.01 artıracaksın veya azaltacaksın. Sonra da elde ettiğin sayı sınırların dahilinde ise uygulayacaksın, yani ekranı yeni değer ile güncelleyeceksin. Sen her kürsör pozisyonu için ayrı kod yazmışsın, bir sürü if var.
Abi o if'ler sınırları aşmamak için. Ben şimdi o ifleri kaldırıp kafa karışıklığını giderdim ama kodda düzelme olmadı.
Mantık şöyle. encoder() fonksiyonunda "sayac" değişkeni içine sayı üretiliyor.
Sonra bu "sayac" değişkenini kürsörün konumuna göre sayi1,sayi2,sayi3,sayi4 değişkenine aktarıyorum.
Enson da senin söylediğin kodlama ile çarpma ve toplama yapıyorum.

Yani biraz karmaşık görünsede kod doğru gibi geliyor bana.
Bu kodun en temiz hali.

Kod:
switch (kursor_konum) {
      case 0:
        kursor_konum++;
        sayi1 = 0;
        break;
      case 1:
        u8g2.drawLine(64, 41, 71, 41);
        u8g2.drawLine(64, 42, 71, 42);
        
        sayi2 = sayac;
        break;
      case 2:
        u8g2.drawLine(88, 41, 95, 41);
        u8g2.drawLine(88, 42, 95, 42);
        
        sayi3 = sayac;
        break;
      case 3:
        u8g2.drawLine(100, 41, 107, 41);
        u8g2.drawLine(100, 42, 107, 42);
        
        sayi4 = sayac;
        break;
    }
    voltaj_akim = sayi1 * 10.0 + sayi2 * 1.0 + sayi3 * 0.1 + sayi4 * 0.01;
 
ayrı ayrı sayılara gerek yok. Aşağıda bir taslak olarak yazdım. Satır sayısına bakma, mantığın kolaylığına ve anlaşılırlığına bak.

Kod:
#define MAX_VOLTAJ 30.0
#define MIN_VOLTAJ 0.0

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


void loop()
{
    if (enkoder_sola_dondu)
    {
        artieksi = -1;
    }
    else
    {
        artieksi = 1;
    }

    float yeni_voltaj = voltaj + artieksi * katsayilar[kursor_pos];

    if ((yeni_voltaj <= MAX_VOLTAJ) && (yeni_voltaj >= MIN_VOLTAJ))
    {
        voltaj = yeni_voltaj;
        ekrani_tazele();
    }
}
 
Aynı işlemi float yerine integer olarak da yapabilirsin. virgülden sonra iki basamağı temsilen voltaj değerini 100 ile çarpılmış kabul edersin:

Kod:
uint16_t voltaj;

void loop()
{
    voltaj = 2500; // voltaj 25.00 Volt
    voltaj = 5;    // voltaj 0.05 Volt
}
 
ayrı ayrı sayılara gerek yok. Aşağıda bir taslak olarak yazdım. Satır sayısına bakma, mantığın kolaylığına ve anlaşılırlığına bak.

Kod:
#define MAX_VOLTAJ 30.0
#define MIN_VOLTAJ 0.0

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


void loop()
{
    if (enkoder_sola_dondu)
    {
        artieksi = -1;
    }
    else
    {
        artieksi = 1;
    }

    float yeni_voltaj = voltaj + artieksi * katsayilar[kursor_pos];

    if ((yeni_voltaj <= MAX_VOLTAJ) && (yeni_voltaj >= MIN_VOLTAJ))
    {
        voltaj = yeni_voltaj;
        ekrani_tazele();
    }
}
Abi senin kodlada atlıyor bu yav.
 

Forum istatistikleri

Konular
7,285
Mesajlar
123,214
Üyeler
2,939
Son üye
Cano

Son kaynaklar

Son profil mesajları

Freemont2.0 herbokolog Freemont2.0 wrote on herbokolog's profile.
nick iniz yakıyor
:D
Freemont2.0 posta Freemont2.0 wrote on posta's profile.
Merhabalar :)
az bilgili çok meraklı
Prooffy semih_s Prooffy wrote on semih_s's profile.
Merhaba, sizden DSO2C10 hakkında bilgi rica ettim. Yanıtlarsanız sevinirim...
Unal taydin Unal wrote on taydin's profile.
Timur Bey, Arduino kontrollü bir akü şarj cihazı yapmaya çalışıyorum. Aklımdaki fikri basit bir çizim olarak konu açmıştım. Özellikle sizin fikirlerinizi çok önemsiyorum.
Back
Top