From ea821d5d01fd9165382d1688625ea7f68464a4e6 Mon Sep 17 00:00:00 2001 From: Rob Tillaart Date: Fri, 10 Jun 2022 12:47:33 +0200 Subject: [PATCH] add SUB ALL + ALL CALL functions (#16) breaking changes (sync with PCA9634 / 35) * add SUBADDR + CALL_ALL functions * rename reset() to configure() * add mode1 and mode2 parameter to configure() en begin(). * update documentation. * renamed PCA9685_MODE2_STOP to PCA9685_MODE2_ACK * minor edits --- PCA9685.cpp | 212 ++++++++++++++++++++++++++++------------- PCA9685.h | 127 ++++++++++++++++++------ README.md | 125 +++++++++++++++++++++--- keywords.txt | 30 +++++- library.json | 2 +- library.properties | 2 +- test/unit_test_001.cpp | 17 ++++ 7 files changed, 402 insertions(+), 113 deletions(-) diff --git a/PCA9685.cpp b/PCA9685.cpp index f4277af..e949949 100644 --- a/PCA9685.cpp +++ b/PCA9685.cpp @@ -2,65 +2,36 @@ // FILE: PCA9685.cpp // AUTHOR: Rob Tillaart // DATE: 24-apr-2016 -// VERSION: 0.3.4 +// VERSION: 0.4.0 // PURPOSE: Arduino library for I2C PCA9685 16 channel PWM // URL: https://github.com/RobTillaart/PCA9685_RT // // HISTORY: // 0.1.0 2016-04-24 initial BETA version // 0.1.1 2019-01-30 testing && fixing + // 0.2.0 2020-05-25 refactor; ESP32 begin(sda,scl) // 0.2.1 2020-06-19 fix library.json // 0.2.2 2020-09-21 fix #1 + add getFrequency() // 0.2.3 2020-11-21 fix digitalWrite (internal version only) + // 0.3.0 2020-11-22 fix setting frequency // 0.3.1 2021-01-05 Arduino-CI + unit test // 0.3.2 2021-01-14 WireN support // 0.3.3 2021-12-23 update library.json, license, readme, minor edits // 0.3.4 2022-01-03 add channelCount() +// 0.4.0 2022-06-09 breaking changes (sync with pca9634) +// rename reset() to configure() +// add mode1 and mode2 parameter to configure. +// add SUB CALL and ALL CALL functions. +// update documentation. +// renamed PCA9685_MODE2_STOP to PCA9685_MODE2_ACK +// add mode parameters to begin() -#include "PCA9685.h" - - -// REGISTERS CONFIGURATION - check datasheet for details -#define PCA9685_MODE1 0x00 -#define PCA9685_MODE2 0x01 - -// MODE1 REGISTERS -#define PCA9685_RESTART 0x80 -#define PCA9685_EXTCLK 0x40 -#define PCA9685_AUTOINCR 0x20 -#define PCA9685_SLEEP 0x10 -#define PCA9685_SUB1 0x08 -#define PCA9685_SUB2 0x04 -#define PCA9685_SUB3 0x02 -#define PCA9685_ALLCALL 0x01 - -// MODE2 REGISTERS (see datasheet) -#define PCA9685_INVERT 0x10 -#define PCA9685_OCH 0x08 -#define PCA9685_OUTDRV 0x04 -#define PCA9685_OUTNE 0x03 - -// REGISTERS - CHANNELS -#define PCA9685_CHANNEL_0 0x06 // 0x06 + 4*channel is base per channel - -// REGISTERS - FREQUENCY -#define PCA9685_PRE_SCALER 0xFE -// REGISTERS - Subaddressing I2C - not implemented -#define PCA9685_SUBADR(x) (0x01+(x)) // x = 1..3 -#define PCA9685_ALLCALLADR 0x05 - -// REGISTERS - ALL_ON ALL_OFF - partly implemented -#define PCA9685_ALL_ON_L 0xFA -#define PCA9685_ALL_ON_H 0xFB -#define PCA9685_ALL_OFF_L 0xFC -#define PCA9685_ALL_OFF_H 0xFD // used for allOFF() +#include "PCA9685.h" -// NOT IMPLEMENTED YET -#define PCA9685_TESTMODE 0xFF // do not be use. see datasheet. ////////////////////////////////////////////////////////////// @@ -69,14 +40,15 @@ // PCA9685::PCA9685(const uint8_t deviceAddress, TwoWire *wire) { - _address = deviceAddress; - _wire = wire; - _error = 0; + _address = deviceAddress; + _wire = wire; + _channelCount = 16; + _error = PCA9685_OK; } #if defined (ESP8266) || defined(ESP32) -bool PCA9685::begin(uint8_t sda, uint8_t scl) +bool PCA9685::begin(uint8_t sda, uint8_t scl, uint8_t mode1_mask, uint8_t mode2_mask) { _wire = &Wire; if ((sda < 255) && (scl < 255)) @@ -86,17 +58,17 @@ bool PCA9685::begin(uint8_t sda, uint8_t scl) _wire->begin(); } if (! isConnected()) return false; - reset(); + configure(mode1_mask, mode2_mask); return true; } #endif -bool PCA9685::begin() +bool PCA9685::begin(uint8_t mode1_mask, uint8_t mode2_mask) { _wire->begin(); if (! isConnected()) return false; - reset(); + configure(mode1_mask, mode2_mask); return true; } @@ -109,36 +81,37 @@ bool PCA9685::isConnected() } -void PCA9685::reset() +void PCA9685::configure(uint8_t mode1_mask, uint8_t mode2_mask) { _error = PCA9685_OK; - writeMode(PCA9685_MODE1, PCA9685_AUTOINCR | PCA9685_ALLCALL); - writeMode(PCA9685_MODE2, PCA9685_OUTDRV); + + setMode1(mode1_mask); + setMode2(mode2_mask); } -void PCA9685::writeMode(uint8_t reg, uint8_t value) +uint8_t PCA9685::writeMode(uint8_t reg, uint8_t value) { - _error = PCA9685_OK; - if ((reg != PCA9685_MODE1) && (reg != PCA9685_MODE2)) + if ((reg == PCA9685_MODE1) || (reg == PCA9685_MODE2)) { - _error = PCA9685_ERR_MODE; - return; + writeReg(reg, value); + return PCA9685_OK; } - writeReg(reg, value); + _error = PCA9685_ERR_MODE; + return PCA9685_ERROR; } uint8_t PCA9685::readMode(uint8_t reg) { - _error = PCA9685_OK; - if ((reg != PCA9685_MODE1) && (reg != PCA9685_MODE2)) + if ((reg == PCA9685_MODE1) || (reg == PCA9685_MODE2)) { - _error = PCA9685_ERR_MODE; - return 0; + _error = PCA9685_OK; + uint8_t value = readReg(reg); + return value; } - uint8_t value = readReg(reg); - return value; + _error = PCA9685_ERR_MODE; + return PCA9685_ERROR; } @@ -152,7 +125,7 @@ void PCA9685::setPWM(uint8_t channel, uint16_t onTime, uint16_t offTime) return; } offTime &= 0x0FFFF; // non-doc feature - to easy set figure 8 P.17 - uint8_t reg = PCA9685_CHANNEL_0 + (channel << 2); + uint8_t reg = PCA9685_CHANNEL(channel); writeReg2(reg, onTime, offTime); } @@ -173,7 +146,7 @@ void PCA9685::getPWM(uint8_t channel, uint16_t* onTime, uint16_t* offTime) _error = PCA9685_ERR_CHANNEL; return; } - uint8_t reg = PCA9685_CHANNEL_0 + (channel << 2); + uint8_t reg = PCA9685_CHANNEL(channel); _wire->beginTransmission(_address); _wire->write(reg); _error = _wire->endTransmission(); @@ -198,18 +171,20 @@ void PCA9685::setFrequency(uint16_t freq, int offset) if (_freq < PCA9685_MIN_FREQ) _freq = PCA9685_MIN_FREQ; if (_freq > PCA9685_MAX_FREQ) _freq = PCA9685_MAX_FREQ; // removed float operation for speed - // faster but equal accurate + // faster and equal accurate // uint8_t scaler = round(25e6 / (_freq * 4096)) - 1; uint8_t scaler = 48828 / (_freq * 8) - 1; uint8_t mode1 = readMode(PCA9685_MODE1); - writeMode(PCA9685_MODE1, mode1 | PCA9685_SLEEP); + writeMode(PCA9685_MODE1, mode1 | PCA9685_MODE1_SLEEP); scaler += offset; writeReg(PCA9685_PRE_SCALER, scaler); writeMode(PCA9685_MODE1, mode1); } +// returns the actual used frequency. +// therefore it does not use offset int PCA9685::getFrequency(bool cache) { _error = PCA9685_OK; @@ -233,7 +208,7 @@ void PCA9685::digitalWrite(uint8_t channel, uint8_t mode) _error = PCA9685_ERR_CHANNEL; return; } - uint8_t reg = PCA9685_CHANNEL_0 + (channel << 2); + uint8_t reg = PCA9685_CHANNEL(channel); if (mode != LOW) writeReg2(reg, 0x1000, 0x0000); else writeReg2(reg, 0x0000, 0x0000); } @@ -249,11 +224,112 @@ void PCA9685::allOFF() int PCA9685::lastError() { int e = _error; - _error = 0; + _error = PCA9685_OK; return e; } +///////////////////////////////////////////////////// +// +// SUB CALL - ALL CALL +// +bool PCA9685::enableSubCall(uint8_t nr) +{ + if ((nr == 0) || (nr > 3)) return false; + uint8_t prev = getMode1(); + uint8_t reg = prev; + if (nr == 1) reg |= PCA9685_MODE1_SUB1; + else if (nr == 2) reg |= PCA9685_MODE1_SUB2; + else reg |= PCA9685_MODE1_SUB3; + // only update if changed. + if (reg != prev) setMode1(reg); + return true; +} + + +bool PCA9685::disableSubCall(uint8_t nr) +{ + if ((nr == 0) || (nr > 3)) return false; + uint8_t prev = getMode1(); + uint8_t reg = prev; + if (nr == 1) reg &= ~PCA9685_MODE1_SUB1; + else if (nr == 2) reg &= ~PCA9685_MODE1_SUB2; + else reg &= ~PCA9685_MODE1_SUB3; + // only update if changed. + if (reg != prev) setMode1(reg); + return true; +} + + +bool PCA9685::isEnabledSubCall(uint8_t nr) +{ + if ((nr == 0) || (nr > 3)) return false; + uint8_t reg = getMode1(); + if (nr == 1) return (reg & PCA9685_MODE1_SUB1) > 0; + if (nr == 2) return (reg & PCA9685_MODE1_SUB2) > 0; + return (reg & PCA9685_MODE1_SUB3) > 0; +} + + +bool PCA9685::setSubCallAddress(uint8_t nr, uint8_t address) +{ + if ((nr == 0) || (nr > 3)) return false; + writeReg(PCA9685_SUBADR(nr), address); + return true; +} + + +uint8_t PCA9685::getSubCallAddress(uint8_t nr) +{ + if ((nr == 0) || (nr > 3)) return 0; + uint8_t address = readReg(PCA9685_SUBADR(nr)); + return address; +} + + +bool PCA9685::enableAllCall() +{ + uint8_t prev = getMode1(); + uint8_t reg = prev | PCA9685_MODE1_ALLCALL; + // only update if changed. + if (reg != prev) setMode1(reg); + return true; +} + + +bool PCA9685::disableAllCall() +{ + uint8_t prev = getMode1(); + uint8_t reg = prev & ~PCA9685_MODE1_ALLCALL; + // only update if changed. + if (reg != prev) setMode1(reg); + return true; +} + + +bool PCA9685::isEnabledAllCall() +{ + uint8_t reg = getMode1(); + return reg & PCA9685_MODE1_ALLCALL; +} + + +bool PCA9685::setAllCallAddress(uint8_t address) +{ + writeReg(PCA9685_ALLCALLADR, address); + return true; +} + + +uint8_t PCA9685::getAllCallAddress() +{ + uint8_t address = readReg(PCA9685_ALLCALLADR); + return address; +} + + + + ////////////////////////////////////////////////////////////// // // PRIVATE diff --git a/PCA9685.h b/PCA9685.h index 89735bd..39df1db 100644 --- a/PCA9685.h +++ b/PCA9685.h @@ -3,7 +3,7 @@ // FILE: PCA9685.h // AUTHOR: Rob Tillaart // DATE: 24-apr-2016 -// VERSION: 0.3.4 +// VERSION: 0.4.0 // PURPOSE: Arduino library for I2C PCA9685 16 channel PWM // URL: https://github.com/RobTillaart/PCA9685_RT @@ -12,18 +12,65 @@ #include "Wire.h" -#define PCA9685_LIB_VERSION (F("0.3.4")) +#define PCA9685_LIB_VERSION (F("0.4.0")) // ERROR CODES -#define PCA9685_OK 0x00 -#define PCA9685_ERROR 0xFF -#define PCA9685_ERR_CHANNEL 0xFE -#define PCA9685_ERR_MODE 0xFD -#define PCA9685_ERR_I2C 0xFC +#define PCA9685_OK 0x00 +#define PCA9685_ERROR 0xFF +#define PCA9685_ERR_CHANNEL 0xFE +#define PCA9685_ERR_MODE 0xFD +#define PCA9685_ERR_I2C 0xFC // get/setFrequency() -#define PCA9685_MIN_FREQ 24 -#define PCA9685_MAX_FREQ 1526 +#define PCA9685_MIN_FREQ 24 +#define PCA9685_MAX_FREQ 1526 + + +// REGISTERS CONFIGURATION - check datasheet for details +#define PCA9685_MODE1 0x00 +#define PCA9685_MODE2 0x01 + +// Configuration bits MODE1 register +#define PCA9685_MODE1_RESTART 0x80 // 0 = disable 1 = enable +#define PCA9685_MODE1_EXTCLK 0x40 // 0 = internal 1 = external (datasheet) +#define PCA9685_MODE1_AUTOINCR 0x20 // 0 = disable 1 = enable +#define PCA9685_MODE1_SLEEP 0x10 // 0 = normal 1 = sleep +#define PCA9685_MODE1_SUB1 0x08 // 0 = disable 1 = enable +#define PCA9685_MODE1_SUB2 0x04 // 0 = disable 1 = enable +#define PCA9685_MODE1_SUB3 0x02 // 0 = disable 1 = enable +#define PCA9685_MODE1_ALLCALL 0x01 // 0 = disable 1 = enable +#define PCA9685_MODE1_NONE 0x00 + +// Configuration bits MODE2 register +#define PCA9685_MODE2_BLINK 0x20 // 0 = dim 1 = blink +#define PCA9685_MODE2_INVERT 0x10 // 0 = normal 1 = inverted +#define PCA9685_MODE2_ACK 0x08 // 0 = on STOP 1 = on ACK +#define PCA9685_MODE2_TOTEMPOLE 0x04 // 0 = open drain 1 = totem-pole +#define PCA9685_MODE2_OUTNE 0x03 // datasheet +#define PCA9685_MODE2_NONE 0x00 + +// (since 0.4.0) +#define PCA9685_SUBADR(x) (0x01 + (x)) // x = 1..3 +#define PCA9685_ALLCALLADR 0x05 + +// REGISTERS - CHANNELS +// 0x06 + 4*channel is base per channel +#define PCA9685_CHANNEL_0 0x06 +#define PCA9685_CHANNEL(x) (0x06 + ((x) * 4)) // x = 0..15 + +// REGISTERS - ALL_ON ALL_OFF - partly implemented +#define PCA9685_ALL_ON_L 0xFA +#define PCA9685_ALL_ON_H 0xFB +#define PCA9685_ALL_OFF_L 0xFC +#define PCA9685_ALL_OFF_H 0xFD // used for allOFF() + +// REGISTERS - FREQUENCY +#define PCA9685_PRE_SCALER 0xFE + +// NOT IMPLEMENTED +// WARNING: DO NOT USE THIS REGISTER (see datasheet) +#define PCA9685_TESTMODE 0xFF // do not be use. see datasheet. + class PCA9685 @@ -32,47 +79,73 @@ class PCA9685 explicit PCA9685(const uint8_t deviceAddress, TwoWire *wire = &Wire); #if defined (ESP8266) || defined(ESP32) - bool begin(uint8_t sda, uint8_t scl); + bool begin(uint8_t sda, uint8_t scl, + uint8_t mode1_mask = PCA9685_MODE1_AUTOINCR | PCA9685_MODE1_ALLCALL, + uint8_t mode2_mask = PCA9685_MODE2_TOTEMPOLE); #endif - bool begin(); - void reset(); - bool isConnected(); + bool begin(uint8_t mode1_mask = PCA9685_MODE1_AUTOINCR | PCA9685_MODE1_ALLCALL, + uint8_t mode2_mask = PCA9685_MODE2_TOTEMPOLE); + void configure(uint8_t mode1_mask, uint8_t mode2_mask); + bool isConnected(); uint8_t channelCount() { return _channelCount; }; // reg = 1, 2 check datasheet for values - void writeMode(uint8_t reg, uint8_t value); - uint8_t readMode(uint8_t reg); + uint8_t writeMode(uint8_t reg, uint8_t value); + uint8_t readMode(uint8_t reg); + + // convenience wrappers + uint8_t setMode1(uint8_t value) { return writeMode(PCA9685_MODE1, value); }; + uint8_t setMode2(uint8_t value) { return writeMode(PCA9685_MODE2, value); }; + uint8_t getMode1() { return readMode(PCA9685_MODE1); }; + uint8_t getMode2() { return readMode(PCA9685_MODE2); }; // single PWM setting, channel = 0..15, // onTime = 0..4095, offTime = 0..4095 // allows shifted PWM's e.g. 2 servo's that do not start at same time. - void setPWM(uint8_t channel, uint16_t onTime, uint16_t offTime); - void getPWM(uint8_t channel, uint16_t* onTime, uint16_t* offTime); + void setPWM(uint8_t channel, uint16_t onTime, uint16_t offTime); + void getPWM(uint8_t channel, uint16_t* onTime, uint16_t* offTime); // single PWM setting, channel = 0..15, offTime = 0..4095 (onTime = 0) - void setPWM(uint8_t channel, uint16_t offTime); + void setPWM(uint8_t channel, uint16_t offTime); // set update frequency for all channels // freq = 24 - 1526 Hz - // note: as the frequency is converted to an 8 bit prescaler + // note: as the frequency is converted to an 8 bit pre-scaler // the frequency set will seldom be exact, but best effort. - void setFrequency(uint16_t freq, int offset = 0); - int getFrequency(bool cache = true); + void setFrequency(uint16_t freq, int offset = 0); + int getFrequency(bool cache = true); // set channel HIGH or LOW (effectively no PWM) - void digitalWrite(uint8_t channel, uint8_t mode); + void digitalWrite(uint8_t channel, uint8_t mode); // for backwards compatibility; will be removed in future - void setON(uint8_t channel) { digitalWrite(channel, HIGH); }; - void setOFF(uint8_t channel) { digitalWrite(channel, LOW); }; + void setON(uint8_t channel) { digitalWrite(channel, HIGH); }; + void setOFF(uint8_t channel) { digitalWrite(channel, LOW); }; // experimental for 0.3.0 - void allOFF(); - - int lastError(); + void allOFF(); + + int lastError(); + + ///////////////////////////////////////////////////// + // + // SUB CALL - ALL CALL (since 0.4.0) + // + // nr = { 1, 2, 3 } + bool enableSubCall(uint8_t nr); + bool disableSubCall(uint8_t nr); + bool isEnabledSubCall(uint8_t nr); + 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(); private: diff --git a/README.md b/README.md index e1251dc..91727a6 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,10 @@ # PCA9685_RT -Arduino library for I2C PCA9685 16 channel PWM extender. +Arduino library for PCA9685 I2C 12 bit PWM LED driver, 16 channel. -# Description +## Description This library is to control the I2C PCA9685 PWM extender. The 16 channels are independently configurable in steps of 1/4096. @@ -29,24 +29,76 @@ Lower frequencies do better than higher frequencies. ## Interface + ### Constructor -- **PCA9685(uint8_t deviceAddress, TwoWire \* wire = &Wire)** I2C address + optional I2C interface. -- **bool begin()** initializes the library after startup. Mandatory. -- **bool begin(uint8_t sda, uint8_t scl)** idem, ESP32 ESP8266 only. -Library does not support multiple Wire instances (yet). -- **void reset()** resets the library to start up conditions. +- **PCA9685(uint8_t deviceAddress, TwoWire \* wire = &Wire)** Constructor with I2C device address, +and optional the Wire interface as parameter. +- **bool begin(uint8_t mode1_mask = PCA9685_MODE1_AUTOINCR | PCA9685_MODE1_ALLCALL, uint8_t mode2_mask = PCA9685_MODE2_TOTEMPOLE)** +initializes the library after startup. Optionally setting the MODE1 and MODE2 configuration registers. +See PCA9685.h and datasheet for settings possible. +- **bool begin(uint8_t sda, uint8_t scl, uint8_t mode1_mask = PCA9685_MODE1_AUTOINCR | PCA9685_MODE1_ALLCALL, uint8_t mode2_mask = PCA9685_MODE2_TOTEMPOLE)** +idem, ESP32 ESP8266 only. +- **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 PCA9685.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. -### Mode +### Mode registers + +Used to configure the PCA9685 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 + +(added 0.4.0) + +| Name | Value | Description | +|:------------------------|:-----:|:-----------------------------------| +| PCA9685_MODE1_RESTART | 0x80 | 0 = disable 1 = enable | +| PCA9685_MODE1_EXTCLK | 0x40 | 0 = internal 1 = external | +| PCA9685_MODE1_AUTOINCR | 0x20 | 0 = disable 1 = enable | +| PCA9685_MODE1_SLEEP | 0x10 | 0 = normal 1 = sleep | +| PCA9685_MODE1_SUB1 | 0x08 | 0 = disable 1 = enable | +| PCA9685_MODE1_SUB2 | 0x04 | 0 = disable 1 = enable | +| PCA9685_MODE1_SUB3 | 0x02 | 0 = disable 1 = enable | +| PCA9685_MODE1_ALLCALL | 0x01 | 0 = disable 1 = enable | +| PCA9685_MODE1_NONE | 0x00 | | +| | | | +| PCA9685_MODE2_BLINK | 0x20 | 0 = dim 1 = blink | +| PCA9685_MODE2_INVERT | 0x10 | 0 = normal 1 = inverted | +| PCA9685_MODE2_STOP | 0x08 | 0 = on STOP 1 = on ACK | +| PCA9685_MODE2_TOTEMPOLE | 0x04 | 0 = open drain 1 = totem-pole | +| PCA9685_MODE2_OUTNE | 0x03 | check datasheet | +| PCA9685_MODE2_NONE | 0x00 | | -- **writeMode(uint8_t reg, uint8_t value)** configuration of one of the two configuration registers. -check datasheet for details. -- **uint8_t readMode(reg)** reads back the configured mode, -useful to add or remove a single flag (bit masking) +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: +```cpp +ledArray.writeMode(PCA9685_MODE2, 0b00110100); + +// would become + +uint8_t mode2_mask = PCA9685_MODE2_BLINK | PCA9685_MODE2_INVERT | PCA9685_MODE2_TOTEMPOLE; +ledArray.writeMode(PCA9685_MODE2, mode2_mask); + +// or even + +ledArray.setMode2(PCA9685_MODE2_BLINK | PCA9685_MODE2_INVERT | PCA9685_MODE2_TOTEMPOLE); + +``` ### PWM @@ -104,6 +156,48 @@ When using offset, the **getFrequency(false)** will return the adjusted **preSca | PCA9685_ERR_I2C | 0xFC | PCA9685 I2C communication error | +### SUB CALL and ALL CALL + +(new since 0.4.0) + +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 PCA9685 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 PCA9685 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()** + + ## Operation See examples @@ -113,8 +207,9 @@ See examples - improve documentation - add unit tests (if possible) -- investigate set/getFrequency int vs uint16_t ? -- - +- investigate int vs uint16_t ? + - **setFrequency(), getFrequency(), \_freq** +- sync with PCA9634/35/85 where possible + - error handling? diff --git a/keywords.txt b/keywords.txt index e858598..38de7c9 100644 --- a/keywords.txt +++ b/keywords.txt @@ -6,11 +6,16 @@ PCA9685 KEYWORD1 # Methods and Functions (KEYWORD2) begin KEYWORD2 -reset KEYWORD2 +configure KEYWORD2 isConnected KEYWORD2 writeMode KEYWORD2 readMode KEYWORD2 +setMode1 KEYWORD2 +setMode2 KEYWORD2 +getMode1 KEYWORD2 +getMode2 KEYWORD2 + setPWM KEYWORD2 getPWM KEYWORD2 @@ -24,6 +29,13 @@ allOFF KEYWORD2 lastError KEYWORD2 +enableSubCall KEYWORD2 +disableSubCall KEYWORD2 +isEnabledSubCall KEYWORD2 +enableAllCall KEYWORD2 +disableAllCall KEYWORD2 +isEnabledAllCall KEYWORD2 + # Constants (LITERAL1) PCA9685_LIB_VERSION LITERAL1 @@ -37,3 +49,19 @@ PCA9685_ERR_I2C LITERAL1 PCA9685_MIN_FREQ LITERAL1 PCA9685_MAX_FREQ LITERAL1 +PCA9685_MODE1_RESTART LITERAL1 +PCA9685_MODE1_EXTCLK LITERAL1 +PCA9685_MODE1_AUTOINCR LITERAL1 +PCA9685_MODE1_SLEEP LITERAL1 +PCA9685_MODE1_SUB1 LITERAL1 +PCA9685_MODE1_SUB2 LITERAL1 +PCA9685_MODE1_SUB3 LITERAL1 +PCA9685_MODE1_ALLCALL LITERAL1 +PCA9685_MODE1_NONE LITERAL1 + +PCA9685_MODE2_BLINK LITERAL1 +PCA9685_MODE2_INVERT LITERAL1 +PCA9685_MODE2_ACK LITERAL1 +PCA9685_MODE2_TOTEMPOLE LITERAL1 +PCA9685_MODE2_OUTNE LITERAL1 +PCA9685_MODE2_NONE LITERAL1 diff --git a/library.json b/library.json index d9dc21c..63b7e00 100644 --- a/library.json +++ b/library.json @@ -15,7 +15,7 @@ "type": "git", "url": "https://github.com/RobTillaart/PCA9685_RT.git" }, - "version": "0.3.4", + "version": "0.4.0", "license": "MIT", "frameworks": "arduino", "platforms": "*", diff --git a/library.properties b/library.properties index 1d658b6..19d1018 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=PCA9685_RT -version=0.3.4 +version=0.4.0 author=Rob Tillaart maintainer=Rob Tillaart sentence=Arduino library for I2C PCA9685 16 channel PWM diff --git a/test/unit_test_001.cpp b/test/unit_test_001.cpp index 2e3ed91..41c175d 100644 --- a/test/unit_test_001.cpp +++ b/test/unit_test_001.cpp @@ -49,6 +49,22 @@ unittest_teardown() unittest(test_constants) { + fprintf(stderr, "\nregisterss"); + assertEqual(PCA9685_MODE1 , 0x00); + assertEqual(PCA9685_MODE2 , 0x01); + assertEqual(PCA9685_ALLCALLADR , 0x05); + assertEqual(PCA9685_CHANNEL_0 , 0x06); + + assertEqual(PCA9685_ALL_ON_L , 0xFA); + assertEqual(PCA9685_ALL_ON_H , 0xFB); + assertEqual(PCA9685_ALL_OFF_L , 0xFC); + assertEqual(PCA9685_ALL_OFF_H , 0xFD); + + assertEqual(PCA9685_PRE_SCALER , 0xFE); + assertEqual(PCA9685_TESTMODE , 0xFF); + + + fprintf(stderr, "\nerrorcodes"); assertEqual(PCA9685_OK , 0x00); assertEqual(PCA9685_ERROR , 0xFF); assertEqual(PCA9685_ERR_CHANNEL, 0xFE); @@ -62,6 +78,7 @@ unittest(test_constructor) PCA9685 ledArray(0x20); assertTrue(ledArray.begin()); assertTrue(ledArray.isConnected()); + assertEqual(16, ledArray.channelCount()); }