View previous topic :: View next topic |
Author |
Message |
dezso
Joined: 04 Mar 2010 Posts: 102
|
NXP's PCA9685 driver |
Posted: Mon Apr 02, 2012 11:00 pm |
|
|
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 the 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 preferred 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 preferred 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 register 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; // update 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 select next pcs9685
i2c_start(); // Start
i2c_write(address); // Address of selected pca9685
i2c_write(LED0 + 4 * led); // select selected 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 update 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 % greater 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 approach 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 data 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 register
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 register 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 wide, 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 |
|
|
codyfinden
Joined: 13 Jul 2012 Posts: 2
|
PCA9532 |
Posted: Fri Jul 13, 2012 10:00 am |
|
|
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
|
|
Posted: Wed Sep 12, 2012 10:28 am |
|
|
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 |
|
|
aaronik19
Joined: 25 Apr 2011 Posts: 297
|
|
Posted: Sun Jul 14, 2013 1:34 pm |
|
|
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
|
Re: NXP's PCA9685 driver |
Posted: Wed Feb 18, 2015 5:32 am |
|
|
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: 19 Location: Kaunas, Lithuania
|
|
Posted: Thu Oct 05, 2017 5:32 am |
|
|
Thanx a lot, dezso!
Your code works perfectly! |
|
|
AnaA
Joined: 11 Nov 2017 Posts: 1
|
Doubt |
Posted: Sat Nov 11, 2017 7:12 pm |
|
|
Hi 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: 21708
|
|
Posted: Sun Nov 12, 2017 11:13 am |
|
|
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. |
|
|
nerd_66
Joined: 12 Apr 2019 Posts: 1
|
NXP's PCA9685 |
Posted: Fri Apr 12, 2019 10:09 am |
|
|
Could you explain what the line output_toggle(PIN_D5) does, please? It's in the void main. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Apr 12, 2019 12:27 pm |
|
|
I suspect it's just an LED, which toggles to show activity in the main loop.
If it's toggling, it means the main loop has not locked up. It means the
program is running. |
|
|
Sterngleiter
Joined: 07 Jan 2013 Posts: 90
|
pca9685 |
Posted: Fri Oct 11, 2019 2:23 pm |
|
|
hello,
Can someone help me please. I do not understand that with all_led_on and all_led_off. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Oct 11, 2019 6:50 pm |
|
|
Here is his code for that routine:
Quote: | void PCA9685AllLedOff(int address)
{
i2c_start(); // Start
i2c_write(address); // select pca9685
i2c_write(0xfc); // All LED Off register
i2c_write(0b00000000); // data for ALL_LED_OFF_L
i2c_write(0b00010000); // data for ALL_LED_OFF_H
i2c_stop(); // Stop
} |
The PCA9685 data sheet says on page 16:
Quote: |
Two methods can be used to do an orderly shutdown.
The fastest is to write a logic 1 to bit 4 in register ALL_LED_OFF_H.
The other method is to write logic 1 to bit 4 in each active PWM channel LEDn_OFF_H register. |
He is doing the method shown in bold above. |
|
|
Sterngleiter
Joined: 07 Jan 2013 Posts: 90
|
|
Posted: Sat Oct 12, 2019 12:59 am |
|
|
I have already seen that. Which bit do I have to put in order to switch everything back on? |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
|
Johnny5
Joined: 05 Aug 2020 Posts: 1
|
Sending signal to LED15 full on from BeagleBone Black |
Posted: Wed Aug 05, 2020 3:19 pm |
|
|
What would code look like in C programming to send signal on I2C bus from BeagleBone Black to PCA9685 LED15 to FULLON? |
|
|
|