Giter VIP home page Giter VIP logo

pca9635's Introduction

Arduino CI Arduino-lint JSON check GitHub issues

License: MIT GitHub release PlatformIO Registry

PCA9635

Arduino library for PCA9635 I2C LED driver, 16 channel PWM, 8 bit.

Description

This library is to control the I2C PCA9635 PWM extender. The 16 channels are independently configurable in steps of 1/256. This allows for better than 1% fine tuning of the duty-cycle of the PWM signal.

0.5.0 Breaking change

Version 0.5.0 introduced a breaking change. You cannot set the pins in begin() any more. This reduces the dependency of processor dependent Wire implementations. The user has to call Wire.begin() and can optionally set the Wire pins before calling begin().

Related

Interface

#include "PCA9635.h"

Constructor

  • PCA9635(uint8_t deviceAddress, TwoWire *wire = &Wire) Constructor with I2C device address, and optional the Wire interface as parameter.
  • bool begin(uint8_t mode1_mask = PCA963X_MODE1_ALLCALL, uint8_t mode2_mask = PCA963X_MODE2_NONE) initializes the library after startup. Optionally setting the MODE1 and MODE2 configuration registers. See PCA9635.h and datasheet for settings possible.
  • void configure(uint8_t mode1_mask, uint8_t mode2_mask) To configure the library after startup one can set the MODE1 and MODE2 configuration registers. See PCA9635.h and datasheet for settings possible.
  • bool isConnected() checks if address is available on I2C bus.
  • uint8_t channelCount() returns the number of channels = 16.

LedDriverMode

Configure LED behaviour.

  • uint8_t setLedDriverMode(uint8_t channel, uint8_t mode) mode is 0..3 See datasheet for full details.
    • returns error code, see below.
  • uint8_t setLedDriverMode(uint8_t mode) set same mode for ALL channels.
  • uint8_t getLedDriverMode(uint8_t channel) returns the current mode of the channel.
LED mode Value Description
PCA963X_LEDOFF 0x00 led is 100% off, default @startup
PCA963X_LEDON 0x01 led is 100% on.
PCA963X_LEDPWM 0x02 set LED in PWM mode, 0..255
PCA963X_LEDGRPPWM 0x03 add LED to the GRPPWM*

* all LEDs in the group GRPPWM can be set to the same PWM value in one set. This is ideal to trigger e.g. multiple LEDs (servo's) at same time.

Read and write

Read and write individual values to LED channels. Requires LEDs' DriverMode of the specific channels to be in PWM mode.

  • uint8_t write1(uint8_t channel, uint8_t value) writes a single 8 bit PWM value.
  • uint8_t write3(uint8_t channel, uint8_t R, uint8_t G, uint8_t B) writes three consecutive PWM registers. typical use is to write R, G, B values for a full colour LED.
  • uint8_t writeN(uint8_t channel, uint8_t * array, uint8_t count) write count consecutive PWM registers. May return PCA963X_ERR_CHAN if array has too many elements (including channel as offset).

Mode registers

Used to configure the PCA963x general behaviour.

  • uint8_t writeMode(uint8_t reg, uint8_t value) configuration of one of the two configuration registers. Check datasheet for details.
  • uint8_t readMode(uint8_t reg) reads back the configured mode, useful to add or remove a single flag (bit masking).
  • uint8_t setMode1(uint8_t value) convenience wrapper.
  • uint8_t setMode2(uint8_t value) convenience wrapper.
  • uint8_t getMode1() convenience wrapper.
  • uint8_t getMode2() convenience wrapper.

Constants for mode registers

Name Value Description
PCA963X_MODE1_AUTOINCR2 0x80 Read Only, 0 = disable 1 = enable
PCA963X_MODE1_AUTOINCR1 0x40 Read Only, bit1
PCA963X_MODE1_AUTOINCR0 0x20 Read Only, bit0
PCA963X_MODE1_SLEEP 0x10 0 = normal 1 = sleep
PCA963X_MODE1_SUB1 0x08 0 = disable 1 = enable
PCA963X_MODE1_SUB2 0x04 0 = disable 1 = enable
PCA963X_MODE1_SUB3 0x02 0 = disable 1 = enable
PCA963X_MODE1_ALLCALL 0x01 0 = disable 1 = enable
PCA963X_MODE1_NONE 0x00
----
PCA963X_MODE2_BLINK 0x20 0 = dim 1 = blink
PCA963X_MODE2_INVERT 0x10 0 = normal 1 = inverted
PCA963X_MODE2_STOP 0x08 0 = on STOP 1 = on ACK
PCA963X_MODE2_TOTEMPOLE 0x04 0 = open drain 1 = totem-pole
PCA963X_MODE2_NONE 0x00

These constants makes it easier to set modes without using a non descriptive bit mask. The constants can be merged by OR-ing them together, see snippet:

ledArray.writeMode(PCA963X_MODE2, 0b00110100);

// would become

uint8_t mode2_mask = PCA963X_MODE2_BLINK | PCA963X_MODE2_INVERT | PCA963X_MODE2_TOTEMPOLE;
ledArray.writeMode(PCA963X_MODE2, mode2_mask);

// or even

ledArray.setMode2(PCA963X_MODE2_BLINK | PCA963X_MODE2_INVERT | PCA963X_MODE2_TOTEMPOLE);

Group PWM and frequency

Check datasheet for the details.

  • void setGroupPWM(uint8_t value) sets all channels that are part of the PWM group to value.
  • uint8_t getGroupPWM() get the current PWM setting of the group.
  • void setGroupFREQ(uint8_t value) is used for blinking the group of configured LED. Value goes from 0 to 255 with each step representing an increase of approx. 41 ms. So 0x00 results in 41 ms blinking period (on AND off) and 0xFF in approx. 10.5 s.
  • uint8_t getGroupFREQ() returns the set frequency of the PWM group.

Miscellaneous

  • int lastError() returns PCA963X_OK if all is OK, and other error codes otherwise.
Error code Value Description
PCA963X_OK 0x00 Everything went well
PCA963X_ERROR 0xFF Generic error
PCA963X_ERR_WRITE 0xFE Tries to write more elements than PWM channels
PCA963X_ERR_CHAN 0xFD Channel out of range
PCA963X_ERR_MODE 0xFC Invalid mode
PCA963X_ERR_REG 0xFB Invalid register
PCA963X_ERR_I2C 0xFA I2C communication error

SUB CALL and ALL CALL

Please read the datasheet to understand the working of SUB CALL and ALL CALL.

Since version 0.4.0 there is (experimental) support for the SUB CALL and ALL CALL functions. It needs more testing and if there are issues, please report.

AllCall is automatically activated for each device on startup.

Description

SUB CALL allows one to make groups of PCA9635 devices and control them on group level. The number of groups one can make depends on free I2C addresses on one I2C bus. Using multiple I2C buses or multiplexers will even increase the possible number. Every PCA9635 device can be member of up to three of these groups. To become member one needs to set the setSubCallAddress(nr, address) and enable it with enableSubCall(nr).

In the same way one can become member of an ALL CALL group. Typically there is only one such group but one can configure more of them by applying different addresses.

Interface

The functions to enable all/sub-addresses are straightforward:

  • bool enableSubCall(uint8_t nr) nr = 1,2,3

  • bool disableSubCall(uint8_t nr) nr = 1,2,3

  • bool isEnabledSubCall(uint8_t nr) nr = 1,2,3

  • bool setSubCallAddress(uint8_t nr, uint8_t address)

  • uint8_t getSubCallAddress(uint8_t nr)

  • bool enableAllCall()

  • bool disableAllCall()

  • bool isEnabledAllCall()

  • bool setAllCallAddress(uint8_t address)

  • uint8_t getAllCallAddress()

OutputEnable

Since 0.4.3 (experimental) support to control the OE (Output Enable) pin of the PCA9635. This OE pin can control all LEDs simultaneously. It also allows to control multiple devices by connecting the OE pins. Think of simultaneous switching ON/OFF or get dimming with a high frequency PWM. Or use 2 modules alternatively by placing an inverter in between.

See datasheet for the details

  • bool setOutputEnablePin(uint8_t pin = 255) sets the IO pin to connect to the OE pin of the device. A value of 255 indicates no pin set/selected. Sets the OE pin to HIGH. Returns true on success.
  • bool setOutputEnable(uint8_t value) Sets the OE pin HIGH or LOW. All non zero values are LOW. Returns true on success.
  • uint8_t getOutputEnable() get the current value of the OE pin. If pin is not set/selected it will return HIGH.

Note: the OE is LOW active. The user has to set the power on value by means of a PULL UP / DOWN resistor.

I2C Software reset

The goal of this function is to reset ALL devices on the bus. When using the software reset, ALL devices attached to the bus are set to their hardware startup conditions. Generally, there are multiple definitions of software resets by the I2C inventor NXP. To accommodate this, two different modes for this function have been defined and tested (see PCA9634).

  • Method 1 is a tested method which is specific to the PCA9635. Since the number of different types of I2C chips is very large, side-effects on other chips might be possible. Before using this method, consult the data sheets of all chips on the bus to mitigate potential undefined states.
  • Method 0 is a somewhat “general” method which resets many chips on the I2C-bus. However, this method DOES NOT reset the PCA9635 chip. Therefore, consult the data sheet of all different chips on the bus to mitigate potential undefined states.

When only working with PCA9635 chips on a bus, only method 1 is required.

ledArray.I2C_SoftwareReset(1);  //  for method 1
ledArray.I2C_SoftwareReset(0);  //  for method 0

In case you experience issues with this function on your chips (non-PCA9635), please give feedback, so the documentation can be improved.

For further details of the development, see - #10 (PCA9634 repo)

LEDOUT

Experimental, needs testing, read datasheet 7.3.6

The LEDOUT0 (14) .. LEDOUT3 (17) registers can be used to set the operational mode how each channel / LED is controlled. The typical use case is to use PWM per channel but one can also set a channel / LED fully ON or OFF. These functions are a fast way to switch multiple LEDs ON/OFF.

The 4 registers LEDOUT0 .. LEDOUT3 each control 4 channels

register channels mask layout notes
0 0 .. 3 33221100 every channel has 2 bits.
1 4 .. 7 idem
2 8 .. 11 idem
3 12 .. 15 idem
  • uint8_t writeLedOut(uint8_t reg, uint8_t mask)
    • reg = 0..3, if larger than 3 PCA963X_ERROR returned.
    • mask see below.
  • uint8_t readLedOut(uint8_t reg)
    • reg = 0..3, if larger than 3 0x00 is returned. Use with care.
    • returns the register

To set channel 6 OFF and 7 ON simultaneously:

uint8_t mask = PCA.readLedOut(1);
mask &= 0b00001111;  //  set OFF both 6 and 7
mask |= 0b01000000;  //  set ON 7
PCA.writeLedOut(1, mask);

Future

Must

  • improve documentation
    • restructure readme.md

Should

  • improve error handling
    • return values etc.
    • documentation.
  • keep in sync with PCA9634/5 developments
  • remove OLD #defines PCA9635_... => PCA963X
    • const int?

Could

  • unit tests
    • SUB CALL if possible?
    • ALL CALL if possible?
  • add examples
    • read/writeLedOut()
  • setOutputEnablePWM(uint16_t value) PWM support ?
    • getter?
  • restructure function groups
    • in .cpp to match .h
    • readme.md
  • setGroupFreq()
    • set time in milliseconds and round to nearest value?

Wont

  • consider implementing
    • clearMode1() and clearMode2() functions.
    • only upon request.
  • merge with PCA9634/5 and a PCA963X base class if possible
    • not easy, more alignment is desirable.
    • only upon request.
  • setGroupPWM()
    • PWM also in %% ? (trivial for user)

Support

If you appreciate my libraries, you can support the development and maintenance. Improve the quality of the libraries by providing issues and Pull Requests, or donate through PayPal or GitHub sponsors.

Thank you,

pca9635's People

Contributors

aspyra avatar robtillaart avatar sidecutter avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

pca9635's Issues

Request: Compatibility with PCA 9634

Hi Rob Tillaart,
Thank you for such a wonderful library. Unfortunatly, I'm working with the smaller brother (8 LEDs, 8 bit) of the same chip. It is called PCA9634 and I'm not sure if your library can be adapted for that chip. Since I'm an absolute noob, I was wondering if you would find the time and take a look at it, if this would be feasible.

I have the datasheet to this chip attached: Datasheet PCA9634.pdf

Thank you in advance!
Lux

fix documentation OutputEnable

  • fix documentation OutputEnable functions

Some todo

  • check if I2C software reset from PCA9634 works on 35 too.
  • check SUB CALL and ALL CALL functions.

Problem with ESP32 wire

First of all, thanks for this wonderful library. I'm using the PCA9635 for a project and I'm controlling it through an ESP32. After putting a bunch of print statements in this library and the Wire library, I found that when using the PCA9635::begin() function for the ESP32 that accepts the SDA and SCL pin numbers, the _wire->begin(sda, scl) function calls the slave version of the begin() function in the wire library. I was able to get the master version of the begin function by changing the offending line to _wire->begin(sda, scl, (uint32_t)100000);. There is something wonky about the overloading and i'm not sure why it chose one over the other, but by passing the uint32_t frequency, it gets the right one. Now my test sketch is properly running on the ESP32 like it did with the same sketch on an arduino uno.

I'm not sure if this is the correct fix, but it allows me to move forward with my project. I'm happy to provide any more info that you might need to address this. I'm also happy to submit a PR with my change. Just let me know what you prefer.

PCA9635 delay between write1(..) commands and configuration clarity

Hello,
I have been utilizing the PCA9635 chip C++ files in order to independently control 16 LEDs (the total available amount on chip) within my circuit system. I have a Teensy 3.6 as the master and 2 PCA9635s (32 LEDs total) as the slaves.

When using version 0.3.3 (before realizing most recent version) the output pwm signals successuflly reached the 3.3 supply voltage of the teensy 3.6; but there is a delay of ~270 micro seconds in between consecutive write1(...) commands. I am currently trying to determine if this is an intrinsic delay of the hardware or a config problem.

Below is the code i use with version 0.3.3. The setOutputMode method was slightly altered in order to successfully invert the pin outputs (this was fixed with more recent versions).

PCA9635 ledArray(0xF);
PCA9635 ledArray2(0x1F);

//pin assignments etc etc not showing 


setup(){

  ledArray.begin();
  ledArray2.begin();
  ledArray.setOutputMode(invert); //see updated method below 
  ledArray2.setOutputMode(invert);

/////////updated version of setoutputmode in PCA9635.cpp file (version 0.3.3)///////////////

oid PCA9635::setOutputMode(bool invert) {
  uint8_t oldmode = readReg(PCA9635_MODE2);
  uint8_t newmode;
  if (invert) {
    newmode = oldmode | PCA9635_INVERT;
  } else {
    newmode = oldmode & ~PCA9635_INVERT;
  }
  writeReg(PCA9635_MODE2, newmode);


////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  for (int ch = 0; ch < 16; ch++)
  {
    ledArray.setLedDriverMode(ch, PCA9635_LEDPWM);
    ledArray2.setLedDriverMode(ch, PCA9635_LEDPWM);
    ledArray.write1(ch, 0);
    ledArray2.write1(ch, 0);
  }

}


loop(){
 curr = micros();

  if (sw1 && (curr - prev1) >= OnTime1)
  {
    ledArray2.write1(H15, 0);
    ledArray2.write1(H16, 255);
    sw1 = !sw1;
    prev1 = curr;
  }

  if ((curr - prev1) >= OffTime1)
  {
    ledArray2.write1(H16, 0);
    ledArray2.write1(H15, 255);

    sw1 = !sw1;
    prev1 = curr;
  }

}

I have recently implemented a similar script with the updated version 0.4.4. Below is the config code with the new version. My problem with this script is that the output pins H15 and H16 are only reaching mV values instead of the desired 3.3V supplied by the teensy 3.6.

Code:

PCA9635 ledArray(0xF);
PCA9635 ledArray2(0x1F);

// pin assignments etc etc 


setup(){

  ledArray.begin();
  ledArray2.begin();

  uint8_t prev11 = ledArray.getMode1();
  uint8_t prev12 = ledArray.getMode2();
  uint8_t prev21 = ledArray2.getMode1();
  uint8_t prev22 = ledArray2.getMode2();
  
  uint8_t mode11_maskc = prev11 | PCA9635_MODE1_NONE;
  uint8_t mode12_maskc = prev12 | PCA9635_MODE2_INVERT;  //Invert is desired so that pin outputs are low on startup

  uint8_t mode21_maskc = prev21 | PCA9635_MODE1_NONE;
  uint8_t mode22_maskc = prev22 | PCA9635_MODE2_INVERT;


  ledArray.setMode2(mode12_maskc);
  ledArray2.setMode2(mode22_maskc);


  for (int ch = 0; ch < 16; ch++)
  {
    ledArray.setLedDriverMode(ch, PCA9635_LEDPWM);
    ledArray2.setLedDriverMode(ch, PCA9635_LEDPWM);
    ledArray.write1(ch, 0);
    ledArray2.write1(ch, 0);
  }

}

loop(){

 curr = micros();

  if (sw1 && (curr - prev1) >= OnTime1)
  {
    ledArray2.write1(H15, 0);
    ledArray2.write1(H16, 255);
    sw1 = !sw1;
    prev1 = curr;
  }

  if ((curr - prev1) >= OffTime1)
  {
    ledArray2.write1(H16, 0);
    ledArray2.write1(H15, 255);

    sw1 = !sw1;
    prev1 = curr;
  }

}

Please reach back out if you need further clarity. Any help would be greatly appreciated.

Best,

Artimus Steven

(updated code tags for readability)

Channel 15 weird behaviour when GRPPWM set

I am experiencing some problems with channel 15.

When driver mode is set to PCA9635_LEDOFF / PCA9635_LEDON, the channel works correctly.
However, if I set each channel to PCA9635_LEDGRPPWM, set each channel's PWM to 255, and sweep through group PWM values, all channels work correctly - except the last. There is no output, checked with an oscilloscope.

This is what I do in setup:

ledArray.begin();
ledArray.writeMode(PCA9635_MODE2, 0b00010101);
delay(1);
for (int channel = 0; channel < 16; channel++)
{
    ledArray.setLedDriverMode(channel, PCA9635_LEDGRPPWM);
    ledArray.write1(channel, 255);
    delay(1);
}
ledArray.setGroupPWM(0);

And this is the sweep:

//every 2ms
 if (pwms < 256)
     pwm::ledArray.setGroupPWM(pwms);
else
     pwm::ledArray.setGroupPWM(511 - pwms);

if (++pwms == 512)
     pwms = 0;

At first, I thought that I might have damaged the driver while probing, but I assembled another board and it has the same problem. I have checked the PCB design and the physical board, and there is no difference between the circuits of all channels.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.