PIC C İLE FREKANS ÖLÇME (YARDIM İHTİYACI)

Başlatan atiframazanoglu, 20 Kasım 2008, 10:50:45

atiframazanoglu

PCWH PIC C PROGRAMINI KULLANARAK PIC18F452'YE YAZILIM YAPMAYA ÇALIŞIYORUM. YAZILIMIN KONUSU RD5'E (28NCİ BACAK) GÖNDERİLEN PULSE'IN FREKANSINI PIC ÖLÇMELİ. ANA KONU BUDUR. GERİYE KALANINI HALLEDEBİLECEĞİMİ DÜŞÜNÜYORUM AMA BAŞINDA TAKILDIM KALDIM. AKLIMA delay_ms( ) KOMUTU GELDİ. BU KOMUTUN İÇİNDE İŞLEM YAPABİLİYORMUYUM BİLMİYORUM, YOKSA BU KOMUT KENDİNE HAS KULLANILAN BİR KOMUT MU? YA DA BU KOMUTLA OLMAZ İSE HANGİ KOMUTU KULLANABİLİRİM? BENİM DERDİM 2 SANİYE İÇİNDE BU FREKANS ÖLÇÜMÜNÜ YAPTIRABİLMEK. 2 SANİYENİN BİTİMİNDE ÖLÇÜLMÜŞ OLAN FREKANSA GÖRE İŞLEM YAPTIRACAĞIM. YARDIMINIZI RİCA EDİYORUM.

kemalguler



cmmkmh

Arkadaşlar merhaba,

Ben de bukonuda sizlerden yardım bekliyorum. Benim de derdim kısaca yukarıda bahsedlen gibi ancak ben temelde mantığı anlamakda zorluk çekiyorum. Forumda incelediğimde bu konu ile ilgili biçok başlık açılmış ama okudukça kafam daha da karıştı . Timer ve interrupt komutları kullanılmış. Öncelikle bu komutları araştırdım temel olarak ne işe yaradıklarını anladım. ancak frekans ölçüümünde bu ikili komutun hangi mantıkla kullanıldığını basitça anlatabilecek bir arkadaş varsa çok memnun olurum. temel olarak şunu yapmak istiyorum;
  Elimde maksimum 16 kHz sinyal verecek bir sensor var. Bu sensorü kullanarak küçük bir gaztürbini kontrolü yapıyorum. Moturun üzerinden sıcaklık ve rpm ölçümleri alıp geri besleme olarak PIC'e göndereceğim (30F4013, CCS C  kullanıyorum). elimdeki RPM sensorunden aldığım sinyalleri işlemem lazım. ancak işin ABC'sini bir türlü oturtamadım. yani temel mantığı nedir?
yardımlarınızı bekliyorum.. :(  :(

Erol YILMAZ

Frekans ölçmek için temelde 2 tane yöntem var.

1. Yüksek Frekanslar için:
Belirli bir sürede gelen harici sinyalleri saymak !

2. Düşük Frekanslar için:
Harici sinyaller arasında hızlı bir OSC ile bilinen OSC'nin sinyallerini saymak.

1 hz den 2 khz e kadar olan bir sinyalin frekansını ölçeceksem,
bunu düşük frekanslar grubuna kabul ederim ve
ve mesela 200 kHz lik başka bir bilinen OSC sinyalini sayarım.
Harici Pals geldiğinde bir kesme oluşturur ve sayaç değerime bakar
ve bir sonraki ölçüm için SIFIRlarım.

Bir de en önemli kriter olarak dsPIC gibi kaliteli bi mcu yu CCS kodları ile kirletmem. :P

cmmkmh

Allegro Hocam öncelikle teşekkürler,
Sonunda anlayabileceğim bir lisanda bir bilgi ile karşılaştım :). Şimdi isterseniz yaptığım işi kısaca anlatayım. Yukarıda değindiğim gibi küçük gaz türbin motorunun devir / dak sını hesaplamak istiyorum bunu yaparken elimdeki sensör ile 7 tane kanatı bulunan kompresörün kanat geçişlerini saycağım. Yanlış düşünmüyorsam 8 high sinyali bir devire karşılık gelmekte. motor maximum 110 000 rpm (dev/dak) hızda dönmekte. minimum (başlatma presedüründe sıfırdan başlamakta tabi ki) 30 000 rpm de çalışmakta.
Bu durumda
-Maksimum; 110 000 rpm = 1833.33 dev/saniye=1833.33*8=14 666.66 pulse/saniye,
-Minimum ; 30 000 rpm = 500 dev/saniye= 4000 pulse/ saniye
olarak hesaplanabilir (Eğer yanlış düşünmüyorsam)
Bu hesaplamaya göre 4 ile 15 kHz arasında çalışmış olmaktayım. Yani sizin belirttiğiniz sınıflamaya göre yüksek frekans aralığında olmakta.

Şimdi sizin yaptığınız kesme ve timer açıklamasına gelecek olursak.
Bir Kitapta (PIC Microcontrollers "An Introduction to Microelectroniks", BATES, Martin);
1- ilk seçenekte sunduğunuz belirli bir zaman aralığındaki pulse sayısını ölçme metodu için örneğin 1 sn için hata yüzdesinin %2 gibi olduğu (tabi kitaptaki örnek için) ve 1 sn beklemenin de partik uygulamalarda pek uygun olmadığı gibi bir yorum yapılmış. Herhangi bir frekans aralığı da belirtmemiş.
2- ikinci seçenekte anlattığınız yöntemin kullanıldığından bahsetmiş. Şimdisizin anlattıklarınızı anlayıp anlamadığımı test etmek istiyorum :)

-Mesela elimizde sizin dediğiniz gibi frekansı bilinen bir sinyalimiz olsun (bu sinyal,  değerlerini ayarladığımız "timer" lardan biri oluyor değil mi?)
-Şimdi Harici sinyal gelince kesme oluyor ve biz bu anda sayaç ile kaç sinyal oluştuğunu sayıyoruz. sayılan bu sinyallerin frekansını bildiğimizden de kendi sinyalimizi buluyoruz.. Şimdi umarım yanlış anlamamışşımdır. Peki internetten araştırırken dikkatimi çekmişti. Kesmeler timerın aşma değerini geçtikten sonra oluşuyor die bişeyler okumuştum. Sizin belirttiğinizde ise  her high sinyalinde bir kesme mi oluşuyor? bu kısımı biraz detaylandırabilir misniz? Aslında en çok takıldığım nokta burası..

Çok uzun oldu kusura bakmayın...

yardımlarınız için tekrar teşekkürler..

cmmkmh


atiframazanoglu

Öncelikle arkadaşlar hepinize kafa yorduğunuz ve zaman ayırdığınız için teşekkür ederim.
Size yazdığım bu mevzu hakkında örnek bir program elimde var. Program şöyle;
LIST	P=18F452
#INCLUDE "P18F452.INC"

LEDLER		EQU		h'80'
			ORG		h'0000'
			GOTO	BASLA
			ORG		h'0004'
			GOTO	KESME_ALT_PROG
BASLA
			CLRF	TRISC
			CLRF	PORTB
			BANKSEL	TRISB
			MOVLW	B'00001000'
			MOVWF	TRISB
			BSF		PIE1,2
			BANKSEL	PORTB

			MOVLW	B'00000101'
			MOVWF	CCP2CON

			MOVLW	b'00000001'
			MOVLW	T0CON
			
			MOVLW	H'C0'
			MOVWF	INTCON
			CLRF	LEDLER
SONSUZ_DONGU
			GOTO	SONSUZ_DONGU
KESME_ALT_PROG
			CLRF	TMR0L
			CLRF	TMR0H
			BCF		INTCON,GIE

			MOVLW	b'00000000'
			MOVWF	LEDLER
			MOVLW	d'100'
			SUBWF	CCPR2H,W
			BTFSC	STATUS,C
			GOTO	ANA_PROG_DON

			MOVLW	b'00010000'
			MOVWF	LEDLER
			MOVLW	d'80'
			SUBWF	CCPR2H,W
			BTFSC	STATUS,C
			GOTO	ANA_PROG_DON

			MOVLW	b'00110000'
			MOVWF	LEDLER
			MOVLW	d'60'
			SUBWF	CCPR2H,W
			BTFSC	STATUS,C
			GOTO	ANA_PROG_DON

			MOVLW	b'01110000'
			MOVWF	LEDLER
			MOVLW	d'40'
			SUBWF	CCPR2H,W
			BTFSC	STATUS,C
			GOTO	ANA_PROG_DON

			MOVLW	b'11110000'
			MOVWF	LEDLER
			
ANA_PROG_DON
			MOVF	LEDLER,W
			MOVWF	PORTC
			BCF		PIR1,2
			
			BSF		INTCON,GIE
			RETFIE

			END

Tabi bu program başta 16f628'e göreydi ve ben biraz oynadım. İçinde belki hatalarım olabilir. Hatta var ki PROTEUS 'da denediğimde tam isteneni sağlamadı.
Programın amacı dört adet led'i frekansın miktarına göre teker teker yakarak bir bar oluşturmak. Yani ne kadar frekans, o kadar yanan led.
Ben bu programı doğru çalıştıramadım ama ben bunu çalıştırırım ve ne istiyorsan dahasını da yaptırırım diyebilen bir babayiğit varsa ücreti karşılığında da bu programı yazdırmak istiyorum.
Forum kurallarına göre telefon veya mail yazmak uygun mu bilmiyorum ama bana buradan dönerseniz ben de iletişime geçerim.

Lütfen kodlarımızı code tag içine alalım

ta5cnj

bir örnek vereyim.ccs c ile yazılmıştır. 3310 lcd kullanmıştım. ama farklı  lcd kullanabilirsiniz.
#include "16F877.h"
//#include "lcd.c"
#include "nokia3310.c"

#use delay (clock=4000000)

#use fast_io(D)
#byte lcd        =0x08
#byte tris_lcd    =0x88
/*
// NOKIA_LCD bits
#bit nok_sclk    =lcd.5   // RD5   
#bit nok_sda     =lcd.4   // RD4
#bit nok_dc      =lcd.0   // RD0
#bit nok_cs      =lcd.3   // RD3
#bit nok_res     =lcd.1   // RD1 */

//------------------------------------------------------------------------------
int   durum=0;
int16 value_timer1=0x3CAF;
int   sayac_tmr1=0;
int   sayac0=0;
int   sayac1=0;
int   sayac2=0;
int   sayac3=0;
int   sayac4=0;
char  sayac0_lcd;
char  sayac1_lcd;
char  sayac2_lcd;
char  sayac3_lcd;
char  sayac4_lcd;
//------------------------------------------------------------------------------
int   kesme_tmr1(void);
int   kesme_ext(void);
int   duzenle(int);
//------------------------------------------------------------------------------
// 1 sn lik timer1 kesmesi
//------------------------------------------------------------------------------
#INT_TIMER1
int   kesme_tmr1()
{
   sayac_tmr1++;
   if(sayac_tmr1==20)
   {
      sayac_tmr1=0;
    //  lcd_gotoxy(9,1);
      nokia_gotoxy(9,2);

      sayac0_lcd=duzenle(sayac0);
      sayac1_lcd=duzenle(sayac1);
      sayac2_lcd=duzenle(sayac2);
      sayac3_lcd=duzenle(sayac3);
      sayac4_lcd=duzenle(sayac4);
      
     /* lcd_putc(sayac4_lcd);      
      lcd_putc(sayac3_lcd);      
      lcd_putc(sayac2_lcd);      
      lcd_putc(sayac1_lcd);      
      lcd_putc(sayac0_lcd); */
      printf(nokia_printchar,"=%03u",sayac4_lcd);
      printf(nokia_printchar,"=%03u",sayac3_lcd);
      printf(nokia_printchar,"=%03u",sayac2_lcd);
      printf(nokia_printchar,"=%03u",sayac1_lcd);
      printf(nokia_printchar,"=%03u",sayac0_lcd);

      sayac0=0;     
      sayac1=0;     
      sayac2=0;     
      sayac3=0;     
      sayac4=0;     
   } 
   set_timer1(value_timer1);
}
//------------------------------------------------------------------------------
#INT_EXT
int   kesme_ext()
{
   sayac0++;
   if(sayac0==10)
   {
      sayac0=0;
      sayac1++;
      if(sayac1==10)
      {
         sayac1=0;
         sayac2++;
         if(sayac2==10)
         {
            sayac2=0;
            sayac3++;
            if(sayac3==10)
            {
               sayac3=0;
               sayac4++;
               if(sayac4==10)
               {
                  sayac4=0;
               }
            }
         }
      }
   }
}
//------------------------------------------------------------------------------
int   duzenle(int a)
{
   if(a==0)
   {
      return   '0';
   }
   if(a==1)
   {
      return   '1';
   }
   if(a==2)
   {
      return   '2';
   }
   if(a==3)
   {
      return   '3';
   }
   if(a==4)
   {
      return   '4';
   }
   if(a==5)
   {
      return   '5';
   }
   if(a==6)
   {
      return   '6';
   }
   if(a==7)
   {
      return   '7';
   }
   if(a==8)
   {
      return   '8';
   }
   if(a==9)
   {
      return   '9';
   }
}      
//------------------------------------------------------------------------------
void main(void)
{
//      SET_TRIS_b(0xFF);
//      output_b(0x00);
      setup_timer_1 ( T1_INTERNAL | T1_DIV_BY_1 ); 
      set_timer1(value_timer1);
         
     // lcd_init();
      nokia_init(); 
     // lcd_putc("Frekans=");
      nokia_gotoxy(0,2);
      printf(nokia_printchar,"FREKANS ");

      enable_interrupts(INT_EXT);
      enable_interrupts(INT_TIMER1);
      enable_interrupts(GLOBAL);

      while(TRUE);
}


Lütfen kodlarımızı code tagları içine alıp verelim
TA5CT

cmmkmh

Arkadaşlar merhaba,
Öncelikle yardımlarınız için tekrar teşekkür ederim. İyi niyetle birçok kod ve yorum ilettiniz ancak ben yukarıdaki kodlar anlayacak kadar interruptlar ve timer ile çalışmamıştım. Benim istediğim temel bir kod yazılarak bu işleyişi anlamaktı. Derdimi anlatamadım herhalde. Bu arada Allegro hocamın bahsettiği metodlardan belirli bir süre bekleyerek frekans ölçme işini yaptım. Aşağıda kodu veriyorum;
#include <30F4013.h>
#device ADC=10
#use delay(clock=10000000)
#FUSES HS 
#use rs232(UART2,baud=9600,xmit=PIN_F3,rcv=PIN_F2,parity=N,bits=8)
#use fast_io(B)
#use fast_io(C)
#use fast_io(D)
#use fast_io(F)
unsigned int16 external;
unsigned int16 freq;
unsigned int16 N;
void main(){
/*---------------------------PWM--------------------*/
setup_timer2(TMR_INTERNAL,6000);   
duty =0;       
setup_compare(3,COMPARE_PWM | COMPARE_TIMER2);
/*-------------------------Timer-----------------*/
setup_timer1(TMR_EXTERNAL);
/*-------------------------------------------------*/
while (true){ 
  set_pwm_duty(3, 3000);
   set_timer1(0);  
   delay_ms(100); 
   external =  get_timer1();
   set_timer1(0);   
   freq=external*10;
   N=freq/8*60;
   printf("frekans=%ld \n\r",freq);
   printf("devir=%ld \n\r",N);
}
}

Yukarıda basitce yapmaya çalıştım ve program çalıştı. Amacım burada PWM den ürettiğim %50 duty'e sahip sinyali timer1 ile ölçmekti sonuça olarak bunu yapabildim.Mantık çok basit olamsına rağmen benim gibi zorlanan arkadaşlar için bu kısımı anlatmak istiyorum;
Öncelikle timer2 'nin Period register (PRx) değerini 6000 olarak ayarladım. Bu da timer frekansının  
f_timer=f_clock/[(1+PRx)*4] = 10 000 000 /[(6000+1)*4] =416.59 Hz olarak ayarlanmasını sağladı. Ayrıca PWM komutunda duty için %50 değerini ayarlayabilmek amacıyla 6000*0.5=3000 değerini kullandım. Sonuç olarak elimde %50 duty cycle'a sahip ~ 417 Hz lik bir sinyal olmuş oldu. Bu sinyali de timer1'e vererek ölçüm almaya çalıştım.
Bu noktada Allegro hocamızın belirttiği ilk metodu (sadece onu çözebildiğim için:) kullandım. Amaç belirli bir süre timerın çalışmasına izin verip gelen dış sinyallerle saymasını sağlamak ve daha sonra bu belirli süreyi kullanarak gelen sinyalin frekansını oradan da dev/dak hesabına geçmek. Ben yukarıda da gördüğünüz gibi 100 ms bekleme yaptım. Tabi bundan önce timer1 değerinin sıfırlanması gerekmekte. timer1 sıfırlandıktan hemen sonra 100 ms bekleme ve hemen sonra da timer değerinin okunup devir işlemine geçtim. Burada 100 ms de external kadar sinyal aldıysam 1000 ms de şukadar alırım oran orantıdan değeri 10 ile çarptım ve frekans değerini buldum. sonra da elimdeki motorun bir devirde 8 sinyal vermesi sebebiyle bu değeri 8 ile çarpıp Dev/saniye. daha sonra da 60 ile çarpıp dev/dakikaya geçtim. buraya kadar sorun yok:)
şimdi gelelim sorun olan kısıma;

Elimdeki motoru fuzzy logic ile oluşturduğum kontrolcü ile kontrol etmek istiyorum. ve kodun geri kalan kısımlarında bi ton hesaplama vs var. Her bir döngü için gaz türbinine vereceğim manüpulasyon değerini hesaplayıp yakıt pompasını sürüyorum. Yani eğer ben  her bir döngüde 100 ms beklersem -ki bu pek de az bir zaman değil- benim manüpulasyon değerim 100 ms boyunca sabit kalacak ve motor o süre zarfında kontrol algoritmasının manüpulasyonundan çıkacak. Eğer bu değeri 10 ms 'ye çekersem bu kez de düşük hızlarda devir değeri tam olarak hesaplanamamakta. yani 3000 devir gibi bir çalışma rejiminde (motor normalde başlatma prosesinden sonra 30 bin ila 110 bin arasında çalışıyor ama başlatma işlemi elektrik motoru ile başlayıp 9000 rmp den başlıyor.) doğru değerleri okuyamıyorum. Bu noktalarda ne kadar kritik onun karar verme aşamasındayım ancak böyle bir problem var.

Şimdi az çok yukarıda ne kadar bir pic bilgisine sahip olduğumu ve neler yapıp yapamıyacağımı anlatabilmişimdir :) yeni başlamış biri olarak basit,  bir şekilde allegro hocamın bahsettiği 2. metodu bana anlatıp basit bir CCS C kodu verecek arkadaş var mıdır acep :roll:

Yani kesme değeri oluşturup kesme alt programına gidip daha sonra bilinen timer ile harici timerin değerlerinin karşılaştırılması işlemi nasıl yapılır kabaca?
-gene "harici sinyalin bir yükselişinde kesme verip timer değerini hafızaya almak ve diğer yükselişte oluşan ikinci kesme nin timer değerini alıp fark almak" gibi bir metottan bahsedilmiş bu temelde nasıl çalışmakta?
Çok şey istiyorum farkındayım ama:(  eğer gerçekten  doğru düzgün anlarsam, üye olduğum forumlarda benim gibi yeni başlayan arkadaşların anlayabileceği dilde yazmayı düşünüyorum:)

Kodlarımızı code tagları içinde verelimmm   :evil:

KAZIMUGUR

Malum :
f=1/T >>> T=1/f

Frekans yüksekse , birim zaman aralığında kaç tanedir sayarsın.
Frekans düşükse , çok beklememek için , periyodu ölçersin(bir yükselişden diğer yükselişe , ya da düşüşler , süresi = iki işaret arasında başka bir sayıcı)
Karar vermek içinde , ölçeceğin işaretin frekansı ve ölçme sıklığın ile ilgili yorum var.
(her iki yolda doğruluğu yüksek referans zamanlayıcı gerektiriyor + programı fazla meşgul etme)...
...demek istedi eski CCS ci. :)
Fazlaca çarpmak iyi değil hata katlanır....

cmmkmh

Merhaba arkadaşlar,
Kesme fonksiyonunu kullanarak frekans saydırma işlemi için aşağıdaki programı yazdım. Ancak ekrana yazdırdığı değer çok alakasız bir değer. kod şu şekilde;
#include <30F4013.h>
#device ADC=10
#use delay(clock=10000000)
#FUSES HS  
#use rs232(UART2,baud=9600,xmit=PIN_F3,rcv=PIN_F2,parity=N,bits=8)
#use fast_io(B)
#use fast_io(C)
#use fast_io(D)
#use fast_io(F)
      unsigned int16 external;
      unsigned int16 freq=0;
      
#int_timer1
void kesme(void){
  disable_interrupts(INT_TIMER1);
  disable_interrupts(INTR_GLOBAL);
      external=get_timer2();
      freq=external*10;
      printf("frekans=%ld \n\r",freq);
      set_timer1(2399);
      set_timer2(0);
      enable_interrupts(INT_TIMER1);
      enable_interrupts(INTR_GLOBAL);
     
}
void main(){
      setup_timer2(TMR_INTERNAL|TMR_DIV_BY_1,24999);
      setup_timer1(TMR_INTERNAL|TMR_DIV_BY_1,2499);
      set_timer1(2399);
      set_timer2(0);
       enable_interrupts(INT_TIMER1);
       enable_interrupts(INTR_GLOBAL);
while (TRUE){

               }
}

Yukarıda dikkat edilirse timer1'in frekansı 1000Hz, T1=1ms, timer2'nin frekansı ise 100Hz, T2=10ms. Ayrıca timer1'in başlangıç değerini 2399 alarak 100 tane saydıktan sonra overflow olmasını sağlamaya çalıştım. Bu durumda timer1 100x1ms=100ms sonra aşma yaparak kesme oluşturacak. bu sırada eş zamanlı olarak çalışan timer2 de 10 ms 'lik periyoda sahip olduğundan toplam 10 tane saymış olacak. (10x10ms=100ms).
Ben 100 ms lik kesmede 10 tane timer2 saydığım için bu değeri 10 ile çarpıp 1000ms =1s deki timer2 değerini hesaplarım bu da benim timer2 frekansım olur diye düşündüm. yukarıdaki programda da bunu uygulamaya çalıştım. Ancak sonuç seriport monitöründen şu şekilde ekrana yansıyor;
"frekans=frekan=1230
frekans=250
frekans=250"

Buna hiçbir anlam veremedim. acaba yukarıdaki programda nerede yanlış yapmış olabirim?
(30F4013 - CCS C)