16F877A ile matlab real-time arayüzlü DC motor kontrolü

Başlatan Egemen Engizek, 06 Eylül 2011, 14:32:20

Egemen Engizek

Merhabalar... Gerçek zamanlı matlab arayüzlü fırçalı dc motor kontrolü yapıyorum... Devremde pic 16f877a, max232( seri iletişim entegresi) ve L293D(motor sürücü) var. matlab'de real time target uygulamasını aktif hale getirip constanta herhangi bir sayı veriyorum ve yazdığım sayıya göre motor o hızda dönüyor. motorum 12V 30 rpm fırçalı dc motordur. motoru 2 yönde de hız kontrolünü yapmak istiyorum..saat yönünde istediğim hızda döndürebilirken, ters yönde ( kodun değişimine göre) ya ters dönüyor ya da hiç dönmüyor. Birazdan kodda da belirteceğim gibi 8 bitlik çalışıyorum ve 127 den büyük bir sayı yazarsam ki bunlar negatif sayılar da oluyor, aynı hızda ters döndüremiyorum. mesela +50 yazdıktan sonra motor saat yönünde belli bir hızda dönerken -50 yazdığımda da ters yönde aynı yönde dönmesi gerekli ama olmuyor.donanımsal olarak bir sorunum yok bütün entegreler ve kabloları test ettim, bir problem yok. Kodu pic c compilerda derledim..Sorunu uzun süredir bulamıyorum. Yardımlarınız için şimdiden teşekkürler

#include <16F877A.H>
#fuses HS,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT,NOPUT,NOWRT,NODEBUG,NOCPD
#use delay(clock=20000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, stop=1, parity=N, bits=8)
unsigned int8 a=0;

#int_rda
void kesme()
{
disable_interrupts(GLOBAL);
a=getc();
if (a<=127)
    {
    set_pwm1_duty(a);
    output_high(pin_d1);
    output_low(pin_c3);
    }
    else
    {
    set_pwm1_duty(256-a);
    output_high(pin_c3);
    output_low(pin_d1);
    }
    putc(a);
enable_interrupts(GLOBAL);
}
void main()
{  setup_psp(PSP_DISABLED);
    setup_timer_1(T1_DISABLED);
    setup_ccp1(CCP_PWM);
    setup_ccp2(CCP_OFF);
    setup_timer_2(T2_DIV_BY_4,127,1);
    enable_interrupts(GLOBAL);
    enable_interrupts(int_rda);
    while(1)
    {
      output_high(pin_b0);
    }
    }

Tagli

Öncelikle sorunun iletişimde ya da bilgisayar tarafında olup olmadığından emin olmalısın.

PIC tarafında seri iletişimi tamamen kapat ve a yerine programlama sırasında sabit değerler yazarak deneme yap. Yani a = getc() yerine a = 50 ve a = 206 yaz. Yapacağın gözlemlere göre sonraki adım belirlenebilir.
Gökçe Tağlıoğlu

Egemen Engizek

Çok teşekkür ederim yardımınız için... Sorunumu buldum set_pwm1_duty() komtunun içine 256-a yazınca olmuyormuş. onun yerine;
b=256-a
set_pwm1_duty(b) yazınca çalıştı...Bilgisayarla iletişimde bir problem yok =)

jozzef

Bence sorun belirttiğin gibi olamaz; çünkü bahsettiğin durum c dilinde tanımlı bir durum. Yalnız dikkatimi şu çekti
a=getc();

Yani karakter okumasını istiyorsun, yani istediğin sayıyı gir, 1 den 9a kadar bir rakam okuyabilirsin. Buda demektir ki
if (a<=127)
durumu her zaman geçerli.
Çözüm string okurum bunu int'e çevirip öyle işlemime devam etmek olurdu.
Yanılıyorsam düzeltin lütfen.

JKramer


Kadir Can(16F84)

getc() fonksiyonu basılan tuşu döndürür.
127 yazdığımızda getc() sadece 1'i döndürür.
Döngüye alınmalı.

Egemen Engizek

@JKramer: Hocam zaten 8 bitlik döndürüyorum da 10 bitlik döndürmem için ne yazmam gerekiyor...Projeyi ilerlettim ve motorun ucuna potansiyometre bağladım, geribesleme ile kontrol yapıyorum,

@Kadir Can: Ne demek istediğinizi biraz daha açabilirmisiniz, anlayamadım...

JKramer

Yok ben size yazmamıştım onu. Fonksiyon int8 olduğundan geri dönüş değeri 10 bit olamaz. O zaman daha yüksek bit'li sayıları iki byte halinde gönderip alıcıda birleştirebilirsiniz.

Siz Matlab uygulamanızda bir byte'lık sayıyı (127 mesela) bir seferde gönderiyorsunuz diye anladım ben, diğerleri terminal ekranına tek tek girdiğinizi ('1'+'2'+'7') zannettikleri için öyle dediler.

Egemen Engizek

@JKramer: Hocam şimdi ben biraz ilerlettim ve pot. ile geribesleme alıyorum, pozisyon (açı olarak) kontrol edicem. MATLAB simulinkte PID controller tasarladım. İlk önce 8 bitlik denedim, çok verimsiz bir kontrol oldu, sonra bütün parametrelerimi 10 bite ayarlamaya çalıştım. Bu ayarlamaları da CCS C forumunda yapılan tartışmalardan öğrendiğim kadarıyla yaptım. Bazı insanlar yardımcı olmaya çalıştılar ancak, galiba yazılım mühendisi olsalar gerek, beni aşan ifadeler, fonksiyonlar, tanımlamalar yaptılar =). Sonuç olarak PIC 10 bitlik işlem yapanbiliyor. Ama seri iletişim 10 bitlik yapamıyorum; ya 8 bit ya da 16 bit... Onun için kodumu değiştirdim ve şu hale getirdim;

#include <16F877A.H>
#device ADC=10
#fuses HS,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT,NOPUT,NOWRT,NODEBUG,NOCPD
#use delay(clock=20000000)
#use rs232(baud=115200, xmit=PIN_C6, rcv=PIN_C7, stop=1, parity=N, ERRORS, bits=8)
unsigned long bilgi;
unsigned long a,b;
#int_rda
void kesme()
{
a=getc();
if (a<=511)
    {
    set_pwm1_duty((long)a);
    output_high(pin_d1);
    output_low(pin_c3);
    }
   else
    {
    b=1024-a;
    set_pwm1_duty((long)b);
    output_high(pin_c3);
    output_low(pin_d1);
    }
}
void main()
{  setup_psp(PSP_DISABLED);
    setup_timer_1(T1_DISABLED);
    setup_adc(ADC_CLOCK_INTERNAL);
    setup_adc_ports(ALL_ANALOG);
    setup_ccp1(CCP_PWM);
    setup_ccp2(CCP_OFF);
    setup_timer_2(T2_DIV_BY_1,255,1);
    enable_interrupts(GLOBAL);
    enable_interrupts(int_rda);
 
    while(1)
    {
    set_adc_channel(0);
    delay_us(20);
    bilgi=read_adc();
    delay_us(20);
    putc(bilgi);
    output_high(pin_b0);
    delay_ms(20);
    }
    }

bu set_pwm1_duty((long)a); 10 bitlik PWM cycle üretmesi için gerekliymiş. Çünkü setup_timer_2 ayarlarında ortaya yazacağımız say (PR2) en fazla 255 oluyo. 255ten fazla olursa PWM frekansı değişiyo CCP1 pini temizlenmiyor. Matlab ile ilgilenen, uğraşan bir arkadaşımız varsa, "Packet output standard devices" bloğuna ve "packet input standard devices" bloğuna data type kısımlarına ne yazmalıyız. Onda da sadece 8 bit ve 16 bitlik gönderebiliyoruz, bu da benim istemediğim bir sonuca yol açıyor; 10 bitlik sayıyı 6 bit sola kaydırıp 16 bitlik yapıyor ve buna göre işlem yapıyor. 10 bitlik temiz bir PID kontrol yapmak istiyorum, Kp Ki Kd değerlerini bi şekilde ayarlarım ama bu son yaptığım işlerden sonra motor bile dönmüyor, yardımlarınız için şimdiden teşekkürler...

Egemen Engizek

Bu arada simulink kapalı döngü (closed loop), geribeslemeli ( feedback) modellemeyi merak edenler için blok diyagramını vereyim...http://imageshack.us/photo/my-images/27/pidsimulink.png/
http://imageshack.us/photo/my-images/832/10092011491.jpg/
http://imageshack.us/photo/my-images/163/10092011490.jpg/
devrenin bir kaç resmini de koyayım dedim

Egemen Engizek

Alıntı yapılan: Tagli - 06 Eylül 2011, 15:15:02
Öncelikle sorunun iletişimde ya da bilgisayar tarafında olup olmadığından emin olmalısın.

PIC tarafında seri iletişimi tamamen kapat ve a yerine programlama sırasında sabit değerler yazarak deneme yap. Yani a = getc() yerine a = 50 ve a = 206 yaz. Yapacağın gözlemlere göre sonraki adım belirlenebilir.

zaten bunu en başta yapmıştım... Herşeyi adım adım yapıyorum ki sorun çıkmasın, ama bazen çıkıyor işte

JKramer

- setup_adc(ADC_CLOCK_INTERNAL); muhtemelen 20 MHz için ADC_CLOCK_DIV_32 olmalı. Katalogdan kontrol edersiniz.
- Eğer tek bir kanaldan analog okuma yapacaksanız, while(1) üzerinde (dışında) bir kez kanalı belirlemeniz yeterli.
- read_adc() fonksiyonu GO/!DONE bit'ini test eder, bu satırın ardından bekleme yapmanıza gerek yok.

Bunları hallederseniz adc ile ilgili bir sıkıntı kalmaması gerekiyor. Şimdi adc'yi bir kenara bırakıp (kodunuzda da o bölümleri kaldırın, hatta pwm kısmını da kaldırın) sadece seri haberleşme kısmıyla ilgilenin, çünkü asıl sorun orada :). Matlab'da nasıl seri haberleşme yapılıyor hiçbir fikrim yok ama yapacağınız ilk şey, doğru bir şekilde veri alıp gönderdiğinize emin olmaktır. CCS forumunda denildiği gibi circular buffer örneği olan programı inceleyin. Daha önce dediğim gibi; a=getch() ile 8 bit'lik değer alırsınız, bu yüzden a 255'den büyük olamaz.

Egemen Engizek

Seri haberleşmede problemim yok, verdiğim sabit değeri, değişkeni aynen ekranda görebiliyorum, o konuda bir problemim yok ancak, şunu merak ettim a=getc() 8 bitlik sadece 8 bitlik değer alabiliyorsa, neden compiler bana hata vermedi. En başta dikkat ettiyseniz a'yı 16 bitlik bir sayı olarak tanımladım ve devamında kesme fonksiyonu içerisinde de if else döngüsünün içerisinde karşılaştırmalı olarak da kullandım.

JKramer

Derleyicinin hata vermesi için bir neden yok. Fonksiyon 8 bit'lik değeri döndürdüğü sürece siz isterseniz a değişkenini int32 olarak tanımlayın, bir şey farketmez. Yani geri dönüş değerini fonksiyonda tanımlıdır, değişkeni long olarak tanımladığınızda fonksiyon da long olmuyor.

Aşağıdaki gibi bir deneme yapabilirsiniz mesela, Matlab'dan decimal 600 sayısını gönderin, tekrar Matlab'da kaç okuduğunuza bakın:
...
#int_rda
void kesme()
{
    a=getc();
    putc(a);
}
....

Egemen Engizek