Döner Kodlayıcı: Ne Kadar Zor Olabilir?


Fark etmiş olabileceğiniz gibi, Mbed kullanarak bir STM32 ARM CPU ile çalışıyorum. Mbed’in oldukça basit olduğu bir zaman vardı, ancak Mbed OS’ye dönüştüğünden beri çok şey değişti. Ne yazık ki bu, bulabileceğiniz birçok kitaplığın ve örneğin yeni sistemle çalışmadığı anlamına gelir.

Döner bir kodlayıcıya ihtiyacım vardı – etrafta gördüğünüz “Arduino için 49 kart” kitlerinden birinden ucuz bir tane çıkardım. Ülkedeki en iyi kodlayıcı değil, eminim ama işi yapmalı. Ne yazık ki, Mbed OS’nin kodlayıcı için bir sürücüsü yok ve bulduğum ilk birkaç üçüncü taraf kitaplığı ya yoklama yoluyla çalıştı ya da en son Mbed ile derlenmedi. Elbette, bir kodlayıcıyı okumak gizemli bir süreç değildir. Kodu kendiniz yazmak ne kadar zor olabilir? Gerçekten ne kadar zor. Kodumu ve oraya nasıl geldiğimi paylaşacağımı düşündüm.

Döner kodlayıcıyı okumanın birçok yolu vardır. Bazıları muhtemelen benim yöntemimden daha iyidir. Ayrıca, bu ucuz mekanik kodlayıcılar korkunçtur. Hassas işler yapmaya çalışıyorsanız, muhtemelen optik kodlayıcı gibi farklı bir teknolojiye bakıyor olmalısınız. Bunlardan birini kusursuz bir şekilde okumak neredeyse imkansız olduğu için bundan bahsediyorum.

Bu yüzden amacım basitti: Kesintiye dayalı bir şey istedim. Bulduklarımın çoğu, belirli aralıklarla bir işlevi çağırmanızı veya bir zamanlayıcı kesintisi ayarlamanızı gerektiriyordu. Ardından kodlayıcıyı izlemek için bir durum makinesi oluşturdular. Sorun değil, ancak bu, hareket etmese bile kodlayıcıyı kontrol etmek için çok fazla işlemci tükettiğiniz anlamına gelir. STM32 CPU bir pin değişikliği ile kolayca kesinti yapabilir, benim de istediğim buydu.

Yakalayış

Sorun, elbette, mekanik anahtarların sıçramasıdır. Bu nedenle, donanımda veya yazılımda bu sıçramayı filtrelemeniz gerekir. Gerçekten bir kapasitörden daha fazla herhangi bir donanım eklemek istemedim, bu yüzden yazılımın bunu halletmesi gerekecekti.

Ayrıca kesinlikle gerekli olandan daha fazla kesinti kullanmak istemedim. Mbed sistemi, kesintilerin üstesinden gelmeyi kolaylaştırır, ancak biraz gecikme vardır. Aslında, her şey bittikten sonra gecikmeyi ölçtüm ve o kadar da kötü değil – bundan biraz sonra bahsedeceğim. Ne olursa olsun, sadece bir çift kesinti kullanmayı denemeye karar vermiştim.

Teoride

Teoride, bir kodlayıcıyı okumak çocuk oyuncağıdır. İki çıkış vardır, bunlara A ve B diyeceğiz. Düğmeyi çevirdiğinizde bu çıkışlar darbeler gönderir. İçerideki mekanik düzenleme, düğme bir yönde döndüğünde, A’dan gelen darbeler, B’den gelen darbelerden 90 derece ileride olacak şekildedir. Diğer yöne çevirirseniz, faz tersine çevrilir.

İnsanlar genellikle darbelerin pozitif olduğunu düşünürler, ancak çoğu gerçek kodlayıcının toprakla teması ve bir yukarı çekme direnci olacaktır, bu nedenle aslında hiçbir şey olmadığında çıkışlar genellikle yüksektir ve darbeler gerçekten düşük darbelerdir. Şemada, kimse düğmeyi çevirmediğinde, uzun bir yüksek sinyal uzantısı olduğunu görebilirsiniz.

Diyagramın sol tarafında, B sinyalinin her seferinde A sinyalinden önce düştüğüne dikkat edin. B’yi A’nın düşen kenarında örneklerseniz, bu durumda her zaman 0 alırsınız. Darbelerin genişliği elbette çevirdiğiniz hıza bağlıdır. Diğer tarafa döndüğünüzde, diyagramın sağ tarafındaki kasayı alıyorsunuz. Burada, önce A sinyali azalır. Aynı noktadan örnek alırsanız, B şimdi 1’dir.

A, B veya saat yönünde ve saat yönünün tersine etiketlerde sihirli bir şey olmadığını unutmayın. Bunun gerçekten tek anlamı “bir yol” ve “diğer yol”dur. Kodlayıcının nasıl hareket ettiğini beğenmediyseniz, A ve B’yi değiştirebilir veya yazılımda değiştirebilirsiniz. Bu yönleri keyfi olarak seçtim. Genellikle, A kanalının saat yönünde “önlemesi” beklenir, ancak bu aynı zamanda ölçtüğünüz kenara ve her şeyi nasıl bağladığınıza da bağlıdır. Yazılımda, zaman içinde nerede olduğunuz hakkında bir fikir edinmek için genellikle bir yön için bir sayıma bir ekler ve diğer yön için çıkarırsınız.

Bu tür girdileri okumanın birçok yolu vardır. Örnek alıyorsanız, iki bitten bir durum makinesi oluşturmak ve bu şekilde işlemek oldukça kolaydır. Çıktı gri bir kod oluşturur, böylece kötü durumları ve kötü durum geçişlerini atabilirsiniz. Ancak, giriş sinyalinizden eminseniz, bundan çok daha kolay olabilir. Sadece A’nın bir kenarında B’yi okuyun (veya tam tersi). Biraz daha sağlamlık istiyorsanız diğer kenarı doğrulayabilirsiniz.

Uygulamada

Ne yazık ki, gerçek mekanik kodlayıcılar yukarıdaki şemaya benzemiyor. Daha çok şuna benziyorlar:

Bu bir soruna yol açar. A girişinin her iki kenarında da kesinti yapıyorsanız (kapsamdaki üst iz), her iki kenarda da bir dizi darbe alırsınız. B’nin A’nın her bir kenarında farklı durumlarda olduğuna dikkat edin, bu nedenle toplamda çift sayıda darbe alırsanız toplam sayınız sıfır olacaktır. Şanslıysanız, doğru yönde tek bir sayı alabilirsiniz. Ya da yanlış yöne gidebilirsin. Ne dağınıklık.

Ancak A’nın örnekleme kenarında, B kaya gibi sağlamdır. Dürbün üzerindeki alt iz düz bir çizgi gibi görünür çünkü tüm B geçişleri bu ölçekte ekranın dışındadır. Bir kodlayıcıyı kolayca geri döndürmenin sırrı budur. A değiştiğinde, B kararlıdır ve bunun tersi de geçerlidir. Gri bir kod olduğu için bu mantıklıdır, ancak basit bir kod çözücüyü mümkün kılan içgörüdür.

Plan

Yani plan, A’nın yüksekten düşüğe gittiğini fark etmek ve ardından B’yi okumaktır. Ardından B değişene kadar A’yı görmezden gelin. B’yi izlemek istiyorsanız, elbette, aynı sorun var, bu yüzden değişiklikte kararlı olan A’ya kilitlemeniz gerekiyor. Benim durumumda, iki kesinti daha kullanmak istemedim, bu yüzden bu mantığı takip ediyorum:

  1. A düştüğünde, B’nin durumunu kaydedin ve sayımı güncelleyin. Ardından bir kilit bayrağı ayarlayın
  2. A tekrar düşerse, kilit bayrağı ayarlanmışsa veya B değişmemişse hiçbir şey yapmayın.
  3. A yükseldiğinde, B değiştiyse, B’nin durumunu kaydedin ve kilit bayrağını kaldırın.

Yani yukarıdaki skop izinde üst izdeki ilk düşüş B okumamıza neden olur. Bundan sonra B değişmediği için ekrandaki geçişlerin hiçbirinin etkisi olmaz. B’nin gürültülü bir yüksekten düşüğe geçişinden sonra ortaya çıkan ekranın yükselen kenarı, algoritmanın kilidini açan taraf olacaktır.

Sorun

Yine de bir sorun var. Tüm şema, B’nin düşen kenara kıyasla A için gerçek bir yükselen kenarda farklı olacağı fikrine dayanır. B’nin değişmediği bir durum var ama yine de A kenarını kabul etmek istiyoruz. İşte o zaman yön değiştirirsiniz. B’yi izlediyseniz, çözülmesi kolay olurdu, ancak bu daha fazla kod ve iki kesinti daha. Bunun yerine, bir düğmeyi çeviren bir kişi için, farklı yönlere çok hızlı bir şekilde çılgınca çevirirseniz, kodlayıcının bir veya iki tıklamasının yanlış yöne gittiğini bile fark etmeyeceğinize karar verdim. Fark edeceğiniz şey, ince bir ayar yaparsanız ve ardından kasten diğer yöne çevirirseniz.

B’nin önceki durumunu bildiğinizi ve bir süre içinde (birkaç yüz milisaniye gibi) hiçbir şeyin değişmediğini düşündüğünüzde, kod B fikrini bilinmeyene sıfırlar, böylece bir sonraki B sinyali ne olursa olsun geçerli kabul edilir.

ben kullandım Kernel::Clock::now Mbed’den bir özellik. Bunu bir kesinti servis rutininden (ISR) aramanız gerekip gerekmediği net değil, ancak öyleyim ve sorunsuz çalışıyor gibi görünüyor.

Diğer tek sorun, okumanın ortasında sayının değişmediğinden emin olmaktır. Emin olmak için okuma etrafındaki kesmeleri devre dışı bıraktım.

kod

Kodu adresinde bulabilirsiniz. GitHub. Tüm açıklamaları yaptıysanız, takip etmekte sorun yaşamamalısınız.



void Encoder::isrRisingA()
{
   int b=BPin; // read B
   if (lock && lastB==b) return; // not time to unlock
// if lock=0 and _lastB==b these two lines do nothing
// but if lock is 1 and/or _lastB!=b then one of them does something
   lock=0;
   lastB=b;
   locktime=Kernel::Clock::now()+locktime0; // even if not locked, timeout the lastB
}

// The falling edge is where we do the count
// Note that if you pause a bit, the lock will expire because otherwise
// we have to monitor B also to know if a change in direction occurred
// It is tempting to try to mutually lock/unlock the ISRs, but in real life
// the edges are followed by a bunch of bounce edges while B is stable
// B will change while A is stable
// So unless you want to also watch B against A, you have to make some
// compromise and this works well enough in practice
void Encoder::isrFallingA()
{
   int b;
   // clear lock if timedout and in either case forget lastB if we haven't seen an edge in a long time
   if (locktime<Kernel::Clock::now())
     {
     lock=0;
     lastB=2; // impossible value so we must read this event
     }
   if (lock) return; // we are locked so done
   b=BPin; // read B
   if (b==lastB) return; // no change in B
   lock=1; // don't read the upcoming bounces
   locktime=Kernel::Clock::now()+locktime0; // set up timeout for lock
   lastB=b; // remember where B is now
   accum+=(b?-1:1); // finally, do the count!
}


Kesintiyi ayarlamak kolaydır çünkü InterruptIn sınıf. Bu bir şey gibi DigitalIn nesne, ancak yükselen veya düşen kenara bir işlev eklemenin bir yolu vardır. Bu durumda ikisini de kullanırız.

gecikme

Bu kurulumda bir kesintiyi işlemenin ne kadar zaman aldığını merak ettim, böylece ayarlarsanız kod kullanılabilir #define TEST_LATENCY 1. Sonuçlarımın bir videosunu görebilirsiniz, ancak TLDR: Kesinti almak 10 mikrosaniyeden fazla sürmedi ve genellikle bunun yaklaşık yarısı.

Kodlayıcıyı doğru yapmak, düşündüğümden biraz daha zordu, ancak çoğunlukla daha fazla kesintiyi işlemek istemediğim için. A pinine göre B pinini izlemek ve B’nin doğru durumunu gerçekten anlamak için kodu değiştirmek yeterince basit olacaktır. Bu değişikliği denerseniz, işte başka bir fikir: kesintiler arasındaki süreyi ölçerek, şunları yapabilirsiniz: ayrıca, kodlayıcının ne kadar hızlı döndüğü hakkında bir fikir edinin; bu, bazı uygulamalar için faydalı olabilir.

Gray kodu ve bunun yararlı olduğu bazı yerleri tazelemek istiyorsanız, bundan daha önce bahsetmiştik. Tüm bunlar garip bir şekilde tanıdık geliyorsa, 2017’de Mbed’in eski bir sürümünde bir kodlayıcı kullandım. Bu durumda, bir zamanlayıcı kesintisinde girişleri periyodik olarak yoklayan bir hazır kitaplık kullandım. Ama dediğim gibi, böyle şeyleri gerçekleştirmenin her zaman birden fazla yolu vardır.

[Headline image: “Rotary Encoder” by SparkFunElectronics, CC BY 2.0.  Awesome.]


Kaynak : https://hackaday.com/2022/04/20/a-rotary-encoder-how-hard-can-it-be/

Yorum yapın