C18 Diliyle Robot Programlama – Servo Motor Kullanma

Bu projede amaç bir servo motorunu çalıştıracak şekilde mikrodenetleyiciyi programlamak. Aslında yapacağımız çalışmada toplamda 7 servo motoru sürebilecek kapasitede bir program yazmış olacağız.

Servonun ne olduğundan biraz bahsedelim. Servo bir mil etrafında dönebilen motorlu bir yapıdır. Bu yapı kameralarda, robotik aksamların hareket ettirilmesinde (kol, bacak, kafa) kullanılır. Temelde aldığı sinyale göre döner, böylece servoya bağlı cihazın yönü değişir.

Aşağıda örnek bir servo motoru ve bu motora bağlı parça görünmektedir;

Bir tane servoyu sürmek için yapılması gereken işlem timer 0’ı yaklaşık her 20 milisaniyede bir belli bir duty(gecikme – lojik 1’de kalma süresi) zamanı kadar lojik1’de tutmak. Servo aldığı lojik1 sinyalinin süresine göre dönme işlemi gerçekleştirecek. Bu sinyal çok uzun verilirse servo tamamen bir yöne döner ve zorlanır, eğer bu sinyal lojik sıfır olarak kalırsa bu sefer servo tamamen ters yöne döner ve yine zorlanır. Bu zamanlamayı öyle bir ayarlamalıyız ki ne çok fazla, ne de çok az voltaj vermeliyiz. Benim hesaplarıma göre yaklaşık 20 milisaniyede bir 3 milisaniyeden küçük bir duty değeriyle işlem yaptığımızda servo normale yakın çalışıyor. Bu değerlerle kendi işlemlerinizi yapmak için oynayabilirsiniz.

Bir servo motoru için pwm modülünü kullanarak bu sinyali belirlenen kriterlerde üretmek mümkün. Buna benzer bir örneği pwm konusunu işlerken yapmıştık. Bu başlık altında birden fazla servo motoru 3 adet timer ile nasıl sürebileceğimizi göreceğiz.

Elimizde 7 adet çıkış olsun (Bu çıkışlar için C portunu kullanacağız). Bu çıkışların her birini bir servoya bağladığımızı düşünelim. Bu çıkışların hepsi de ilk kısımda anlattığım gibi bir sinyal vermek zorunda. ~3 ms’den az olmak kaydıyla lojik1 üreten ve bunu  yaklaşık her 20 ms’de bir tekrarlayan bir sinyal. Mikrodenetleyicinin bu her bir çıkış için ayrı timer modülü kullanması beklenemez. Bu çıkışlardan istediğimiz değerleri almak için bize 3 timer yeterlidir.

  • Timer0: Her bir 20 ms’lik değeri tutacak. Bu 20 ms içinde bütün servolara bir kez lojik 1 sinyali gönderilecek.
  • Timer1: Oluşturduğumuz 20 ms’lik periyodun içinde yer alan her bir lojik1’lik değeri birbirinden ayıran periyodu tutacak.
  • Timer2: Oluşturulan her bir dalganın lojik 1’de kalma süresini (duty değeri) belirleyecek.

Yukarıdaki dalga şekli üretmek istediğimiz şeklin bir prototipi gibi düşünülebilir. Yukarıdaki şekilde 3 adet servoyu sürecek şekilde bir dalga üretilmiştir. (0: Timer0, 1: Timer1, 2: Timer2)

Bu tür bir dalgayı kesme mekanizmasıyla üreteceğiz. Önce Timer0’ı 20 ms sonra kesme üretecek şekilde ayarlayacağız. Daha sonra Timer0 kesmesi oluştuğunda Timer0 kesmesi kendisini tekrar 20 ms sonra kesme üretecek şekilde ayarlayacak, Timer1’i de yaklaşık 3 ms sonra kesme üretecek şekilde ayarlayacağız ve Timer2 modülünü kullanıma açıp içine ilk duty değerimizi yazacağız (toplamda 7 adet duty değerimizin olduğunu varsayıyoruz.) Mantıken Timer2’ye yazacağımız değer Timer1’deki ~3ms’lik değerden küçük olmalıdır. Bu nedenle Timer1’den önce Timer2 kesmesi oluşacaktır. Timer2 kesmesinin görevi hangi uçtan lojik1 sinyali geliyorsa o ucu lojik0 yapmaktır. Böylece duty değeri kadar zaman geçtiğinde sinyalimizi lojik0’a çekerek lojik1’in zamanını kontrol etmiş oluruz. Timer2’den sonra Timer1 kesmesi gelecek ve kendisini tekrardan ~3 ms sonra kesme üretecek şekilde açacak ve Timer2 modülünü yeni duty değeri ile açacak. Bu şekilde kontrol bir Timer2’ye bir Timer1’e geçecek. 20 ms’lik süre bittiği zaman bu sefer kontrolü Timer0 devre alacak ve Timer1 ile Timer2’yi tüm bu olayları baştan gerçekleştirmeleri için açacak. Süreç bu şekilde işleyecek.

Toplam periyot 20 ms ve her bir servoya ayrılacak sürenin de yaklaşık 3 ms olduğunu kabul edersek;
3*6 = 18 < 20 ifadesine göre 6 adet servo sürebiliyoruz.

Her bir servoya ayrılacak süreyi 2,7 ms civarında tutarsak;
2,7 * 7 = 18,9 < 20 ifadesine göre 7 adet servo sürebilmemiz mümkün.

Program kodu aşağıdaki gibi olacak;

<pre><pre>/*
 * File:   servo_main.c
 * Author: cagatay
 *
 * Created on 10 ?ubat 2012 Cuma, 10:25
 */

#include <p18cxxx.h>
#include <stdio.h>
#include <delays.h>
#include <timers.h>
#include <pwm.h>
#include <portb.h>

#pragma config OSC = HS, WDT = OFF, LVP = OFF

unsigned int duty[10] = {10,20,30,40,50,60,70};
int sayac = 0;

void int_handler(void);

#pragma code high_vector=0x08
void high_interrupts (void) {
    _asm GOTO int_handler _endasm
}

#pragma code low_vector=0x18
void low_interrupts (void) {
    _asm GOTO int_handler _endasm
}

#pragma code
#pragma interrupt int_handler
void int_handler(void) {
    if(INTCONbits.TMR0IF == 1) {
        WriteTimer0(178);

        sayac = 0;

        OpenTimer1(TIMER_INT_ON & T1_SOURCE_INT & T1_PS_1_1 & T1_OSC1EN_OFF);
        WriteTimer1(62936);

        OpenTimer2(TIMER_INT_ON  & T2_PS_1_4 & T2_POST_1_8);
        PR2 = duty[sayac];

        PORTCbits.RC0 = 1;
        PORTCbits.RC7 = 1;

        INTCONbits.TMR0IF = 0;
    }

    if(PIR1bits.TMR1IF == 1) {
        WriteTimer1(62936);
        sayac++;

        OpenTimer2(TIMER_INT_ON  & T2_PS_1_4 & T2_POST_1_8);
        PR2 = duty[sayac];

        if(sayac == 1) {
            PORTCbits.RC1 = 1;
            PORTCbits.RC7 = 1;
        }
        if(sayac == 2) {
            PORTCbits.RC2 = 1;
            PORTCbits.RC7 = 1;
        }
        if(sayac == 3) {
            PORTCbits.RC3 = 1;
            PORTCbits.RC7 = 1;
        }
        if(sayac == 4) {
            PORTCbits.RC4 = 1;
            PORTCbits.RC7 = 1;
        }
        if(sayac == 5) {
            PORTCbits.RC5 = 1;
            PORTCbits.RC7 = 1;
        }
        if(sayac == 6) {
            PORTCbits.RC6 = 1;
            PORTCbits.RC7 = 1;
            CloseTimer1();
        }

        PIR1bits.TMR1IF = 0;
    }

    if(PIR1bits.TMR2IF == 1) {
        CloseTimer2();

        if(sayac == 0)
            PORTCbits.RC0 = 0;
        if(sayac == 1)
            PORTCbits.RC1 = 0;
        if(sayac == 2)
            PORTCbits.RC2 = 0;
        if(sayac == 3)
            PORTCbits.RC3 = 0;
        if(sayac == 4)
            PORTCbits.RC4 = 0;
        if(sayac == 5)
            PORTCbits.RC5 = 0;
        if(sayac == 6)
            PORTCbits.RC6 = 0;

        PORTCbits.RC7 = 0;

        PIR1bits.TMR2IF = 0;
    }
}

void main(void) {
    int i,j;

    TRISC = 0; //servo cikisi icin RC0 pini kullanilacak
    PORTC = 0;

    CloseADC();
    ADCON1 = 0x0F; // ADC girisleri dijital

    RCONbits.IPEN = 1;
    INTCONbits.GIE = 1;
    INTCONbits.TMR0IE = 1;
    PIE1bits.TMR1IE = 1;
    PIE1bits.TMR2IE = 1;

    OpenTimer0(TIMER_INT_ON & T0_SOURCE_INT & T0_PS_1_256 );
    WriteTimer0(178);

    while(1) {
        for(i=23;i<=55;i++) {
            duty[0]=i;
            Delay10KTCYx(4);
        }

        Delay10KTCYx(4);

        for(i=55;i>=13;i--) {
            duty[0]=i;
            Delay10KTCYx(4);
        }
    }
}</pre>

Satır 17’de Timer 2 ile belirleyeceğimiz duty değerlerini tutan diziyi tanımlıyoruz. Bu dizideki değerlerin hepsi 2,7 ms’den az bir süreye karşılık geliyor. Satır 18’de bu dizinin indisi olarak kullanacağımız sayaç değişkenini tanımlıyoruz.

Satır 35’de Timer 0 kesmesinin gelip gelmediği kontrol ediliyor. Eğer Timer 0 kesmesi gelirse (20 ms’lik periyot biterse) bu bloktaki kodlar çalışacak.

Satır 36’da Timer 0 yeniden 20 ms sonra kesme üretecek şekilde ayarlanıyor.

Satır 38’de sayaç sıfırlanıyor (her 20 ms’de bir sıfırlanması gerekiyor ki bütün portlara baştan duty sinyalleri verilebilsin).

Satır 40’da Timer 1 modülü açılıyor. Satır 41’de Timer 1 modülü yaklaşık 2,7 ms sonra kesme üretecek şekilde ayarlanıyor. (20 ms’lik periyotlar içinde 7 adet bölge oluşturacak, bu bölgelere de duty değerlerini koyacağız -2,7 ms’den küçük olmak şartıyla- )

NOT:Satır 40’da OpenTimer1() fonksiyonunun aldığı parametrelere dikkat ettin. Son parametre olan T1_OSC1EN_OFF parametresi Timer1’in RC1 pinine bağlı osilatör tarafından tetiklenmesini engeler. Bu parametreyi vermek Timer modüllerinin düzenli çalışması ve çıkışlarını C portundan verebilmesi için çok önemlidir. Eğer bu parametreyi kullanmazsanız C portundan düzgün çıkış alamazsınız çünkü portlardan biri başka bir işlem için kullanıldığından amacımıza uygun çalışmayacaktır. Bu da kurduğumuz düzeneğin altüst olmasına neden olur.

Satır 43’de Timer 2 modülü açılıyor. Satır 44’de Timer 2 modülü duty dizisinde sayaç indisinde bulunan değere karşılık gelen süre sonunda kesme üretecek şekilde ayarlanıyor.

Satır 46’da RC0 pini lojik-1 yapılıyor. Her 20 ms’de bir bu kesmenin içinde sıfır nolu pinden başlayarak çıkışları vereceğimiz için ilk çıkışımızı bu noktada veriyoruz. RC0 pinine lojik 1 verdikten sonra Timer 2 kesme üretecek ve bizim lojik-1 yaptığımız pini lojik-0’a indirecek.

Satır 47’de RC7 pinini de lojik-1 yaptım. Bunun servo  motor sürmeye doğrudan bir katkısı yok. RC7 pinini bütün dalgaları tek bir ekranda görebilmek için yaptım. RC7 pini oluşturduğumuz bütün dalgaları osiloskopta ardarda görebilmemizi sağlayacak. Normalde her bir pinden tek bir dalgayı gözlemlememiz gerekir. Oluşturduğumuz bu dalgalar birbirine karışmamalı.

Satır 52’de Timer 1 kesmesinin gelip gelmediği kontrol ediliyor. Eğer Timer 1 kesmesi geldiyse bu bloktaki kodlar çalışacak.

Satır 53’de Timer 1 kendisini yine yaklaşık 2,7 ms sonra kesme üretecek şekilde ayarlıyor. (Unutmayalım şu anda hala Timer 0 ile oluşturduğumuz 20 ms’lik periyodun içerisindeyiz.)

Satır 54’de bir sonraki pine çıkış vermek için sayaç indisi bir artırılıyor.

Satır 56’de Timer 2 modülü açılıyor (Bu aşamaya gelene kadar önceki verdiğimiz sinyal çoktan sıfırlanmış olacak, Timer 0 kesmesi içerisinde açtığımız Timer 2 modülü kesmesini üretmiş ve RC0 pinini sıfırlamıştır, bu yüzden biz de Timer 2 modülünü tekrar kesme üretecek şekilde ayarlayabiliriz).

Satır 57’de Timer 2 modülüne yeni duty değeri yazılıyor. Timer 2’yi kesme üretecek şekilde ayarladığımıza göre artık lojik-1 yapmamız gereken pini bulmamız gerek. Lojik-1 yapmamız gereken pin sayaç değişkeninin değerine sahip pin olacak.

Satır 59-83 arasında sayaç indisine denk gelen pinin çıkışı lojik-1 yapılıyor. (RC7 pinini gözlem için kullanacağımızdan o pini de her seferinde lojik-1 yapıyoruz).

Satır 82’de farklı bir işlem yapıyoruz. Eğer RC6 pini lojik-1 olacaksa (7. Servo motoruna bağlı olacak ve aynı zamanda son 20 ms’lik periyor içinde son pin) bu pini lojik-1 yaptığımızda Timer 1’i kapatıyoruz. Çünkü bu andan sonra artık Timer 1’in kesme üretmesine gerek yok. 20 ms’lik süre bitecek ve Timer 0 kesmesi devreye girecek. Timer 0 kesmesi zaten kendi içerisinde Timer 1 modülünü açacak.

Satır 88’de Timer 2 kesmesinin gelip gelmediği kontrol ediliyor. Eğer Timer 2 kesmesi geldiyse bu bloktaki kodlar çalışacak.

Satır 89’da diğerlerinden farklı olarak Timer 2 kendini kesme oluşturmak için ayarlamıyor, kendisini kapatıyor. Çünkü Timer 2 kesmesininn görevi burada zamanlamayla alakalı değildir. Timer 2 kesmesini sadece daha önceden lojik-1 yapılmış pinleri sıfırlamak için kullanıyoruz. Timer 2 kesmesi Timer 0 ve Timer 1 kesmeleri tarafından tetiklenir ve Timer 2 kesmesinde yapılan tek şey, Timer 0 ve Timer 1 ile set edilen pinleri sıfırlamaktır. Böylece istediğimiz dalga şeklini elde etmiş oluruz. (Her bir Timer 2 kesmesi, ilgili pinlerden biri lojik-1 yapıldıktan sonra 2,7 ms’den daha erken bir sürede gelir.)

Satır 91-106 arasında sayaç değişkenine karşılık gelen pin lojik-0 yapılıyor (RC7 pinini de gözlem için kullandığımızdan o da lojik-0 yapılıyor).

Satır 115 ve satır 116’da C portu çıkış için ayarlanıyor ve ilk değer olarak tüm pinlere lojik-0 çıkışı veriliyor.

Satır 118 ve satır 119’da ADC modülü kapatılıyor ve tüm ADC pinleri dijital giriş/çıkış olacak şekilde ayarlanıyor.

Satır 121-125 arasında global kesmeler ve timer kesmeleri kullanıma açılıyor.

Satır 127 ve satır 128’de tüm tetiklemeleri başlatacak olan ilk 20 ms’lik periyodu Timer 0 ile üretiyoruz.

Satır 130’da program kesmeleri beklemek üzere sonsuz döngüye giriyor.

Bu programı çalıştırdığımız zaman RC0-RC6 pinlerinden aldığımız çıkışlar şu şekilde olacak;

  • RC0 çıkışı, duty=10:

  • RC1 çıkışı, duty=20:

  • RC2 çıkışı, duty=30:

  • RC3 çıkışı, duty=40:

  • RC4 çıkışı, duty=50:

  • RC5 çıkışı, duty=60:

  • RC6 çıkışı, duty=70:

Örnek olarak RC7 pininden aldığımız çıkışı da aşağıda görebiliriz (Normalde bu programla 7 adet çıkış alıyoruz ama aşağıdaki fotoğrafta 6 adet dalga görülüyor, çünkü 7. Dalgayı da ekleyince osiloskop ekranında ayırt edilmesi çok zor oluyordu, çünkü 20 ms’lik periyotun sonuyla başı birbirinden ayırt edilmiyordu. Bu yüzden 6 çıkış sinyalini RC7’ye yönlendirerek örnek aldım). Bütün dalgalar bir araya geldiğinde en başta yapmak istediğimiz şekli oluşturduklarını görebiliriz. Genel anlamda yapmış olduğumuz şey bu;

Bir Cevap Yazın

Aşağıya bilgilerinizi girin veya oturum açmak için bir simgeye tıklayın:

WordPress.com Logosu

WordPress.com hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Google+ fotoğrafı

Google+ hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Twitter resmi

Twitter hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Facebook fotoğrafı

Facebook hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Connecting to %s