Bilgi tazeleme - PIC girişindeki kare dalga frekansı hakkında.

Başlatan tayyar, 13 Ağustos 2010, 12:05:08

tayyar

Selamlar;

Uzun süredir programlama işine uzak kaldığımdan dolayı biraz paslandım. biraz teknik bilgi geri dönüşe ihtiyacım var:)

Olayımı size kısaca şöyle anlatayım.
Maksimum 600m/dk hızla gidebilen bir makinam var. Bu makinanın hız bilgisini okuyarak başka sistemlere aktarmam gerekiyor. Bu işlem için bana en tanıdık yöntem makinenin döner aksamından biryere rotary encoder yerleştirmek ve encoderden gelen bilgiyi pic ile okuyarak ilgili sisteme aktarmak olarak geliyor.

Uygun bir döner aksam bulabilmek için takometreyi alıp makine çalışır durumdayken bir kaç ölçüm yaptım ve makineyle eşdeğer giden 2 farklı nokta buldum oraları kullanmayı planlıyorum. (şöyleki makine 200m/dk ile giderken bahsettiğim dönen silindir 200RPM ile dönüyor ve bu durum diğer hızlar içinde aynı)

Elimde KOYO marka bir rotary encoder var. 1 turda 1000 pals veriyor. A, B ve Z uçları var. (modeli TRD-J 100-RZ)

Ben bu iş için 16F877 (4MHZ) kullanmayı planlıyorum (rs232 modülü olduğu için)

Yapacakları:
Rotary encoderden gelen darbeleri sayacak
Belirli sürelerde oluşan kesmelerde saydığı darbeleri süreye bölerek hız bilgisini hesaplayacak
2x16 lcd de gösterecek (kalibrasyon ve veri doğruluğunu kontrol edebilmek için)
RS232 üzerinden pc ye hız bilgisini gönderecek

Biraz hesaplama yaparsak asıl soruma gelebiliriz.

Şöyleki makine 600m/dk da giderken benim encoderi bağlayacağım nokta 600RPM ile dönüyor olacak. Bu durumda encoder A veya B ucundan 1 dk da 600x1000= 600000 pals üzertmiş olacak. saniyeye vurduğumuzda 1 saniyede 10000 pals üretecek (10khz). Z ucundan ise 1 saniyede 10 darbe gelmiş olacak.

4Mhz kristal ile çalışan bir 16F877 normal bir girişinden 10khz lik bir kare dalganın darbelerini sayabilir mi?
Sayabildiğini varsaysak kullanmam gereken değişken CCS C de "long int" mi olmalı ?
Bu darbeleri sayarken aynı zamanda lcd sürmek ve RS232 den veri göndermek gibi diğer işlemleri de yapabilir mi?

Not: bu makine sürekli 600 de gitmiyor. 35-60-150-200-250-300-350-400 gibi basamaklarda gidiyor genellikle.
--EGEMENLİK KAYITSIZ ŞARTSIZ MİLLETİNDİR--

parda

Öncelikle devirde 1000 pals yerine daha düşük çıkış veren bir encoder kullanarak palsleri saymak yerine 2 pals arasındaki süreyi veya tam bir turun süresini ölçerek sonuca gitmeniz sizi hız bilgisine daha çabuk ulaştıracaktır ve diğer işlerinize daha fazla zaman kalacaktır. Lcd ve seri den bilgi göndermenizde hiç bir şeyi etkilemeyecek arka arkaya hızı ölç, lcd yi güncelle, seriden veri gönder gibi en basit program mantığında bile lcd okunamayacak kadar sık güncellenir. Kaldıki bu işleri kesmeler aracılığıyla yaparsanız tadından yenmez herhalde :)

Esen kalın
Bildiğim tek şey, hiç bir şey bilmediğimdir.

stlg

z ucu her turda bir pals uretir onu kullanırsanız birebir olcum yapma imkanınız dogabilir

16 bitlik degiskeni int16 olarak tanımlayabilirsiniz
ALLAHA EMANET OLUNUZ Hoştur bana senden gelen,  Ya gonca gül yahut diken,  Ya kaftandır yahut kefen,  Kereminde hoş lütfunda hoş.

acemi2010

Merhaba;
4MHZ'lik hız ile tek darbeyi ölçmeyi planladığınızı varsayarsak
4 MHz kristal için makina çevrimi 1MHz yapar. Dolayısı ile 10KHz lik darbe için yaklaşık 100 adet makina çevrimi demektir. Bunun anlamı ise 50 makina çevriminde darbenin genliğini ölçer, daha sonraki 50 mak çev'de ise hızı hesap edip Usart'a ve LCD'ye göndermeniz gerekir. Yazılımınızı bu sınıra göre yapmanız ve sığdırmanız mümkün, ama işlemcinin bu limitler içerisinde başka extra işler yapaması güçleşecek anlamına geliyor. Eğer planladığınız Usart haberleşme protokolleriniz karmaşık ise, 100 mak. çev.'i yetmeyecektir kesinlikle .

Ben olsam- 20MHz'lik kristal kullanırım ki toplam üst limitim 500 makina çevrimine çıkar.
- Hız bilgisinin güncellenme periodunun ne olmasına gerektiğine karar verir, buna göre ölçüm mantığını tanımlardım.
- EĞER TEK DARBE ÖLÇÜLECEKSE: CCP modülü kullanırdım (Interrupt'lı yazılım yapardım). Ki 16 bitlik sonuç olacağı için mecburen 2byte'lık bir değişken ile hesaplamaları yapmanız gerekecek.
- EĞER TAM TUR ZAMANI ÖLÇÜLECEKSE : Counter modunda, bir timer ile tam tur zamanını ölçerdim.
- Eğer halen 500 mak çev'i yetmiyorsa (ve 877'de ısrarlı isem - max clk feq = 20MHz) düşük darbe sayısı üreten bir enkodur bulunabilir veya encoder redüktör'lerinden kullanmak da bir çözüm olabilir. 500 mak çevrimi konfigürasyonunuza göre rahat rahat yetiyor gibi görünüyor gerçi ama...

saygılarımla
Timuçin

tayyar

Cevap yazan arkadaşlara çok teşekkürler

Fxdev linkini verdiğin konuyu bulamamıştım aramalarımda heralde gözümden kaçmış :( iyi geldi benim açımdan

devreyi 20mhz de çalıştırabilirim çok eskiklerde PBP ile yazarken 20mhz lik kristal ve lcd ikilisi çok uyumlu olmamıştı o nedenle 4 mhz seçmek istedim. Şimdiki kanımda 20 mhz ccs de lcd ye problem yaratmaz diyor. deneyerek görmek gerekli.

z ucunu kullanmayı bende düşündüm 60rpm üstü hızlarda problem yok ancak 60rpm de 1 sn de 1 darbe gelecek daha aşağı hızlarda kararsızlık olabilir diye düşünüyorum (timer kesmesinin 1 sn olduğunu varsayarak)

ben encoderin A ucunu rb0 kesmesinden değilde normal bir pin yardımıyla okumayı düşünmüştüm. bu nedenle 10khz lik darbeleri sayabilirmiyim diye sordum

normal bir pinden gelen darbeleri okuyacağım sonrasında timer kesmesine girip okunan değeri hıza dönüştürüp lcd ye ve rs232 ye göndereceğim sonra tekrar sayacağım bu şekilde tekrar eden bir döngü olmasını düşünüyorum

hem rb0 hemde timer kesmesinin aynı anda aktif olması progamın çalışmasında sıkıntı yaratabilir diye kalmış aklımda hakikaten öylemiydi yoksa ben çok fazla bilgiyimi unuttum kestiremedim.

pazartesi günü denemelerime başalyacağım bakalım hayırlısı.

bu arada encoderi fabrikanın bakım bölümünden aldığım için farklı bir alternatif seçeneğim yok (aslında var ama oda 1 turda 16000 pals veriyor )
100 palslik bir encoder olsa işler daha kolay olabilirdi evet ama malesef hali hazırda elimin altında yok.

satın almaya kalksak 100 pals lik bir encoder ne kadardır aceba fiyatlarını biliyor musunuz? ve nereden temin edebiliriz?
--EGEMENLİK KAYITSIZ ŞARTSIZ MİLLETİNDİR--

tayyar

Aklımdaki program dizlimi şöyle (sadece timer kesmesi ile)

................
...............
...............include bölümleri
...............

int16 sayac=0;
int hiz=0;

timer kesme ayarları;(1 sn ye kurduğumuzu varsayalım)

timer kesmesi
{

hızı hesapla; //hiz=((sayac/1000)x60)
lcd ye gönder;
rs232ye gönder;
sayacı sıfırla;

}


main void
{

lcd init;
rs232init;
while(true)
{

if(portb.1==1)

{
sayac++;

}

}
}



Bu dizilim çalışır diye umuyorum, sizce ekstra olarak rb0 kesmesine gerek varmı burada ?
--EGEMENLİK KAYITSIZ ŞARTSIZ MİLLETİNDİR--

mufitsozen

while(true){
    while(portb.1==0);
    sayac++;
    while(portb.1==1);
}

seklinde degistirmeniz daha dogru olmazmi?  portb.1 her sifirdan bire gectiginde sayac artirilir. sizin yazdiginiz sekilde portb.1 de bir gordukce sayaci artiriyorsunuz, sadece 0-1 yada 1-0 gecisinde artirmaniz lazim. yoksa sayac yanlis deger icerir

birde rs232 ile yollama ve lcd kisminin timer kesmesinin disinda yapilmasi bence daha iyi olur.
Aptalca bir soru yoktur ve hiç kimse soru sormayı bırakana kadar aptal olmaz.

tayyar

Selamlar;

Encoderimin 100pulselik olduğunu öğrendikten sonra işlerin biraz daha hafifleyeceğini düşünerek yola devam ediyorum.

Encoderden gelen pulseleri saydırma işlemini timer 1 arabirimine yaptırmayı düşünüyorum ancak sormak istediğim bir şey var.
Timer 1 i sayıcı olarak set ettikten sonra belirlenen değere ulaşınca kesmeye gitmesiyle ilgili örnekler mevcut peki biz şöyle yapsak void main den önce kesme alt programı belirtmesek,
#int_timer1
void timer1_kesme()
{
.....
}
void main()

{
...
}



Kısmı olmasa yani



Biz direk main fonksiyonu içerisinde

setup_timer_1(T1_EXTERNAL | t1_div_by_1); //dış sinyal kaynağı ayarlandı
set_timer1(0); // değer 0 dan başalyarak sayacak


Şeklinde sayıcı olarak çalışacağını belirtsek ve programın içerisinde

long x;
x=get_timer1();


şeklinde timer 1 in o anki değerini okumaya çalışsak düzgün bir değer görebilir miyiz??


Amacım yükselen kenarda sayma işlemini timer1 e yaptırıp sonrasında timer2 ile kesmeye giderek hızı hesaplatmak ve diğer işlemleri yaptırmak
--EGEMENLİK KAYITSIZ ŞARTSIZ MİLLETİNDİR--

tayyar

timer 1 kısmı tamam tıkır tıkır sayıyor dış kaynağa bağlı olarak şimdi sıra geldi timer2 ye...

Ben bi yerde hesap hatası yapıyorum galiba

timer2 maksimum ne kadar sürede bir kesmeye gitmek üzere ayarlanabilir? 1 sn 2 sn ???

timer2 bölme oranına 16
PR2 ye 249 (+1 ekleniyor)
post scaler değerine 16 giriyorum

timer2 hesabının sonucu olarak 4mhz de max 64msn kesme süresi alabiliyorum

ben bunu 1-2 sn ye ayarlayamaz mıyım? ayarlanabiliyorsa nasıl?

düz bir hesap yapıyorum şimdi (teorik hesap)

benim encoder çıkışımdan 1 turda 100 darbe geliyor
600RPM ile dönüyor olsa 60 sn de 600 tur, 1 sn de 10 tur dönüyor demektir

bu durumda 1 sn de encoderden 1000 pals gelir.

buda 1 msn de 1 pals demek

kesme 64 msn de olacaksa benim sayabilecegim maksimum değer 64???

burada benim timer2 kesme süresini arttırmam gerekiyor
nasıl yapılır nasıl edilir ?

yoksa ben yanlışmı hesaplıyorum ????

----------

şimdiye kadar yaptıklarımdan bahsedeyim kısaca

timer1 modülünü dış kaynaktan tetiklenecek şekilde sayıcı olarak ayarladım
böylece main fonksiyonu içerisinde biraz daha serbestlik yakalayabiliyorum

timer1 in saydığı darbeleri lcd ye attım problem yok tıkır tıkır artıyor 1khz de okuyabiliyorum

şimdi timer2 yi de kurup hız bilgisine geçeceğim ayhnı zamanda lcd veri gönderimini de timer2 kesmesi içerisinde halledeceğim.

tabi timer2 yi adam gibi kurabilirsem...
--EGEMENLİK KAYITSIZ ŞARTSIZ MİLLETİNDİR--

JKramer

Ben timer2'yi kullanma amacınızı anlayamadım. İstediğiniz, belirlediğiniz zamanda hız bilgisini hesaplama ve lcd'yi tazeleme mi? Timer2 kesmesi içerisine her kesmede bir arttırdığınız bir değişken koyup, değeri istediğiniz zamanla örtüştüğünde bir değişkeni set edersiniz. Bu değişkenin de ana döngüde set edilip edilmediğini kontrol edersiniz, set edilmişse hesaplamaları yapıp lcd'yi güncellersiniz.

Bir de projenizde en önemli kısım darbelerin sayılması, bence önceliği en yüksek olan iş için kesme (timer1) kullanmak yerine ana döngüde sayıcı içeriğini okumak ileride sıkıntı yaratabilir.

tayyar

Hocam timer1i sayıcı olarak çalıştırdım. main fonksiyonu içerisinde sayma işlemini yapıyor
timer2 yi de yaklasım 60ms ye kurdum
global bir değişken tanımlayıp timer2 alt programı içerisinde kesme geldiğinde 1 arttırarak kullanıyorum
100 adet kesme geldiginde ki bu 6 saniye demek oluyor (yaklasık olarak)
timer2 alt programı lcd ye timer1 in değerini yazdırıyor.
ayrıca s değişkeninin değerinide lcd ye yazdırarak kaçıncı kesmede olduğunu görebiliyorum.



#include "main.h"
#include <LCD.C>
#use fast_io(b)
#use fast_io(d)
long x=0;
int8 s=0;


#int_timer2
void timer2_kesme()
{

   disable_interrupts(INT_timer2);
   disable_interrupts(GLOBAL);
   s++;
      if(s==20)
      {
      x=x/2;
      printf(LCD_PUTC, "\f%Lu",x);

      s=0;
      set_timer1(0);
      }
      printf(LCD_PUTC, "\n%d",s);
}
void main()
{
   setup_adc_ports(NO_ANALOGS);
   setup_adc(ADC_OFF);
   setup_psp(PSP_DISABLED);
   setup_spi(SPI_SS_DISABLED);
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
   setup_timer_1(T1_EXTERNAL|T1_DIV_BY_1);
   setup_timer_2(T2_DIV_BY_16,234,16);
   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);

   set_tris_c(0x0F);
   set_tris_d(0x00);
   lcd_init();
   set_timer1(0);
   
   while(true)
   {
   enable_interrupts(INT_timer2);
   enable_interrupts(GLOBAL);
 
   x=get_timer1();
    }

}



--EGEMENLİK KAYITSIZ ŞARTSIZ MİLLETİNDİR--

OG

Frekans ölçmek yerine @parda'nın bahsettiği yöntem (period ölçmek) daha hızlı sonuç verir. Ancak onda da sonuçlar fazla sayıda değişken içereceğinden bir ortalama alman gerekebilir.

Frekans ölçmeyi tercih ediyorsanız frekansmetrellerdeki gibi RB0/INT daha hızlı sonuç verecektir.



FORUMU İLGİLENDİREN KONULARA ÖM İLE CEVAP VERİLMEZ.

tayyar

[IMG]http://i33.tinypic.com/9gaotz.png[/img]

#include "main.h"
#include <LCD.C>
#use fast_io(b)
#use fast_io(d)
long x=0;
int8 s=0;
#int_timer2
void timer2_kesme()
   {
      disable_interrupts(INT_timer2);
      disable_interrupts(GLOBAL);
      s++;
         if(s==20)
         {
         x=x/2;
         printf(LCD_PUTC, "\f%Lu",x);
         s=0;
         set_timer1(0);
         }
         printf(LCD_PUTC, "\n%d",s); // bu saır sadece geri bildirim için
   }
void main()
{
   setup_adc_ports(NO_ANALOGS);
   setup_adc(ADC_OFF);
   setup_psp(PSP_DISABLED);
   setup_spi(SPI_SS_DISABLED);
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
   setup_timer_1(T1_EXTERNAL|T1_DIV_BY_1);
   setup_timer_2(T2_DIV_BY_16,234,16);
   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);
      set_tris_c(0x0F);
      set_tris_d(0x00);
      lcd_init();
      set_timer1(0);
         while(true)
            {
            enable_interrupts(INT_timer2);
            enable_interrupts(GLOBAL);
               x=get_timer1();
            }

}


Timer1 ile yaptım oldu sanırım. tabi programda olmayacak şeyler vardır/olabilir. henüz bunun çekirdek program olduğu geliştirilmesi gerektiği unutulmamalıdır. girişine maksimum 1khz gelecek olan bir sayıcı için timer1 yeterli değilmidir sizce ? PIC sayma işlemi sırasında başka işlerle de uğraşabiliyor olduğu için bana daha mantıklı geldi rb0/int e göre.

fikirlerinizi paylaşırsanız sevinirim. eleştirin lütfen.

Dosyalar:
http://www.4shared.com/file/l8JhrLhp/lcd.html
--EGEMENLİK KAYITSIZ ŞARTSIZ MİLLETİNDİR--

ErsinErce

Kesmenin içindeki printf'leri kaldırırsanız sonuçlar düzelicektir, kesme içindeki işlemleri mümkün olduğunca az tutmaya çalışın

tayyar

similasyonda problem yok gibi gözüküyor gerçekte henüz deneme fırsatım olmadı baklım problem çıkarsa dediğiniz gibi deneyeceğim
--EGEMENLİK KAYITSIZ ŞARTSIZ MİLLETİNDİR--