PCA9685PW FM+ I2C 16 Kanal 12Bit PWM Kontrolcüsü

Yayınlandı: 21 Temmuz 2015 / İncelemeler
Etiketler:, , , , , ,

pca9685_head   PCA9685 isimli entegre NXP firması tarafından üretilmiş FM+ (Fast mode-plus 1MHz ) hızında I2C protokolü ile dış dünyayla bağlantı kuran, 16 çıkış kanalına sahip bir PWM kontrolcüsüdür. Çıkış frekansı 24Hz ile 1526Hz arasında ayarlanabilmektedir.  Varsayılan çıkış frekansı 200Hz’dir.  PCA9685 dahili 25MHz bir osilatör barındırmakla birlikte harici bir clock giriş pinine de sahiptir. Bu pine uygulayacağımız harici bir clock sinyali ile (max 50MHz) birden fazla PCA9685’in senkronize çıkış vermesini sağlayabiliriz.  Çıkışlar yazılımla 25ma sink open-drain veya  25ma sink, 10ma source akımlarını sağlayabilecek  şekilde totem pole olarak ayarlanabilir.  PCA9685 2.3V -5.5V aralığında çalışmakla beraber bütün girişler 5.5V toleranslıdır. Tek I2C hattına 62 PCA9685 aynı anda bağlanabilir, bu sayede  62*16= 992 adet PWM çıkışını mikrodenetleyicimizin sadece 2 pinini kullanarak elde edebiliriz. Bu entegrenin diğer bir güzelliği ise PWM çıkışlarına gecikme ekleyebilmemizdir.  Bu şekilde h-bridge mosfet uygulamalrında dead time sağlamamıza olanak sağlanmıştır. Bununla birlikte her çıkış birbirinden farklı duty oranında veya her çıkış aynı duty oranıyla sinyal üretebilmektedir.

Aşağıda  bu entegreyle ilgili yazmış olduğum kodları görebilirsiniz. Bu uygulamada nispeten yeni bir 8bit mcu olan Microchip firmasına ait PIC16f1827 entegresini kullandım.  Bu mcu pek çok yeteneğe sahip ve bir çok uygulamada kullanılabilir joker bir mcu.  Kodlara bakalım;

#include <built_in.h>

#define MODE1 0x00
#define MODE2 0x01
#define LED0 0x06
#define LED1 0x0A
#define LED2 0x0E
#define LED3 0x12
#define LED4 0x16
#define LED5 0x1A
#define LED6 0x1E
#define LED7 0x22
#define LED8 0x26
#define LED9 0x2A
#define LED10 0x2E
#define LED11 0x32
#define LED12 0x36
#define LED13 0x3A
#define LED14 0x3E
#define LED15 0x42
#define LED_ALL 0xFA
#define PRE_SCALE 0xFE

char PCA9685_adresi = 0xAA; // Benim bağantımda 1+010101+W(0) şeklinde bir PCA9685 adresi konfigüre edildi.
long Osilator_Frekansi_Hz=25000000; //Dahili osilatör kullanıldı. Bu osilatörün frekansı 25MHz.)

int i=0;
//*********************************************************************************
//*********************************************************************************

void PCA9685_Cikis_Frekansi_Ayarla(int frekans) // PCA9685 çıkış frekansını değiştiren fonksiyon.
{
 char oku;
 int prescale_degeri;
 
 prescale_degeri= (Osilator_Frekansi_Hz/(4096*frekans))-1; //Prescale değeri burada hesaplanıyor.
 
 I2C1_Start();                 // Burada MODE1 registerının içeriği okundarak kayıt altına alındı.
 I2C1_Wr(PCA9685_adresi);
 I2C1_Wr(MODE1);
 I2C1_Repeated_Start();
 I2C1_Wr(PCA9685_adresi|0x01); // PCA9685'den veri okunacağı için PCA9685 adresinin en sağdaki biti 1 yapılıyor.
 oku=I2C1_Rd(0);               // Tek register verisi okunacağı için ACK gönderilmedi.
 I2C1_Stop();
 
 
 I2C1_Start();              // Prescale değerini değiştirmek için ilk olarak Sleep moduna geçiliyor.
 I2C1_Wr(PCA9685_adresi);
 I2C1_Wr(MODE1);
 I2C1_Wr(oku|0x10);
 I2C1_Stop();

 I2C1_Start();              // Yukarıda hesaplanan prescale değeri PRE_SCALE registerına yazılıyor.
 I2C1_Wr(PCA9685_adresi);
 I2C1_Wr(PRE_SCALE);
 I2C1_Wr(0x79);
 I2C1_Stop();

 I2C1_Start();                // MODE1 registerı eski haline getiriliyor ve sleep modundan çıkılıyor.
 I2C1_Wr(PCA9685_adresi);
 I2C1_Wr(MODE1);
 I2C1_Wr(oku);
 I2C1_Stop();
}

//*********************************************************************************
//*********************************************************************************

void PCA9685_Cikis_Duty_Ayarla(char LED, int binde) // PCA9685 çıkışlarının Duty oranını ayarlayan fonksiyon.
{
 int deger;  
 deger=(4096.0/1000.0)*(float)binde;

 I2C1_Start();
 I2C1_Wr(PCA9685_adresi);
 I2C1_Wr(LED);
 I2C1_Wr(0);
 I2C1_Stop();
 
 I2C1_Start();
 I2C1_Wr(PCA9685_adresi);
 I2C1_Wr((LED+1));
 I2C1_Wr(0);
 I2C1_Stop();

 I2C1_Start();
 I2C1_Wr(PCA9685_adresi);
 I2C1_Wr((LED+2));
 I2C1_Wr(lo(deger));
 I2C1_Stop();

 I2C1_Start();
 I2C1_Wr(PCA9685_adresi);
 I2C1_Wr((LED+3));
 I2C1_Wr(hi(deger));
 I2C1_Stop();
}

//*********************************************************************************
//*********************************************************************************

void main() {
OSCCON=0b01110000;
ANSELB=0b00000000;
delay_ms(100);
trisb.f0=0;
latb.f0=0;
I2C1_Init(500000);

I2C1_Start();              // I2C1 modülünden Start sinyali gönder.
I2C1_Wr(PCA9685_adresi);   // PCA9685 entegresinin adres bilgisini gönder. (Benim bağlantımda 1010101 + 0 şeklinde).
I2C1_Wr(MODE1);            // MODE1 registerının adresini gönder.
I2C1_Wr(0x01);             // MODE1 registerına 0x01 verisini yaz. (ALL_CALL aktif)
I2C1_Stop();               //I2C1 modülünden STOP sinyali gönder.

PCA9685_Cikis_Frekansi_Ayarla(50); // PCA9685 çıkış frekansı 50 Hz olarak ayarlandı.

while(1)
        {
         PCA9685_Cikis_Duty_Ayarla(LED0,i); //LED0 çıkışı i/1000 duty ile çalışsın.
         PCA9685_Cikis_Duty_Ayarla(LED1,i+5); //LED0 çıkışı (i+5)/1000 duty ile çalışsın.
         PCA9685_Cikis_Duty_Ayarla(LED2,i+10); //LED0 çıkışı (i+10)/1000 duty ile çalışsın.
         PCA9685_Cikis_Duty_Ayarla(LED3,i+15); //LED0 çıkışı (i+15)/1000 duty ile çalışsın.
         PCA9685_Cikis_Duty_Ayarla(LED4,i+20); //LED0 çıkışı (i+20)/1000 duty ile çalışsın.
         
         //PCA9685_Cikis_Duty_Ayarla(LED_ALL, 500); // Bütün ledler %50 Duty ile çalışsın.
         
         i++;
         if(i>980)i=0;
         delay_ms(5); // 5ms^'de bir çıkış duty oranlarını değiştir.
        }
}

Kodlardan görüleceği üzere PWM duty saykılını 1000 parçaya böldüm. Aslında entegre 12 bit olduğu için 4096 adıma da bölebiliriz. Fakat ben daha kolay kontrol sağlamak adına böyle bir şey yaptım. Çok hassas çıkış ayarlamak gerektiğinde 4096 adıma bölüp kullanabiliriz çıkışları. Buradaki kodları kullanıp servo motor kontrolü yapmak isteyen arkadaşlar için frekansı 50Hz olarak ayarladım. Piyasadaki analog hobi servo motorlarının 20ms’lik periyodlarla sürülmesi standart gibi kabul edildiği için frkansı 50Hz seçtiğimi söyleyebilirim.

Ayrıca uygulamayı entegre henüz elimde olmadığından Proteus simülasyonu şeklinde yaptığımı da ekleyim. Simülasyonu çalıştırmak için Proteus 8.2 sürümüne sahip olmalısınız. 8.1’de de çalışır muhtemelen.  Kodları buradan indirebilirsiniz.

Aşağıda   servo motorlarla ilgili güzel bir uygulamanın videosunu paylaşıyorum. Gerçi burada dijital servolar kullanılmış ama analog servolarla yapılmaması için de bir sebep yok. Videoyu sonuna kadar izlemenizi tavsiye ederim.

Sorularınızı ve yorumlarınızı aşağıdan iletebilirsiniz.

Bu yazıyla ilgili dosyalara buradan ulaşabilirsiniz.

May the force be with you.

yorum
  1. Andy dedi ki:

    Hi.
    Great code for the PCA9685! I have a few questions on the code.
    In the PCA9685_Cikis_Duty_Ayarla function, Why are you writing LED and then (0) value, and why do you only go up to LED+3 and not LED+15?
    Also in LED+3 you write hi(deger) which is xx in 0b1111xx11. why?

    What is the function of char ‘oku’? and what is happening when you write (oku|0x10)?

    Thanks again for publishing this code, im a beginner and would really like to learn as much as I can.

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 )

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 )

Google+ fotoğrafı

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

Connecting to %s