I2C driver for the NXP chip PCA9685 16xPWM LED controller A total of 4 RGBA LEDs can be controlled by 1 chip giving a RGBA value in the form of: rgba(0-255,0-255,0-255,0-100) then dimmed by 0-100%

Dependencies:   mbed

Files at this revision

API Documentation at this revision

Comitter:
Bas
Date:
Fri Jun 15 23:30:43 2012 +0000
Commit message:

Changed in this revision

i2c.c Show annotated file Show diff for this revision Revisions of this file
i2c.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
pca9685_reg.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/i2c.c	Fri Jun 15 23:30:43 2012 +0000
@@ -0,0 +1,237 @@
+#include "mbed.h"
+#include "i2c.h"
+#include "pca9685_reg.h"    /* Light Driver Chip */
+
+I2C i2c(p9, p10); // sda, scl
+
+
+/******************************************/
+/*                                        */
+/*   Probe the I2C bus, and show the      */
+/*   user what we have found              */
+/*                                        */
+/*                                        */
+/*                                        */
+/******************************************/
+void i2c_probe(void) 
+{
+    printf("Searching for I2C devices...\n");
+    
+    int count = 0;
+    for (int address=4; address<256; address+=2) {
+        if (!i2c.write(address, NULL, 0)) { // 0 returned is ok
+            printf(" - I2C device found at address 0x%02X\r\n", address);
+            count++;
+        }
+    }
+    printf("%d devices found\r\n", count);
+}
+
+/******************************************/
+/*                                        */
+/* Philips PCA9685 I2C Driver, 16 channel */
+/* Lighting controler chip                */
+/*                                        */
+/*                                        */
+/******************************************/
+
+/******************************************/
+/*                                        */
+/* Init code for the PCA9685              */
+/*                                        */
+/******************************************/
+
+void init_pca9685(unsigned char address)
+{
+    unsigned char buf[30];
+    
+    printf("Setting up channel %d\n\r",address);
+    
+    buf[0] = PCA9685_MODE1;
+    buf[1] = PCA9685_AI;
+    //buf[2] = PCA9685_OUTDRV;
+    buf[2] = PCA9685_INVRT;
+    i2c.write(address,(char *) buf, 3);
+}
+
+/******************************************/
+/*                                        */
+/* Send data to a given channel of a      */
+/* given PCA9685 chip                     */
+/*                                        */
+/******************************************/
+
+void pca9685_led(unsigned char addr, int led, unsigned char *values) 
+{
+    unsigned char buf[5];
+   
+    if (led == PCA9685_ALL_LEDS) {
+        buf[0] = PCA9685_ALL_LED_ON_L;
+    } else {
+        buf[0] = PCA9685_BASE(led);
+    }
+
+    buf[1] = values[0];
+    buf[2] = values[1];
+    buf[3] = values[2];
+    buf[4] = values[3];
+    i2c.write(addr, (char *)buf, 5);
+}
+
+/******************************************/
+/*                                        */
+/* Send all the data to a single rgba led */
+/* at once to the PCA9685 chip            */
+/*                                        */
+/*                                        */
+/*                                        */
+/* color[0] = red (0-255)                 */
+/* color[1] = green (0-255)               */
+/* color[2] = blue (0-255)                */
+/* color[3] = amber (0-100%)              */
+/* total brightness = level (0-100%)      */
+/******************************************/
+void pca9685_rgba_led(unsigned char addr, int rgba_led, unsigned char *color, unsigned char level)
+{
+    int led;
+    unsigned int on, off;
+    unsigned char buf[17];
+    
+    if (level > 100){
+        //printf("Level percentage range 0 - 100 (Trying for %d)\n\r",level);
+        return;
+        }
+    if (color[3] > 100) {
+        //printf("Amber percentage range 0 - 100 (Trying for %d)\n\r",color[3]);
+        return;
+        }
+    on=0;
+    if (rgba_led == PCA9685_ALL_LEDS) {
+        buf[0] = PCA9685_ALL_LED_ON_L;
+        if (color[0]==0 || level==0){
+            buf[1]=0;
+            buf[1+PCA9685_LED_ON_H] = 0;//buf[2]
+            buf[3]=0;
+            buf[1+PCA9685_LED_OFF_H] = PCA9685_LED_OFF;//buf[4]
+            //printf("all on, zero brightness\r\n");
+        }
+        else if (color[0]==0xff && level==100){
+            buf[1]=0;
+            buf[1+PCA9685_LED_ON_H] = PCA9685_LED_ON;//buf[2]
+            buf[3]=0;
+            buf[1+PCA9685_LED_OFF_H] = 0;//buf[4]
+            //printf("all on, full brightness\r\n");
+        }
+        else{
+            off = (4096 * color[0]*level) / 0xff / 100;
+            buf[1+PCA9685_LED_ON_L] = on & 0xff;//buf[1]
+            buf[1+PCA9685_LED_ON_H] = (on >> 8) & 0xf;//buf[2]
+            buf[1+PCA9685_LED_OFF_L] = off & 0xff;//buf[3]
+            buf[1+PCA9685_LED_OFF_H] = (off >> 8) & 0xf;//buf[4]
+        }
+        i2c.write(addr, (char *)buf, 5);
+        //printf("All leds on\r\n");
+        return;
+    } else {
+        buf[0] = PCA9685_BASE(rgba_led);
+    }    
+    for (led=0;led<=3;led++){
+        if (color[led]==0 || level==0){
+        buf[led*4+1+PCA9685_LED_ON_H] = 0; 
+        buf[led*4+1+PCA9685_LED_OFF_H] = PCA9685_LED_OFF;
+        continue;
+        }
+        if (((color[led]==100 && led==3)|| color[led]==0xff)&& level==100) {
+        buf[led*4+1+PCA9685_LED_ON_H] = PCA9685_LED_ON;
+        buf[led*4+1+PCA9685_LED_OFF_H] = 0;
+        continue;
+        }
+        off = (4096 * color[led]*level) / 0xff / 100;
+        buf[led*4+1+PCA9685_LED_ON_L] = on & 0xff;
+        buf[led*4+1+PCA9685_LED_ON_H] = (on >> 8) & 0xf;
+        buf[led*4+1+PCA9685_LED_OFF_L] = off & 0xff;
+        buf[led*4+1+PCA9685_LED_OFF_H] = (off >> 8) & 0xf;
+    }
+    i2c.write(addr, (char *)buf, 17);
+}
+/******************************************/
+/*                                        */
+/* Calculate the register values for a    */
+/* given brightness percentage            */
+/*                                        */
+/******************************************/
+
+void pca9685_brightness(int percent, unsigned char *values) 
+{
+    unsigned int on, off;
+
+    if (percent == 0) {
+    values[PCA9685_LED_ON_H] = 0; 
+    values[PCA9685_LED_OFF_H] = PCA9685_LED_OFF;
+    return;
+    }
+    if (percent == 100) {
+    values[PCA9685_LED_ON_H] = PCA9685_LED_ON;
+    values[PCA9685_LED_OFF_H] = 0;
+    return;
+    }
+    on = 0;
+    off = (4096 * percent) / 100;
+    values[PCA9685_LED_ON_L] = on & 0xff;//0
+    values[PCA9685_LED_ON_H] = (on >> 8) & 0xf;//1
+    values[PCA9685_LED_OFF_L] = off & 0xff;//2
+    values[PCA9685_LED_OFF_H] = (off >> 8) & 0xf;//3
+}
+
+/******************************************/
+/*                                        */
+/* Set a given channel to a given level   */
+/*                                        */
+/******************************************/
+void channel_light(unsigned char ch, unsigned char lev)
+{
+    char            chip,led;     /* Chip Number, channel number */
+    unsigned char   v[4];         /* register data for givern level */
+    
+    led = ch%16;
+    v[0]=0;
+    v[1]=0;
+    v[2]=0;
+    v[3]=0;
+    
+    if(lev > 100){
+        printf("Level percentage range 0 - 100 (Trying for %d)\n\r",lev);
+        return;
+    }
+    
+    switch(ch/16){
+        case    0    :
+            chip=LEDDRV1;
+            break;
+        case    1    :
+            chip=LEDDRV2;
+            break;
+        case    2    :
+            chip=LEDDRV3;
+            break;
+        case    3    :
+            chip=LEDDRV4;
+            break;
+        case    4    :
+            chip=LEDDRV5;
+            break;
+        case    5    :
+            chip=LEDDRV6;
+            break;
+        default      :
+            printf("Error unknown chip %d\n\r",ch/16);
+            return;
+    }
+    
+    printf("Setting channel %d to brightness level %d chip = %d(%d),%d\n\r",
+        ch,lev,chip,ch/16,led);
+    pca9685_brightness(lev,v);    /* Calculate the brightness level */
+    printf("Brightness level is %02x,%02x,%02x,%02x\n\r",v[0],v[1],v[2],v[3]);
+    pca9685_led(chip,led,v);      /* Send to chip */
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/i2c.h	Fri Jun 15 23:30:43 2012 +0000
@@ -0,0 +1,10 @@
+/* I2C bus Functions */
+
+void i2c_probe(void);
+
+void init_pca9685(unsigned char address);
+void channel_light(unsigned char ch, unsigned char lev);
+void pca9685_led(unsigned char addr, int led, unsigned char *values);
+void pca9685_rgba_led(unsigned char addr, int rgba_led, unsigned char *color, unsigned char level);
+void pca9685_brightness(int percent, unsigned char *values);
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Fri Jun 15 23:30:43 2012 +0000
@@ -0,0 +1,69 @@
+#include "mbed.h"
+#include "i2c.h"
+#include "pca9685_reg.h"
+
+DigitalOut myled(LED1);
+Serial pc(USBTX, USBRX);
+AnalogIn mypot(p20);
+
+int main() {
+   
+    
+    unsigned char level;
+    unsigned char rgba_color[4];
+    
+    i2c_probe();
+    init_pca9685(LEDDRV1);
+
+    rgba_color[0]=0xff; //red (0-255)
+    rgba_color[1]=0x00; //green (0-255)
+    rgba_color[2]=0x00; //blue (0-255)
+    rgba_color[3]=100; //amber (0-100) (0.00-1.00)
+    
+    while (1) {
+        level=mypot*100;
+        printf("level: %d\r\n",level);
+        pca9685_rgba_led(LEDDRV1,RGBA_LED1,rgba_color,level);
+        //channel_light(0,r*level/0xff);
+        //channel_light(1,g*level/0xff);
+        //channel_light(2,b*level/0xff);
+
+    }
+}
+
+/* void loop() {
+  int r, g, b;
+ 
+  // fade from blue to violet
+  for (r = 0; r < 256; r++) { 
+    analogWrite(REDPIN, r);
+    delay(FADESPEED);
+  } 
+  // fade from violet to red
+  for (b = 255; b > 0; b--) { 
+    analogWrite(BLUEPIN, b);
+    delay(FADESPEED);
+  } 
+  // fade from red to yellow
+  for (g = 0; g < 256; g++) { 
+    analogWrite(GREENPIN, g);
+    delay(FADESPEED);
+  } 
+  // fade from yellow to green
+  for (r = 255; r > 0; r--) { 
+    analogWrite(REDPIN, r);
+    delay(FADESPEED);
+  } 
+  // fade from green to teal
+  for (b = 0; b < 256; b++) { 
+    analogWrite(BLUEPIN, b);
+    delay(FADESPEED);
+  } 
+  // fade from teal to blue
+  for (g = 255; g > 0; g--) { 
+    analogWrite(GREENPIN, g);
+    delay(FADESPEED);
+  } 
+}
+--------------------------------------------------------------------------------
+*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Fri Jun 15 23:30:43 2012 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/737756e0b479
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pca9685_reg.h	Fri Jun 15 23:30:43 2012 +0000
@@ -0,0 +1,128 @@
+#ifndef __PCA9685_H
+#define __PCA9685_H
+
+/* Devices */
+#define LEDDRV1    0xb8
+#define LEDDRV2    0xba
+#define LEDDRV3    0xd8
+#define LEDDRV4    0xda
+#define LEDDRV5    0xc8
+#define LEDDRV6    0xca
+
+/* RGBA LED's. Max 4 on 1 chip */
+#define RGBA_LED1   0
+#define RGBA_LED2   1
+#define RGBA_LED3   2
+#define RGBA_LED4   3
+
+/* Registers */
+#define PCA9685_MODE1            0x00
+#define PCA9685_MODE2            0x01
+#define PCA9685_SUBADR1            0x02
+#define PCA9685_SUBADR2            0x03
+#define PCA9685_SUBADR3            0x04
+#define PCA9685_ALLCALLADR        0x05
+#define PCA9685_LED0_ON_L        0x06
+#define PCA9685_LED0_ON_H        0x07
+#define PCA9685_LED0_OFF_L        0x08
+#define PCA9685_LED0_OFF_H        0x09
+#define PCA9685_LED1_ON_L        0x0a
+#define PCA9685_LED1_ON_H        0x0b
+#define PCA9685_LED1_OFF_L        0x0c
+#define PCA9685_LED1_OFF_H        0x0d
+#define PCA9685_LED2_ON_L        0x0e
+#define PCA9685_LED2_ON_H        0x0f
+#define PCA9685_LED2_OFF_L        0x10
+#define PCA9685_LED2_OFF_H        0x11
+#define PCA9685_LED3_ON_L        0x12
+#define PCA9685_LED3_ON_H        0x13
+#define PCA9685_LED3_OFF_L        0x14
+#define PCA9685_LED3_OFF_H        0x15
+#define PCA9685_LED4_ON_L        0x16
+#define PCA9685_LED4_ON_H        0x17
+#define PCA9685_LED4_OFF_L        0x18
+#define PCA9685_LED4_OFF_H        0x19
+#define PCA9685_LED5_ON_L        0x1a
+#define PCA9685_LED5_ON_H        0x1b
+#define PCA9685_LED5_OFF_L        0x1c
+#define PCA9685_LED5_OFF_H        0x1d
+#define PCA9685_LED6_ON_L        0x1e
+#define PCA9685_LED6_ON_H        0x1d
+#define PCA9685_LED6_OFF_L        0x20
+#define PCA9685_LED6_OFF_H        0x21
+#define PCA9685_LED7_ON_L        0x22
+#define PCA9685_LED7_ON_H        0x23
+#define PCA9685_LED7_OFF_L        0x24
+#define PCA9685_LED7_OFF_H        0x25
+#define PCA9685_LED8_ON_L        0x26
+#define PCA9685_LED8_ON_H        0x27
+#define PCA9685_LED8_OFF_L        0x28
+#define PCA9685_LED8_OFF_H        0x29
+#define PCA9685_LED9_ON_L        0x2a
+#define PCA9685_LED9_ON_H        0x2b
+#define PCA9685_LED9_OFF_L        0x2c
+#define PCA9685_LED9_OFF_H        0x2d
+#define PCA9685_LED10_ON_L        0x2e
+#define PCA9685_LED10_ON_H        0x2f
+#define PCA9685_LED10_OFF_L        0x30
+#define PCA9685_LED10_OFF_H        0x31
+#define PCA9685_LED11_ON_L        0x32
+#define PCA9685_LED11_ON_H        0x33
+#define PCA9685_LED11_OFF_L        0x34
+#define PCA9685_LED11_OFF_H        0x35
+#define PCA9685_LED12_ON_L        0x36
+#define PCA9685_LED12_ON_H        0x37
+#define PCA9685_LED12_OFF_L        0x38
+#define PCA9685_LED12_OFF_H        0x39
+#define PCA9685_LED13_ON_L        0x3a
+#define PCA9685_LED13_ON_H        0x3b
+#define PCA9685_LED13_OFF_L        0x3c
+#define PCA9685_LED13_OFF_H        0x3d
+#define PCA9685_LED14_ON_L        0x3e
+#define PCA9685_LED14_ON_H        0x3f
+#define PCA9685_LED14_OFF_L        0x40
+#define PCA9685_LED14_OFF_H        0x41
+#define PCA9685_LED15_ON_L        0x42
+#define PCA9685_LED15_ON_H        0x43
+#define PCA9685_LED15_OFF_L        0x44
+#define PCA9685_LED15_OFF_H        0x45
+#define PCA9685_ALL_LED_ON_L       0xfa
+#define PCA9685_ALL_LED_ON_H       0xfb
+#define PCA9685_ALL_LED_OFF_L      0xfc
+#define PCA9685_ALL_LED_OFF_H      0xfd
+#define PCA9685_PRE_SCALE      0xfe
+
+/* 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
+
+#define PCA9685_LED_BUFSIZ       0x04
+#define PCA9685_BASE(led) ((led * 4) + 6)
+#define PCA9685_BASE_RGBA(led) (led * 16 + 6)
+#define PCA9685_LED_ON_L       0x00
+#define PCA9685_LED_ON_H       0x01
+#define PCA9685_LED_OFF_L      0x02
+#define PCA9685_LED_OFF_H      0x03
+#define PCA9685_ALL_LEDS    -1
+
+
+#endif
\ No newline at end of file