From 5e0efa22aa8cb81ef88433605b4a01e22f49bcf2 Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Tue, 1 Dec 2020 14:50:30 +0100 Subject: [PATCH] Removed PCA9685 (old design remnant) --- src/PCA9685.cpp | 864 ------------------------------------------------ src/PCA9685.h | 245 -------------- 2 files changed, 1109 deletions(-) delete mode 100644 src/PCA9685.cpp delete mode 100644 src/PCA9685.h diff --git a/src/PCA9685.cpp b/src/PCA9685.cpp deleted file mode 100644 index 48a7533..0000000 --- a/src/PCA9685.cpp +++ /dev/null @@ -1,864 +0,0 @@ -/* Arduino Library for the PCA9685 16-Channel PWM Driver Module. - Copyright (C) 2016 NachtRaveVL - Copyright (C) 2012 Kasper Skårhøj - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - - Created by Kasper Skårhøj, August 3rd, 2012. - Forked by Vitska, June 18th, 2016. - Forked by NachtRaveVL, July 29th, 2016. - - PCA9685-Arduino - Version 1.2.14 -*/ - -#include "PCA9685.h" - -#define PCA9685_I2C_BASE_ADDRESS (byte)0x40 - -// Register addresses from data sheet -#define PCA9685_MODE1_REG (byte)0x00 -#define PCA9685_MODE2_REG (byte)0x01 -#define PCA9685_SUBADR1_REG (byte)0x02 -#define PCA9685_SUBADR2_REG (byte)0x03 -#define PCA9685_SUBADR3_REG (byte)0x04 -#define PCA9685_ALLCALL_REG (byte)0x05 -#define PCA9685_LED0_REG (byte)0x06 // Start of LEDx regs, 4B per reg, 2B on phase, 2B off phase, little-endian -#define PCA9685_PRESCALE_REG (byte)0xFE -#define PCA9685_ALLLED_REG (byte)0xFA - -// Mode1 register pin layout -#define PCA9685_MODE_RESTART (byte)0x80 -#define PCA9685_MODE_EXTCLK (byte)0x40 -#define PCA9685_MODE_AUTOINC (byte)0x20 -#define PCA9685_MODE_SLEEP (byte)0x10 -#define PCA9685_MODE_SUBADR1 (byte)0x08 -#define PCA9685_MODE_SUBADR2 (byte)0x04 -#define PCA9685_MODE_SUBADR3 (byte)0x02 -#define PCA9685_MODE_ALLCALL (byte)0x01 - -#define PCA9685_SW_RESET (byte)0x06 // Sent to address 0x00 to reset all devices on Wire line -#define PCA9685_PWM_FULL (uint16_t)0x01000 // Special value for full on/full off LEDx modes - -// To balance the load out in a weaved fashion, we use this offset table to distribute -// the load on the outputs in a more interleaving fashion than just a simple 16 offset -// per channel. We can set the off cycle value to be lower than the on cycle, which will -// put the high edge across the 0-4095 phase cycle range, which is supported by device. -static uint16_t phaseDistTable[16] = { 0, 2048, 1024, 3072, 512, 3584, 1536, 2560, 256, 3840, 1280, 2304, 3328, 768, 2816, 1792 }; - -#ifndef PCA9685_ENABLE_SOFTWARE_I2C -PCA9685::PCA9685(TwoWire& i2cWire, PCA9685_PhaseBalancer phaseBalancer) { - _i2cWire = &i2cWire; -#else -PCA9685::PCA9685(PCA9685_PhaseBalancer phaseBalancer) { -#endif - _i2cAddress = 0; - _phaseBalancer = phaseBalancer; - _isProxyAddresser = false; - _lastI2CError = 0; -} - -void PCA9685::resetDevices() { -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - Serial.println("PCA9685::resetDevices"); -#endif - - i2cWire_beginTransmission(0x00); - i2cWire_write(PCA9685_SW_RESET); - i2cWire_endTransmission(); - - delayMicroseconds(10); - -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - checkForErrors(); -#endif -} - -void PCA9685::init(byte i2cAddress, byte mode) { - if (_isProxyAddresser) return; - - // I2C 7-bit address is B 1 A5 A4 A3 A2 A1 A0 - // RW bit added by Arduino core TWI library - _i2cAddress = PCA9685_I2C_BASE_ADDRESS | (i2cAddress & 0x3F); - -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - Serial.print("PCA9685::init i2cAddress: 0x"); - Serial.println(_i2cAddress, HEX); -#endif - - writeRegister(PCA9685_MODE1_REG, PCA9685_MODE_RESTART | PCA9685_MODE_AUTOINC); - writeRegister(PCA9685_MODE2_REG, mode); -} - -#ifndef PCA9685_EXCLUDE_EXT_FUNC - -void PCA9685::initAsProxyAddresser(byte i2cAddress) { - _i2cAddress = i2cAddress & 0xFE; - _isProxyAddresser = true; - -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - Serial.print("PCA9685::initAsProxyAddresser i2cAddress: 0x"); - Serial.println(_i2cAddress, HEX); -#endif -} - -#endif - -byte PCA9685::getI2CAddress() { - return _i2cAddress; -} - -PCA9685_PhaseBalancer PCA9685::getPhaseBalancer() { - return _phaseBalancer; -} - -void PCA9685::setPWMFrequency(float pwmFrequency) { - if (pwmFrequency < 0 || _isProxyAddresser) return; - - // This equation comes from section 7.3.5 of the datasheet, but the rounding has been - // removed because it isn't needed. Lowest freq is 23.84, highest is 1525.88. - int preScalerVal = (25000000 / (4096 * pwmFrequency)) - 1; - if (preScalerVal > 255) preScalerVal = 255; - if (preScalerVal < 3) preScalerVal = 3; - -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - Serial.print("PCA9685::setPWMFrequency pwmFrequency: "); - Serial.print(pwmFrequency); - Serial.print(", preScalerVal: 0x"); - Serial.println(preScalerVal, HEX); -#endif - - // The PRE_SCALE register can only be set when the SLEEP bit of MODE1 register is set to logic 1. - byte mode1Reg = readRegister(PCA9685_MODE1_REG); - writeRegister(PCA9685_MODE1_REG, (mode1Reg = (mode1Reg & ~PCA9685_MODE_RESTART) | PCA9685_MODE_SLEEP)); - writeRegister(PCA9685_PRESCALE_REG, (byte)preScalerVal); - - // It takes 500us max for the oscillator to be up and running once SLEEP bit has been set to logic 0. - writeRegister(PCA9685_MODE1_REG, (mode1Reg = (mode1Reg & ~PCA9685_MODE_SLEEP) | PCA9685_MODE_RESTART)); - delayMicroseconds(500); -} - -void PCA9685::setChannelOn(int channel) { - if (channel < 0 || channel > 15) return; - -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - Serial.println("PCA9685::setChannelOn"); -#endif - - writeChannelBegin(channel); - writeChannelPWM(PCA9685_PWM_FULL, 0); // time_on = FULL; time_off = 0; - writeChannelEnd(); -} - -void PCA9685::setChannelOff(int channel) { - if (channel < 0 || channel > 15) return; - -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - Serial.println("PCA9685::setChannelOff"); -#endif - - writeChannelBegin(channel); - writeChannelPWM(0, PCA9685_PWM_FULL); // time_on = 0; time_off = FULL; - writeChannelEnd(); -} - -void PCA9685::setChannelPWM(int channel, uint16_t pwmAmount) { - if (channel < 0 || channel > 15) return; - -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - Serial.println("PCA9685::setChannelPWM"); -#endif - - writeChannelBegin(channel); - - uint16_t phaseBegin, phaseEnd; - getPhaseCycle(channel, pwmAmount, &phaseBegin, &phaseEnd); - - writeChannelPWM(phaseBegin, phaseEnd); - - writeChannelEnd(); -} - -void PCA9685::setChannelsPWM(int begChannel, int numChannels, const uint16_t *pwmAmounts) { - if (begChannel < 0 || begChannel > 15 || numChannels < 0) return; - if (begChannel + numChannels > 16) numChannels -= (begChannel + numChannels) - 16; - -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - Serial.print("PCA9685::setChannelsPWM numChannels: "); - Serial.println(numChannels); -#endif - - // In avr/libraries/Wire.h and avr/libraries/utility/twi.h, BUFFER_LENGTH controls - // how many channels can be written at once. Therefore, we loop around until all - // channels have been written out into their registers. - - while (numChannels > 0) { - writeChannelBegin(begChannel); - - int maxChannels = min(numChannels, (BUFFER_LENGTH - 1) / 4); - while (maxChannels-- > 0) { - uint16_t phaseBegin, phaseEnd; - getPhaseCycle(begChannel++, *pwmAmounts++, &phaseBegin, &phaseEnd); - - writeChannelPWM(phaseBegin, phaseEnd); - --numChannels; - } - - writeChannelEnd(); - if (_lastI2CError) return; - } -} - -#ifndef PCA9685_EXCLUDE_EXT_FUNC - -void PCA9685::setAllChannelsPWM(uint16_t pwmAmount) { -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - Serial.println("PCA9685::setAllChannelsPWM"); -#endif - - writeChannelBegin(-1); // Special value for ALLLED registers - - uint16_t phaseBegin, phaseEnd; - getPhaseCycle(-1, pwmAmount, &phaseBegin, &phaseEnd); - - writeChannelPWM(phaseBegin, phaseEnd); - - writeChannelEnd(); -} - -uint16_t PCA9685::getChannelPWM(int channel) { - if (channel < 0 || channel > 15 || _isProxyAddresser) return 0; - - byte regAddress = PCA9685_LED0_REG + (channel << 2); - -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - Serial.print("PCA9685::getChannelPWM channel: "); - Serial.print(channel); - Serial.print(", regAddress: 0x"); - Serial.println(regAddress, HEX); -#endif - - i2cWire_beginTransmission(_i2cAddress); - i2cWire_write(regAddress); - if (i2cWire_endTransmission()) { -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - checkForErrors(); -#endif - return 0; - } - - int bytesRead = i2cWire_requestFrom((uint8_t)_i2cAddress, (uint8_t)4); - if (bytesRead != 4) { - while (bytesRead-- > 0) - i2cWire_read(); -#ifdef PCA9685_ENABLE_SOFTWARE_I2C - i2c_stop(); // Manually have to send stop bit in software i2c mode -#endif - _lastI2CError = 4; -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - checkForErrors(); -#endif - return 0; - } - -#ifndef PCA9685_SWAP_PWM_BEG_END_REGS - uint16_t phaseBegin = (uint16_t)i2cWire_read(); - phaseBegin |= (uint16_t)i2cWire_read() << 8; - uint16_t phaseEnd = (uint16_t)i2cWire_read(); - phaseEnd |= (uint16_t)i2cWire_read() << 8; -#else - uint16_t phaseEnd = (uint16_t)i2cWire_read(); - phaseEnd |= (uint16_t)i2cWire_read() << 8; - uint16_t phaseBegin = (uint16_t)i2cWire_read(); - phaseBegin |= (uint16_t)i2cWire_read() << 8; -#endif - -#ifdef PCA9685_ENABLE_SOFTWARE_I2C - i2c_stop(); // Manually have to send stop bit in software i2c mode -#endif - -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - Serial.print(" PCA9685::getChannelPWM phaseBegin: "); - Serial.print(phaseBegin); - Serial.print(", phaseEnd: "); - Serial.println(phaseEnd); -#endif - - // See datasheet section 7.3.3 - uint16_t retVal; - if (phaseEnd >= PCA9685_PWM_FULL) - // Full OFF - // Figure 11 Example 4: full OFF takes precedence over full ON - // See also remark after Table 7 - retVal = 0; - else if (phaseBegin >= PCA9685_PWM_FULL) - // Full ON - // Figure 9 Example 3 - retVal = PCA9685_PWM_FULL; - else if (phaseBegin <= phaseEnd) - // start and finish in same cycle - // Section 7.3.3 example 1 - retVal = phaseEnd - phaseBegin; - else - // span cycles - // Section 7.3.3 example 2 - retVal = (phaseEnd + PCA9685_PWM_FULL) - phaseBegin; - -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - Serial.print(" PCA9685::getChannelPWM retVal: "); - Serial.println(retVal); -#endif - - return retVal; -} - -void PCA9685::enableAllCallAddress(byte i2cAddress) { - if (_isProxyAddresser) return; - - i2cAddress &= 0xFE; - -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - Serial.print("PCA9685::enableAllCallAddress i2cAddress: 0x"); - Serial.println(i2cAddress, HEX); -#endif - - writeRegister(PCA9685_ALLCALL_REG, i2cAddress); - - byte mode1Reg = readRegister(PCA9685_MODE1_REG); - writeRegister(PCA9685_MODE1_REG, (mode1Reg |= PCA9685_MODE_ALLCALL)); -} - -void PCA9685::enableSub1Address(byte i2cAddress) { - if (_isProxyAddresser) return; - - i2cAddress &= 0xFE; - -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - Serial.print("PCA9685::enableSub1Address i2cAddress: 0x"); - Serial.println(i2cAddress, HEX); -#endif - - writeRegister(PCA9685_SUBADR1_REG, i2cAddress); - - byte mode1Reg = readRegister(PCA9685_MODE1_REG); - writeRegister(PCA9685_MODE1_REG, (mode1Reg |= PCA9685_MODE_SUBADR1)); -} - -void PCA9685::enableSub2Address(byte i2cAddress) { - if (_isProxyAddresser) return; - - i2cAddress &= 0xFE; - -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - Serial.print("PCA9685::enableSub2Address i2cAddress: 0x"); - Serial.println(i2cAddress, HEX); -#endif - - writeRegister(PCA9685_SUBADR2_REG, i2cAddress); - - byte mode1Reg = readRegister(PCA9685_MODE1_REG); - writeRegister(PCA9685_MODE1_REG, (mode1Reg |= PCA9685_MODE_SUBADR2)); -} - -void PCA9685::enableSub3Address(byte i2cAddress) { - if (_isProxyAddresser) return; - - i2cAddress &= 0xFE; - -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - Serial.print("PCA9685::enableSub3Address i2cAddress: 0x"); - Serial.println(i2cAddress, HEX); -#endif - - writeRegister(PCA9685_SUBADR3_REG, i2cAddress); - - byte mode1Reg = readRegister(PCA9685_MODE1_REG); - writeRegister(PCA9685_MODE1_REG, (mode1Reg |= PCA9685_MODE_SUBADR3)); -} - -void PCA9685::disableAllCallAddress() { - if (_isProxyAddresser) return; - -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - Serial.println("PCA9685::disableAllCallAddress"); -#endif - - byte mode1Reg = readRegister(PCA9685_MODE1_REG); - writeRegister(PCA9685_MODE1_REG, (mode1Reg &= ~PCA9685_MODE_ALLCALL)); -} - -void PCA9685::disableSub1Address() { - if (_isProxyAddresser) return; - -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - Serial.println("PCA9685::disableSub1Address"); -#endif - - byte mode1Reg = readRegister(PCA9685_MODE1_REG); - writeRegister(PCA9685_MODE1_REG, (mode1Reg &= ~PCA9685_MODE_SUBADR1)); -} - -void PCA9685::disableSub2Address() { - if (_isProxyAddresser) return; - -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - Serial.println("PCA9685::disableSub2Address"); -#endif - - byte mode1Reg = readRegister(PCA9685_MODE1_REG); - writeRegister(PCA9685_MODE1_REG, (mode1Reg &= ~PCA9685_MODE_SUBADR2)); -} - -void PCA9685::disableSub3Address() { - if (_isProxyAddresser) return; - -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - Serial.println("PCA9685::disableSub3Address"); -#endif - - byte mode1Reg = readRegister(PCA9685_MODE1_REG); - writeRegister(PCA9685_MODE1_REG, (mode1Reg &= ~PCA9685_MODE_SUBADR3)); -} - -void PCA9685::enableExtClockLine() { -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - Serial.println("PCA9685::enableExtClockLine"); -#endif - - // The PRE_SCALE register can only be set when the SLEEP bit of MODE1 register is set to logic 1. - byte mode1Reg = readRegister(PCA9685_MODE1_REG); - writeRegister(PCA9685_MODE1_REG, (mode1Reg = (mode1Reg & ~PCA9685_MODE_RESTART) | PCA9685_MODE_SLEEP)); - writeRegister(PCA9685_MODE1_REG, (mode1Reg |= PCA9685_MODE_EXTCLK)); - - // It takes 500us max for the oscillator to be up and running once SLEEP bit has been set to logic 0. - writeRegister(PCA9685_MODE1_REG, (mode1Reg = (mode1Reg & ~PCA9685_MODE_SLEEP) | PCA9685_MODE_RESTART)); - delayMicroseconds(500); -} - -#endif - -byte PCA9685::getLastI2CError() { - return _lastI2CError; -} - -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - -static const char *textForI2CError(byte errorCode) { - switch (errorCode) { - case 0: - return "Success"; - case 1: - return "Data too long to fit in transmit buffer"; - case 2: - return "Received NACK on transmit of address"; - case 3: - return "Received NACK on transmit of data"; - default: - return "Other error"; - } -} - -void PCA9685::checkForErrors() { - if (_lastI2CError) { - Serial.print(" PCA9685::checkErrors lastI2CError: "); - Serial.print(_lastI2CError); - Serial.print(": "); - Serial.println(textForI2CError(getLastI2CError())); - } -} - -#endif - -void PCA9685::getPhaseCycle(int channel, uint16_t pwmAmount, uint16_t *phaseBegin, uint16_t *phaseEnd) { - // Set delay - if (channel < 0) { - // All channels - *phaseBegin = 0; - } - else if (_phaseBalancer == PCA9685_PhaseBalancer_Linear) { - // Distribute high phase area over entire phase range to balance load. - *phaseBegin = channel * (4096 / 16); - } - else if (_phaseBalancer == PCA9685_PhaseBalancer_Weaved) { - // Distribute high phase area over entire phase range to balance load. - *phaseBegin = phaseDistTable[channel]; - } - else { - *phaseBegin = 0; - } - - // See datasheet section 7.3.3 - if (pwmAmount == 0) { - // Full OFF => time_off[12] = 1; - *phaseEnd = PCA9685_PWM_FULL; - } - else if (pwmAmount >= PCA9685_PWM_FULL) { - // Full ON => time_on[12] = 1; time_off = ignored; - *phaseBegin |= PCA9685_PWM_FULL; - *phaseEnd = 0; - } - else { - *phaseEnd = *phaseBegin + pwmAmount; - if (*phaseEnd >= PCA9685_PWM_FULL) - *phaseEnd -= PCA9685_PWM_FULL; - } -} - -void PCA9685::writeChannelBegin(int channel) { - byte regAddress; - - if (channel != -1) - regAddress = PCA9685_LED0_REG + (channel * 0x04); - else - regAddress = PCA9685_ALLLED_REG; - -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - Serial.print(" PCA9685::writeChannelBegin channel: "); - Serial.print(channel); - Serial.print(", regAddress: 0x"); - Serial.println(regAddress, HEX); -#endif - - i2cWire_beginTransmission(_i2cAddress); - i2cWire_write(regAddress); -} - -void PCA9685::writeChannelPWM(uint16_t phaseBegin, uint16_t phaseEnd) { -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - Serial.print(" PCA9685::writeChannelPWM phaseBegin: "); - Serial.print(phaseBegin); - Serial.print(", phaseEnd: "); - Serial.println(phaseEnd); -#endif - -#ifndef PCA9685_SWAP_PWM_BEG_END_REGS - i2cWire_write(lowByte(phaseBegin)); - i2cWire_write(highByte(phaseBegin)); - i2cWire_write(lowByte(phaseEnd)); - i2cWire_write(highByte(phaseEnd)); -#else - i2cWire_write(lowByte(phaseEnd)); - i2cWire_write(highByte(phaseEnd)); - i2cWire_write(lowByte(phaseBegin)); - i2cWire_write(highByte(phaseBegin)); -#endif -} - -void PCA9685::writeChannelEnd() { - i2cWire_endTransmission(); - -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - checkForErrors(); -#endif -} - -void PCA9685::writeRegister(byte regAddress, byte value) { -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - Serial.print(" PCA9685::writeRegister regAddress: 0x"); - Serial.print(regAddress, HEX); - Serial.print(", value: 0x"); - Serial.println(value, HEX); -#endif - - i2cWire_beginTransmission(_i2cAddress); - i2cWire_write(regAddress); - i2cWire_write(value); - i2cWire_endTransmission(); - -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - checkForErrors(); -#endif -} - -byte PCA9685::readRegister(byte regAddress) { -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - Serial.print(" PCA9685::readRegister regAddress: 0x"); - Serial.println(regAddress, HEX); -#endif - - i2cWire_beginTransmission(_i2cAddress); - i2cWire_write(regAddress); - if (i2cWire_endTransmission()) { -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - checkForErrors(); -#endif - return 0; - } - - int bytesRead = i2cWire_requestFrom((uint8_t)_i2cAddress, (uint8_t)1); - if (bytesRead != 1) { - while (bytesRead-- > 0) - i2cWire_read(); -#ifdef PCA9685_ENABLE_SOFTWARE_I2C - i2c_stop(); // Manually have to send stop bit in software i2c mode -#endif - _lastI2CError = 4; -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - checkForErrors(); -#endif - return 0; - } - - byte retVal = i2cWire_read(); - -#ifdef PCA9685_ENABLE_SOFTWARE_I2C - i2c_stop(); // Manually have to send stop bit in software i2c mode -#endif - -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - Serial.print(" PCA9685::readRegister retVal: 0x"); - Serial.println(retVal, HEX); -#endif - - return retVal; -} - -#ifdef PCA9685_ENABLE_SOFTWARE_I2C -bool __attribute__((noinline)) i2c_start(uint8_t addr); -void __attribute__((noinline)) i2c_stop(void) asm("ass_i2c_stop"); -bool __attribute__((noinline)) i2c_write(uint8_t value) asm("ass_i2c_write"); -uint8_t __attribute__((noinline)) i2c_read(bool last); -#endif - -void PCA9685::i2cWire_beginTransmission(uint8_t addr) { - _lastI2CError = 0; -#ifndef PCA9685_ENABLE_SOFTWARE_I2C - _i2cWire->beginTransmission(addr); -#else - i2c_start(addr); -#endif -} - -uint8_t PCA9685::i2cWire_endTransmission(void) { -#ifndef PCA9685_ENABLE_SOFTWARE_I2C - return (_lastI2CError = _i2cWire->endTransmission()); -#else - i2c_stop(); - return (_lastI2CError = 0); -#endif -} - -uint8_t PCA9685::i2cWire_requestFrom(uint8_t addr, uint8_t len) { -#ifndef PCA9685_ENABLE_SOFTWARE_I2C - return _i2cWire->requestFrom(addr, len); -#else - i2c_start(addr | 0x01); - return (_readBytes = len); -#endif -} - -size_t PCA9685::i2cWire_write(uint8_t data) { -#ifndef PCA9685_ENABLE_SOFTWARE_I2C - return _i2cWire->write(data); -#else - return (size_t)i2c_write(data); -#endif -} - -uint8_t PCA9685::i2cWire_read(void) { -#ifndef PCA9685_ENABLE_SOFTWARE_I2C - return (uint8_t)(_i2cWire->read() & 0xFF); -#else - if (_readBytes > 1) { - _readBytes -= 1; - return (uint8_t)(i2c_read(false) & 0xFF); - } - else { - _readBytes = 0; - return (uint8_t)(i2c_read(true) & 0xFF); - } -#endif -} - -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - -void PCA9685::printModuleInfo() { - Serial.println(""); Serial.println(" ~~~ PCA9685 Module Info ~~~"); - - Serial.println(""); Serial.println("i2c Address:"); - Serial.print("0x"); - Serial.println(_i2cAddress, HEX); - - Serial.println(""); Serial.println("Phase Balancer:"); - switch (_phaseBalancer) { - case PCA9685_PhaseBalancer_None: - Serial.println("PCA9685_PhaseBalancer_None"); break; - case PCA9685_PhaseBalancer_Linear: - Serial.println("PCA9685_PhaseBalancer_Linear"); break; - case PCA9685_PhaseBalancer_Weaved: - Serial.println("PCA9685_PhaseBalancer_Weaved"); break; - default: - Serial.println(""); break; - } - - if (!_isProxyAddresser) { - Serial.println(""); Serial.println("Proxy Addresser:"); - Serial.println("false"); - - Serial.println(""); Serial.println("Mode1 Register:"); - byte mode1Reg = readRegister(PCA9685_MODE1_REG); - Serial.print("0x"); - Serial.print(mode1Reg, HEX); - Serial.print(", Bitset:"); - if (mode1Reg & PCA9685_MODE_RESTART) - Serial.print(" PCA9685_MODE_RESTART"); - if (mode1Reg & PCA9685_MODE_EXTCLK) - Serial.print(" PCA9685_MODE_EXTCLK"); - if (mode1Reg & PCA9685_MODE_AUTOINC) - Serial.print(" PCA9685_MODE_AUTOINC"); - if (mode1Reg & PCA9685_MODE_SLEEP) - Serial.print(" PCA9685_MODE_SLEEP"); - if (mode1Reg & PCA9685_MODE_SUBADR1) - Serial.print(" PCA9685_MODE_SUBADR1"); - if (mode1Reg & PCA9685_MODE_SUBADR2) - Serial.print(" PCA9685_MODE_SUBADR2"); - if (mode1Reg & PCA9685_MODE_SUBADR3) - Serial.print(" PCA9685_MODE_SUBADR3"); - if (mode1Reg & PCA9685_MODE_ALLCALL) - Serial.print(" PCA9685_MODE_ALLCALL"); - Serial.println(""); - - Serial.println(""); Serial.println("Mode2 Register:"); - byte mode2Reg = readRegister(PCA9685_MODE2_REG); - Serial.print("0x"); - Serial.print(mode2Reg, HEX); - Serial.print(", Bitset:"); - if (mode2Reg & PCA9685_MODE_INVRT) - Serial.print(" PCA9685_MODE_INVRT"); - if (mode2Reg & PCA9685_MODE_OCH_ONACK) - Serial.print(" PCA9685_MODE_OCH_ONACK"); - if (mode2Reg & PCA9685_MODE_OUTDRV_TPOLE) - Serial.print(" PCA9685_MODE_OUTDRV_TPOLE"); - if (mode2Reg & PCA9685_MODE_OUTNE_HIGHZ) - Serial.print(" PCA9685_MODE_OUTNE_HIGHZ"); - if (mode2Reg & PCA9685_MODE_OUTNE_TPHIGH) - Serial.print(" PCA9685_MODE_OUTNE_TPHIGH"); - Serial.println(""); - - Serial.println(""); Serial.println("SubAddress1 Register:"); - byte subAdr1Reg = readRegister(PCA9685_SUBADR1_REG); - Serial.print("0x"); - Serial.println(subAdr1Reg, HEX); - - Serial.println(""); Serial.println("SubAddress2 Register:"); - byte subAdr2Reg = readRegister(PCA9685_SUBADR2_REG); - Serial.print("0x"); - Serial.println(subAdr2Reg, HEX); - - Serial.println(""); Serial.println("SubAddress3 Register:"); - byte subAdr3Reg = readRegister(PCA9685_SUBADR3_REG); - Serial.print("0x"); - Serial.println(subAdr3Reg, HEX); - - Serial.println(""); Serial.println("AllCall Register:"); - byte allCallReg = readRegister(PCA9685_ALLCALL_REG); - Serial.print("0x"); - Serial.println(allCallReg, HEX); - } - else { - Serial.println(""); Serial.println("Proxy Addresser:"); - Serial.println("true"); - } -} - -#endif - - -#ifndef PCA9685_EXCLUDE_SERVO_EVAL - -PCA9685_ServoEvaluator::PCA9685_ServoEvaluator(uint16_t n90PWMAmount, uint16_t p90PWMAmount) { - n90PWMAmount = constrain(n90PWMAmount, 0, PCA9685_PWM_FULL); - p90PWMAmount = constrain(p90PWMAmount, n90PWMAmount, PCA9685_PWM_FULL); - - _coeff = new float[2]; - _isCSpline = false; - - _coeff[0] = n90PWMAmount; - _coeff[1] = (p90PWMAmount - n90PWMAmount) / 180.0f; -} - -PCA9685_ServoEvaluator::PCA9685_ServoEvaluator(uint16_t n90PWMAmount, uint16_t zeroPWMAmount, uint16_t p90PWMAmount) { - n90PWMAmount = constrain(n90PWMAmount, 0, PCA9685_PWM_FULL); - zeroPWMAmount = constrain(zeroPWMAmount, n90PWMAmount, PCA9685_PWM_FULL); - p90PWMAmount = constrain(p90PWMAmount, zeroPWMAmount, PCA9685_PWM_FULL); - - if (p90PWMAmount - zeroPWMAmount != zeroPWMAmount - n90PWMAmount) { - _coeff = new float[8]; - _isCSpline = true; - - // Cubic spline code adapted from: https://shiftedbits.org/2011/01/30/cubic-spline-interpolation/ - /* "THE BEER-WARE LICENSE" (Revision 42): Devin Lane wrote this [part]. As long as you retain - * this notice you can do whatever you want with this stuff. If we meet some day, and you - * think this stuff is worth it, you can buy me a beer in return. */ - - float x[3] = { 0, 90, 180 }; - float y[3] = { (float)n90PWMAmount, (float)zeroPWMAmount, (float)p90PWMAmount }; - float c[3], b[2], d[2], h[2], l[1], u[2], a[1], z[2]; // n = 3 - - h[0] = x[1] - x[0]; - u[0] = z[0] = 0; - c[2] = 0; - - for (int i = 1; i < 2; ++i) { - h[i] = x[i + 1] - x[i]; - l[i - 1] = (2 * (x[i + 1] - x[i - 1])) - h[i - 1] * u[i - 1]; - u[i] = h[i] / l[i - 1]; - a[i - 1] = (3 / h[i]) * (y[i + 1] - y[i]) - (3 / h[i - 1]) * (y[i] - y[i - 1]); - z[i] = (a[i - 1] - h[i - 1] * z[i - 1]) / l[i - 1]; - } - - for (int i = 1; i >= 0; --i) { - c[i] = z[i] - u[i] * c[i + 1]; - b[i] = (y[i + 1] - y[i]) / h[i] - (h[i] * (c[i + 1] + 2 * c[i])) / 3; - d[i] = (c[i + 1] - c[i]) / (3 * h[i]); - - _coeff[4 * i + 0] = y[i]; // a - _coeff[4 * i + 1] = b[i]; // b - _coeff[4 * i + 2] = c[i]; // c - _coeff[4 * i + 3] = d[i]; // d - } - } - else { - _coeff = new float[2]; - _isCSpline = false; - - _coeff[0] = n90PWMAmount; - _coeff[1] = (p90PWMAmount - n90PWMAmount) / 180.0f; - } -} - -PCA9685_ServoEvaluator::~PCA9685_ServoEvaluator() { - if (_coeff) delete[] _coeff; -} - -uint16_t PCA9685_ServoEvaluator::pwmForAngle(float angle) { - float retVal; - angle = constrain(angle + 90, 0, 180); - - if (!_isCSpline) { - retVal = _coeff[0] + (_coeff[1] * angle); - } - else { - if (angle <= 90) { - retVal = _coeff[0] + (_coeff[1] * angle) + (_coeff[2] * angle * angle) + (_coeff[3] * angle * angle * angle); - } - else { - angle -= 90; - retVal = _coeff[4] + (_coeff[5] * angle) + (_coeff[6] * angle * angle) + (_coeff[7] * angle * angle * angle); - } - } - - return (uint16_t)constrain((uint16_t)roundf(retVal), 0, PCA9685_PWM_FULL); -}; - -#endif diff --git a/src/PCA9685.h b/src/PCA9685.h deleted file mode 100644 index f3a6717..0000000 --- a/src/PCA9685.h +++ /dev/null @@ -1,245 +0,0 @@ -/* Arduino Library for the PCA9685 16-Channel PWM Driver Module. - Copyright (C) 2016 NachtRaveVL - Copyright (C) 2012 Kasper Skårhøj - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - - Created by Kasper Skårhøj, August 3rd, 2012. - Forked by Vitska, June 18th, 2016. - Forked by NachtRaveVL, July 29th, 2016. - - PCA9685-Arduino - Version 1.2.14 -*/ - -#ifndef PCA9685_H -#define PCA9685_H - -// Library Setup - -// NOTE: It is recommended to avoid editing library files directly and instead use custom -// build flags. While most custom build systems support such, the Arduino IDE does not. -// Be aware that editing this file directly will affect all projects using this library. - -// Uncomment this define to enable use of the software i2c library (min 4MHz+ processor required). -//#define PCA9685_ENABLE_SOFTWARE_I2C 1 // http://playground.arduino.cc/Main/SoftwareI2CLibrary - -// Uncomment this define if wanting to exclude extended functionality from compilation. -//#define PCA9685_EXCLUDE_EXT_FUNC 1 - -// Uncomment this define if wanting to exclude ServoEvaluator assistant from compilation. -//#define PCA9685_EXCLUDE_SERVO_EVAL 1 - -// Uncomment this define to swap PWM low(begin)/high(end) phase values in register reads/writes (needed for some chip manufacturers). -//#define PCA9685_SWAP_PWM_BEG_END_REGS 1 - -// Uncomment this define to enable debug output. -//#define PCA9685_ENABLE_DEBUG_OUTPUT 1 - -// Hookup Callout: Servo Control -// -PLEASE READ- -// Many 180 degree controlled digital servos run on a 20ms pulse width (50Hz update -// frequency) based duty cycle, and do not utilize the entire pulse width for their -// -90/+90 degree control. Typically, 2.5% of the 20ms pulse width (0.5ms) is considered -// -90 degrees, and 12.5% of the 20ms pulse width (2.5ms) is considered +90 degrees. -// This roughly translates to raw PCA9685 PWM values of 102 and 512 (out of the 4096 -// value range) for -90 to +90 degree control, but may need to be adjusted to fit your -// specific servo (e.g. some I've tested run ~130 to ~525 for their -90/+90 degree -// control). -// -// -ALSO- -// Please be aware that driving some servos past their -90/+90 degrees of movement can -// cause a little plastic limiter pin to break off and get stuck inside of the gearing, -// which could potentially cause the servo to become jammed and no longer function. -// -// See the PCA9685_ServoEvaluator class to assist with calculating PWM values from Servo -// angle values, if you desire that level of fine tuning. - -#if defined(ARDUINO) && ARDUINO >= 100 -#include -#else -#include -#endif -#ifndef PCA9685_ENABLE_SOFTWARE_I2C -#include - -// Define BUFFER_LENGTH on platforms that don't natively define such. -#ifndef BUFFER_LENGTH -#ifdef I2C_BUFFER_LENGTH -#define BUFFER_LENGTH I2C_BUFFER_LENGTH -#else -#warning "i2c BUFFER_LENGTH not defined - using default of 32, which may not be supported by your microcontroller's hardware. Check Wire.h (or similar) file for your hardware and manually define to remove this warning." -#define BUFFER_LENGTH 32 -#endif -#endif // /ifndef BUFFER_LENGTH - -#endif // /ifndef PCA9685_ENABLE_SOFTWARE_I2C - -// Channel update strategy used when multiple channels are being updated in batch: -#define PCA9685_MODE_OCH_ONACK (byte)0x08 // Channel updates commit after individual channel update ACK signal, instead of after full-transmission STOP signal - -// Output-enabled/active-low-OE-pin=LOW driver control modes (see datasheet Table 12 and Fig 13, 14, and 15 concerning correct usage of INVRT and OUTDRV): -#define PCA9685_MODE_INVRT (byte)0x10 // Enables channel output polarity inversion (applicable only when active-low-OE-pin=LOW) -#define PCA9685_MODE_OUTDRV_TPOLE (byte)0x04 // Enables totem-pole (instead of open-drain) style structure to be used for driving channel output, allowing use of an external output driver -// NOTE: 1) Chipset's breakout must support this feature (most do, some don't) -// 2) When in this mode, INVRT mode should be set according to if an external N-type external driver (should use INVRT) or P-type external driver (should not use INVRT) is more optimal -// 3) From datasheet Table 6. subnote [1]: "Some newer LEDs include integrated Zener diodes to limit voltage transients, reduce EMI, and protect the LEDs, and these -MUST BE- driven only in the open-drain mode to prevent overheating the IC." - -// Output-not-enabled/active-low-OE-pin=HIGH driver control modes (see datasheet Section 7.4 concerning correct usage of OUTNE): -// NOTE: Active-low-OE pin is typically used to synchronize multiple PCA9685 devices together, or as an external dimming control signal. -#define PCA9685_MODE_OUTNE_HIGHZ (byte)0x02 // Sets all channel outputs to high-impedance state (applicable only when active-low-OE-pin=HIGH) -#define PCA9685_MODE_OUTNE_TPHIGH (byte)0x01 // Sets all channel outputs to HIGH (applicable only when in totem-pole mode and active-low-OE-pin=HIGH) - -#define PCA9685_MIN_CHANNEL 0 -#define PCA9685_MAX_CHANNEL 15 -#define PCA9685_CHANNEL_COUNT 16 - -typedef enum { - PCA9685_PhaseBalancer_None = -1, // Disables phase balancing, all high phase areas start at begining of cycle - PCA9685_PhaseBalancer_Linear = 0, // Balances all outputs linearly, 256 steps away from previous output - PCA9685_PhaseBalancer_Weaved, // Balances first few outputs better, steps away from previous shorten towards last output - - PCA9685_PhaseBalancer_Count -} PCA9685_PhaseBalancer; -// NOTE: Phase balancing essentially means that the start of the high phase cycle, for each channel, is shifted by some amount, so that a large voltage sink doesn't occur all at once. - -class PCA9685 { -public: -#ifndef PCA9685_ENABLE_SOFTWARE_I2C - // May use a different Wire instance than Wire. Some chipsets, such as Due/Zero/etc., - // have a Wire1 class instance that uses the SDA1/SCL1 lines instead. - // Supported i2c baud rates are 100kHz, 400kHz, and 1000kHz. - PCA9685(TwoWire& i2cWire = Wire, PCA9685_PhaseBalancer phaseBalancer = PCA9685_PhaseBalancer_Linear); -#else - // Minimum supported i2c baud rate is 100kHz, which means minimum supported processor - // speed is 4MHz+ while running i2c standard mode. For 400kHz i2c baud rate, minimum - // supported processor speed is 16MHz+ while running i2c fast mode. - PCA9685(PCA9685_PhaseBalancer phaseBalancer = PCA9685_PhaseBalancer_Linear); -#endif - - // Should be called only once in setup(), before any init()'s, but after Wire.begin(). - // Only should be called once on any Wire instance to do a software reset, which - // will affect all devices on that line. This helps when you're constantly rebuilding - // and reuploading to ensure all the devices on that line are reset properly. - void resetDevices(); - - // Called in setup(). The i2c address here is the value of the A0, A1, A2, A3, A4 and - // A5 pins ONLY, as the class takes care of its internal base address. i2cAddress - // should be a value between 0 and 61, since only 62 boards can be addressed. - void init(byte i2cAddress = 0, byte mode = PCA9685_MODE_OUTDRV_TPOLE); - -#ifndef PCA9685_EXCLUDE_EXT_FUNC - // Called in setup(). Used when instance talks through to AllCall/Sub1-Sub3 instances - // as a proxy object. Using this method will disable any method that performs a read - // or conflicts certain states. - void initAsProxyAddresser(byte i2cAddress = 0xE0); -#endif - - byte getI2CAddress(); - PCA9685_PhaseBalancer getPhaseBalancer(); - - // Min: 24Hz, Max: 1526Hz, Default: 200Hz (as Hz increases channel resolution widens, but raw pre-scaler value, as computed per datasheet, also becomes less affected inversly) - void setPWMFrequency(float pwmFrequency); - - // Turns channel either full on or full off - void setChannelOn(int channel); - void setChannelOff(int channel); - - // PWM amounts 0 - 4096, 0 full off, 4096 full on - void setChannelPWM(int channel, uint16_t pwmAmount); - void setChannelsPWM(int begChannel, int numChannels, const uint16_t *pwmAmounts); - -#ifndef PCA9685_EXCLUDE_EXT_FUNC - // Sets all channels, but won't distribute phases - void setAllChannelsPWM(uint16_t pwmAmount); - - // Returns PWM amounts 0 - 4096, 0 full off, 4096 full on - uint16_t getChannelPWM(int channel); - - // Enables multiple talk-through paths via i2c bus (lsb/bit0 must stay 0) - // To use, create a new class instance using initAsSubAddressed() with said address - void enableAllCallAddress(byte i2cAddress = 0xE0); - void enableSub1Address(byte i2cAddress = 0xE2); - void enableSub2Address(byte i2cAddress = 0xE4); - void enableSub3Address(byte i2cAddress = 0xE8); - void disableAllCallAddress(); - void disableSub1Address(); - void disableSub2Address(); - void disableSub3Address(); - - // Allows external clock line to be utilized (once enabled cannot be disabled) - void enableExtClockLine(); -#endif - - byte getLastI2CError(); - -#ifdef PCA9685_ENABLE_DEBUG_OUTPUT - void printModuleInfo(); - void checkForErrors(); -#endif - -private: -#ifndef PCA9685_ENABLE_SOFTWARE_I2C - TwoWire *_i2cWire; // Wire class instance to use -#endif - byte _i2cAddress; // Module's i2c address - PCA9685_PhaseBalancer _phaseBalancer; // Phase balancer scheme to distribute load across phase range - bool _isProxyAddresser; // Instance is a proxy for sub addressing (disables certain functionality) - byte _lastI2CError; // Last i2c error - - void getPhaseCycle(int channel, uint16_t pwmAmount, uint16_t *phaseBegin, uint16_t *phaseEnd); - - void writeChannelBegin(int channel); - void writeChannelPWM(uint16_t phaseBegin, uint16_t phaseEnd); - void writeChannelEnd(); - - void writeRegister(byte regAddress, byte value); - byte readRegister(byte regAddress); - -#ifdef PCA9685_ENABLE_SOFTWARE_I2C - uint8_t _readBytes; -#endif - void i2cWire_beginTransmission(uint8_t); - uint8_t i2cWire_endTransmission(void); - uint8_t i2cWire_requestFrom(uint8_t, uint8_t); - size_t i2cWire_write(uint8_t); - uint8_t i2cWire_read(void); -}; - -#ifndef PCA9685_EXCLUDE_SERVO_EVAL - -// Class to assist with calculating Servo PWM values from angle values -class PCA9685_ServoEvaluator { -public: - // Uses a linear interpolation method to quickly compute PWM output value. Uses - // default values of 2.5% and 12.5% of phase length for -90/+90. - PCA9685_ServoEvaluator(uint16_t n90PWMAmount = 102, uint16_t p90PWMAmount = 512); - - // Uses a cubic spline to interpolate due to an offsetted zero angle that isn't - // exactly between -90/+90. This takes more time to compute, but gives a more - // accurate PWM output value along the entire range. - PCA9685_ServoEvaluator(uint16_t n90PWMAmount, uint16_t zeroPWMAmount, uint16_t p90PWMAmount); - - ~PCA9685_ServoEvaluator(); - - // Returns the PWM value to use given the angle (-90 to +90) - uint16_t pwmForAngle(float angle); - -private: - float *_coeff; // a,b,c,d coefficient values - bool _isCSpline; // Cubic spline tracking, for _coeff length -}; - -#endif - -#endif