├── README.md ├── examples └── RGB_Ramp │ └── RGB_Ramp.ino ├── library.json ├── library.properties ├── pca9633-proto.png └── src ├── pca9633.cpp └── pca9633.h /README.md: -------------------------------------------------------------------------------- 1 | ## pca9633 2 | 3 | Library to control the NXP / Philips / Texas Instruments PCA9633 four channel 8 bit LED control chip. This chip has four LED driver channels each with its own PWM brightness control, as well as a separate global brightness control. Can be used to control RGB+W LED strips if you use external drivers (transistor, etc.) 4 | 5 | --- 6 | ### Quick start: 7 | 8 | ```c 9 | // include library 10 | #include 11 | #include // OPTIONAL see below under setup() 12 | 13 | // create instance 14 | PCA9633 rgbw; 15 | 16 | void setup() { 17 | Wire.begin(SDA,SCL); // OPTIONAL not needed if you use the 3rd begin below and are using default i2c pins for your board 18 | 19 | // initilize library with device i2c address. 20 | // consult datasheet for available addresses and use an i2c scan routine to verify 21 | rgbw.begin(0x60); 22 | 23 | // rgbw.begin(0x60, 50); // optionally set fade delay if using library to transistion between PWM values 24 | 25 | // rgbw.begin(0x60, 50, true); // set fade delay and also init the i2c bus using Arduino defaults for your board 26 | } 27 | 28 | void loop() { 29 | rgbw.setrgbw(128,128,128,128); // set all four outputs to 50% 30 | delay(500); 31 | 32 | rgbw.setrgbw(0,0,0,0); // set all four outputs to 0% 33 | delay(500); 34 | 35 | rgbw.setpwm(0,255); // ramp channel 0 to 100% 36 | delay(500); 37 | 38 | rgbw.setpwm(0,0); // ramp channel 0 to 0% 39 | delay(500); 40 | } 41 | ``` 42 | --- 43 | 44 | ### Some functions: 45 | 46 | ```c 47 | // blocking call, fade single channel from current PWM setting to new PWM setting 48 | rgbw.setpwm(uint8_t pwmaddr, uint8_t pwmval); 49 | 50 | // non blocking call, set all four channels in one command, no fade 51 | rgbw.setrgbw(uint8_t p0, uint8_t p1, uint8_t p2, uint8_t p3); 52 | 53 | // update fade step delay, range 0 to 100, default is 10 54 | rgbw.setFade(uint8_t dly); 55 | 56 | // retreive current fade delay; 57 | uint8_t fadeDelay = rgbw.getFade(); 58 | 59 | // set global dimmer pwm. will dim all outputs, consult datasheet 60 | rgbw.setgrouppwm(uint8_t pwm); 61 | 62 | // init library with slave device address 63 | rgbw.begin(uint8_t devAddr); 64 | 65 | // init library with slave device and fade delay 66 | rgbw.begin(uint8_t devAddr, uint8_t fade_delay); 67 | 68 | // init library with slave device, fade delay and also call Wire.begin() internally, only set 3rd argument to true 69 | rgbw.begin(uint8_t devAddr, uint8_t fade_delay, uint8_t i2c_init); 70 | 71 | ``` 72 | 73 | ### My prototype board: 74 | ESP8266 based 4-channel dimmer for large LED strips. Beefy mosfets on each channel with proper 12v drivers. 75 | 76 | ![Image of PCA9633 prototype board](https://github.com/gordonthree/pca9633/blob/master/pca9633-proto.png?raw=true) 77 | -------------------------------------------------------------------------------- /examples/RGB_Ramp/RGB_Ramp.ino: -------------------------------------------------------------------------------- 1 | // include library 2 | #include 3 | #include // OPTIONAL see below under setup() 4 | 5 | // create instance 6 | PCA9633 rgbw; 7 | 8 | void setup() { 9 | #if defined(__AVR__) 10 | Wire.begin(); // OPTIONAL not needed if you use the 3rd begin below and are using default i2c pins for your board 11 | #else 12 | Wire.begin(SDA,SCL); // OPTIONAL not needed if you use the 3rd begin below and are using default i2c pins for your board 13 | #endif 14 | 15 | // initilize library with device i2c address. 16 | // consult datasheet for available addresses and use an i2c scan routine to verify 17 | rgbw.begin(0x60); 18 | 19 | // rgbw.begin(0x60, 50); // optionally set fade delay if using library to transistion between PWM values 20 | 21 | //rgbw.begin(0x60, 50, true); // set fade delay and also init the i2c bus using Arduino defaults for your board 22 | 23 | rgbw.setrgbw(128,128,128,128); // set all four outputs to 50% 24 | delay(500); 25 | } 26 | 27 | void loop() { 28 | 29 | rgbw.setpwm(0,255); // ramp channel 0 to 100% 30 | delay(500); 31 | 32 | rgbw.setpwm(0,0); // ramp channel 0 to 0% 33 | delay(500); 34 | 35 | rgbw.setpwm(1,255); // ramp channel 1 to 100% 36 | delay(500); 37 | 38 | rgbw.setpwm(1,0); // ramp channel 1 to 0% 39 | delay(500); 40 | 41 | rgbw.setpwm(2,255); // ramp channel 2 to 100% 42 | delay(500); 43 | 44 | rgbw.setpwm(2,0); // ramp channel 2 to 0% 45 | delay(500); 46 | } 47 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pca9633", 3 | "version": "1.0.0", 4 | "keywords": "i2c, LED, pca9633, RGB, RGBA, RGTW, PWM", 5 | "description": "Control NXP PCA9633 (and 9632) four channel PWM led driver chips.", 6 | "repository": 7 | { 8 | "type": "git", 9 | "url": "https://github.com/gordonthree/pca9633.git" 10 | }, 11 | "frameworks": "arduino", 12 | "authors": 13 | { 14 | "name": "Gordon McLellan", 15 | "email": "gordonthree@gmail.com", 16 | "url": "https://github.com/gordonthree", 17 | "maintainer": true 18 | }, 19 | "platforms": 20 | [ 21 | "atmelavr", 22 | "atmelmegaavr", 23 | "ststm32", 24 | "espressif8266", 25 | "espressif32" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=pca9633 2 | version=1.0.0 3 | author=Gordon McLellan 4 | maintainer=Gordon McLellan 5 | sentence=Control NXP PCA9633 (and 9632) four channel PWM led driver chips. 6 | paragraph= 7 | category=Device Control 8 | url=https://github.com/gordonthree/pca9633 9 | architectures=* 10 | includes=pca9633.h 11 | -------------------------------------------------------------------------------- /pca9633-proto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gordonthree/pca9633/481558140c1f1a381f62a05761408cebda8c4eab/pca9633-proto.png -------------------------------------------------------------------------------- /src/pca9633.cpp: -------------------------------------------------------------------------------- 1 | #if ARDUINO >= 100 2 | #include "Arduino.h" 3 | #else 4 | #include "WProgram.h" 5 | #endif 6 | 7 | #include 8 | #include "pca9633.h" 9 | 10 | PCA9633::PCA9633() {} 11 | 12 | static void _i2c_wordwrite(uint8_t address, uint8_t cmd, uint16_t theWord) { 13 | // Send output register address 14 | Wire.beginTransmission(address); 15 | Wire.write(cmd); // control register 16 | Wire.write(highByte(theWord)); // high byte 17 | Wire.write(lowByte(theWord)); // send low byte of word data 18 | Wire.endTransmission(); 19 | } 20 | 21 | static void _i2c_write(uint8_t address, uint8_t cmd, uint8_t data) { 22 | // Send output register address 23 | Wire.beginTransmission(address); 24 | Wire.write(cmd); // control register 25 | Wire.write(data); // send byte data 26 | Wire.endTransmission(); 27 | } 28 | 29 | static uint16_t _i2c_wordread(uint8_t address, uint8_t cmd) { 30 | uint16_t result; 31 | uint8_t xlo, xhi; 32 | 33 | Wire.beginTransmission(address); 34 | Wire.write(cmd); // control register 35 | Wire.endTransmission(); 36 | 37 | uint8_t readbytes = Wire.requestFrom(address, (uint8_t) 2); // request two bytes 38 | xhi = Wire.read(); 39 | xlo = Wire.read(); 40 | 41 | result = xhi << 8; // hi byte 42 | result = result | xlo; // add in the low byte 43 | 44 | return result; 45 | } 46 | 47 | static uint8_t _i2c_read(uint8_t address, uint8_t cmd) { 48 | uint8_t result = 0; 49 | uint8_t size = 1; 50 | uint8_t sendStop = true; 51 | 52 | Wire.beginTransmission(address); 53 | Wire.write(cmd); // control register 54 | Wire.endTransmission(); 55 | 56 | // some platforms require bool type or third argument: ESP8266, SAM 57 | #if defined(ESP8266) 58 | uint8_t readbytes = Wire.requestFrom(address, (size_t)size, (bool)sendStop); // request cnt bytes 59 | #else 60 | uint8_t readbytes = Wire.requestFrom(address, size, sendStop); // request cnt bytes 61 | #endif 62 | 63 | result = Wire.read(); 64 | 65 | return result; 66 | } 67 | 68 | uint8_t PCA9633::linearize(uint8_t pwm) { 69 | uint8_t result = pgm_read_byte(ledLinear + pwm); 70 | return result; 71 | } 72 | 73 | void PCA9633::chipinit(void) { // setup chip with desired operating parameters 74 | uint8_t m1 = 0x00; // set sleep = 0, turn on oscillator, disable allcall and subaddrs 75 | uint8_t m2 = ((INVRT) | (OUTDRV)); // output inverted, totem pole drivers enabled 76 | uint8_t ldout = 0xFF; // all outputs under individual and group control 77 | 78 | _i2c_write(_pcaAddr, MODE1, m1); 79 | _i2c_write(_pcaAddr, MODE2, m2); 80 | _i2c_write(_pcaAddr, LEDOUT, ldout); 81 | } 82 | 83 | void PCA9633::begin(uint8_t devAddr) { // just set device address 84 | _pcaAddr = devAddr; 85 | chipinit(); // setup chip 86 | setFade(10); 87 | } 88 | 89 | void PCA9633::begin(uint8_t devAddr, uint8_t fade_delay) { // set device address and fade delay 90 | _pcaAddr = devAddr; 91 | chipinit(); // setup chip 92 | setFade(fade_delay); 93 | } 94 | 95 | void PCA9633::begin(uint8_t devAddr, uint8_t fade_delay, uint8_t i2c_init) { // set device address, fade delay and init i2c using defaults 96 | _pcaAddr = devAddr; 97 | chipinit(); // setup chip 98 | setFade(fade_delay); 99 | if (i2c_init) { // start i2c bus 100 | Wire.begin(); 101 | } 102 | } 103 | 104 | void PCA9633::setrgbw(uint8_t p0, uint8_t p1, uint8_t p2, uint8_t p3) { 105 | _i2c_write(_pcaAddr, PWM0, linearize(p0)); 106 | _i2c_write(_pcaAddr, PWM1, linearize(p1)); 107 | _i2c_write(_pcaAddr, PWM2, linearize(p2)); 108 | _i2c_write(_pcaAddr, PWM3, linearize(p3)); 109 | 110 | } 111 | 112 | void PCA9633::setFade(uint8_t dly) { 113 | _fadeDelay = dly; 114 | if (dly=0 || dly>100) _fadeDelay=10; 115 | } 116 | 117 | uint8_t PCA9633::getFade() { 118 | return _fadeDelay; 119 | } 120 | 121 | void PCA9633::setpwm(uint8_t pwmaddr, uint8_t pwmval) { 122 | uint8_t curval = _i2c_read(_pcaAddr, (pwmaddr + 2)); // read current value 123 | if (curval==pwmval) { 124 | // do nothing 125 | } else if (curval255) newval=255; 128 | _i2c_write(_pcaAddr, (pwmaddr + 2), newval); 129 | delay(_fadeDelay); 130 | } 131 | } else if (curval>pwmval) { // current value greater than requested value 132 | for (int newval=curval-1; newval>=pwmval; newval--) { 133 | if (newval<0) newval=0; 134 | _i2c_write(_pcaAddr, (pwmaddr + 2), newval); 135 | delay(_fadeDelay); 136 | } 137 | } 138 | } 139 | 140 | void PCA9633::setgrouppwm(uint8_t pwm) { 141 | _i2c_write(_pcaAddr, GRPPWM, pwm); 142 | } 143 | -------------------------------------------------------------------------------- /src/pca9633.h: -------------------------------------------------------------------------------- 1 | #ifndef _GLM_PCA9633_H 2 | #define _GLM_PCA9633_H 3 | 4 | #if ARDUINO >= 100 5 | #include "Arduino.h" 6 | #else 7 | #include "WProgram.h" 8 | #include "pins_arduino.h" 9 | #include "WConstants.h" 10 | #endif 11 | 12 | #include 13 | 14 | // #define PCA9633_ADDR (0x62) // slave address 15 | 16 | // auto increment definitions Datasheet page 10 17 | #define AI2 (0x80) // mask for AI2 18 | #define AI1 (0x40) // mask for AI1 19 | #define AI0 (0x20) // mask for AI0 20 | 21 | #define INCOFF (0x00) // AI2:0 000 no auto increment 22 | #define INCALL (0x80) // AI2:0 100 auto inc all registers 23 | #define INCIND (0xA0) // AI2:0 101 auto inc individual pwm registers 24 | #define INCGBL (0xC0) // AI2:0 110 auto inc global registers 25 | #define INCINDGBL (0xE0) // AI2:0 111 auto inc individual and global registers 26 | 27 | // register definitions Datasheet page 11 28 | #define MODE1 (0x00) // mode register 1 29 | #define MODE2 (0x01) // mode register 2 30 | #define PWM0 (0x02) // PWM0 brightness control led0 31 | #define PWM1 (0x03) // PWM0 brightness control led0 32 | #define PWM2 (0x04) // PWM0 brightness control led0 33 | #define PWM3 (0x05) // PWM0 brightness control led0 34 | #define GRPPWM (0x06) // group brightness (duty cycle) 35 | #define GRPFREQ (0x07) // group frequency 36 | #define LEDOUT (0x08) // LED output state 37 | #define SUBADR1 (0x09) // i2c bus sub address 1 38 | #define SUBADR2 (0x0A) // i2c bus sub address 1 39 | #define SUBADR3 (0x0B) // i2c bus sub address 1 40 | #define ALLCALLADR (0x0C) // LED All Call i2c address 41 | 42 | // MODE1 definitions 43 | #define SLEEP (0x10) // bit 4, low power mode enable, RW 44 | #define SUB1 (0x08) // bit 3, PCA9633 responds to sub address 1 45 | #define SUB2 (0x04) // bit 2, PCA9633 responds to sub address 2 46 | #define SUB3 (0x02) // bit 1, PCA9633 responds to sub address 3 47 | #define ALLCALL (0x01) // bit 0, PCA9633 responds to all call address 48 | 49 | // MODE2 definitions 50 | #define DMBLINK (0x20) // bit 5, group control dim or blink 51 | #define INVRT (0x10) // bit 4, output logic invert (1=yes, 0=no) 52 | #define OCH (0x08) // bit 3, 0=output change on stop, 1=output change on ACK 53 | #define OUTDRV (0x04) // bit 2, output drivers 0=open drain, 1=totem poll - push pull 54 | #define OUTNE1 (0x02) // bit 1, 2 bits see page 13, 16 pin device only 55 | #define OUTNE0 (0x01) // bit 0, see above 56 | 57 | // linearized brightness values 58 | const size_t len_ledLinear = 256; 59 | const uint8_t ledLinear[] PROGMEM = { 60 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 61 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 62 | 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 63 | 0x05, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x09, 0x09, 0x0A, 0x0A, 0x0B, 0x0B, 64 | 0x0C, 0x0C, 0x0D, 0x0D, 0x0E, 0x0F, 0x0F, 0x10, 0x11, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 65 | 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1F, 0x20, 0x21, 0x23, 0x24, 0x26, 0x27, 0x29, 0x2B, 0x2C, 66 | 0x2E, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3A, 0x3C, 0x3E, 0x40, 0x43, 0x45, 0x47, 0x4A, 0x4C, 0x4F, 67 | 0x51, 0x54, 0x57, 0x59, 0x5C, 0x5F, 0x62, 0x64, 0x67, 0x6A, 0x6D, 0x70, 0x73, 0x76, 0x79, 0x7C, 68 | 0x7F, 0x82, 0x85, 0x88, 0x8B, 0x8E, 0x91, 0x94, 0x97, 0x9A, 0x9C, 0x9F, 0xA2, 0xA5, 0xA7, 0xAA, 69 | 0xAD, 0xAF, 0xB2, 0xB4, 0xB7, 0xB9, 0xBB, 0xBE, 0xC0, 0xC2, 0xC4, 0xC6, 0xC8, 0xCA, 0xCC, 0xCE, 70 | 0xD0, 0xD2, 0xD3, 0xD5, 0xD7, 0xD8, 0xDA, 0xDB, 0xDD, 0xDE, 0xDF, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 71 | 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xED, 0xEE, 0xEF, 0xEF, 0xF0, 0xF1, 0xF1, 0xF2, 72 | 0xF2, 0xF3, 0xF3, 0xF4, 0xF4, 0xF5, 0xF5, 0xF6, 0xF6, 0xF6, 0xF7, 0xF7, 0xF7, 0xF8, 0xF8, 0xF8, 73 | 0xF9, 0xF9, 0xF9, 0xF9, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFC, 74 | 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 75 | 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFF, 0xFF}; 76 | 77 | class PCA9633 { 78 | public: 79 | PCA9633(); 80 | void begin(uint8_t devAddr); // set i2c address 81 | void begin(uint8_t devAddr, uint8_t fade_delay); // set i2c address and fade delay 82 | void begin(uint8_t devAddr, uint8_t fade_delay, uint8_t i2c_init); // set i2c address, fade delay and start i2c bus 83 | void setrgbw(uint8_t p0, uint8_t p1, uint8_t p2, uint8_t p3); // set all four pwm reg at once 84 | void setpwm(uint8_t pwmaddr, uint8_t pwmval); // set a single PWM register 85 | void setgrouppwm(uint8_t pwm); // group dimming 86 | void chipinit(); // reset chip to desired startup state 87 | void setFade(uint8_t fade_delay); 88 | uint8_t linearize(uint8_t pwm); // read from linearize lookup table and return value 89 | uint8_t getFade(); 90 | 91 | private: 92 | uint8_t _pcaAddr; 93 | uint8_t _oldred, _oldgreen, _oldblue, _oldwhite, _fadeDelay; 94 | }; 95 | 96 | 97 | #endif // end _GLM_PCA9633_H 98 | --------------------------------------------------------------------------------