CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug Reports on this forum. Send them to support@ccsinfo.com

NXP's PCA9685 driver

 
Post new topic   Reply to topic    CCS Forum Index -> Code Library
View previous topic :: View next topic  
Author Message
dezso



Joined: 04 Mar 2010
Posts: 102

View user's profile Send private message

NXP's PCA9685 driver
PostPosted: Mon Apr 02, 2012 11:00 pm     Reply with quote

Recently I needed to work with this chip and couldn't find any driver for it. I hope will be able to help some else with this and maybe get some improvement on the code.
16ch 12bit PWM with 40 to 1000hz period.
The skeleton and IDEA's from this page
PCA9685 Datasheet
Comment's/correction's/fix/update's welcome.
Code:

/*
 * File:   pca9685.h
 * Author: DevXP
 *
 * GNU GENERAL PUBLIC LICENSE!
 * Created on April 2, 2012, 8:28 AM
 */

#use I2C(master, slow, i2c1, force_hw)
#include <pca9685_reg.h>

void pca9685_init(int address)
{
/********************************************/
/* Init code for the PCA9685                */
/* Input:[address of the pc9685 to init]    */
/* Output:[void]                            */
/********************************************/
    i2c_start();                // Start
    i2c_write(address);         // Slave address
    i2c_write(MODE1);           // Mode 1 address
    i2c_write(0b00110001);      // Setting mode to sleep so we can change teh default PWM frequency
    i2c_stop();                 // Stop
    delay_ms(1);                // Required 50 us delay
    i2c_start();                // Start
    i2c_write(address);         // Slave address
    i2c_write(0xfe);            // PWM frequency PRE_SCALE address
    i2c_write(0x04);            // osc_clk/(4096*update_rate) // 25000000/(4096*1500)= 4.069 ~4
    i2c_stop();                 // Stop
    delay_ms(1);                // delay at least 500 us
    i2c_start();                // Start
    i2c_write(address);         // Slave address
    i2c_write(MODE1);           // Mode 1 register address
    i2c_write(0xa1);            // Set to our prefered mode[ Reset, INT_CLK, Auto-Increment, Normal Mode]
    i2c_stop();                 // Stop
    delay_ms(1);                // delay at least 500 us
    i2c_start();                // Start
    i2c_write(address);         // Slave Address
    i2c_write(MODE2);           // Mode2 register address
    i2c_write(0b00000100);      // Set to our prefered mode[Output logic state not inverted, Outputs change on STOP,
    i2c_stop();                 // totem pole structure, When OE = 1 (output drivers not enabled), LEDn = 0]
}

void pca9685_send_all(int address)
{
/********************************************/
/* Update all PWM register of PCA9685       */
/* Input:[address of the pc9685 to update]  */
/* Output:[void]                            */
/********************************************/
    int i = 0;                  // temp regiter for LEDCOUNT
    int pwm;                    // temp register for PWM
    for(i=0; i<=LEDCOUNT; i++)  // cycle thru all 16 LED
    {
        i2c_start();            // Start
        i2c_write(address);     // write to selected pca9685
        i2c_write(LED0 + 4 * i);// start from LED0 address, each pwm constructed from
        i2c_write(0x00);        // 4 12bit register, LED_ON_L
        i2c_write(0x00);        // LED_ON_H
        pwm = PWMData[i];       // update selected LED data in the array
        i2c_write(pwm);         // LED_OFF_L
        pwm = PWMData[i]>>8;    // updtae selected LED data in the array
        i2c_write(pwm);         // LED_OFF_H
        i2c_stop();             // Stop
    }
}

void pca9685_send(int address, long value, int led)
{
/********************************************/
/* pca9685_send(long value, int led)        */
/* Send the 12 bit PWM data to the register */
/* Input[ 0to4095 pwm, 0to15LED channel]    */
/* Output[void]                             */
/********************************************/
    int pwm;                    // temp variable for PWM
    if(value > 4095)            // if larger than 4095 than full on
            value = 4095;       // cant be larger than 4095
    if(led > 15)                // if LED larger than 15 than on other chip
            led = 15;           //***** need to implement to selecet next pcs9685
    i2c_start();                // Start
    i2c_write(address);         // Address of selected pca9685
    i2c_write(LED0 + 4 * led);  // select slected LED address
    i2c_write(0x00);            // LED_ON_L
    i2c_write(0x00);            // LED_ON_H
    pwm = value;                // PWM value lo byte
    i2c_write(pwm);             // LED_OFF_L
    pwm = value>>8;             // pwm 16 bit long, now shift upper 8 to lower 8
    i2c_write(pwm);             // LED_OFF_H
    i2c_stop();                 // STop
}


void pca9685_brightness(int address, int percent, int led)
{
/********************************************/
/* Calculate the register values for a      */
/* given percentage and dupdate pca9685     */
/* Input:[address of the chip where LED is_ */
/* percent of PWM on period 0%to100%      _ */
/* LED to set brightness 0to15]             */
/* Output:[void]                            */
/********************************************/
    long x;                             // temp variable
    float off;                          // temp variable
    const float onePercent = 40.96;
    if (percent < 1) {                  // if % less than 1 than LED OFF
    PWMData[led] = PCA9685_LED_OFF>>8;  // update data in array in case we use update all LED next
    pca9685_send(address,0,led);        // update selected LED
    return;                             // return from function
    }
    if (percent >= 100) {               // if % greather than 100 than LED ON
    PWMData[led] = PCA9685_LED_ON>>8;   // update data in array
    pca9685_send(address,4095,led);     // update selected LED
    return;                             // return from function
    }
    off = onePercent * percent;         // different aproach with float need to check if code faster than int32 way ?
//    off = (int32)4096 * percent;        // calculate percent (max*percent)/100
//    off = off / 100;                    // ex (4096*50%)=204800/100=2048
//    x = make16(off>>8,off);             // make 16 of 32 ?! why.. dont care at this time
    PWMData[led] = off;                 // update datat array in case we update all LED next
    pca9685_send(address,off,led);      // send it to pca9685
}
void PCA9685AllLedOff(int address)
{
    i2c_start();                        // Start
    i2c_write(address);                 // select pca9685
    i2c_write(0xfc);         // AllLED Off regiter
    i2c_write(0b00000000);              // data
    i2c_write(0b00010000);              // data
    i2c_stop();                         // Stop
}


Code:

/*
 * File:   pca9685_reg.h
 * Author: DevXP
 *
 * GNU GENERAL PUBLIC LICENSE
 * Created on April 2, 2012, 8:28 AM
 */

#ifndef pca9685_H
#define pca9685_H
/* General registers */
#define PCA9685 0x80                    // I2C address for PCA9865 with all inputs at zero
#define   Reset   0x01         // Reset the device
#define MODE1   0x00          // 0x00 location for Mode1 register address
#define MODE2   0x01          // 0x01 location for Mode2 reigster address
#define LED0    0x06          // location for start of LED0 registers
#define LEDCOUNT 15          // number of LEDS to light 15 max
/* Devices */
#define LEDDRV1    0xb80                // 1st PCA9685
#define LEDDRV2    0xb82                // 2nd PCA9685
#define LEDDRV3    0xd84                // 3rd PCA9685
#define LEDDRV4    0xd86                // 4th PCA9685
#define LEDDRV5    0xc88                // 5th PCA9685
#define LEDDRV6    0xc8a                // 6th PCA9685
#define LEDDRV7    0xc8c                // 7th PCA9685
#define LEDDRV8    0xc8e                // 8th PCA9685
#define LEDDRV9    0xc90                // 9th PCA9685
/* MODE1 bits */
#define PCA9685_RESTART 0x80
#define PCA9685_EXTCLK  0x40
#define PCA9685_AI      0x20
#define PCA9685_SLEEP   0x10
#define PCA9685_SUB1    0x08
#define PCA9685_SUB2    0x04
#define PCA9685_SUB3    0x02
#define PCA9685_ALLCALL 0x01

/* MODE2 bits */
#define PCA9685_INVRT   0x10
#define PCA9685_OCH     0x08
#define PCA9685_OUTDRV  0x04
#define PCA9685_OUTNE1  0x02
#define PCA9685_OUTNE0  0x01

/* LEDX_ON_H bits */
#define PCA9685_LED_ON 0x10

/* LEDX_OFF_H bits */
#define PCA9685_LED_OFF 0x10
/* PWM data variables, 16bit vide, 12bit used*/
long PWMData[16]= {
    //MSB        LSB
    0b010000000000,    // Channel 0
    0b101000000000,    // Channel 1
    0b000011000000,    // Channel 2
    0b000001100000,    // Channel 3
    0b000001011010,    // Channel 4
    0b000000000000,    // Channel 5
    0b000000000000,    // Channel 6
    0b000000000000,    // Channel 7
    0b000000000000,    // Channel 8
    0b000000000000,    // Channel 9
    0b000000000000,    // Channel 10
    0b000000000000,    // Channel 11
    0b000000000000,    // Channel 12
    0b000000000000,    // Channel 13
    0b000000000000,    // Channel 14
    0b000000000000     // Channel 15
};
#endif


Code:

/*
 * File:   main.c Test program
 * Author: DevXP
 *
 * GNU GENERAL PUBLIC LICENSE!
 * Created on April 2, 2012, 8:26 AM
 */

#include <main.h>
#include <pca9685.h>

void main()
{
    pca9685_init(LEDDRV1);
    pca9685_brightness(LEDDRV1,22,0);
    long pwm = 0;
    while(true)
    {
        output_toggle(PIN_D5);
        delay_ms(5);
        if(pwm < 4095)
        {
            pwm+=5;
        }
        else
        {
            pwm = 0;
        }
        pca9685_send(LEDDRV1,pwm,1);
        //PCA9685AllLedOff(LEDDRV1);
    }
}

_________________
I'm could be wrong many time's, at least I know what I'm doing Smile
codyfinden



Joined: 13 Jul 2012
Posts: 2

View user's profile Send private message

PCA9532
PostPosted: Fri Jul 13, 2012 10:00 am     Reply with quote

Wow your driver looks great! I've been trying to develop a driver for a similar LED driver the PCA9532, with not much luck. I don't have a PCA9685 to test, is there any problems with your code? I'm ordering a couple and I will test it and give you some feedback! Thanks.
dezso



Joined: 04 Mar 2010
Posts: 102

View user's profile Send private message

PostPosted: Wed Sep 12, 2012 10:28 am     Reply with quote

Works fine for me, on my second project using this driver..
_________________
I'm could be wrong many time's, at least I know what I'm doing Smile
aaronik19



Joined: 25 Apr 2011
Posts: 245

View user's profile Send private message

PostPosted: Sun Jul 14, 2013 1:34 pm     Reply with quote

Dear Friends, did some one achieve to create fade-in and fade-out ramps with this driver? Let say that channel 1 needs to have 5s fade in 30% to 75% and 9 seconds fade out from 100% to 20%?

I need to achieve that the fade-in fade-out values can be customized by clients.

Thanks
Volidemor



Joined: 18 Feb 2015
Posts: 1

View user's profile Send private message

Re: NXP's PCA9685 driver
PostPosted: Wed Feb 18, 2015 5:32 am     Reply with quote

Quote:

Recently I needed to work with this chip and couldn't find any driver for it. I hope will be able to help some else with this and maybe get some improvement on the code.
16ch 12bit PWM with 40 to 1000hz period.
The skeleton and IDEA's from this page
PCA9685 Datasheet



Hello, I am interested in what program you wrote the code?
and any text in the file #include <main.h>
Thank you very much for your reply

Добрый день, меня интересует в какой программе вы писали код?
и какой текст находится в файле #include <main.h>
Большое спасибо за ответ
vasiliok



Joined: 17 May 2011
Posts: 7
Location: Kaunas, Lithuania

View user's profile Send private message

PostPosted: Thu Oct 05, 2017 5:32 am     Reply with quote

Thanx a lot, dezso!
Your code works perfectly!
AnaA



Joined: 11 Nov 2017
Posts: 1

View user's profile Send private message

Doubt
PostPosted: Sat Nov 11, 2017 7:12 pm     Reply with quote

Hi Smile I had already generated a PWM signal with this code, but I have the doubt. I try to change two signals with two different percentage like this.
Code:

pca9685_send(LEDDRV1, 4000, 12);
__delay_ms(1000);
pca9685_send(LEDDRV1, 100, 12);
__delay_ms(1000);
}

but it is not possible I would like to know why. Hope I make myself clear.
PCM programmer



Joined: 06 Sep 2003
Posts: 20142

View user's profile Send private message

PostPosted: Sun Nov 12, 2017 11:13 am     Reply with quote

Quote:
__delay_ms(1000);

This means you are using XC8. CCS is different in many ways compared
to XC8. You most likely did not translate the CCS code to XC correctly.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> Code Library All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group