/*
* Author: Jon Trulson <jtrulson@ics.com>
* Copyright (c) 2015 Intel Corporation.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
#include <string>
#include <mraa/i2c.h>
#include <mraa/gpio.h>
#define PCA9685_I2C_BUS 0
#define PCA9685_DEFAULT_I2C_ADDR 0x60
// internal oscillator frequency
#define PCA9685_INTERNAL_OSC 25000000.0
// This is a 'special' LED number, used to refer to the ALL_LED registers
// that affect all LED outputs at once.
#define PCA9685_ALL_LED 0xff
namespace upm {
/**
* @brief PCA9685 PWM Controller library
* @defgroup pca9685 libupm-pca9685
* @ingroup adafruit i2c led motor
*/
/**
* @library pca9685
* @sensor pca9685
* @comname PCA9685 PWM Controller
* @type led motor
* @man adafruit
* @web http://www.adafruit.com/products/815
* @con i2c
*
* @brief API for the PCA9685 16-channel, 12-bit PWM LED Controller
*
* This controller is also used on the Adafruit* Motor Shield v2.3
* board to control up to 4 DC motors, 2 stepper motors, and 2 servo
* motors.
*
* This module was tested with the Adafruit Motor Shield v2.3
*
* @image html pca9685.jpg
* @snippet pca9685.cxx Interesting
*/
class PCA9685 {
public:
/**
* PCA9685 registers
*/
typedef enum { REG_MODE1 = 0x00,
REG_MODE2 = 0x01,
REG_I2C_SA1 = 0x02, // I2C subaddress 1
REG_I2C_SA2 = 0x03,
REG_I2C_SA3 = 0x04,
REG_ALLCALL = 0x05, // I2C all call address
// LED output PWM control
REG_LED0_ON_L = 0x06, // LED0 ON low byte
REG_LED0_ON_H = 0x07, // LED0 ON high byte
REG_LED0_OFF_L = 0x08, // LED0 OFF low byte
REG_LED0_OFF_H = 0x09, // LED0 OFF high byte
REG_LED1_ON_L = 0x0a,
REG_LED1_ON_H = 0x0b,
REG_LED1_OFF_L = 0x0c,
REG_LED1_OFF_H = 0x0d,
REG_LED2_ON_L = 0x0e,
REG_LED2_ON_H = 0x0f,
REG_LED2_OFF_L = 0x10,
REG_LED2_OFF_H = 0x11,
REG_LED3_ON_L = 0x12,
REG_LED3_ON_H = 0x13,
REG_LED3_OFF_L = 0x14,
REG_LED3_OFF_H = 0x15,
REG_LED4_ON_L = 0x16,
REG_LED4_ON_H = 0x17,
REG_LED4_OFF_L = 0x18,
REG_LED4_OFF_H = 0x19,
REG_LED5_ON_L = 0x1a,
REG_LED5_ON_H = 0x1b,
REG_LED5_OFF_L = 0x1c,
REG_LED5_OFF_H = 0x1d,
REG_LED6_ON_L = 0x1e,
REG_LED6_ON_H = 0x1f,
REG_LED6_OFF_L = 0x20,
REG_LED6_OFF_H = 0x21,
REG_LED7_ON_L = 0x22,
REG_LED7_ON_H = 0x23,
REG_LED7_OFF_L = 0x24,
REG_LED7_OFF_H = 0x25,
REG_LED8_ON_L = 0x26,
REG_LED8_ON_H = 0x27,
REG_LED8_OFF_L = 0x28,
REG_LED8_OFF_H = 0x29,
REG_LED9_ON_L = 0x2a,
REG_LED9_ON_H = 0x2b,
REG_LED9_OFF_L = 0x2c,
REG_LED9_OFF_H = 0x2d,
REG_LED10_ON_L = 0x2e,
REG_LED10_ON_H = 0x2f,
REG_LED10_OFF_L = 0x30,
REG_LED10_OFF_H = 0x31,
REG_LED11_ON_L = 0x32,
REG_LED11_ON_H = 0x33,
REG_LED11_OFF_L = 0x34,
REG_LED11_OFF_H = 0x35,
REG_LED12_ON_L = 0x36,
REG_LED12_ON_H = 0x37,
REG_LED12_OFF_L = 0x38,
REG_LED12_OFF_H = 0x39,
REG_LED13_ON_L = 0x3a,
REG_LED13_ON_H = 0x3b,
REG_LED13_OFF_L = 0x3c,
REG_LED13_OFF_H = 0x3d,
REG_LED14_ON_L = 0x3e,
REG_LED14_ON_H = 0x3f,
REG_LED14_OFF_L = 0x40,
REG_LED14_OFF_H = 0x41,
REG_LED15_ON_L = 0x42,
REG_LED15_ON_H = 0x43,
REG_LED15_OFF_L = 0x44,
REG_LED15_OFF_H = 0x45,
// 0x46-0xf9 reserved
REG_ALL_LED_ON_L = 0xfa, // write all LED ON L
REG_ALL_LED_ON_H = 0xfb, // write all LED ON H
REG_ALL_LED_OFF_L = 0xfc, // write all LED OFF L
REG_ALL_LED_OFF_H = 0xfd, // write all LED OFF H
REG_PRESCALE = 0xfe,
REG_TESTMODE = 0xff // don't use
} PCA9685_REG_T;
/**
* MODE1 bits
*/
typedef enum { MODE1_ALL_CALL = 0x01, // all call status
MODE1_SUB3 = 0x02, // subcall 3 status
MODE1_SUB2 = 0x04, // subcall 2 status
MODE1_SUB1 = 0x08, // subcall 1 status
MODE1_SLEEP = 0x10, // sleep/normal mode
MODE1_AI = 0x20, // auto-increment enable
MODE1_EXTCLK = 0x40, // external clock enable
MODE1_RESTART = 0x80 // restart status
} PCA9685_MODE1_T;
/**
* MODE2 bits
*/
typedef enum { MODE2_OUTNE0 = 0x01, // output driver enable bit 0
MODE2_OUTNE = 0x02, // output driver enable bit 1
MODE2_OUTDRV = 0x04, // output open-drain/totem pole
MODE2_OCH = 0x08, // output change on STOP or ACK
MODE2_INVRT = 0x10, // output logic state invert
MODE2_RESERVE0 = 0x20, // reserved
MODE2_RESERVE1 = 0x40, // reserved
MODE2_RESERVE2 = 0x80 // reserved
} PCA9685_MODE2_T;
/**
* PCA9685 constructor
*
* @param bus I2C bus to use
* @param address Address for this device
*/
PCA9685(int bus, uint8_t address = PCA9685_DEFAULT_I2C_ADDR, bool raw = false);
/**
* PCA9685 destructor
*/
~PCA9685();
/**
* Writes a byte value into a register
*
* @param reg Register location to write into
* @param byte Byte to write
* @return True if successful
*/
bool writeByte(uint8_t reg, uint8_t byte);
/**
* Writes a word value into a register. Note: the device must have the
* auto-increment bit set in the MODE1 register to work.
*
* @param reg Register location to write into
* @param word Word to write
* @return True if successful
*/
bool writeWord(uint8_t reg, uint16_t word);
/**
* Reads a byte value from a register
*
* @param reg Register location to read from
* @return Value in a specified register
*/
uint8_t readByte(uint8_t reg);
/**
* Reads a word value from a register. Note: the device must have the
* auto-increment bit set in the MODE1 register to work.
*
* @param reg Register location to read from
* @return Value in a specified register
*/
uint16_t readWord(uint8_t reg);
/**
* Puts the device in or out of the sleep mode. The device is always
* in the sleep mode upon power-up.
*
* @param sleep True to put the device in the sleep mode, false to put out
* @return True if successful
*/
bool setModeSleep(bool sleep);
/**
* Sets or clears the FULL ON bit for a given LED
*
* @param led LED number; valid values are 0-15, PCA9685_ALL_LED
* @param val True to set the bit, false to clear it
* @return True if successful
*/
bool ledFullOn(uint8_t led, bool val);
/**
* Sets or clears the FULL OFF bit for a given LED. If the FULL ON
* bit is also set, FULL OFF has precedence.
*
* @param led LED number; valid values are 0-15, PCA9685_ALL_LED
* @param val True to set the bit, false to clear it
* @return True if successful
*/
bool ledFullOff(uint8_t led, bool val);
/**
* Sets the 'LED on' time (0-4,095). See the PCA9685 datasheet for details.
*
* @param led LED number; valid values are 0-15, PCA9685_ALL_LED
* @param time 12-bit value at which point the LED turns on
* @return True if successful
*/
bool ledOnTime(uint8_t led, uint16_t time);
/**
* Sets the 'LED off' time (0-4,095). See the PCA9685 datasheet for details.
*
* @param led LED number; valid values are 0-15, PCA9685_ALL_LED
* @param time 12-bit value at which point the LED turns off
* @return True if successful
*/
bool ledOffTime(uint8_t led, uint16_t time);
/**
* Sets the prescale value. See the PCA9685 datasheet for
* details. The prescale can only be set when the device is in
* the sleep mode.
*
* @param prescale Prescale value
* @return True if successful
*/
bool setPrescale(uint8_t prescale);
/**
* Sets the prescale value based on a desired frequency in Hz. The
* prescale can only be set when the device is in the sleep mode.
*
* @param hz Desired frequency in Hz
* @param oscFreq Oscillator frequency; default is 25 MHz
* @return True if successful
*/
bool setPrescaleFromHz(float hz,
float oscFreq=PCA9685_INTERNAL_OSC);
/**
* Enables or disables the restart capability of the controller
*
* @param enabled True to enable, false to disable
* It is enabled by default.
*/
void enableRestart(bool enabled) { m_restartEnabled = enabled; };
private:
/**
* Enables the I2C register auto-increment. This needs to be enabled
* for write/readWord() to work. The contructor enables this by
* default.
*
* @param ai True to enable, false otherwise
*/
bool enableAutoIncrement(bool ai);
bool m_restartEnabled;
mraa_i2c_context m_i2c;
uint8_t m_addr;
};
}