mirror of
https://github.com/RobTillaart/Arduino.git
synced 2024-10-03 18:09:02 -04:00
0.4.0 PCA9685_RT
This commit is contained in:
parent
dc45c57956
commit
f26f864731
@ -2,66 +2,37 @@
|
||||
// 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()
|
||||
|
||||
// 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
|
||||
|
@ -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 @@ public:
|
||||
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();
|
||||
void allOFF();
|
||||
|
||||
int lastError();
|
||||
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:
|
||||
|
@ -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
|
||||
|
||||
- **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)
|
||||
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 | |
|
||||
|
||||
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?
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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": "*",
|
||||
|
@ -1,5 +1,5 @@
|
||||
name=PCA9685_RT
|
||||
version=0.3.4
|
||||
version=0.4.0
|
||||
author=Rob Tillaart <rob.tillaart@gmail.com>
|
||||
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
|
||||
sentence=Arduino library for I2C PCA9685 16 channel PWM
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user