CCP ile devir ölçümü

Başlatan Mucit23, 15 Aralık 2014, 10:06:33

Mucit23

Merhaba arkadaşlar.

628 ile devir ölçmeye çalışıyorum. Şuanda yaptığım iş 1 sn içerinde timer1 ile gelen puls'ları sayıp elde ettiğim bu sayıyı 60 ile çarpıp ekrana yazıyorum. Fakat çözünürlüğüm çok düşük oluyor.

Örneğin Timer girişine 1hz uygulasam 60rpm olarak ekranda görüyorum. Frekansı 2Hz'e çıkartıyorum devir sayısı 120 oluyor normal olarak. Fakat frekansı 1.5Hz yapsam bu sefer devir sayısı 60-120 arasında değişiyor.

Timer yöntemi hassasiyeti düşürüyor.  Benim iki puls arası süreyi ölçmem gerekiyor. Bunuda CCP donanımı ile yapmak istiyorum. Daha önce bu donanımı pwm özelliği dışında kullanmadım. CCP yi burada nasıl kullanmam gerekiyor?

Kabil ATICI

Düşük hızlarda çözünürlüğü artırmak için frekans yerine periyot ölçülüyor.
1 saniyedeki sinyal sayısı yerine , 1 sinyal periyodu (darbe+boşluk) esnasındaki darbe sayısı. Frekansa çevirmek için 1/T şeklinde alıyorsun.
Senin projende dişarıdan ölçeceğin sinyali için harici kesme kullan. Timer1  normal timer olarak ayarla.
ilk gelen kesmede timer1 başlat, ikincisinde durdur. (herikiside yükselen kenarda)Timer1 bilgisini al ve değerlendir, okuyacağın değer sinyalin periyodunu verir. Timer1 ön bölücülerinin ayarı da hassasiyetini.
ambar7


sadogan

CCS kodları
#INT_CCP2   
void capture_int ()
{
   set_timer1(0);
}

   /////////////////init kodları//////////////////////
   setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
   setup_ccp2(CCP_CAPTURE_RE); //yükselen kenarda
   CCP_2_HIGH=0x00; 
   CCP_2_LOW=0x00;  
   set_timer1(0);
   enable_interrupts(INT_CCP2);
   enable_interrupts(GLOBAL);
//////////////////////////////////////////

Sistem şu sekilde çalışıyor:
CCP2 interruptu aktif edilince ilgili, pin her yükselen kanarda
interrupt oluşturuyor ve tmr1 içerigini "CCP_2" değişkenine aktarıyor.
CCP_2 değişkeni ccs c tarafından tanımlamış.

Mucit23

Ben kendi aklımdaki yöntemi yazayım. Çalışacağını tahmin ediyorum.

CCP kesmesini yükselen kenarda oluşacak şekilde kuracağım. Ayrıca timer1'i de her 1 ms aralıklarla kesme oluşturacak şekilde ayarlayacağım. Sonra ccp kesmesi oluştuğunda bir bayrağı aktif edeceğim. Tekrar CCP kesmesi oluştuğunda bayrağı 0 yapacağım. Timer1 interrupt rutini içerisinde ise bu bayrak 1 ise her kesme oluştuğunda bir sayacın değerini arttıracağım. Bayrak 0 olduğunda ise sayacın değerini alacağım. Bu sayacın değeri benim doğrudan peryodum dur.

sayacın değeri ms cinsinden olduğu için devir=(1000/sayac)*60 şeklinde bir formül ile devri hesaplarım diye düşünüyorum. Akşam hemen deneyeceğim.

Salih

RB0 kesmesini önce yükselen kenar olarak ayarla,
bu ayarda iken kesmeye gidince Tmr1'i sıfırla,
aynı anda kesmeyi düşen kenar olarak ayarla,
düşen kenarda kesmeye gidilince Tmr1'i okuyarak
devir sayısını hesapla. Yine kesmeyi yükselen kenar olarak ayarla.

Mucit23

Ben bir deneme yaptım. Önceki mesajımda anlattığım yöntemi uyguladım. Periyot ölçmem gerekiyor. Puls genişliği işimi görmüyor. Sebebi ise devir ölçümünü motor miline yapıştırılacak olan beyaz bir band vasıtasıyla yapmam gerekmesi.

RB0 kesmesi yerine CCP  yükselen kenar kesmesini kullandım. ilk denememde 20-30Hz'e kadar gayet iyi bir sonuç aldım. Fakat yüksek frekanslarda çözünürlüğümün çok düştüğünü gördüm. Bende hemen kesme oluşma süresini 1ms den 100us ye düşürdüm. Bununla birlikte pic16F628'deki 4mhz dahili osilatörden vazgeçip 20mhz harici osilatör bağlamak zorunda kaldım. Şuanda 100uS de çözünürlük idare eder. Fakat pek tatmin edici değil.

Yazdığım kodlar aşağıdaki gibi.
#include <16F628A.h>

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES HS                       //Internal RC Osc, no CLKOUT
#FUSES NOBROWNOUT               //No brownout reset
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O

#use delay(clock=20000000)

#use fast_io(a)
#use fast_io(b)

#include <lcd_driver.c>

static float frequency=0,rpm=0;
static int16 period=0,period_counter=0;
static int1 ccp_flag1=0,ccp_flag2=0;

//****************** Timer1 Kesmesi *****************************
#int_timer1 // Timer1 kesmesi
void Timer1_isr()
{ 
set_timer1(65067);//100us kesme için gerekli preload değeri

  if(ccp_flag1)
  {
    period_counter++;
  }
  if(ccp_flag2)
  {
    period=period_counter;
    period_counter=0;
    ccp_flag1=0;ccp_flag2=0;
  }
  output_bit(pin_b0,ccp_flag1);

clear_interrupt(int_timer1);       
}

#int_ccp1
void ccp1_isr()
{
  if(ccp_flag1)ccp_flag2=1;
  ccp_flag1=1;
  clear_interrupt(int_ccp1);  
}

void main()
{
  set_tris_a(0x00);
  set_tris_b(0x08);
  output_a(0x00);
  output_b(0x00);
  
  setup_ccp1(CCP_CAPTURE_RE);
  setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
  set_timer1(65043);
  enable_interrupts(INT_timer1);
  clear_interrupt(INT_CCP1);
  enable_interrupts(INT_CCP1);
  enable_interrupts(GLOBAL); 
 
  lcd_init();
  
  lcd_gotoxy(1,1);

   while(TRUE)
   {
      frequency=(float)10000/period;
      rpm=frequency*60;
      lcd_gotoxy(1,1);
      printf(lcd_putc,"Freq=%.1fHz ",frequency);
      lcd_gotoxy(1,2);   
      printf(lcd_putc,"Hiz = %.1fd/dk ",rpm);      
      
   }

}


Şöyle bir soru sorayım. 0-5Hz ile 500Hz arasını mümkün olan en yüksek hassasiyette ölçmek istersem ne yapmam gerekir. Harici sayac kullanmadan salih hocamın bahsettiği yöntemi uygulasam düşük frekanslarda timerde taşma olur. 16 bit timer yetmez 32 bit timer lazım olur.

Okan AKÇA

#7
devir dakikadaki tur sayısı olduguna göre 1sn periyotta rb0 kesmesi veya cpp ile tur sayısını  her dakikanın sonunda  baska degişkene aktar hepsi bu

mesaj birleştirme:: 15 Aralık 2014, 22:48:22

#int_ext
void  ext_kesmesi ()   
{
  disable_interrupts(INT_timer0);
  disable_interrupts(INT_timer3);
 
 
if(motor_yonu==0)
{
  if(input(pin_b0)==1&&input(pin_b1)==0)    if(encoder>0) encoder--;
  if(input(pin_b0)==1&&input(pin_b1)==1)    encoder++;               
}

if(motor_yonu==1)
  {
  if(input(pin_b0)==1&&input(pin_b1)==1)    if(encoder>0) encoder--;
  if(input(pin_b0)==1&&input(pin_b1)==0)    encoder++;   
 
  }
rpm_buffer++;
enable_interrupts(INT_timer0);
enable_interrupts(INT_timer3);

}



#int_timer3
void  timer3_kesme ()   
{
set_timer3(45536);rpm_timer++;
if(rpm_timer>=4)
  {
  rpm_timer=0;
  rpm=rpm_buffer;
  rpm_buffer=0;
  }



Kabil ATICI

74hc590 ile 32 bit veya 24 bit periyotmetre yapın ve 8 bit port üzerinden ölçtüğünüz değeri okuyun. Sadece devre biraz karışık olur.
ambar7

Mucit23

Hocam gerek kalmadı. Şuanda Pic16F628&20Mhz osilatör ile 0.5Hz ile 200Hz arasını yeterli hassasiyette ölçebiliyorum. Buda 10000rpm'in üstünde bir değer elde etmek demek. Bundan sonrası için frekans bölücüler kademeler vs yapmak gerekir. Yada en sağlamı puls saymak.

sadogan

Alıntı yapılan: Mucit23 - 15 Aralık 2014, 12:55:48
Ben kendi aklımdaki yöntemi yazayım. Çalışacağını tahmin ediyorum.

CCP kesmesini yükselen kenarda oluşacak şekilde kuracağım. Ayrıca timer1'i de her 1 ms aralıklarla kesme oluşturacak şekilde ayarlayacağım. Sonra ccp kesmesi oluştuğunda bir bayrağı aktif edeceğim. Tekrar CCP kesmesi oluştuğunda bayrağı 0 yapacağım. Timer1 interrupt rutini içerisinde ise bu bayrak 1 ise her kesme oluştuğunda bir sayacın değerini arttıracağım. Bayrak 0 olduğunda ise sayacın değerini alacağım. Bu sayacın değeri benim doğrudan peryodum dur.

sayacın değeri ms cinsinden olduğu için devir=(1000/sayac)*60 şeklinde bir formül ile devri hesaplarım diye düşünüyorum. Akşam hemen deneyeceğim.
Bu şekilde ölçüm yapacak isen capture ile yapmanın bir anlamı yok.
external interrupt ile yapmak ile aynı.
ccp1 interrupt geldiğinde tmr1 içeriği ccp_1 (16 bitlik) değişkenine aktarılıyor.
tmr1 içeriği sizin peryodunuz zaten. tmr1 içeriğinden peryotun süresini hesaplayıp sonrada
gelen darbenin frekansını hesaplayabilirsin.
Ancak düşük frekanslarda tmr1 de taşma olabilir o zaman tmr1 interrupt u kullanmak sorunu çözer.


hakanmos

Merhabalar,

Mucit hocam, yazmış olduğunuz programı denedim. Fakat; "Hocam gerek kalmadı. Şuanda Pic16F628&20Mhz osilatör ile 0.5Hz ile 200Hz arasını yeterli hassasiyette ölçebiliyorum. Buda 10000rpm'in üstünde bir değer elde etmek demek" yazmışsınız. Bu son hassas devir ölçüm programını da paylaşabilir misiniz?