encoder pals okumada bu kod doğru mu?

Başlatan zamzam23, 01 Temmuz 2011, 16:17:03

zamzam23

#int_ext
void harici_kesme () 
{
   if(input(pin_c5))
         {
           pals=pals+1;
         }
    else
    {
    pals=pals-1;
    }
}

Bu kod encoderden veri kaçırmadan okumak için yeterli mi?

Tagli

C5'e bağlı olan şey nedir? Yön tespiti mi isteniyor?
Gökçe Tağlıoğlu

zamzam23

evet. encoder A kanalı RB0 a, B kanalı C5 e bağlı ve yön tesbiti isteniyor.Kod aslında şu şekilde:
#int_ext
void harici_kesme () 
{
   if(input(pin_c5))
         {
yon=1;
           pals=pals+1;
         }
    else
    {
yon=0;
    pals=pals-1;
    }
}


aslında problemim şu:
bu kodla encoderi sağa sola elimle döndürüp pals değerini lcd ekrana yazdırdığımda pals değeri artıyor ve azalıyor sorun yok gibi. ama motor belirli bir hızda dönerken elimle tutup encoderi durdurdugum zaman motor titreme yapıyor ama encoder donmuyor ama pals artmaya deva ediyor ustelik daha hızlı bir sekılde. ama dedim sorun benim verdıgım bu kodda mı, yoksa motoru elimle tutarken kart üzerinden yüksek akım gectıgı zaman RB0 veya C5 binlerine geçen parazitlerden dolayı mı encoder pals okumaya devam ediyor. sıkıntı bu.

Tagli

#3
Benim bildigim kadariyla, quadrature encoder denilen bu cihazlarda donme yonunu belirleyebilmek icin bir onceki okuma sonucunu da sakalamak gerekiyor. Ben bu sekilde calistirmistim. Internette arastirirsan ornek kod bulabilirsin rahatlikla. Burada da daha once konusulmustu.

Senin kodun sorunu surada: Kesme olusmadigi halde motor donmus olabilir. Cunku kesmeyi tek bacaga bakarak algilamaya calisiyorsun. Halbuki her bir ayrik donme sonucunda A veya B cikislarindan sadece biri degisir. Sorun su ki sen sadece A'yi kontrol ediyorsun.
Gökçe Tağlıoğlu

zamzam23

örnek kodu buldum bir tane ama B4-B7 portu değişince olusan kesmeyi kullanmıslar RB0 lıını bulamadım yardımcı olursan sevinirim.

Tagli

Zaten ben de PORTB kesmesi kullanmistim. RB0 kesmesi ile mumkun olacagini sanmiyorum. Cunku iki bacagin degisimini de kontrol etmen gerekiyor, program ana dongusunu hizli isletebiliyorsan kesme kullanmadan surekli yoklama yaparak da okuma yapabilirsin ama tavsiye etmiyorum.

Belki harici bir XOR kapisi kullanmayi deneyebilirsin ama bu durumda hem dusen hem de yukselen kenarlarda kesmeye gitmen gerekir ki yanlis hatirlamiyorsam bu mumkun degil. Belki baska harici parcalar kullanarak birseyler yapilabilir ama bence ugrastigina degmez. Sen en iyisi PORTB kesmesi kullan.
Gökçe Tağlıoğlu

camby

Dışarda Xor ile yapman da mümkün fakat bir önceki durumu kaydetmen gerekiyor yani 1 adet flip-flop ile önceki durumla yeni durumu karşılaştırıp yönü belirleyeceksin.

1 - MCU dışına yapabileceğin donanım : http://www.fpga4fun.com/QuadratureDecoder.html

2 - PORTB değişim kesmesi ise yapılmış çalışma : https://www.picproje.org/index.php/topic,28724.msg195994.html#msg195994

Burada okuma 4x mod. Yani 1024'lük encoder'ın var ise 4096 çözünürlük ve yön tayini yapabiliyorsun.

btfss		FAZA		; A fazının okunması 
		clrf		yeni_a
		btfsc		FAZA
		movlw		.1
		btfsc		FAZA
		movwf		yeni_a	

		btfss		FAZB		; B fazının okunması
		clrf		yeni_b
		btfsc		FAZB
		movlw		.1
		btfsc		FAZB
		movwf		yeni_b

		movf		yeni_b,w	; eskiA xor yeniB
		xorwf		eski_a,f	
		
 		btfss		eski_a,0
		incf		saatyonu
		btfsc		eski_a,0
		incf		tersyon
 
		movf		yeni_a,w	; yeni değerler , eski oldu
		movwf		eski_a
		movf		yeni_b,w
		movwf		eski_b


3 - Senin yaptığın 1 x mod ve yön  . A veya B fazını PORTB0 kesmesine verip , her kesme geldiğinde diğer faza bakılıyor.

Sorun : Motor titreme yaptığında ( yani çok hızlı bir şekilde + ve - yönde hareket ettiğinde ) okuma işlemi saçmalıyor.

Olası Çözümüm : Öncelikle parazit olduğunu sanmıyorum. 8bitlik bu MCU'ların hız kapasiteleri sınırlı. Belli bir max frekansta pulse işleyebilirler. Örneğin benim yaptığım 4x okumada 2 kesme arası süre 8.33 us'den küçük olduğunda , gelen sinyallere yetişemiyordu ve üst üste kesmeler ve pulseLar yığılıyordu.

Sende de bu durum olabilir titreme durumunda bir kesmeden çıkılmadan tekrar kesme geliyor. Bunun üstüne ilavaten kesme oluştuğu andan itibaren c5 pinini okuyana kadar titremeden dolayı C5'in durumu da değişiyor.

Yani bu durumda zamanla çok önemli 200 ns'nin kıymeti var. Sen bir de C dilini kullanmışsın amanlama durumu daha da kötü olmuş.

Acaba kesmeye girme ile çıkma arası geçen süreyi söyleyebilir misin ?

ilhan_mkp


Encoder çıkışını A ve B olarak kullandığına göre yön kontrolüde yapacaksın demektir yoksa tek çıkış işini görebilirdi.
Kesme kullanmak işini kolaylaştırır ancak kesmesizde halledebilirsin. Aşağıda işin temelini açıklayacağım. İlave sorun olursa cevaplarım.
Encoder şayet bildiğim rotary encoder tipinde çalışıyor ise iki çıkışı olmalı ki sende iki çıkştan bahsediyorsun. Bu çıkışlar birbirine göre 90 derece faz farklıdır. Bu nedenle A ve B çıkışlarının muhtemel kombinasyonları şöyle olacaktır;
(AB) , 10 , 11 , 01 , 00 şeklinde.
Şimdi okuma için yapılacak işlem
ILK= PORTB & (%11000000)   bu komut ile yalnızca encoder bitlerini alıp ILK değişkenine vermiş oluyoruz.
Ardından yeniden bir okuma daha yapacağız.
IKINCI=PORTB & (%11000000) aynı şekilde yine encoder bitlerini IKINCI isimli değişkene vermiş oluyoruz.
Şayet ILK=IKINCI ise encoder dönmüyor demektir. O halde Yeniden IKINCI yi okumak üzere programı o satıra yönlendiririz.
Diyelimki ILK ile IKINCI bir birinden farklı. O halde encoder döndü demektir.  Bu durumda önce dönüş yönünü tespit etmemiz gerekir.
YON=(ILK.Bit7) ^(IKINCI.Bit6)  Açıklaması ILK,7. biti ile IKINCI nin 6.cı bitini XOR işlemine tabi tutuyoruz.
ILK=10 olsun
IKINCI=11 olsun
O halde YON=1 XOR 1=0 olacaktır. O halde YON=0 ise saat istikametinde (terside olabilir ama biz öyle kabul edelim)dönüş var demek olacaktır. Şimdi birde tersini inceleyelim.
Yani ILK=00 ve IKINCI=01 Olsun
YON=0 XOR 1 =1 olacaktır.
Bu şekilde emcoderin dönüş yönü kolaylıkla tespit edilebilir.
Yön tespiti yapıldıktan sonra
Şayet YON=0 ise SAYAC=SAYAC+1
Şayet YON=1 ise SAYAC=SAYAC-1 şeklinde saydırma yapılarak enkoder okuma kısmından çıkılır.
Hepsi bu kadar. Tabiiki sayac değerlerinin sınırlarını artırma yada eksiltme kısımlarında tanımlaman gerekir. Sayacın belli değerin üzerine çıkmaması yada belli değerin altına inmemesini bu bölümde tanımlaman gerekiyor.

Normal şartlarda ilk okuma ile ikinci okuma arasında belirli bir zaman sayacıda çalıştırarak encoderin dönmemesi halinde o zaman sayacı değerine hem saydırıp hemde ikinci okumayı yaparsın. Encoder hala dönmemiş ve senin zaman sayacında limite gelmiş ise sayamadan encoder okumadan çıkman ve daha sonra yeniden gelmen daha uygun olur.
İşte bu kadar encoder okuma.

pcde bir kenarda duran yanliş hatirlamiyoysam eteye ağit not

camby

#8
Alıntı yapılan: ilhan_mkp - 01 Temmuz 2011, 19:25:58

Encoder çıkışını A ve B olarak kullandığına göre yön kontrolüde yapacaksın demektir yoksa tek çıkış işini görebilirdi.
Kesme kullanmak işini kolaylaştırır ancak kesmesizde halledebilirsin. Aşağıda işin temelini açıklayacağım. İlave sorun olursa cevaplarım.
Encoder şayet bildiğim rotary encoder tipinde çalışıyor ise iki çıkışı olmalı ki sende iki çıkştan bahsediyorsun. Bu çıkışlar birbirine göre 90 derece faz farklıdır. Bu nedenle A ve B çıkışlarının muhtemel kombinasyonları şöyle olacaktır;
(AB) , 10 , 11 , 01 , 00 şeklinde.
Şimdi okuma için yapılacak işlem
ILK= PORTB & (%11000000)   bu komut ile yalnızca encoder bitlerini alıp ILK değişkenine vermiş oluyoruz.
Ardından yeniden bir okuma daha yapacağız.
IKINCI=PORTB & (%11000000) aynı şekilde yine encoder bitlerini IKINCI isimli değişkene vermiş oluyoruz.
Şayet ILK=IKINCI ise encoder dönmüyor demektir. O halde Yeniden IKINCI yi okumak üzere programı o satıra yönlendiririz.
Diyelimki ILK ile IKINCI bir birinden farklı. O halde encoder döndü demektir.  Bu durumda önce dönüş yönünü tespit etmemiz gerekir.
YON=(ILK.Bit7) ^(IKINCI.Bit6)  Açıklaması ILK,7. biti ile IKINCI nin 6.cı bitini XOR işlemine tabi tutuyoruz.
ILK=10 olsun
IKINCI=11 olsun
O halde YON=1 XOR 1=0 olacaktır. O halde YON=0 ise saat istikametinde (terside olabilir ama biz öyle kabul edelim)dönüş var demek olacaktır. Şimdi birde tersini inceleyelim.
Yani ILK=00 ve IKINCI=01 Olsun
YON=0 XOR 1 =1 olacaktır.
Bu şekilde emcoderin dönüş yönü kolaylıkla tespit edilebilir.
Yön tespiti yapıldıktan sonra
Şayet YON=0 ise SAYAC=SAYAC+1
Şayet YON=1 ise SAYAC=SAYAC-1 şeklinde saydırma yapılarak enkoder okuma kısmından çıkılır.
Hepsi bu kadar. Tabiiki sayac değerlerinin sınırlarını artırma yada eksiltme kısımlarında tanımlaman gerekir. Sayacın belli değerin üzerine çıkmaması yada belli değerin altına inmemesini bu bölümde tanımlaman gerekiyor.

Normal şartlarda ilk okuma ile ikinci okuma arasında belirli bir zaman sayacıda çalıştırarak encoderin dönmemesi halinde o zaman sayacı değerine hem saydırıp hemde ikinci okumayı yaparsın. Encoder hala dönmemiş ve senin zaman sayacında limite gelmiş ise sayamadan encoder okumadan çıkman ve daha sonra yeniden gelmen daha uygun olur.
İşte bu kadar encoder okuma.

pcde bir kenarda duran yanliş hatirlamiyoysam eteye ağit not

Maalesef katılamıyorum dediklerinize , yanlız encoder okuma mantığı eski değerler ile yeni değeri XOR ile karşılaştırmak doğru..

- I/O Kesmesi kullanılmadan okuma yapmaya çalışılır ise kesinlikle  hareket halinde puls kaçarılmış olur.

- Belirli bi döngü içerisinde okuma yapılırsa okuma yapmaktan başka bir kod işletilemez.

- Timer kesmesi yapılıp okumaya gidilirse de pulse kaçırılır. Bu timerın süresi ne olacak falan filan bi sürü sorun var.

Eğer ben yanlış anlamadıysam dediklerinizi , bu encoder okuma mantığınızda büyük bir hata var. Yukarıda puls hızından dolayı I/O kesme süresi bile yetmez iken başka bir kesme yada kesmesiz çalışmak düşünülemez.

Tagli

C18'de PORTB kesmesi için yazmış olduğum güncelleme kodu aşağıda. Elbette bu koda RBIF bayrağını kontrol ederek girmen gerekiyor, ben başka kesme olmadığı için yazdığım kodda bunu atlamıştım. Sanırım A ve B çıkışlarını RB5 ve RB6'ya bağlamıştım.
#pragma interrupt updateEncoder
void updateEncoder(void){
	unsigned char encNew, sum;
	
	encNew = (PORTB >> 4) & 0b00000011;
	sum = encOld + encNew;
	
	if (sum == 1 || sum == 7 || sum == 14 || sum == 8){
		encCounter++;
	}
	else if (sum == 11 || sum == 13 || sum == 4 || sum == 2){
		encCounter--;
	}
	
	encOld = (encNew << 2) & 0b00001100;
	INTCONbits.RBIF = 0;
	return;	
}


Kod belki daha etkin bir şekilde yazılabilirdi, emin değilim...
Gökçe Tağlıoğlu

ilhan_mkp

camby hocam ben o notu önceki duruma bakılması gerektiğini söylediğiniz için paylaştım yazı bana ağit değil belirteyim

zamzam23

#11
İlginiz ve cevaplarınız için teşekkür ederim.işlemci 18f4520 ve 10*4=40mhz de calıstırıyorum.hız problemı olurmu bılmıyorum hıc kesmeler kac sn sürüyor hesaplamaya calısmadım. İnternette şöyle bir kod buldum demek istediğiniz şeyle alakalı sanırım ve RB0 kulanarak yazılmıs.micro c ile.çalısıp çalısmadıgını bilmiyorum ama pazartesi denemeyi düşünüyorum.
void interrupt()               // enter when RB0 Falling
{
  if(portb.f0&portb.f1)         // By checking B0 and B1, Position and Direction Find
  {count++;                     // If "+" direction
  }
  else                          // If "-" direction
  {count--;
  }
  INTCON.INTF=0;                // Clear flag
}

Bu kodu ccs ye nasıl uyarlarız?

bir de bu var:
#define B	RB1
#define C	RB2

static void interrupt
rotary_encoder(void)
{
	GIE=0;
	counter_old=counter;
	if(B==1)
		counter=counter-36;
	else
		counter=counter+36;
	
	if((counter_old-counter)>0)
	{
		flag=1;
		if(C)
			tur--;
	}
	else
	{
		flag=0;
		if(C)
			tur++;
	}
	INTF=0;
	GIE=1;
}

iyildirim

#12
İlk kod kısa ve daha esnek..

CCS bilmem ama, port, resister tanımları doğru olduktan sonra herhangi bir C derleyisici ile çalışabilir.

Qencoder için genel olarak xor operatörü kullanmak daha mantıklı..

Kesme sadece A veya B kanalının düşen veya yükselen kenarında tetiklenirse operatör olarak and veya xor kullanılırsa x1 modda, 

kesme A veya B kanalının hem yükselen hemde düşen kenarında tetiklenirse ve operatör olarak xor kullanılırsa x2 modda,

her iki kanalın her iki kenarında da tetiklenirse ve bir önceki b0,  ile b1 in son durumu xor operatorü ile kullanılırsa x4 modda çalışır.

zamzam23

#13
şimdi benim yapmak istediğim sadece RB0 ve diğer herhangi bir pin i kullanarak encoderi dogru okumak. sizin dediğiniz
"Kesme sadece A veya B kanalının düşen veya yükselen kenarında tetiklenirse operatör olarak and veya xor kullanılırsa x1 modda, "
buna göre benim ilk mesajda verdiğim kod bu mantıkla çalısıyor ama Xor falan kulanmadım orda. bunu bi acıklar mısın kod dogru mu değil mi.dogruysa peki neden motor sıkısma anında pals sayısı durması yerine daha da artıyor.motor yavas da donerken aynı sey oluyor bir yerde sıkıntı var ama nerde

camby

zamzam23 ,
Kodun doğru , olası sorunu ve çözümünü aşağıda yazdım tekrar yazıyorum titreşim anında motor açısal olarak çok hızlı hareket ettiği için kesmeleri ve C5 girişlerini kaçırıyor ve saçmalıyor. Kodunun titreşim hızına yetişip yetişemediğini görmemiz için kesmeden ne kadar sürede çıktığına bakman lazım öncelikle , bunun dışında motorun titreşimi için tahmini bir hız belirleyebiliriz , motora verdiğin sinyalin frekansı nedir ? Encoder'ın çözünürlüğü nedir?