How to put the PCA9685 into PWM mode

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

How to put the PCA9685 into PWM mode

Jump to solution
1,641 Views
dfansler
Contributor II

I have my PCA9685 running on a Raspberry Pi using C.  BUT, the only thing it will do is turn on and turn off all outputs together.  Since the same registers are used for using the leds with and without PWM, how do you tell the PCA9685 that you want to use PWM?

Here is the registers and their values I am using:

char writeBuff[2] = {0x00}; // register mode 1
char writeBuff2[2] = {0x10}; //sleep
char writeBuff3[2] = {0xfe}; //register set pwm hz to 50 (0x7a)n
char writeBuff4[2] = {0x7a};  //value
char writeBuff15[2] = {0x00}; // register mode 1
char writeBuff16[2] = {0x00}; //no sleep
char writeBuff5[2] = {0x01}; //register mode 2
char writeBuff6[2] = {0x06}; //change on ack, totem poll output
char writeBuff7[2] = {0x06}; //register led 0 on low byte
char writeBuff8[2] = {0xf0};  //value
char writeBuff9[2] = {0x07}; //register LED 0 On High 3,2,1,0
char writeBuff10[2] = {0x00}; //value
char writeBuff11[2] = {0x08}; //register led0 off low
char writeBuff12[2] = {0xf0};  //value
char writeBuff13[2] = {0x09}; //register led0 off high (3,2,1,0)
char writeBuff14[2] = {0x00}; //value



Thanks,

David
YouTube "Sailing Solo at 70"
Solo sailed from Greece to North Carolina 2022

0 Kudos
Reply
1 Solution
1,522 Views
dfansler
Contributor II

After weeks of gnashing of teeth, and help from the forum, I have gotten the PCA9685 to work for controlling an RC Servo.  I thought I would share the solution in what it takes, and my software solution.

First, with I2C and the PCA9685 NOT in sequential progression, one must establish I2C communication, send two bytes (register and the data), then close the I2C communication.  That is the gist of how to make the PCA9685 chip work.  So here is my code:

////////////pca9695.h/////////////
extern int address;
#define MODE1   0x00          // 0x00 location for Mode1 register address
#define MODE2   0x01          // 0x01 location for Mode2 register address
/* Devices */
#
/* LEDX_ON_H bits */
#define PCA9685_LED_ON 0x10
/* LEDX_OFF_H bits */
#define PCA9685_LED_OFF 0x10
extern long PWMData[];
typedef unsigned char   u8;
int i2c_init(void);
void i2c_close(void);
int i2c_write(u8 slave_addr, u8 reg, u8 data);
int i2c_read(u8 slave_addr, u8 reg, u8 *result);
////////////end pca9685.h ///////////

///////////pca9685.c //////////////
#include <stdio.h>
#include <sys/types.h> // open
#include <sys/stat.h>  // open
#include <fcntl.h>     // open
#include <unistd.h>    // read/write usleep
#include <stdlib.h>    // exit
#include <inttypes.h>  // unsigned char, etc
#include <linux/i2c-dev.h> // I2C bus definitions
#include <sys/ioctl.h>			//Needed for I2C port

#include "pca9685.h"

 int fd,g;
    uint8_t writeBuffer[10]={0};

int asd_address = 0x40;
int data_address = 0x00;
int cnt = 2,len;

    char writeBuff[10]={0};
    char readBuff[10] = {0};
    void init_iic(void);
    void write_iic(void);
    void set_iic_add(void);
    void close_iic(void);

void init_iic(void)
{
    
        fd = open("/dev/i2c-1", O_RDWR);
        if(fd < 0) {
                printf("Cannot open device file...\n");
                return ;
        }
        printf("open done\n");
}
void set_iic_add(void)
{
    if (ioctl(fd,I2C_SLAVE,asd_address) < 0)
    {
        printf("error in address\n");
        exit(2);
    }
    printf("set add done\n");
}
void write_iic(void)
{
    printf("made it to write\n");
        g = write(fd,writeBuff,len);
    printf("g = %d \n",g);
    if (g != len)
        {
            printf("write failed\n");
            exit(3);
        }
        printf("write done\n");
}
void close_iic(void)
{
    close(fd);
}


int main(int argc, char **argv) 
{
    init_iic();
    set_iic_add();  
    writeBuff[0] = 0x00;        //mode 1 sleep
    writeBuff[1] = 0x10;
    len = 2;
    write_iic();
    close_iic();
    
    init_iic();
    set_iic_add();  
    writeBuff[0] = 0xfe;        //set pwm to 50 hz
    writeBuff[1] = 0x7a;
    len = 2;
    write_iic();
    close_iic();
 
     init_iic();
    set_iic_add();  
    writeBuff[0] = 0x00;        //mode1 wakeup
    writeBuff[1] = 0x00;
    len = 2;
    write_iic();
    close_iic();
  
     init_iic();
    set_iic_add();  
    writeBuff[0] = 0x01;        //register for mode 2
    writeBuff[1] = 0x06;        //use ack and no auto increment
    len = 2;
    write_iic();
    close_iic();

    init_iic();
    set_iic_add();  
    writeBuff[0] = 0x06;         //register for led0 on low byte  0x6c for 2ms  0xd8 for 1.5ms
    writeBuff[1] = 0x00;            //0x3a for 1ms
    len = 2;
    write_iic();
    close_iic();

    init_iic();
    set_iic_add();  
    writeBuff[0] = 0x07;        // register for led0 off low byte (7-0) 0x0d for 2ms, 1.5ms
    writeBuff[1] = 0x0e;        //0x0e for 1ms
    len = 2;
    write_iic();
    close_iic();
 
     init_iic();
    set_iic_add();  
    writeBuff[0] = 0x08;        //register for led0 high byte (3,2,1,0)  0x0e for 1ms to 2 ms
    writeBuff[1] = 0x0e;       
    len = 2;
    write_iic();
    close_iic();

    init_iic();
    set_iic_add();  
    writeBuff[0] = 0x09;        //register for led0 off low byte (0-7)  0x0f for 1ms to 2 ms
    writeBuff[1] = 0x0f;
    len = 2;
    write_iic();
    close_iic();
    return(0);
}

My code initializes the PCA9685 by putting the chip to sleep in MODE1 in register 0x00, set the PWM to 50Hz in register 0xfe, takes the chip out of sleep in MODE1 (register 0x00), sets MODE2 (register 0x01) to use ACK and not to increment the registers automatically.  Then LED0 is set to a 1ms pulse.  End of program.  other values for LED0 (up to 2ms) are shown in the code.  This is not the final code - an ADC will be added to provide the input for controlling the width of the pulse.

Thanks to all who have helped me.

David

View solution in original post

0 Kudos
Reply
4 Replies
1,596 Views
guoweisun
NXP TechSupport
NXP TechSupport

Please read the datasheet page16-7.3.3 LED output and PWM control for more detail PWM setting by related registers.

 

0 Kudos
Reply
1,564 Views
dfansler
Contributor II

Thanks for the reply.  I have read the areas suggested and feel I am sending the correct information.  So I have a question about sending the information.  When a string of data is sent to the PCA9685, does the chip know what is data and what is a register address?  In other words, if I were to send the following information to the PCA9685 as a series of contiguous bytes, would the chip know what were registers an what was data?

open I2C
0x40     send PCA9685 address
0x00    address for mode 1 register
0x10    put chip to sleep data
0xfe    address for pwm prescale register
0x7a   data for pwm prescale 
0x00   address for mode 1 register
0x00   data to wake up chip

kind regards

David

0 Kudos
Reply
1,539 Views
guoweisun
NXP TechSupport
NXP TechSupport

You can use below two write methods:

guoweisun_0-1698719008704.png

 

0 Kudos
Reply
1,523 Views
dfansler
Contributor II

After weeks of gnashing of teeth, and help from the forum, I have gotten the PCA9685 to work for controlling an RC Servo.  I thought I would share the solution in what it takes, and my software solution.

First, with I2C and the PCA9685 NOT in sequential progression, one must establish I2C communication, send two bytes (register and the data), then close the I2C communication.  That is the gist of how to make the PCA9685 chip work.  So here is my code:

////////////pca9695.h/////////////
extern int address;
#define MODE1   0x00          // 0x00 location for Mode1 register address
#define MODE2   0x01          // 0x01 location for Mode2 register address
/* Devices */
#
/* LEDX_ON_H bits */
#define PCA9685_LED_ON 0x10
/* LEDX_OFF_H bits */
#define PCA9685_LED_OFF 0x10
extern long PWMData[];
typedef unsigned char   u8;
int i2c_init(void);
void i2c_close(void);
int i2c_write(u8 slave_addr, u8 reg, u8 data);
int i2c_read(u8 slave_addr, u8 reg, u8 *result);
////////////end pca9685.h ///////////

///////////pca9685.c //////////////
#include <stdio.h>
#include <sys/types.h> // open
#include <sys/stat.h>  // open
#include <fcntl.h>     // open
#include <unistd.h>    // read/write usleep
#include <stdlib.h>    // exit
#include <inttypes.h>  // unsigned char, etc
#include <linux/i2c-dev.h> // I2C bus definitions
#include <sys/ioctl.h>			//Needed for I2C port

#include "pca9685.h"

 int fd,g;
    uint8_t writeBuffer[10]={0};

int asd_address = 0x40;
int data_address = 0x00;
int cnt = 2,len;

    char writeBuff[10]={0};
    char readBuff[10] = {0};
    void init_iic(void);
    void write_iic(void);
    void set_iic_add(void);
    void close_iic(void);

void init_iic(void)
{
    
        fd = open("/dev/i2c-1", O_RDWR);
        if(fd < 0) {
                printf("Cannot open device file...\n");
                return ;
        }
        printf("open done\n");
}
void set_iic_add(void)
{
    if (ioctl(fd,I2C_SLAVE,asd_address) < 0)
    {
        printf("error in address\n");
        exit(2);
    }
    printf("set add done\n");
}
void write_iic(void)
{
    printf("made it to write\n");
        g = write(fd,writeBuff,len);
    printf("g = %d \n",g);
    if (g != len)
        {
            printf("write failed\n");
            exit(3);
        }
        printf("write done\n");
}
void close_iic(void)
{
    close(fd);
}


int main(int argc, char **argv) 
{
    init_iic();
    set_iic_add();  
    writeBuff[0] = 0x00;        //mode 1 sleep
    writeBuff[1] = 0x10;
    len = 2;
    write_iic();
    close_iic();
    
    init_iic();
    set_iic_add();  
    writeBuff[0] = 0xfe;        //set pwm to 50 hz
    writeBuff[1] = 0x7a;
    len = 2;
    write_iic();
    close_iic();
 
     init_iic();
    set_iic_add();  
    writeBuff[0] = 0x00;        //mode1 wakeup
    writeBuff[1] = 0x00;
    len = 2;
    write_iic();
    close_iic();
  
     init_iic();
    set_iic_add();  
    writeBuff[0] = 0x01;        //register for mode 2
    writeBuff[1] = 0x06;        //use ack and no auto increment
    len = 2;
    write_iic();
    close_iic();

    init_iic();
    set_iic_add();  
    writeBuff[0] = 0x06;         //register for led0 on low byte  0x6c for 2ms  0xd8 for 1.5ms
    writeBuff[1] = 0x00;            //0x3a for 1ms
    len = 2;
    write_iic();
    close_iic();

    init_iic();
    set_iic_add();  
    writeBuff[0] = 0x07;        // register for led0 off low byte (7-0) 0x0d for 2ms, 1.5ms
    writeBuff[1] = 0x0e;        //0x0e for 1ms
    len = 2;
    write_iic();
    close_iic();
 
     init_iic();
    set_iic_add();  
    writeBuff[0] = 0x08;        //register for led0 high byte (3,2,1,0)  0x0e for 1ms to 2 ms
    writeBuff[1] = 0x0e;       
    len = 2;
    write_iic();
    close_iic();

    init_iic();
    set_iic_add();  
    writeBuff[0] = 0x09;        //register for led0 off low byte (0-7)  0x0f for 1ms to 2 ms
    writeBuff[1] = 0x0f;
    len = 2;
    write_iic();
    close_iic();
    return(0);
}

My code initializes the PCA9685 by putting the chip to sleep in MODE1 in register 0x00, set the PWM to 50Hz in register 0xfe, takes the chip out of sleep in MODE1 (register 0x00), sets MODE2 (register 0x01) to use ACK and not to increment the registers automatically.  Then LED0 is set to a 1ms pulse.  End of program.  other values for LED0 (up to 2ms) are shown in the code.  This is not the final code - an ADC will be added to provide the input for controlling the width of the pulse.

Thanks to all who have helped me.

David

0 Kudos
Reply