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%
i2c.c@0:62c7c3514d8f, 2012-06-15 (annotated)
- Committer:
- Bas
- Date:
- Fri Jun 15 23:30:43 2012 +0000
- Revision:
- 0:62c7c3514d8f
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
Bas | 0:62c7c3514d8f | 1 | #include "mbed.h" |
Bas | 0:62c7c3514d8f | 2 | #include "i2c.h" |
Bas | 0:62c7c3514d8f | 3 | #include "pca9685_reg.h" /* Light Driver Chip */ |
Bas | 0:62c7c3514d8f | 4 | |
Bas | 0:62c7c3514d8f | 5 | I2C i2c(p9, p10); // sda, scl |
Bas | 0:62c7c3514d8f | 6 | |
Bas | 0:62c7c3514d8f | 7 | |
Bas | 0:62c7c3514d8f | 8 | /******************************************/ |
Bas | 0:62c7c3514d8f | 9 | /* */ |
Bas | 0:62c7c3514d8f | 10 | /* Probe the I2C bus, and show the */ |
Bas | 0:62c7c3514d8f | 11 | /* user what we have found */ |
Bas | 0:62c7c3514d8f | 12 | /* */ |
Bas | 0:62c7c3514d8f | 13 | /* */ |
Bas | 0:62c7c3514d8f | 14 | /* */ |
Bas | 0:62c7c3514d8f | 15 | /******************************************/ |
Bas | 0:62c7c3514d8f | 16 | void i2c_probe(void) |
Bas | 0:62c7c3514d8f | 17 | { |
Bas | 0:62c7c3514d8f | 18 | printf("Searching for I2C devices...\n"); |
Bas | 0:62c7c3514d8f | 19 | |
Bas | 0:62c7c3514d8f | 20 | int count = 0; |
Bas | 0:62c7c3514d8f | 21 | for (int address=4; address<256; address+=2) { |
Bas | 0:62c7c3514d8f | 22 | if (!i2c.write(address, NULL, 0)) { // 0 returned is ok |
Bas | 0:62c7c3514d8f | 23 | printf(" - I2C device found at address 0x%02X\r\n", address); |
Bas | 0:62c7c3514d8f | 24 | count++; |
Bas | 0:62c7c3514d8f | 25 | } |
Bas | 0:62c7c3514d8f | 26 | } |
Bas | 0:62c7c3514d8f | 27 | printf("%d devices found\r\n", count); |
Bas | 0:62c7c3514d8f | 28 | } |
Bas | 0:62c7c3514d8f | 29 | |
Bas | 0:62c7c3514d8f | 30 | /******************************************/ |
Bas | 0:62c7c3514d8f | 31 | /* */ |
Bas | 0:62c7c3514d8f | 32 | /* Philips PCA9685 I2C Driver, 16 channel */ |
Bas | 0:62c7c3514d8f | 33 | /* Lighting controler chip */ |
Bas | 0:62c7c3514d8f | 34 | /* */ |
Bas | 0:62c7c3514d8f | 35 | /* */ |
Bas | 0:62c7c3514d8f | 36 | /******************************************/ |
Bas | 0:62c7c3514d8f | 37 | |
Bas | 0:62c7c3514d8f | 38 | /******************************************/ |
Bas | 0:62c7c3514d8f | 39 | /* */ |
Bas | 0:62c7c3514d8f | 40 | /* Init code for the PCA9685 */ |
Bas | 0:62c7c3514d8f | 41 | /* */ |
Bas | 0:62c7c3514d8f | 42 | /******************************************/ |
Bas | 0:62c7c3514d8f | 43 | |
Bas | 0:62c7c3514d8f | 44 | void init_pca9685(unsigned char address) |
Bas | 0:62c7c3514d8f | 45 | { |
Bas | 0:62c7c3514d8f | 46 | unsigned char buf[30]; |
Bas | 0:62c7c3514d8f | 47 | |
Bas | 0:62c7c3514d8f | 48 | printf("Setting up channel %d\n\r",address); |
Bas | 0:62c7c3514d8f | 49 | |
Bas | 0:62c7c3514d8f | 50 | buf[0] = PCA9685_MODE1; |
Bas | 0:62c7c3514d8f | 51 | buf[1] = PCA9685_AI; |
Bas | 0:62c7c3514d8f | 52 | //buf[2] = PCA9685_OUTDRV; |
Bas | 0:62c7c3514d8f | 53 | buf[2] = PCA9685_INVRT; |
Bas | 0:62c7c3514d8f | 54 | i2c.write(address,(char *) buf, 3); |
Bas | 0:62c7c3514d8f | 55 | } |
Bas | 0:62c7c3514d8f | 56 | |
Bas | 0:62c7c3514d8f | 57 | /******************************************/ |
Bas | 0:62c7c3514d8f | 58 | /* */ |
Bas | 0:62c7c3514d8f | 59 | /* Send data to a given channel of a */ |
Bas | 0:62c7c3514d8f | 60 | /* given PCA9685 chip */ |
Bas | 0:62c7c3514d8f | 61 | /* */ |
Bas | 0:62c7c3514d8f | 62 | /******************************************/ |
Bas | 0:62c7c3514d8f | 63 | |
Bas | 0:62c7c3514d8f | 64 | void pca9685_led(unsigned char addr, int led, unsigned char *values) |
Bas | 0:62c7c3514d8f | 65 | { |
Bas | 0:62c7c3514d8f | 66 | unsigned char buf[5]; |
Bas | 0:62c7c3514d8f | 67 | |
Bas | 0:62c7c3514d8f | 68 | if (led == PCA9685_ALL_LEDS) { |
Bas | 0:62c7c3514d8f | 69 | buf[0] = PCA9685_ALL_LED_ON_L; |
Bas | 0:62c7c3514d8f | 70 | } else { |
Bas | 0:62c7c3514d8f | 71 | buf[0] = PCA9685_BASE(led); |
Bas | 0:62c7c3514d8f | 72 | } |
Bas | 0:62c7c3514d8f | 73 | |
Bas | 0:62c7c3514d8f | 74 | buf[1] = values[0]; |
Bas | 0:62c7c3514d8f | 75 | buf[2] = values[1]; |
Bas | 0:62c7c3514d8f | 76 | buf[3] = values[2]; |
Bas | 0:62c7c3514d8f | 77 | buf[4] = values[3]; |
Bas | 0:62c7c3514d8f | 78 | i2c.write(addr, (char *)buf, 5); |
Bas | 0:62c7c3514d8f | 79 | } |
Bas | 0:62c7c3514d8f | 80 | |
Bas | 0:62c7c3514d8f | 81 | /******************************************/ |
Bas | 0:62c7c3514d8f | 82 | /* */ |
Bas | 0:62c7c3514d8f | 83 | /* Send all the data to a single rgba led */ |
Bas | 0:62c7c3514d8f | 84 | /* at once to the PCA9685 chip */ |
Bas | 0:62c7c3514d8f | 85 | /* */ |
Bas | 0:62c7c3514d8f | 86 | /* */ |
Bas | 0:62c7c3514d8f | 87 | /* */ |
Bas | 0:62c7c3514d8f | 88 | /* color[0] = red (0-255) */ |
Bas | 0:62c7c3514d8f | 89 | /* color[1] = green (0-255) */ |
Bas | 0:62c7c3514d8f | 90 | /* color[2] = blue (0-255) */ |
Bas | 0:62c7c3514d8f | 91 | /* color[3] = amber (0-100%) */ |
Bas | 0:62c7c3514d8f | 92 | /* total brightness = level (0-100%) */ |
Bas | 0:62c7c3514d8f | 93 | /******************************************/ |
Bas | 0:62c7c3514d8f | 94 | void pca9685_rgba_led(unsigned char addr, int rgba_led, unsigned char *color, unsigned char level) |
Bas | 0:62c7c3514d8f | 95 | { |
Bas | 0:62c7c3514d8f | 96 | int led; |
Bas | 0:62c7c3514d8f | 97 | unsigned int on, off; |
Bas | 0:62c7c3514d8f | 98 | unsigned char buf[17]; |
Bas | 0:62c7c3514d8f | 99 | |
Bas | 0:62c7c3514d8f | 100 | if (level > 100){ |
Bas | 0:62c7c3514d8f | 101 | //printf("Level percentage range 0 - 100 (Trying for %d)\n\r",level); |
Bas | 0:62c7c3514d8f | 102 | return; |
Bas | 0:62c7c3514d8f | 103 | } |
Bas | 0:62c7c3514d8f | 104 | if (color[3] > 100) { |
Bas | 0:62c7c3514d8f | 105 | //printf("Amber percentage range 0 - 100 (Trying for %d)\n\r",color[3]); |
Bas | 0:62c7c3514d8f | 106 | return; |
Bas | 0:62c7c3514d8f | 107 | } |
Bas | 0:62c7c3514d8f | 108 | on=0; |
Bas | 0:62c7c3514d8f | 109 | if (rgba_led == PCA9685_ALL_LEDS) { |
Bas | 0:62c7c3514d8f | 110 | buf[0] = PCA9685_ALL_LED_ON_L; |
Bas | 0:62c7c3514d8f | 111 | if (color[0]==0 || level==0){ |
Bas | 0:62c7c3514d8f | 112 | buf[1]=0; |
Bas | 0:62c7c3514d8f | 113 | buf[1+PCA9685_LED_ON_H] = 0;//buf[2] |
Bas | 0:62c7c3514d8f | 114 | buf[3]=0; |
Bas | 0:62c7c3514d8f | 115 | buf[1+PCA9685_LED_OFF_H] = PCA9685_LED_OFF;//buf[4] |
Bas | 0:62c7c3514d8f | 116 | //printf("all on, zero brightness\r\n"); |
Bas | 0:62c7c3514d8f | 117 | } |
Bas | 0:62c7c3514d8f | 118 | else if (color[0]==0xff && level==100){ |
Bas | 0:62c7c3514d8f | 119 | buf[1]=0; |
Bas | 0:62c7c3514d8f | 120 | buf[1+PCA9685_LED_ON_H] = PCA9685_LED_ON;//buf[2] |
Bas | 0:62c7c3514d8f | 121 | buf[3]=0; |
Bas | 0:62c7c3514d8f | 122 | buf[1+PCA9685_LED_OFF_H] = 0;//buf[4] |
Bas | 0:62c7c3514d8f | 123 | //printf("all on, full brightness\r\n"); |
Bas | 0:62c7c3514d8f | 124 | } |
Bas | 0:62c7c3514d8f | 125 | else{ |
Bas | 0:62c7c3514d8f | 126 | off = (4096 * color[0]*level) / 0xff / 100; |
Bas | 0:62c7c3514d8f | 127 | buf[1+PCA9685_LED_ON_L] = on & 0xff;//buf[1] |
Bas | 0:62c7c3514d8f | 128 | buf[1+PCA9685_LED_ON_H] = (on >> 8) & 0xf;//buf[2] |
Bas | 0:62c7c3514d8f | 129 | buf[1+PCA9685_LED_OFF_L] = off & 0xff;//buf[3] |
Bas | 0:62c7c3514d8f | 130 | buf[1+PCA9685_LED_OFF_H] = (off >> 8) & 0xf;//buf[4] |
Bas | 0:62c7c3514d8f | 131 | } |
Bas | 0:62c7c3514d8f | 132 | i2c.write(addr, (char *)buf, 5); |
Bas | 0:62c7c3514d8f | 133 | //printf("All leds on\r\n"); |
Bas | 0:62c7c3514d8f | 134 | return; |
Bas | 0:62c7c3514d8f | 135 | } else { |
Bas | 0:62c7c3514d8f | 136 | buf[0] = PCA9685_BASE(rgba_led); |
Bas | 0:62c7c3514d8f | 137 | } |
Bas | 0:62c7c3514d8f | 138 | for (led=0;led<=3;led++){ |
Bas | 0:62c7c3514d8f | 139 | if (color[led]==0 || level==0){ |
Bas | 0:62c7c3514d8f | 140 | buf[led*4+1+PCA9685_LED_ON_H] = 0; |
Bas | 0:62c7c3514d8f | 141 | buf[led*4+1+PCA9685_LED_OFF_H] = PCA9685_LED_OFF; |
Bas | 0:62c7c3514d8f | 142 | continue; |
Bas | 0:62c7c3514d8f | 143 | } |
Bas | 0:62c7c3514d8f | 144 | if (((color[led]==100 && led==3)|| color[led]==0xff)&& level==100) { |
Bas | 0:62c7c3514d8f | 145 | buf[led*4+1+PCA9685_LED_ON_H] = PCA9685_LED_ON; |
Bas | 0:62c7c3514d8f | 146 | buf[led*4+1+PCA9685_LED_OFF_H] = 0; |
Bas | 0:62c7c3514d8f | 147 | continue; |
Bas | 0:62c7c3514d8f | 148 | } |
Bas | 0:62c7c3514d8f | 149 | off = (4096 * color[led]*level) / 0xff / 100; |
Bas | 0:62c7c3514d8f | 150 | buf[led*4+1+PCA9685_LED_ON_L] = on & 0xff; |
Bas | 0:62c7c3514d8f | 151 | buf[led*4+1+PCA9685_LED_ON_H] = (on >> 8) & 0xf; |
Bas | 0:62c7c3514d8f | 152 | buf[led*4+1+PCA9685_LED_OFF_L] = off & 0xff; |
Bas | 0:62c7c3514d8f | 153 | buf[led*4+1+PCA9685_LED_OFF_H] = (off >> 8) & 0xf; |
Bas | 0:62c7c3514d8f | 154 | } |
Bas | 0:62c7c3514d8f | 155 | i2c.write(addr, (char *)buf, 17); |
Bas | 0:62c7c3514d8f | 156 | } |
Bas | 0:62c7c3514d8f | 157 | /******************************************/ |
Bas | 0:62c7c3514d8f | 158 | /* */ |
Bas | 0:62c7c3514d8f | 159 | /* Calculate the register values for a */ |
Bas | 0:62c7c3514d8f | 160 | /* given brightness percentage */ |
Bas | 0:62c7c3514d8f | 161 | /* */ |
Bas | 0:62c7c3514d8f | 162 | /******************************************/ |
Bas | 0:62c7c3514d8f | 163 | |
Bas | 0:62c7c3514d8f | 164 | void pca9685_brightness(int percent, unsigned char *values) |
Bas | 0:62c7c3514d8f | 165 | { |
Bas | 0:62c7c3514d8f | 166 | unsigned int on, off; |
Bas | 0:62c7c3514d8f | 167 | |
Bas | 0:62c7c3514d8f | 168 | if (percent == 0) { |
Bas | 0:62c7c3514d8f | 169 | values[PCA9685_LED_ON_H] = 0; |
Bas | 0:62c7c3514d8f | 170 | values[PCA9685_LED_OFF_H] = PCA9685_LED_OFF; |
Bas | 0:62c7c3514d8f | 171 | return; |
Bas | 0:62c7c3514d8f | 172 | } |
Bas | 0:62c7c3514d8f | 173 | if (percent == 100) { |
Bas | 0:62c7c3514d8f | 174 | values[PCA9685_LED_ON_H] = PCA9685_LED_ON; |
Bas | 0:62c7c3514d8f | 175 | values[PCA9685_LED_OFF_H] = 0; |
Bas | 0:62c7c3514d8f | 176 | return; |
Bas | 0:62c7c3514d8f | 177 | } |
Bas | 0:62c7c3514d8f | 178 | on = 0; |
Bas | 0:62c7c3514d8f | 179 | off = (4096 * percent) / 100; |
Bas | 0:62c7c3514d8f | 180 | values[PCA9685_LED_ON_L] = on & 0xff;//0 |
Bas | 0:62c7c3514d8f | 181 | values[PCA9685_LED_ON_H] = (on >> 8) & 0xf;//1 |
Bas | 0:62c7c3514d8f | 182 | values[PCA9685_LED_OFF_L] = off & 0xff;//2 |
Bas | 0:62c7c3514d8f | 183 | values[PCA9685_LED_OFF_H] = (off >> 8) & 0xf;//3 |
Bas | 0:62c7c3514d8f | 184 | } |
Bas | 0:62c7c3514d8f | 185 | |
Bas | 0:62c7c3514d8f | 186 | /******************************************/ |
Bas | 0:62c7c3514d8f | 187 | /* */ |
Bas | 0:62c7c3514d8f | 188 | /* Set a given channel to a given level */ |
Bas | 0:62c7c3514d8f | 189 | /* */ |
Bas | 0:62c7c3514d8f | 190 | /******************************************/ |
Bas | 0:62c7c3514d8f | 191 | void channel_light(unsigned char ch, unsigned char lev) |
Bas | 0:62c7c3514d8f | 192 | { |
Bas | 0:62c7c3514d8f | 193 | char chip,led; /* Chip Number, channel number */ |
Bas | 0:62c7c3514d8f | 194 | unsigned char v[4]; /* register data for givern level */ |
Bas | 0:62c7c3514d8f | 195 | |
Bas | 0:62c7c3514d8f | 196 | led = ch%16; |
Bas | 0:62c7c3514d8f | 197 | v[0]=0; |
Bas | 0:62c7c3514d8f | 198 | v[1]=0; |
Bas | 0:62c7c3514d8f | 199 | v[2]=0; |
Bas | 0:62c7c3514d8f | 200 | v[3]=0; |
Bas | 0:62c7c3514d8f | 201 | |
Bas | 0:62c7c3514d8f | 202 | if(lev > 100){ |
Bas | 0:62c7c3514d8f | 203 | printf("Level percentage range 0 - 100 (Trying for %d)\n\r",lev); |
Bas | 0:62c7c3514d8f | 204 | return; |
Bas | 0:62c7c3514d8f | 205 | } |
Bas | 0:62c7c3514d8f | 206 | |
Bas | 0:62c7c3514d8f | 207 | switch(ch/16){ |
Bas | 0:62c7c3514d8f | 208 | case 0 : |
Bas | 0:62c7c3514d8f | 209 | chip=LEDDRV1; |
Bas | 0:62c7c3514d8f | 210 | break; |
Bas | 0:62c7c3514d8f | 211 | case 1 : |
Bas | 0:62c7c3514d8f | 212 | chip=LEDDRV2; |
Bas | 0:62c7c3514d8f | 213 | break; |
Bas | 0:62c7c3514d8f | 214 | case 2 : |
Bas | 0:62c7c3514d8f | 215 | chip=LEDDRV3; |
Bas | 0:62c7c3514d8f | 216 | break; |
Bas | 0:62c7c3514d8f | 217 | case 3 : |
Bas | 0:62c7c3514d8f | 218 | chip=LEDDRV4; |
Bas | 0:62c7c3514d8f | 219 | break; |
Bas | 0:62c7c3514d8f | 220 | case 4 : |
Bas | 0:62c7c3514d8f | 221 | chip=LEDDRV5; |
Bas | 0:62c7c3514d8f | 222 | break; |
Bas | 0:62c7c3514d8f | 223 | case 5 : |
Bas | 0:62c7c3514d8f | 224 | chip=LEDDRV6; |
Bas | 0:62c7c3514d8f | 225 | break; |
Bas | 0:62c7c3514d8f | 226 | default : |
Bas | 0:62c7c3514d8f | 227 | printf("Error unknown chip %d\n\r",ch/16); |
Bas | 0:62c7c3514d8f | 228 | return; |
Bas | 0:62c7c3514d8f | 229 | } |
Bas | 0:62c7c3514d8f | 230 | |
Bas | 0:62c7c3514d8f | 231 | printf("Setting channel %d to brightness level %d chip = %d(%d),%d\n\r", |
Bas | 0:62c7c3514d8f | 232 | ch,lev,chip,ch/16,led); |
Bas | 0:62c7c3514d8f | 233 | pca9685_brightness(lev,v); /* Calculate the brightness level */ |
Bas | 0:62c7c3514d8f | 234 | printf("Brightness level is %02x,%02x,%02x,%02x\n\r",v[0],v[1],v[2],v[3]); |
Bas | 0:62c7c3514d8f | 235 | pca9685_led(chip,led,v); /* Send to chip */ |
Bas | 0:62c7c3514d8f | 236 | } |
Bas | 0:62c7c3514d8f | 237 |