From 2e0ef7f99409310397c15d94bd11f4da81eec1b2 Mon Sep 17 00:00:00 2001 From: rob tillaart Date: Mon, 30 May 2022 17:42:29 +0200 Subject: [PATCH] 0.2.0 PCA9634 --- libraries/PCA9634/PCA9634.cpp | 125 +++++++++++++++++++++++++-- libraries/PCA9634/PCA9634.h | 52 +++++++---- libraries/PCA9634/README.md | 113 +++++++++++++++++------- libraries/PCA9634/keywords.txt | 12 ++- libraries/PCA9634/library.json | 2 +- libraries/PCA9634/library.properties | 2 +- 6 files changed, 250 insertions(+), 56 deletions(-) diff --git a/libraries/PCA9634/PCA9634.cpp b/libraries/PCA9634/PCA9634.cpp index c60319f6..8cfae6fb 100644 --- a/libraries/PCA9634/PCA9634.cpp +++ b/libraries/PCA9634/PCA9634.cpp @@ -2,7 +2,7 @@ // FILE: PCA9634.cpp // AUTHOR: Rob Tillaart // DATE: 03-01-2022 -// VERSION: 0.1.2 +// VERSION: 0.2.0 // PURPOSE: Arduino library for PCA9634 I2C LED driver // URL: https://github.com/RobTillaart/PCA9634 // @@ -10,6 +10,14 @@ // 0.1.0 2022-01-03 initial version -- based upon 0.3.2 PCA9635 // 0.1.1 2022-01-04 minor fixes // 0.1.2 2022-04-13 issue #7 add constants and functions for mode registers. +// +// 0.2.0 2022-05-29 breaking changes +// rename reset() to configure() +// add mode1 and mode2 parameter to configure. +// add SUB CALL and ALL CALL functions. +// update documentation. +// renamed PCA9634_MODE2_STOP to PCA9634_MODE2_ACK + #include "PCA9634.h" @@ -38,7 +46,7 @@ bool PCA9634::begin(uint8_t sda, uint8_t scl) _wire->begin(); } if (! isConnected()) return false; - reset(); + configure(); return true; } #endif @@ -48,7 +56,7 @@ bool PCA9634::begin() { _wire->begin(); if (! isConnected()) return false; - reset(); + configure(); return true; } @@ -61,13 +69,13 @@ bool PCA9634::isConnected() } -void PCA9634::reset() +void PCA9634::configure(uint8_t mode1_mask, uint8_t mode2_mask) { _data = 0; _error = 0; - uint8_t mode1_mask = PCA9634_MODE1_AUTOINCR2 | PCA9634_MODE1_ALLCALL; - writeReg(PCA9634_MODE1, mode1_mask); // AUTOINCR | NOSLEEP | ALLADRR 0x81 + setMode1(mode1_mask); + setMode2(mode2_mask); } @@ -179,15 +187,116 @@ uint8_t PCA9634::getLedDriverMode(uint8_t channel) } -// note error flag is reset after read! +// note error flag is set to PCA9634_OK after read! int PCA9634::lastError() { int e = _error; - _error = 0; + _error = PCA9634_OK; return e; } + +///////////////////////////////////////////////////// +// +// SUB CALL - ALL CALL +// +bool PCA9634::enableSubCall(uint8_t nr) +{ + if ((nr == 0) || (nr > 3)) return false; + uint8_t prev = getMode1(); + uint8_t reg = prev; + if (nr == 1) reg |= PCA9634_MODE1_SUB1; + else if (nr == 2) reg |= PCA9634_MODE1_SUB2; + else reg |= PCA9634_MODE1_SUB3; + // only update if changed. + if (reg != prev) setMode1(reg); + return true; +} + + +bool PCA9634::disableSubCall(uint8_t nr) +{ + if ((nr == 0) || (nr > 3)) return false; + uint8_t prev = getMode1(); + uint8_t reg = prev; + if (nr == 1) reg &= ~PCA9634_MODE1_SUB1; + else if (nr == 2) reg &= ~PCA9634_MODE1_SUB2; + else reg &= ~PCA9634_MODE1_SUB3; + // only update if changed. + if (reg != prev) setMode1(reg); + return true; +} + + +bool PCA9634::isEnabledSubCall(uint8_t nr) +{ + if ((nr == 0) || (nr > 3)) return false; + uint8_t reg = getMode1(); + if (nr == 1) return (reg & PCA9634_MODE1_SUB1) > 0; + if (nr == 2) return (reg & PCA9634_MODE1_SUB2) > 0; + return (reg & PCA9634_MODE1_SUB3) > 0; +} + + +bool PCA9634::setSubCallAddress(uint8_t nr, uint8_t address) +{ + if ((nr == 0) || (nr > 3)) return false; + writeReg(PCA9634_SUBADR(nr), address); + return true; +} + + +uint8_t PCA9634::getSubCallAddress(uint8_t nr) +{ + if ((nr == 0) || (nr > 3)) return 0; + uint8_t address = readReg(PCA9634_SUBADR(nr)); + return address; +} + + +bool PCA9634::enableAllCall() +{ + uint8_t prev = getMode1(); + uint8_t reg = prev | PCA9634_MODE1_ALLCALL; + // only update if changed. + if (reg != prev) setMode1(reg); + return true; +} + + +bool PCA9634::disableAllCall() +{ + uint8_t prev = getMode1(); + uint8_t reg = prev & ~PCA9634_MODE1_ALLCALL; + // only update if changed. + if (reg != prev) setMode1(reg); + return true; +} + + +bool PCA9634::isEnabledAllCall() +{ + uint8_t reg = getMode1(); + return reg & PCA9634_MODE1_ALLCALL; +} + + +bool PCA9634::setAllCallAddress(uint8_t address) +{ + writeReg(PCA9634_ALLCALLADR, address); + return true; +} + + +uint8_t PCA9634::getAllCallAddress() +{ + uint8_t address = readReg(PCA9634_ALLCALLADR); + return address; +} + + + ///////////////////////////////////////////////////// // // PRIVATE diff --git a/libraries/PCA9634/PCA9634.h b/libraries/PCA9634/PCA9634.h index c059e5cd..4d7a3dbe 100644 --- a/libraries/PCA9634/PCA9634.h +++ b/libraries/PCA9634/PCA9634.h @@ -3,7 +3,7 @@ // FILE: PCA9634.h // AUTHOR: Rob Tillaart // DATE: 03-01-2022 -// VERSION: 0.1.2 +// VERSION: 0.2.0 // PURPOSE: Arduino library for PCA9634 I2C LED driver, 8 channel // URL: https://github.com/RobTillaart/PCA9634 @@ -12,7 +12,7 @@ #include "Wire.h" -#define PCA9634_LIB_VERSION (F("0.1.2")) +#define PCA9634_LIB_VERSION (F("0.2.0")) #define PCA9634_MODE1 0x00 #define PCA9634_MODE2 0x01 @@ -42,24 +42,25 @@ // Configuration bits MODE1 register -#define PCA9634_MODE1_AUTOINCR2 0x80 // RO, 0 = disable 1 = enable -#define PCA9634_MODE1_AUTOINCR1 0x40 // RO, bit1 -#define PCA9634_MODE1_AUTOINCR0 0x20 // RO bit0 +#define PCA9634_MODE1_AUTOINCR2 0x80 // ReadOnly, 0 = disable 1 = enable +#define PCA9634_MODE1_AUTOINCR1 0x40 // ReadOnly, bit1 +#define PCA9634_MODE1_AUTOINCR0 0x20 // ReadOnly, bit0 #define PCA9634_MODE1_SLEEP 0x10 // 0 = normal 1 = sleep #define PCA9634_MODE1_SUB1 0x08 // 0 = disable 1 = enable #define PCA9634_MODE1_SUB2 0x04 // 0 = disable 1 = enable #define PCA9634_MODE1_SUB3 0x02 // 0 = disable 1 = enable #define PCA9634_MODE1_ALLCALL 0x01 // 0 = disable 1 = enable +#define PCA9634_MODE1_NONE 0x00 // Configuration bits MODE2 register #define PCA9634_MODE2_BLINK 0x20 // 0 = dim 1 = blink #define PCA9634_MODE2_INVERT 0x10 // 0 = normal 1 = inverted -#define PCA9634_MODE2_STOP 0x08 // 0 = on STOP 1 = on ACK +#define PCA9634_MODE2_ACK 0x08 // 0 = on STOP 1 = on ACK #define PCA9634_MODE2_TOTEMPOLE 0x04 // 0 = open drain 1 = totem-pole +#define PCA9634_MODE2_NONE 0x00 - -// NOT IMPLEMENTED YET -#define PCA9634_SUBADR(x) (0x0E +(x)) // x = 1..3 +// (since 0.2.0) +#define PCA9634_SUBADR(x) (0x0D +(x)) // x = 1..3 #define PCA9634_ALLCALLADR 0x11 @@ -72,7 +73,8 @@ public: bool begin(uint8_t sda, uint8_t scl); #endif bool begin(); - void reset(); + void configure(uint8_t mode1_mask = PCA9634_MODE1_ALLCALL, + uint8_t mode2_mask = PCA9634_MODE2_NONE); bool isConnected(); uint8_t channelCount() { return _channelCount; }; @@ -82,10 +84,10 @@ public: // single PWM setting uint8_t write1(uint8_t channel, uint8_t value); - + // RGB setting, write three consecutive PWM registers uint8_t write3(uint8_t channel, uint8_t R, uint8_t G, uint8_t B); - + // generic worker, write N consecutive PWM registers uint8_t writeN(uint8_t channel, uint8_t* arr, uint8_t count); @@ -100,15 +102,33 @@ public: // TODO PWM also in %% ? - void setGroupPWM(uint8_t value) { writeReg(PCA9634_GRPPWM, value); } - uint8_t getGroupPWM() { return readReg(PCA9634_GRPPWM); } + void setGroupPWM(uint8_t value) { writeReg(PCA9634_GRPPWM, value); }; + uint8_t getGroupPWM() { return readReg(PCA9634_GRPPWM); }; // TODO set time in milliseconds and round to nearest value? - void setGroupFREQ(uint8_t value) { writeReg(PCA9634_GRPFREQ, value); } - uint8_t getGroupFREQ() { return readReg(PCA9634_GRPFREQ); } + void setGroupFREQ(uint8_t value) { writeReg(PCA9634_GRPFREQ, value); }; + uint8_t getGroupFREQ() { return readReg(PCA9634_GRPFREQ); }; int lastError(); + ///////////////////////////////////////////////////// + // + // SUB CALL - ALL CALL (since 0.2.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: // DIRECT CONTROL uint8_t writeReg(uint8_t reg, uint8_t value); // returns error status. diff --git a/libraries/PCA9634/README.md b/libraries/PCA9634/README.md index 872572bf..1d128d21 100644 --- a/libraries/PCA9634/README.md +++ b/libraries/PCA9634/README.md @@ -30,15 +30,19 @@ library is a 8 channel derived variation of the PCA9635 class. - **PCA9634(uint8_t deviceAddress, TwoWire \*wire = &Wire)** Constructor with I2C device address, and optional the Wire interface as parameter. - **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. +- **bool begin(uint8_t sda, uint8_t scl)** idem, ESP32 ESP8266 only. +- **void configure(uint8_t mode1_mask = PCA9634_MODE1_ALLCALL, uint8_t mode2_mask = PCA9634_MODE2_NONE)** +configures the library, optionally setting the MODE1 and MODE2 configuration registers. +See PCA9634.h and datasheet for settings possible. +**configure()** is typically used at startup. - **bool isConnected()** checks if address is available on I2C bus. - **uint8_t channelCount()** returns the number of channels = 8. ### LedDriverMode +Configure LED behaviour. + - **uint8_t setLedDriverMode(uint8_t channel, uint8_t mode)** mode is 0..3 See datasheet for full details. - **uint8_t getLedDriverMode(uint8_t channel)** returns the current mode of the channel. @@ -55,49 +59,56 @@ 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. +- **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. +- **uint8_t writeN(uint8_t channel, uint8_t \* array, uint8_t count)** +write count consecutive PWM registers. May return **PCA9634_ERR_WRITE** 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. +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. +- **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.1.2) -| Name | Value | Description | -|:------------------------|:-----:|:--------------------------------| -| PCA9634_MODE1_AUTOINCR2 | 0x80 | RO, 0 = disable 1 = enable | -| PCA9634_MODE1_AUTOINCR1 | 0x40 | RO, bit1 | -| PCA9634_MODE1_AUTOINCR0 | 0x20 | RO bit0 | -| PCA9634_MODE1_SLEEP | 0x10 | 0 = normal 1 = sleep | -| PCA9634_MODE1_SUB1 | 0x08 | 0 = disable 1 = enable | -| PCA9634_MODE1_SUB2 | 0x04 | 0 = disable 1 = enable | -| PCA9634_MODE1_SUB3 | 0x02 | 0 = disable 1 = enable | -| PCA9634_MODE1_ALLCALL | 0x01 | 0 = disable 1 = enable | -| | | | -| PCA9634_MODE2_BLINK | 0x20 | 0 = dim 1 = blink | -| PCA9634_MODE2_INVERT | 0x10 | 0 = normal 1 = inverted | -| PCA9634_MODE2_STOP | 0x08 | 0 = on STOP 1 = on ACK | -| PCA9634_MODE2_TOTEMPOLE | 0x04 | 0 = open drain 1 = totem-pole | +| Name | Value | Description | +|:------------------------|:-----:|:-----------------------------------| +| PCA9634_MODE1_AUTOINCR2 | 0x80 | Read Only, 0 = disable 1 = enable | +| PCA9634_MODE1_AUTOINCR1 | 0x40 | Read Only, bit1 | +| PCA9634_MODE1_AUTOINCR0 | 0x20 | Read Only, bit0 | +| PCA9634_MODE1_SLEEP | 0x10 | 0 = normal 1 = sleep | +| PCA9634_MODE1_SUB1 | 0x08 | 0 = disable 1 = enable | +| PCA9634_MODE1_SUB2 | 0x04 | 0 = disable 1 = enable | +| PCA9634_MODE1_SUB3 | 0x02 | 0 = disable 1 = enable | +| PCA9634_MODE1_ALLCALL | 0x01 | 0 = disable 1 = enable | +| | | | +| PCA9634_MODE2_BLINK | 0x20 | 0 = dim 1 = blink | +| PCA9634_MODE2_INVERT | 0x10 | 0 = normal 1 = inverted | +| PCA9634_MODE2_STOP | 0x08 | 0 = on STOP 1 = on ACK | +| PCA9634_MODE2_TOTEMPOLE | 0x04 | 0 = open drain 1 = totem-pole | These constants makes it easier to set modes without using a non descriptive -bitmask. The constants can be merged by OR-ing them together, see snippet: +bit mask. The constants can be merged by OR-ing them together, see snippet: ```cpp ledArray.writeMode(PCA9634_MODE2, 0b00110100); @@ -116,10 +127,14 @@ ledArray.setMode2(PCA9634_MODE2_BLINK | PCA9634_MODE2_INVERT | PCA9634_MODE2_TOT ### 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(value)** see datasheet for details. -- **uint8_t getGroupFREQ()** returns the freq of the PWM 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 @@ -137,6 +152,44 @@ ledArray.setMode2(PCA9634_MODE2_BLINK | PCA9634_MODE2_INVERT | PCA9634_MODE2_TOT | PCA9634_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.2.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 PCA9634 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 PCA9634 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 @@ -146,7 +199,9 @@ See examples - improve documentation - unit tests + - SUB CALL if possible? + - ALL CALL if possible? - add examples -- follow PCA9635 developments +- sync with PCA9635 developments - merge with PCA9635 and a PCA963X base class if possible diff --git a/libraries/PCA9634/keywords.txt b/libraries/PCA9634/keywords.txt index 1c7a171f..d0332463 100644 --- a/libraries/PCA9634/keywords.txt +++ b/libraries/PCA9634/keywords.txt @@ -7,7 +7,7 @@ PCA9634 KEYWORD1 # Methods and Functions (KEYWORD2) begin KEYWORD2 -reset KEYWORD2 +configure KEYWORD2 isConnected KEYWORD2 setLedDriverMode KEYWORD2 @@ -31,6 +31,13 @@ getGroupFREQ KEYWORD2 lastError KEYWORD2 +enableSubCall KEYWORD2 +disableSubCall KEYWORD2 +isEnabledSubCall KEYWORD2 +enableAllCall KEYWORD2 +disableAllCall KEYWORD2 +isEnabledAllCall KEYWORD2 + # Constants ( LITERAL1) PCA9634_LIB_VERSION LITERAL1 @@ -50,8 +57,11 @@ PCA9634_MODE1_SUB1 LITERAL1 PCA9634_MODE1_SUB2 LITERAL1 PCA9634_MODE1_SUB3 LITERAL1 PCA9634_MODE1_ALLCALL LITERAL1 +PCA9634_MODE1_NONE LITERAL1 PCA9634_MODE2_BLINK LITERAL1 PCA9634_MODE2_INVERT LITERAL1 PCA9634_MODE2_STOP LITERAL1 PCA9634_MODE2_TOTEMPOLE LITERAL1 +PCA9634_MODE2_NONE LITERAL1 + diff --git a/libraries/PCA9634/library.json b/libraries/PCA9634/library.json index 82771dc6..e7fef60c 100644 --- a/libraries/PCA9634/library.json +++ b/libraries/PCA9634/library.json @@ -15,7 +15,7 @@ "type": "git", "url": "https://github.com/RobTillaart/PCA9634.git" }, - "version": "0.1.2", + "version": "0.2.0", "license": "MIT", "frameworks": "arduino", "platforms": "*", diff --git a/libraries/PCA9634/library.properties b/libraries/PCA9634/library.properties index 3c90020d..1e227944 100644 --- a/libraries/PCA9634/library.properties +++ b/libraries/PCA9634/library.properties @@ -1,5 +1,5 @@ name=PCA9634 -version=0.1.2 +version=0.2.0 author=Rob Tillaart maintainer=Rob Tillaart sentence=Arduino library for PCA9634 I2C LED driver 8 channel