Skip to content

Commit

Permalink
add SUB ALL + ALL CALL functions (#16)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
RobTillaart authored Jun 10, 2022
1 parent b8cd667 commit ea821d5
Show file tree
Hide file tree
Showing 7 changed files with 402 additions and 113 deletions.
212 changes: 144 additions & 68 deletions PCA9685.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.


//////////////////////////////////////////////////////////////
Expand All @@ -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))
Expand All @@ -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;
}

Expand All @@ -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;
}


Expand All @@ -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);
}

Expand All @@ -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();
Expand All @@ -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;
Expand All @@ -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);
}
Expand All @@ -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
Expand Down
Loading

0 comments on commit ea821d5

Please sign in to comment.