PIC16F1827 ile PCA9685 Kullanımı (I2C 16 Kanal PWM entegresi)

Başlatan LukeSkywalker, 19 Temmuz 2015, 10:31:23

LukeSkywalker

Geçenlerde servo motorlarla ilgili araştırma yaparken böyle bir entegrenin varlığından haberdar oldum. Bu sabah Proteus 8.2'de modelinin olduğunu görünce datasheetini indirip biraz kurcaladım. Yüzeysel olarak işimi görecek kadar inceledim.  Aşağıda kodları paylaşıyorum. Belki birilerinin işine yarar.
/////////////////////////////////////////////
/////////////////////////////////////////////
////                                     ////
////            LUKESKYWALKER            ////
////                                     ////
////    MikroC Pro for PIC v.6.6.1       ////
////      PIC16F1827 - INTOSC - 32MHz    ////
////   PCA9685PW FS+ - INTOSC - 25MHz    ////
////             19.07.2015              ////
////                                     ////
/////////////////////////////////////////////
/////////////////////////////////////////////


#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.)

char 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, char yuzde) // PCA9685 çıkışlarının Duty oranını ayarlayan fonksiyon.
{
 int deger;
 deger=(4096/100)*yuzde;

 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();
 delay_ms(5);
}

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

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 duty ile çalışsın.
         PCA9685_Cikis_Duty_Ayarla(LED1,i+5); //LED0 çıkışı %(i+5) duty ile çalışsın.
         PCA9685_Cikis_Duty_Ayarla(LED2,i+10); //LED0 çıkışı %(i+10) duty ile çalışsın.
         PCA9685_Cikis_Duty_Ayarla(LED3,i+15); //LED0 çıkışı %(i+15) duty ile çalışsın.
         PCA9685_Cikis_Duty_Ayarla(LED4,i+20); //LED0 çıkışı %(i+20) duty ile çalışsın.
         
         //PCA9685_Cikis_Duty_Ayarla(LED_ALL, 50); // Bütün ledler %50 Duty ile çalışsın.
         
         i++;
         if(i>80)i=0;
         delay_ms(5); // 5ms^'de bir çıkış duty oranlarını değiştir.
        }
}



Buraya
uygulamanın proje dosyalarını ve Proteus 8.2 simülasyonunu ekliyorum.

İyi çalışmalar.

coskun_yildirim

hocam birden fazla pca9685 i sistem de nasıl adresleyeceğiz nasıl adresliyeceğiz bende bu proje üzerinde çalışıyorum.

mehmet

http://www.ebay.com/sch/i.html?_from=R40&_trksid=p2050601.m570.l1313.TR0.TRC0.H0.Xpca9685.TRS0&_nkw=pca9685&_sacat=0
Tembel işi yapmışlar... 8)

char PCA9685_adresi = 0xAA; // Benim bağantımda 1+010101+W(0) şeklinde bir PCA9685 adresi konfigüre edildi.

satırında 1 000000 0 dan başlayarak 1 111111 0 adresine kadar PCA9685 eklenebilir...
Baştaki 1 sabit, sondaki de R/W için. Aradaki altı bit size verilmiş adresler...

LukeSkywalker

#3
PCA9685'in A0'dan A4'e kadar olan pinlerini dışarıdan istediğiniz şekilde low high yaparak tek I2C hattından toplam 64*16= 1024 adet PWM çıkışı elde edebilirsiniz.

mesaj birleştirme:: 20 Temmuz 2015, 22:28:47

Alıntı yapılan: mehmet - 20 Temmuz 2015, 17:54:18
http://www.ebay.com/sch/i.html?_from=R40&_trksid=p2050601.m570.l1313.TR0.TRC0.H0.Xpca9685.TRS0&_nkw=pca9685&_sacat=0
Tembel işi yapmışlar... 8)

Bu iyimiş. Bunun baskı devresini çizmektense  alır devrenin üzerine yapıştırırım daha iyi :).


mehmet

@LukeSkywalker Hocam, ukalalık etmek istemem ama altı
adres biti görülmekte... (A0-A5)
Ama hesap doğru: (2^6) * 16 = 1024

LukeSkywalker

#5
Estagfirullah, haklisiniz. Yanlis yazmisim. A0-A5 arasi olacakti.

mesaj birleştirme:: 21 Temmuz 2015, 09:29:02

Önemli Düzeltme: Datasheet toplam 62 cihazın aynı anda bağlanabileceğini söylemiş. 2 adres farklı işlemler için kullanılıyormuş.

sametkarakoyunlu

#6
Merhaba aşağıda kod ile ilgili bir sorum var.Umarım görüp dönersiniz.

Sorum: "prescale_degeri" ni nerede kullanıyorsunuz?


/////////////////////////////////////////////
/////////////////////////////////////////////
////                                    ////
////            LUKESKYWALKER            ////
////                                    ////
////    MikroC Pro for PIC v.6.6.1      ////
////      PIC16F1827 - INTOSC - 32MHz    ////
////  PCA9685PW FS+ - INTOSC - 25MHz    ////
////            19.07.2015              ////
////                                    ////
/////////////////////////////////////////////
/////////////////////////////////////////////


#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.)

char 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, char yuzde) // PCA9685 çıkışlarının Duty oranını ayarlayan fonksiyon.
{
int deger;
deger=(4096/100)*yuzde;

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();
delay_ms(5);
}

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

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 duty ile çalışsın.
        PCA9685_Cikis_Duty_Ayarla(LED1,i+5); //LED0 çıkışı %(i+5) duty ile çalışsın.
        PCA9685_Cikis_Duty_Ayarla(LED2,i+10); //LED0 çıkışı %(i+10) duty ile çalışsın.
        PCA9685_Cikis_Duty_Ayarla(LED3,i+15); //LED0 çıkışı %(i+15) duty ile çalışsın.
        PCA9685_Cikis_Duty_Ayarla(LED4,i+20); //LED0 çıkışı %(i+20) duty ile çalışsın.
        
        //PCA9685_Cikis_Duty_Ayarla(LED_ALL, 50); // Bütün ledler %50 Duty ile çalışsın.
        
        i++;
        if(i>80)i=0;
        delay_ms(5); // 5ms^'de bir çıkış duty oranlarını değiştir.
        }
}

[Düzenleme- Kodları, Kod Blokları İçine Alalım.]