123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382 |
- //Source: https://github.com/rambo/pca9635
- #include "pca9635.h"
- // Constructor
- pca9635::pca9635() {
- this->device_address = 0x70; // Default to the all-call address
- this->autoincrement_bits = 0x80; // Autoincrement all
- this->available = false;
- for (uint8_t i = 0; i < NUM_CHANNELS; i++) {
- this->pwm_values[i] = 0;
- }
- for (uint8_t i = 0; i < MODE_REGISTERS; i++) {
- this->led_mode_regs[i] = 0;
- }
- }
- void pca9635::begin(byte dev_addr, boolean wire_begin, boolean init)
- {
- device_address = dev_addr;
- if (wire_begin) {
- if (!Wire.begin(21, 22, 400000UL)) {
- Serial.println("Error while setup i2c bus!");
- }
- this->reset();
- }
- if (init)
- {
- if (this->set_sleep(0x0)) { // Wake up oscillators
- this->available = true;
- delayMicroseconds(500); // Wait for the oscillators to stabilize
- this->set_led_mode(0); // Default to full PWM mode for all drivers
- this->set_invert_output(1); //invert output channels to use with external nfet
- this->set_output_not_active_state(0x00); //LEDn=0 if OE is not set
- this->set_group_control(1); // setup group blinking
- this->set_group_dimming(0x80); // set duty cycle to 50%
- // this->set_driver_mode(0x00); // set to open-drain
- } else {
- this->available = false;
- }
- }
- }
- void pca9635::begin(byte dev_addr, boolean wire_begin)
- {
- pca9635::begin(dev_addr, wire_begin, false);
- }
- boolean pca9635::readSingleReg(uint8_t reg, uint8_t *data) {
- Wire.beginTransmission(this->device_address);
- if (Wire.write(reg | autoincrement_bits) != 1) return false;
- if (Wire.endTransmission(false)) return false; //send restart
- if (Wire.requestFrom((uint16_t) this->device_address,1U,true) == 1U) { // read one byte, then send stop
- *data = Wire.read();
- return true;
- }
- return false;
- }
- boolean pca9635::writeSingleReg(uint8_t reg, uint8_t data) {
- return this->writeManyReg(reg, 1, &data);
-
- /* Wire.beginTransmission(this->device_address);
- if (Wire.write(reg | autoincrement_bits) != 1) return false;
- if (Wire.write(data) != 1) return false;
- if (!Wire.endTransmission(true)) { //send stop
- return true;
- }
- return false;
- */
- }
- boolean pca9635::writeManyReg(uint8_t reg, uint8_t cnt, uint8_t *data) {
- Wire.beginTransmission(this->device_address);
- if (Wire.write(reg | autoincrement_bits) != 1) return false;
- if (Wire.write(data, cnt) != cnt) return false;
- if (!Wire.endTransmission(true)) { //send stop
- return true;
- }
- return false;
- }
- void pca9635::exchangeValueReg(uint8_t *reg, uint8_t mask, uint8_t value) {
- *reg = (*reg & mask) | value;
- }
- boolean pca9635::readModifyWriteReg(uint8_t reg, uint8_t mask, uint8_t value) {
- uint8_t tmp;
- if (!this->readSingleReg(reg, &tmp)) {
- Serial.println("error while reading single reg");
- return false;
- }
- this->exchangeValueReg(&tmp, mask, value);
- return this->writeSingleReg(reg, tmp);
- }
- /**
- * Control the led channel mode, modes:
- * 0=fully off
- * 1=fully on (no PWM)
- * 2=individual PWM only
- * 3=individual and group PWM
- *
- * Remember that led numbers start from 0
- */
- boolean pca9635::set_led_mode(byte ledno, byte mode)
- {
- byte reg = 0x17;
- if ( ledno >= 8 && ledno < 11)
- {
- reg = 0x16;
- }
- if ( ledno >= 4 && ledno < 8)
- {
- reg = 0x15;
- }
- if (ledno < 4)
- {
- reg = 0x14;
- }
- byte value = 0;
- switch (mode)
- {
- case 0:
- value = B00000000;
- break;
- case 1:
- value = B01010101;
- break;
- case 2:
- value = B10101010;
- break;
- case 3:
- value = B11111111;
- break;
- }
- byte mask = B00000000;
- switch(ledno%4)
- {
- case 0:
- mask = (byte)~B00000011;
- break;
- case 1:
- mask = (byte)~B00001100;
- break;
- case 2:
- mask = (byte)~B00110000;
- break;
- case 3:
- mask = (byte)~B11000000;
- break;
- }
- this->exchangeValueReg(&this->led_mode_regs[ledno/4], mask, value);
- return this->readModifyWriteReg(reg | autoincrement_bits, mask, value);
- }
- /**
- * Set mode for all leds
- * 0=fully off
- * 1=fully on (no PWM)
- * 2=individual PWM only
- * 3=individual and group PWM
- */
- boolean pca9635::set_led_mode(byte mode)
- {
- byte value;
- switch (mode)
- {
- case 0:
- value = B00000000;
- break;
- case 1:
- value = B01010101;
- break;
- case 2:
- value = B10101010;
- break;
- case 3:
- value = B11111111;
- break;
- default:
- value = 0;
- break;
- }
- for (uint8_t i = 0; i < MODE_REGISTERS; i++) {
- this->led_mode_regs[i] = value;
- }
- return this->writeManyReg(0x14 | autoincrement_bits, 4, this->led_mode_regs);
- }
- /**
- * Set modes to given leds
- * leds specified in the 'leds'-array
- * modes specified in the 'modes'-array
- */
- boolean pca9635::set_all_led_modes(uint8_t cnt, uint8_t leds[], uint8_t modes[]) {
- for (uint8_t i = 0; i < cnt; i++) {
- uint8_t mode = modes[i];
- if (mode > 3) {
- return false;
- }
- uint8_t ledno = leds[i];
- if (ledno >= NUM_CHANNELS) {
- return false;
- }
- this->exchangeValueReg(&this->led_mode_regs[ledno/4], ~(0x3<<(2*(ledno%4))), mode<<(2*(ledno%4)));
- }
- return this->writeManyReg(0x14 | autoincrement_bits, 4, this->led_mode_regs);
- }
- /**
- * Enable given SUBADDRess (1-3)
- */
- boolean pca9635::enable_subaddr(byte addr)
- {
- byte value;
- switch (addr)
- {
- case 1:
- value = _BV(3); // 0x71
- break;
- case 2:
- value = _BV(2); // 0x72
- break;
- case 3:
- value = _BV(1); // 0x74
- break;
- default:
- value = 0;
- break;
- }
- byte mask = ~value;
- return this->readModifyWriteReg(0x0 | autoincrement_bits, mask, value);
- }
- /**
- * Changes the driver mode between open drain(0x0) and totem-pole (0x1, the default)
- */
- boolean pca9635::set_driver_mode(byte mode)
- {
- return this->readModifyWriteReg(0x01 | autoincrement_bits, (byte)~_BV(2), (mode & 0x01) << 2);
- }
- /**
- * Changes the driver mode between not inverted(0x0, the default) and inverted (0x1)
- */
- boolean pca9635::set_invert_output(byte mode)
- {
- return this->readModifyWriteReg(0x01 | autoincrement_bits, (byte)~_BV(4), (mode & 0x01) << 4);
- }
- /**
- * Changes the driver inactive state: LEDn = 0 (0x00), LEDn = 1 if driver_mode = 1 or LEDn = high-impedance if driver_mode = 0 (0x01, the default) or LEDn = high-impedance (0x10)
- */
- boolean pca9635::set_output_not_active_state(byte mode)
- {
- if (mode > 2) {
- return false;
- }
- return this->readModifyWriteReg(0x01 | autoincrement_bits, (byte)~(_BV(0) | _BV(1)), (mode & 0x03));
- }
- /**
- * Changes the group control mode between dimming(0x0, the default) and blinking (0x1)
- */
- boolean pca9635::set_group_control(byte mode)
- {
-
- return this->readModifyWriteReg(0x01 | autoincrement_bits, (byte)~_BV(5), (mode & 0x01) << 5);
- }
- /**
- * Changes the oscillator mode between sleep (0x1, the default) and active (0x0)
- */
- boolean pca9635::set_sleep(byte sleep)
- {
- return this->readModifyWriteReg(0x00 | autoincrement_bits, (byte)~_BV(4), (sleep & 0x01) << 4);
- }
- /**
- * Sets the pwm value for given led, note that it must have previously been enabled for PWM control with set_mode
- *
- * Remember that led numbers start from 0
- */
- boolean pca9635::set_led_pwm(byte ledno, byte cycle) {
- byte reg = 0x02 + ledno;
- boolean ret = this->writeManyReg(reg | autoincrement_bits, 1, &cycle);
- if (ret) {
- this->pwm_values[ledno] = cycle;
- }
- return ret;
- }
- /**
- * Sets the pwm values for given leds, start led and the cnt of leds to change, note that it must have previously been enabled for PWM control with set_mode
- * the value for each led has to be set in the array.
- *
- * Remember that led numbers start from 0
- */
- boolean pca9635::set_all_led_pwm(uint8_t start_led, uint8_t cnt, uint8_t *cycles) {
- if (start_led >= NUM_CHANNELS || (start_led+cnt) >= NUM_CHANNELS) {
- return false;
- }
- boolean ret = this->writeManyReg((start_led + 0x02) | autoincrement_bits, cnt, cycles);
- if (ret) {
- for (uint8_t i = 0; i < cnt; i++) {
- pwm_values[i+start_led] = *(cycles+i);
- }
- }
- return ret;
- }
- /**
- * Sets the Group pwm value, note that it must have previously been enabled for PWM control and group PWM with set_mode
- */
- boolean pca9635::set_group_dimming(uint8_t cycle) {
- return this->writeManyReg(0x12 | autoincrement_bits, 1, &cycle);
- }
- /**
- * Sets the Group blinking frequency, note that it must have previously been enabled for PWM control and group PWM with set_mode
- */
- boolean pca9635::set_group_blinking(uint8_t freq) {
- return this->writeManyReg(0x13 | autoincrement_bits, 1, &freq);
- }
- /**
- * Do the software-reset song-and-dance, this should reset all drivers on the bus
- */
- boolean pca9635::reset(uint8_t addr) {
- #ifdef I2C_DEVICE_DEBUG
- Serial.println(F("pca9635::reset() called"));
- #endif
- Wire.beginTransmission(addr);
- uint8_t byteArray[2] = {0xA5, 0x5A};
- if (Wire.write(byteArray, 2) != 2) return false;
- byte result = Wire.endTransmission(true);
- if (result > 0)
- {
- #ifdef I2C_DEVICE_DEBUG
- Serial.print(F("FAILED: Wire.write(0x03, 0x5a, 0xa5); returned: "));
- Serial.println(result, DEC);
- #endif
- return false;
- }
- delayMicroseconds(5); // Wait for the reset to complete
- return true;
- }
- boolean pca9635::reset() {
- return this->reset(0x03);
- }
- boolean pca9635::isAvailable() {
- return this->available;
- }
- /**
- * return the current configured pwm value for the selected channel
- */
- uint8_t pca9635::getPWMValue(uint8_t channel) {
- if (channel < NUM_CHANNELS) {
- return this->pwm_values[channel];
- } else {
- return 0;
- }
- }
- // Instance for the all-call address
- pca9635 PCA9635 = pca9635();
|