PCA9685 Servo and LED driver

General discussion on mikroC PRO for PIC.
Post Reply
Author
Message
Bill Legge
Posts: 235
Joined: 28 Oct 2007 03:16
Location: West Australia

PCA9685 Servo and LED driver

#1 Post by Bill Legge » 05 Feb 2017 06:58

After a few days of effort I have got the PCA9685 on the Adafruit board to work properly.
I tried to make use of the LIBSTOCK package but:
1. Found it too confusing.
2. There was a detailed but unfruitful discussion about it not working.

I'm not saying that there is/was anything wrong with the LIBSTOCK package but it did not suit my level of knowledge
partly because it was made so flexible to run on different MCUs with a big variety of compilers.

If anyone else has had this problem with LIBSTOCK - please comment so that the fellows at MikroElektronika can think abut it?

My code follows:

Code: Select all

////////////////////////////////////////////////////////////////////////////////
// Project:         Thumper_Servo_01                                          //
// Fiile:           Main.c                                                    //
// Function:        Test Adafruit servo driver PCA9685                        //
// MCU:             PIC18F8722                                                //
// Board:           BIGPIC5                                                   //
// Power            5V.                                                       //
// Compiler:        mikroC PRO for PIC version 6.6.2                          //
// Programmer:      On-board Mikro                                            //
// Author:          WVL                                                       //
// Date:            February 2013                                             //
////////////////////////////////////////////////////////////////////////////////
#define Write_address 0x80  // I2C address for PCA9865 with no solder bridges
#define Read_address Write_address+1
#define PCA9685_software_reset 0x06
#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 ALL_CH_ON_L_reg   0xFA
#define ALL_CH_ON_H_reg   0xFB
#define ALL_CH_OFF_L_reg  0xFC
#define ALL_CH_OFF_H_reg  0xFD
#define HEARTBEAT LATH0_bit // green led on ETT board
////////////////////////////////////////////////////////////////////////////////
// Prototype functions - functions at end
void PCA9685_init();
void PCA9685_send(unsigned int value, unsigned char led);
void PCA9685AllLedOff();
unsigned char PCA9685_read_byte(unsigned char chip_register);
void PCA9685_write_byte(unsigned char chip_register, unsigned char value);
void PCA9685_write_word(unsigned char chip_register, unsigned int word_value);
void PCA9685_soft_reset();
////////////////////////////////////////////////////////////////////////////////
void main(){
    // block variables
    unsigned char              n = 0;   // LED flash
    unsigned int            loop = 0;   // loop counter
    unsigned int             pwm = 0;
    unsigned int                 x=0;
    // set up ports
    ADCON1 = 0b00001110;                // A0 is analog
    CMCON  = 0b00000111;                // disable comparators
    // set up peripherals
    I2C1_Init(100000);
    PCA9685_init();
    // ports
    INTCON2.RBPU = 1;                   // PORTB pullups disabled
    TRISA = 0b00000001;                 // A0 is A/D input
    TRISC = 0b11111111;                 // I2C2 pins must be inputs

    while(1){
        // Ramp SERVO up
        for(pwm=230;pwm<=902;pwm++){
            PCA9685_send(pwm,1);
            delay_ms(1);
        }
        // Ramp SERVO down
        for(pwm=902;pwm>=230;pwm--){
            PCA9685_send(pwm,1);
            delay_ms(1);
        }
        
        // Use byte write to set LED15 to 50% duty cycle 4096/2 = 2046
        // 2046 = 0x07FE so hi_byte=0x07 and lo_byte = 0xFE
        PCA9685_write_byte(0x42,0);     // turn on low byte
        PCA9685_write_byte(0x43,0);     // turn on high byte
        PCA9685_write_byte(0x44,0xFE);  // turn off low byte
        PCA9685_write_byte(0x45,0x07);  // turn off high byte
        
        // Use word write to set LED14 to 10% duty cycle 4096/10 = 409
        // 409 = 0x0199
        PCA9685_Write_word(0x3E,0x0000);// turn on at time 0
        PCA9685_Write_word(0x40,0x0199);// turn off at time 409 0r 0x0199
        
        // Use byte read to get a register value
        // Must bracket the shift because of precedence
        x = (PCA9685_read_byte(0x45)<<8) + PCA9685_read_byte(0x44) ;
        
        // Housekeep
        HEARTBEAT = ~HEARTBEAT;
        loop++;
        delay_ms(20);

    }
}
////////////////////////////////////////////////////////////////////////////////
// Functions
// Init the chip with pwm frequency and MODE2 settings
void PCA9685_init(){
     I2C1_start();             // Start
     I2C1_Wr(Write_address);   // Slave Write_address
     I2C1_Wr(MODE1);           // Mode 1 ADDRESS
     I2C1_Wr(0b00110001);      // Sleep and change default PWM frequency
     I2C1_stop();              // Stop
     delay_ms(1);              // Required 50 us delay
     I2C1_start();             // Start
     I2C1_Wr(Write_address);   // Slave Write_address
     I2C1_Wr(0xFE);            // PWM frequency PRE_SCALE ADDRESS to set pwm at 100Hz
     I2C1_Wr(0x3C);            // Osc_clk/(4096*update_rate)=25000000/(4096*100)=60=0x3C
     I2C1_stop();              // Stop
     delay_ms(1);              // delay at least 500 us
     I2C1_start();             // Start
     I2C1_Wr(Write_address);   // Slave Write_address
     I2C1_Wr(MODE1);           // Mode 1 register ADDRESS
     I2C1_Wr(0b10100001);      // Set MODE1
     I2C1_stop();              // Stop
     delay_ms(1);              // delay at least 500 us
     I2C1_start();             // Start
     I2C1_Wr(Write_address);   // Slave Address
     I2C1_Wr(MODE2);           // Mode2 register ADDRESS
     I2C1_Wr(0b00000100);      // Set MODE2
     I2C1_stop();              //
}
// Send pulse length[0-4095] to selected LED/SERVO[0-15]
void PCA9685_send(unsigned int value, unsigned char led){
     unsigned char pulse_length;// temp variable for PWM
     I2C1_start();              // Start
     I2C1_Wr(Write_address);    // address of selected pca9685
     I2C1_Wr(LED0 + 4 * led);   // select slected LED ADDRESS
     I2C1_Wr(0x00);             // LED_ON_L
     I2C1_Wr(0x00);             // LED_ON_H
     pulse_length = value;      // PWM value lo byte
     I2C1_Wr(pulse_length);     // LED_OFF_L
     pulse_length = value>>8;   // pwm 16 bit long, now shift upper 8 to lower 8
     I2C1_Wr(pulse_length);     // LED_OFF_H
     I2C1_stop();               // stop
}
void PCA9685AllLedOff(){
     I2C1_start();              // atart
     I2C1_Wr(Write_address);    // select pca9685
     I2C1_Wr(ALL_CH_OFF_L_reg); // All LEDs Off regiter
     I2C1_Wr(0b00000000);       // low byte
     I2C1_Wr(0b00010000);       // high byte, bit4 set so full_off see page 21
     I2C1_stop();               // Stop
}
// Read a byte and return it's value
unsigned char PCA9685_read_byte(unsigned char chip_register){
    unsigned char temp = 0x00;
    I2C1_Start();
    I2C1_Wr(Write_address);
    I2C1_Wr(chip_register);
    I2C1_Start();
    I2C1_Wr(Read_address);
    temp = I2C1_Rd(0);
    I2C1_Stop();
    return temp;
}
void PCA9685_write_byte(unsigned char chip_register, unsigned char value){
     I2C1_Start();
     I2C1_Wr(Write_address);
     I2C1_Wr(chip_register);
     I2C1_Wr(value);
     I2C1_Stop();
}
// Write 16bits to chip_register, increments automatically from lo to hi byte
void PCA9685_write_word(unsigned char chip_register, unsigned int word_value){
     unsigned char hb = 0x00;
     unsigned char lb = 0x00;
     lb = (word_value & 0x00FF);
     hb = ((word_value & 0xFF00) >> 0x08);
     PCA9685_write_byte(chip_register,lb);
     PCA9685_write_byte((chip_register+1),hb);
}
// Soft re-set
void PCA9685_soft_reset(){
    I2C1_Start();
    I2C1_Wr(0x00);
    I2C1_Wr(PCA9685_software_reset);
    I2C1_Stop();
}
Hope this helps someone?
Regards Bill Legge

User avatar
filip
mikroElektronika team
Posts: 11874
Joined: 25 Jan 2008 09:56

Re: PCA9685 Servo and LED driver

#2 Post by filip » 15 Feb 2017 10:54

Hi,

Are you referring to the PWM click example/library from the LibStock ?
If so, could you please explain what do you find here confusing ?

Regards,
Filip.

pomea
Posts: 11
Joined: 02 Dec 2013 18:54

Re: PCA9685 Servo and LED driver

#3 Post by pomea » 22 Dec 2019 16:39

Hi Bill, Filip,
after many trials I could use the ServoClick Library from Mikroe but I agree that it is too complicate. In addition it is really only for Servo motor while I would like to use PCA9685 for both Servo & leds.
The Mikroe librery does not allow to get 0 to 100% duty for Led PWM
Unfortunatly even Bill code doesn't work on my Easypic7 18F45K22. All Leds are off.

Searching in the Library I also found the project "PCA9685 16-Channel 12-bit PWM Controller Demo" MikroC for AVR and I make also a trial with some changes to adapt to MikroC code for PIC18F. I couldn't succed.
Leaving PCA9685.c and PCA9685.h unchanged, my code is this:

#include "PCA9685.c"
#define MAX_count 4096

void setup()
{
ADCON0 =0 ;//ANSEL = 0; // per F887 // Set AN pins to Digital I/O;
ADCON1 = 0x0C;//ANSELH = 0; // per F887 ; 0x00 x 16F877
//CMCON = 0;
TRISA = 0; // Configure PORTA 0,1,2 Input other as output
PORTA = 0; // Clear PORTA
TRISD = 0; // Configure PORTD as output
PORTD = 0; // Clear PORTD
TRISB = 0b00000111; // B1 INC Hour, B2 inc min, B0 store
PORTB = 0;
TRISE = 0; //
PORTE =0;
TRISC = 0 ; // set PORTC as output
PORTC = 0 ; // clear PORTC

/*
CLKPR = 0x80;
CLKPR = 0x00;
PORTB = 0x00;
DDRB = 0x00;
PORTC = 0x00;
DDRC = 0x00;
PORTD = 0x00;
DDRD = 0x00;
TCCR0A = 0x00;
TCCR0B = 0x00;
TCNT0 = 0x00;
OCR0A = 0x00;
OCR0B = 0x00;
TCCR1A = 0x00;
TCCR1B = 0x00;
TCNT1H = 0x00;
TCNT1L = 0x00;
ICR1H = 0x00;
ICR1L = 0x00;
OCR1AH = 0x00;
OCR1AL = 0x00;
OCR1BH = 0x00;
OCR1BL = 0x00;
ASSR = 0x00;
TCCR2A = 0x00;
TCCR2B = 0x00;
TCNT2 = 0x00;
OCR2A = 0x00;
OCR2B = 0x00;
EICRA = 0x00;
EIMSK = 0x00;
PCICR = 0x00;
TIMSK0 = 0x00;
TIMSK1 = 0x01;
TIMSK2 = 0x00;
UCSR0B = 0x00;
ACSR = 0x80;
ADCSRB = 0x00;
DIDR1 = 0x00;
ADCSRA = 0x00;
SPCR = 0x00;
TWCR = 0x00;
*/
PCA9685_init();
}
//void setup();


void main()
{
signed char j = 0;
signed int i = 0;
unsigned int v = 0;

setup();


while(1)
{
for(j = 0; j < 16; j += 2)
{
for(i = 0; i <= 180; i++)
{
v = (MAX_count * sin(0.0174 * i));
PCA9685_set_PWM_duty(j, v, (j * 256));
delay_ms(1);
}
}

for(j = 15; j > 0; j -= 2)
{
for(i = 0; i <= 180; i++)
{
v = (MAX_count * sin(0.0174 * i));
PCA9685_set_PWM_duty(j, v, (j * 256));
delay_ms(1);
}
}


for(j = 0; j < 16; j += 2)
{
for(i = 0; i <= 180; i++)
{
v = (MAX_count * cos(0.0174 * i));
PCA9685_set_PWM_duty(j, v, (j * 256));
delay_ms(1);
}
}

for(j = 15; j > 0; j -= 2)
{
for(i = 0; i < 180; i++)
{
v = (MAX_count * cos(0.0174 * i));
PCA9685_set_PWM_duty(j, v, (j * 256));
delay_ms(1);
}
}
}
}

Any suggestion or other examples available to make it work?
Many Thank
Attachments
DA AVR.rar
(68.92 KiB) Downloaded 107 times

kumar123
Posts: 68
Joined: 17 Oct 2023 07:32

Re: To work with servo motor using PCA9685 module

#4 Post by kumar123 » 17 Nov 2023 06:46

Hi,
I am using PCA9685 module to control servo motor, but I don't know the address of 16- channel of PCA module So I am not getting any voltage and current on channel.
And I am not able to configure the pins of servo_i2cDriverInit() function for our micro-controller.

Regards,
Himanshu

kumar123
Posts: 68
Joined: 17 Oct 2023 07:32

Re: problem to configure pins of micro contrller to connect with PCA9685 module to work with servo motor

#5 Post by kumar123 » 17 Nov 2023 09:58

Hi,
I am using PCA9685 module to control servo motor, but I don't know the address of 16- channel of PCA module So I am not getting any voltage and current on channel.
And I am not able to configure the pins of servo_i2cDriverInit() function for our micro-controller. I have tried to compile this given code but I am getting error, I have tried a lot to resolve this error but I couldn't.
pic_error.png
pic_error.png (220.39 KiB) Viewed 472 times
Regards,
Himanshu

User avatar
IvanJeremic
mikroElektronika team
Posts: 316
Joined: 05 Sep 2022 14:32

Re: PCA9685 Servo and LED driver

#6 Post by IvanJeremic » 20 Nov 2023 08:56

Hi,

Since we are already having this discussion on the forum in the link below, i ask you to not post on other forums and continue our conversation there.
viewtopic.php?f=88&t=80294&p=317727#p317727

Regards,

Ivan.

kumar123
Posts: 68
Joined: 17 Oct 2023 07:32

Re: PCA9685 Servo and LED driver

#7 Post by kumar123 » 08 Dec 2023 09:45

Hi,

Code: Select all

////////////////////////////////////////////////////////////////////////////////
// Project:         Thumper_Servo_01                                          //
// Fiile:           Main.c                                                    //
// Function:        Test Adafruit servo driver PCA9685                        //
// MCU:             PIC18F8722                                                //
// Board:           BIGPIC5                                                   //
// Power            5V.                                                       //
// Compiler:        mikroC PRO for PIC version 6.6.2                          //
// Programmer:      On-board Mikro                                            //
// Author:          WVL                                                       //
// Date:            February 2013                                             //
////////////////////////////////////////////////////////////////////////////////
#define Write_address 0x80  // I2C address for PCA9865 with no solder bridges
#define Read_address Write_address+1
#define PCA9685_software_reset 0x06
#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 ALL_CH_ON_L_reg   0xFA
#define ALL_CH_ON_H_reg   0xFB
#define ALL_CH_OFF_L_reg  0xFC
#define ALL_CH_OFF_H_reg  0xFD
#define HEARTBEAT LATH0_bit // green led on ETT board
////////////////////////////////////////////////////////////////////////////////
// Prototype functions - functions at end
void PCA9685_init();
void PCA9685_send(unsigned int value, unsigned char led);
void PCA9685AllLedOff();
unsigned char PCA9685_read_byte(unsigned char chip_register);
void PCA9685_write_byte(unsigned char chip_register, unsigned char value);
void PCA9685_write_word(unsigned char chip_register, unsigned int word_value);
void PCA9685_soft_reset();
////////////////////////////////////////////////////////////////////////////////
void main(){
    // block variables
    unsigned char              n = 0;   // LED flash
    unsigned int            loop = 0;   // loop counter
    unsigned int             pwm = 0;
    unsigned int                 x=0;
    // set up ports
    ADCON1 = 0b00001110;                // A0 is analog
    CMCON  = 0b00000111;                // disable comparators
    // set up peripherals
    I2C1_Init(100000);
    PCA9685_init();
    // ports
    INTCON2.RBPU = 1;                   // PORTB pullups disabled
    TRISA = 0b00000001;                 // A0 is A/D input
    TRISC = 0b11111111;                 // I2C2 pins must be inputs

    while(1){
        // Ramp SERVO up
        for(pwm=230;pwm<=902;pwm++){
            PCA9685_send(pwm,1);
            delay_ms(1);
        }
        // Ramp SERVO down
        for(pwm=902;pwm>=230;pwm--){
            PCA9685_send(pwm,1);
            delay_ms(1);
        }
        
        // Use byte write to set LED15 to 50% duty cycle 4096/2 = 2046
        // 2046 = 0x07FE so hi_byte=0x07 and lo_byte = 0xFE
        PCA9685_write_byte(0x42,0);     // turn on low byte
        PCA9685_write_byte(0x43,0);     // turn on high byte
        PCA9685_write_byte(0x44,0xFE);  // turn off low byte
        PCA9685_write_byte(0x45,0x07);  // turn off high byte
        
        // Use word write to set LED14 to 10% duty cycle 4096/10 = 409
        // 409 = 0x0199
        PCA9685_Write_word(0x3E,0x0000);// turn on at time 0
        PCA9685_Write_word(0x40,0x0199);// turn off at time 409 0r 0x0199
        
        // Use byte read to get a register value
        // Must bracket the shift because of precedence
        x = (PCA9685_read_byte(0x45)<<8) + PCA9685_read_byte(0x44) ;
        
        // Housekeep
        HEARTBEAT = ~HEARTBEAT;
        loop++;
        delay_ms(20);

    }
}
////////////////////////////////////////////////////////////////////////////////
// Functions
// Init the chip with pwm frequency and MODE2 settings
void PCA9685_init(){
     I2C1_start();             // Start
     I2C1_Wr(Write_address);   // Slave Write_address
     I2C1_Wr(MODE1);           // Mode 1 ADDRESS
     I2C1_Wr(0b00110001);      // Sleep and change default PWM frequency
     I2C1_stop();              // Stop
     delay_ms(1);              // Required 50 us delay
     I2C1_start();             // Start
     I2C1_Wr(Write_address);   // Slave Write_address
     I2C1_Wr(0xFE);            // PWM frequency PRE_SCALE ADDRESS to set pwm at 100Hz
     I2C1_Wr(0x3C);            // Osc_clk/(4096*update_rate)=25000000/(4096*100)=60=0x3C
     I2C1_stop();              // Stop
     delay_ms(1);              // delay at least 500 us
     I2C1_start();             // Start
     I2C1_Wr(Write_address);   // Slave Write_address
     I2C1_Wr(MODE1);           // Mode 1 register ADDRESS
     I2C1_Wr(0b10100001);      // Set MODE1
     I2C1_stop();              // Stop
     delay_ms(1);              // delay at least 500 us
     I2C1_start();             // Start
     I2C1_Wr(Write_address);   // Slave Address
     I2C1_Wr(MODE2);           // Mode2 register ADDRESS
     I2C1_Wr(0b00000100);      // Set MODE2
     I2C1_stop();              //
}
// Send pulse length[0-4095] to selected LED/SERVO[0-15]
void PCA9685_send(unsigned int value, unsigned char led){
     unsigned char pulse_length;// temp variable for PWM
     I2C1_start();              // Start
     I2C1_Wr(Write_address);    // address of selected pca9685
     I2C1_Wr(LED0 + 4 * led);   // select slected LED ADDRESS
     I2C1_Wr(0x00);             // LED_ON_L
     I2C1_Wr(0x00);             // LED_ON_H
     pulse_length = value;      // PWM value lo byte
     I2C1_Wr(pulse_length);     // LED_OFF_L
     pulse_length = value>>8;   // pwm 16 bit long, now shift upper 8 to lower 8
     I2C1_Wr(pulse_length);     // LED_OFF_H
     I2C1_stop();               // stop
}
void PCA9685AllLedOff(){
     I2C1_start();              // atart
     I2C1_Wr(Write_address);    // select pca9685
     I2C1_Wr(ALL_CH_OFF_L_reg); // All LEDs Off regiter
     I2C1_Wr(0b00000000);       // low byte
     I2C1_Wr(0b00010000);       // high byte, bit4 set so full_off see page 21
     I2C1_stop();               // Stop
}
// Read a byte and return it's value
unsigned char PCA9685_read_byte(unsigned char chip_register){
    unsigned char temp = 0x00;
    I2C1_Start();
    I2C1_Wr(Write_address);
    I2C1_Wr(chip_register);
    I2C1_Start();
    I2C1_Wr(Read_address);
    temp = I2C1_Rd(0);
    I2C1_Stop();
    return temp;
}
void PCA9685_write_byte(unsigned char chip_register, unsigned char value){
     I2C1_Start();
     I2C1_Wr(Write_address);
     I2C1_Wr(chip_register);
     I2C1_Wr(value);
     I2C1_Stop();
}
// Write 16bits to chip_register, increments automatically from lo to hi byte
void PCA9685_write_word(unsigned char chip_register, unsigned int word_value){
     unsigned char hb = 0x00;
     unsigned char lb = 0x00;
     lb = (word_value & 0x00FF);
     hb = ((word_value & 0xFF00) >> 0x08);
     PCA9685_write_byte(chip_register,lb);
     PCA9685_write_byte((chip_register+1),hb);
}
// Soft re-set
void PCA9685_soft_reset(){
    I2C1_Start();
    I2C1_Wr(0x00);
    I2C1_Wr(PCA9685_software_reset);
    I2C1_Stop();
}
In the above code they are using PWM for(pwm=230;pwm<=902;pwm++) How to calculate the PWM value?
Regards,
Kumar

Post Reply

Return to “mikroC PRO for PIC General”