stm32f4 ile ac gerilim ölçümü

Başlatan cebeci006, 02 Kasım 2014, 18:52:31

cebeci006

arkadaşlar elimde stm32f4 var voltmetre uygulaması yapıyorum. dc gerilim degerlerini olçüyorum ac degerleri ölçemedim. ölçebilen varsa yardım edebilir mi?

Yuunus

Eger dalganin max degerini yogun ornekleme yaparak  adc ile yakalayabilirsen gayet guzel bir olcum olur tabiki belirli bir frekansa kadar.sebeke frekansinda sorun cikmayacaktir.



fahri-

#2
AC sinyali önce peak dedector ile dc ye çevir. Sonra bulduğun peak değerden efektif değeri hesaplattır. Eğer sadece sinüs AC sinyal ölçeceksen.

cebeci006

arkadaşlar sorunum ac sinyali ölçemedim cihaz ölçmüyor böyle bi özellik mi yok kartta anlamadım sinyal sinus sinyali şeklinde ve ölçtüğüm degeri görmem lazım ama nasıl

Yuunus

Oyle dogrudan Ac sinyal olcebileceginiz bir ozellik yok, Analog digital conv. var onunla da anlik ornekler alabilirsin artik sinusun neresine denk gelirse ???; yada ac sinyali dc ye cevirirsiniz aynen dc olctugunuz gibi Ac sinyali olcersiniz, tabi ne kadar dogru olcersiniz orasi ayri bir konu.

tekosis

bence teorik olarak şöyle yapılabilir. önce ac sinyalin sıfır geçiş noktalarını tespit edin. sonra frekansı ve buradan peryodu bulun. peryot belli olunca ac sinyalin tepe noktasının da zamanlaması belli olacaktır. bu noktaya denk gelecek şekilde zamanlamayı ayarlarsanız tam tepe noktasında ölçüm yapabilirsiniz.
İlim ilim bilmektir, ilim kendin bilmektir, sen kendin bilmezsin, bu nice okumaktır.

cebeci006

tekosis anlatmak istediğim resimdeki tasarımı yaptım dc sinyali gösteriyor ac sinyali hiç ölçmüyor. sinyali görsem periyot ve frekansını bulabilirim.




Goo

Ölçtüğünüz sinyalin frekansı, genliği ve örnekleme frekansınız nedir?

cebeci006

şu an hiç ac sinyal hiç ölçemedim . ama bu stm32f4 ile enfazla 3.6 v 50 Hz ölçmek istiyorum . saniyede 400 KS  ama nasıl

Goo

MCU ADC besleme geriliminiz ne bilmiyorum ama 3.6V'u ölçemeyebilirsiniz. Tepe değerinin 3.3V, min değerinin de 0V olduğunu farzedelim(Negatif değerleri doğrudan ölçemezsiniz). Şimdi burada sinyalin sıfır geçiş noktası yani orta noktası 1,65V. Örnekleme hızınızı 50Hz için başlangıçta düşük tutun. Örneğin 500 Hz örnekleme için 2 ms'lik bir timer kurun ve her kesmeye girdiğinde ADC örneğini bir diziye kaydedin. Anladığım kadarıyla PC tarafında bir arayüz programınız var. Bu programa her 1000 örnekte bir ölçüm değerlerini gönderin. Bu şekilde trigger olayını yapmadığınız için görüntülediğiniz sinyal ileri geri hareket edecektir. Fakat düzgün bir sinüs görmeniz gerekiyor. Eğer sinyalde problem yoksa verileri topladığınız dizi içerisinden ilk1,65V değerini(sıfır geçiş) bulun ve arayüze o noktadan itibaren veriyi gönderin. Bu şekilde trigger olayını da halletmiş olursunuz.

cebeci006

#10
Arm mikrodenetleyici kart tarafında bu dediklerinizi nasıl yapıcam bilmiyorum. elektronik konusunda fazla bilgim yok. arm kodlarım şu şekilde yardımcı olursanız. Vref degerim adc için 3 V. negatif degerleri ölçmeyecem sadece pozitif degerleri ölçeceğim onun için ayrı devre yaptım.

arm kodları
unsigned int  sayi=0,voltaj=0;
char  yazi[16];

int ADC_Olcum(char kanal)
{
     unsigned long temp=0;
 
 char k=0;
 for (k=0;k<128;k++)
  {
  
      temp+=ADC1_Get_Sample(kanal);

  }
 voltaj=(temp/128)*0.7130;
 return voltaj;
 }
 
 
  void usb_yaz()
  {
      IntToStrWithZeros(sayi,yazi);
      delay_ms(1);
    UART3_Write_Text("\n\r");
    UART3_Write_Text(yazi);

  }
  void usb_kesme()
  {
    usb_yaz();
  }
  
   void main()
   {
   GPIO_Digital_Output(&GPIOE_BASE,_GPIO_PINMASK_9);
   GPIOE_ODR.B9=0;
   
   UART3_Init_Advanced(230400,_UART_8_BIT_DATA, _UART_NOPARITY,_UART_ONE_STOPBIT, & _GPIO_MODULE_USART3_PD89);
   
   Lcd_init();
   ADC1_init();
   ADC_Set_Input_Channel(_ADC_CHANNEL_1);
   Lcd_Cmd(_LCD_CURSOR_OFF);
   Lcd_Out(1,1,"Voltmetre");
   while(1)
   {

   sayi=ADC_Olcum(1);
   
   usb_kesme();
   
   }
   
   }

Goo

2 ms periyotlu bir timer kurun(forumda örnekleri var). Bu timer'ın kesmesinin altında bir diziye, sırayla " ADC1_Get_Sample(kanal)" fonksiyonunuz ile verileri kaydedin. Toplamda 1000 adet veriye ulaştığınızda bunu pc'deki programınıza gönderin.

cebeci006

arm kodlarında nasıl yapıcam bilmiyorum.

fırat

#13
Öncelikle merhaba,
Benzer sorunları bende uzun zaman önce yaşayarak çok araştırma yaptım. Sonunda profesyonel bir sistemde
tereddüt etmeden kullanabileceğim bir tasarım geliştirdim. Umarım faydası olur..

AC ölçmek için ilk aşama, sinyali negatif alternanstan kurtarmaktır. (simetrik ölçüm yapabilen bir ADC'ye
sahipseniz buna gerek yok.)

Bunu yapabilmek için aşağıdaki resimdeki devreyi kullanabilirsiniz. Bu devre ile ölçümü yapılacak
sinyalin genlik durumu, denetleyicinin adc girişi için hazır  hale getirilir.



not:Bu işlemi yapabilmek için üzgünüm ki osilaskop ihtiyacınız olacak.

J6 konnektöründen girilecek sinyalin genliği 12vac.  U10:B opamp devresine bağlı RV3 potansiyometresi ile
dc ofset genliği ayarlanır. R6 ve R8 dirençleri arasına osilaskop bağlanır ve RV3 ile sinyalin tamamını pozitif
bölgeye çekilene kadar ayar yapılır. U10:A opampı ile empedans uyumu sağlanır, çıkışında RV2 ile sinyalin
genliği ölçüm skalasına sokulur. Bu devrede sinyalin tepe değeri 3v yapılabilir. Yalnız sinyalin en alt kısmını
sıfır çizgisine yapıştırmayacağız, 100mv kadar yukarıda olmasında fayda var. Artık +3v tepeden tepeye ve
tamamı pozitif kısımda olan sinyalimiz artık denetleyici tarafından örneklenebilir.

AC sinyalin voltaj değerini en etkin okuma yöntemi TRUE RMS ölçüm tekniğidir. Genel olarak ac ölçümünde tepe
değerini hesaplayarak 0.707 ile çarpma yöntemi kullanılır bu sadece kusursuz sinüs işareti için geçerlidir. Sinyalin
Crest faktörü değiştiğinde ölçüm sonucu hatalı olacaktır. Bu yüzden true rms ölçü aletleri emsallerine göre daha pahalıdır.

TrueRms için aşağıdaki denklemi kullanacağız.



Önce bol miktarda örnekleme yapıp ram'e kayıt yapacağız. Ne kadar çok örnek o kadar doğru sonuç.
Kullanacağınız işlemcinin türüne göre max. örnekleme yapmanız önemli.

aşağıdaki kodlar, yukarıdaki formülün işlenmiş halidir. Ben bu kodları mplab xc8'de yazdım
adc fonksiyonları hariç gerisi taşınabilir seviyede, az bir uğraş ile istediğiniz derleyici için
taşıyabilirsiniz.

voltaj_olcum(); isimli fonksiyonu çağırmanız yeterli, "Voltaj_rms" isimli yazmaca sonucu atacaktır.
cihazın kalibresi için "v_katsayi" isimli yazmaca belirleyeceğiniz bir kat sayı ile en doğru sonucu elde
edebilirsiniz.

Ör: 220vac ölçüm yapabilmek için 12vac çıkış veren bir trafonun seconderinden çıkan sinyali
mevcut devreye vererek ölçtüğünüzde, ölçümü 12v için yapmış olursunuz, sonucu (220/12) kat
sayısı ile çarptığınızda sonuç gerçek değere yaklaşır.  Tabi bu değerleri çok hassas ve dikkatli
belirlemelesiniz.

not:18F26K22 ile gerçekledim, çok güvenilir sonuçlar aldım.

////////////////////////////////////////////////////////////////////////////////////

#define OrnekSayisi     500

float   rms_sin,
        crest_factor_sin ;


float   adc_veri[500],
        v_katsayi;


//--------------------------------------

void rms_olcum(unsigned int *adres )
{

float          *p;

unsigned       k;

float          Vmin_sin=32000,
               Vmax_sin=0,
               toplam_sin = 0,               
               ortalama_sin,               
               V_duzeltme_sin ,
               Vp_sin;

      //---------------------------
      // dizinin en düsük ve en büyük volt seviyeleri bulunur

      Vmin_sin = 32000.000;
      Vmax_sin = 0;

      p=adres;
     
      for(k=0;k<OrnekSayisi;k++) 
      {       
          if( *p < Vmin_sin ) Vmin_sin = *p;
          if( *p > Vmax_sin ) Vmax_sin = *p;

          p++;
      }     
     
     //---------------------------
     
      V_duzeltme_sin = Vmin_sin + ( ( Vmax_sin - Vmin_sin ) / 2 );

      p=adres;

      toplam_sin = 0;

      for(k=0;k<OrnekSayisi;k++)
      {                       
          *p = *p - V_duzeltme_sin ;                            // dc ofset, sinyalden çikartilir ve sinyal simetrik hale getirilir.
         
          *p = *p  *  *p;                                       // herbir örnegin karesi alinir
         
          toplam_sin = toplam_sin + *p;                         // herbir örnek ana yigin ile toplanir
         
          p++;
      }

      ortalama_sin = toplam_sin / (float)OrnekSayisi;           // yigin örnek sayisina bölünerek ortalama alinir

      rms_sin = sqrt ( ortalama_sin ) ;                         // ortalamanin kare kökü, rms degeri üretir.

     
}

//--------------------------------------

void ADC_ornekle()
{
    unsigned int i,k,veri;
    float volt,toplam;
   
    for(i=0;i<OrnekSayisi;i++)
    {
        ConvertADC();
        while(BusyADC());

        adc_veri=ReadADC();

        delay_1ms_x(1);  //1ms bekle     
    }   
}

//---------------------------- Bu fonksiyon çağrılarak ölçüm yapılır

void voltaj_olcum(void)
{
    ADC_ornekle();                          //500 float ornek yukler

    rms_olcum(adc_veri);                    //500 olcum sonucunundan rms degeri elde eder.

    Voltaj_rms = rms_sin * v_katsayi;     //elde edilen rms deger istenilen katayi ile carpilir.   
   
}

cebeci006

#14
eyvallah sagolasın fırat.

benim sistemin tasarımı şu şekilde



negatif degerleri kurtulmak için resimdeki küçük devreyi yaptım. kodlama ve adc ayarlarıyla ilgili sıkıntım var. gönderdiğin kodlarım stm32f4 için uyarlamaya çalışacagım yapabilirsem.

stm32f4 ün adc ayarlamarıyla ilgili bilgisi olanlar yardımcı olursa sevinirim.

dışardan vref+ , vdd  besleme gerilimi verecekmiyim bunlar hangi pinlere kaç v olacak yardımcı olabilecekler varsa.

mesaj birleştirme:: 05 Kasım 2014, 23:04:27

fırat verdiğin kodları şu şekilde uyarladım ama çalıştıramadım.
#define OrnekSayisi     500
float   rms_sin,
        crest_factor_sin ;

float        v_katsayi;
        
 float          *p;

unsigned       k;

float          Vmin_sin=32000,
               Vmax_sin=0,
               toplam_sin = 0,
               ortalama_sin,
               V_duzeltme_sin ,
               Vp_sin;

float  sayi=0,*voltaj=0;
char  yazi[16];

void rms_olcum(unsigned int *adres )
{

      //---------------------------
      // dizinin en düsük ve en büyük volt seviyeleri bulunur

      Vmin_sin = 32000.000;
      Vmax_sin = 0;

      p=adres;

      for(k=0;k<OrnekSayisi;k++)
      {
          if( *p < Vmin_sin ) Vmin_sin = *p;
          if( *p > Vmax_sin ) Vmax_sin = *p;

          p++;
      }

     //---------------------------
      V_duzeltme_sin = Vmin_sin + ( ( Vmax_sin - Vmin_sin ) / 2 );

      p=adres;

      toplam_sin = 0;

      for(k=0;k<OrnekSayisi;k++)
      {
          *p = *p - V_duzeltme_sin ;                            // dc ofset, sinyalden çikartilir ve sinyal simetrik hale getirilir.

          *p = *p  *  *p;                                       // herbir örnegin karesi alinir

          toplam_sin = toplam_sin + *p;                         // herbir örnek ana yigin ile toplanir

          p++;
      }

      ortalama_sin = toplam_sin / (float)OrnekSayisi;           // yigin örnek sayisina bölünerek ortalama alinir

      rms_sin = sqrt ( ortalama_sin ) ;                         // ortalamanin kare kökü, rms degeri üretir.

}
//--------------------------------------
  int ADC_Olcum(char kanal)
{
      //    unsigned long temp=0;
  float *adc_veri;
 char k=0;
 for (k=0;k<OrnekSayisi;k++)
  {

      adc_veri=ADC1_Get_Sample(kanal);

  }
      voltaj=adc_veri;
 return voltaj;
 }

//---------------------------- Bu fonksiyon çağrılarak ölçüm yapılır

  void usb_yaz()
  {
    IntToStrWithZeros(sayi,yazi);
    delay_ms(1);
    UART3_Write_Text("\n\r");
    UART3_Write_Text(yazi);

  }
  
  void usb_kesme()
  {
    usb_yaz();
  }
  
   void main()
   {
   GPIO_Digital_Output(&GPIOE_BASE,_GPIO_PINMASK_9);
   GPIOE_ODR.B9=0;
   UART3_Init_Advanced(230400,_UART_8_BIT_DATA, _UART_NOPARITY,_UART_ONE_STOPBIT, & _GPIO_MODULE_USART3_PD89);
   ADC1_init();
   ADC_Set_Input_Channel(_ADC_CHANNEL_1);

   while(1)
   {
    ADC_Olcum(1);                          //500 float ornek yukler
    rms_olcum(voltaj);                    //500 olcum sonucunundan rms degeri eder.
    sayi = rms_sin * 0.7130;     //elde edilen rms deger istenilen katayi ile carpilir.
    usb_kesme();
   }
   }