From 2aa24162c34721750020b9419caa6f660a689940 Mon Sep 17 00:00:00 2001 From: Jon Trulson Date: Thu, 26 Feb 2015 18:27:24 -0700 Subject: [PATCH] pca9685: Initial implementation This library implements generic support for the pca9685 16 channel 12 bit PWM LED controller. This controller is used on some Adafruit motor shields. Signed-off-by: Jon Trulson Signed-off-by: Zion Orent Signed-off-by: Mihai Tudor Panu --- examples/c++/CMakeLists.txt | 3 + examples/c++/pca9685.cxx | 78 ++++++++ examples/javascript/pca9685.js | 85 +++++++++ src/pca9685/CMakeLists.txt | 5 + src/pca9685/jsupm_pca9685.i | 9 + src/pca9685/pca9685.cxx | 283 +++++++++++++++++++++++++++++ src/pca9685/pca9685.h | 315 +++++++++++++++++++++++++++++++++ src/pca9685/pyupm_pca9685.i | 13 ++ 8 files changed, 791 insertions(+) create mode 100644 examples/c++/pca9685.cxx create mode 100644 examples/javascript/pca9685.js create mode 100644 src/pca9685/CMakeLists.txt create mode 100644 src/pca9685/jsupm_pca9685.i create mode 100644 src/pca9685/pca9685.cxx create mode 100644 src/pca9685/pca9685.h create mode 100644 src/pca9685/pyupm_pca9685.i diff --git a/examples/c++/CMakeLists.txt b/examples/c++/CMakeLists.txt index 558cac3b..d9575c24 100644 --- a/examples/c++/CMakeLists.txt +++ b/examples/c++/CMakeLists.txt @@ -98,6 +98,7 @@ add_executable (l298-example l298.cxx) add_executable (l298-stepper-example l298-stepper.cxx) add_executable (at42qt1070-example at42qt1070.cxx) add_executable (grovemd-example grovemd.cxx) +add_executable (pca9685-example pca9685.cxx) include_directories (${PROJECT_SOURCE_DIR}/src/hmc5883l) include_directories (${PROJECT_SOURCE_DIR}/src/grove) @@ -177,6 +178,7 @@ include_directories (${PROJECT_SOURCE_DIR}/src/ina132) include_directories (${PROJECT_SOURCE_DIR}/src/l298) include_directories (${PROJECT_SOURCE_DIR}/src/at42qt1070) include_directories (${PROJECT_SOURCE_DIR}/src/grovemd) +include_directories (${PROJECT_SOURCE_DIR}/src/pca9685) target_link_libraries (hmc5883l-example hmc5883l ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries (groveled-example grove ${CMAKE_THREAD_LIBS_INIT}) @@ -276,3 +278,4 @@ target_link_libraries (l298-example l298 ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries (l298-stepper-example l298 ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries (at42qt1070-example at42qt1070 ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries (grovemd-example grovemd ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries (pca9685-example pca9685 ${CMAKE_THREAD_LIBS_INIT}) diff --git a/examples/c++/pca9685.cxx b/examples/c++/pca9685.cxx new file mode 100644 index 00000000..0806e40c --- /dev/null +++ b/examples/c++/pca9685.cxx @@ -0,0 +1,78 @@ +/* + * Author: Jon Trulson + * 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. + */ + +#include +#include +#include +#include "pca9685.h" + +using namespace std; + +int main(int argc, char **argv) +{ +//! [Interesting] + // Instantiate an PCA9685 on I2C bus 0 + + upm::PCA9685 *leds = new upm::PCA9685(PCA9685_I2C_BUS, + PCA9685_DEFAULT_I2C_ADDR); + + // put device to sleep + leds->setModeSleep(true); + + // setup a period of 50Hz + leds->setPrescaleFromHz(50); + + // wake device up + leds->setModeSleep(false); + + // Setup a 50% duty cycle -- on time at 0, off time at 2048 (4096 / 2) + // Set for all channels + + leds->ledOnTime(PCA9685_ALL_LED, 0); + leds->ledOffTime(PCA9685_ALL_LED, 2048); + + // but, turn channel 3 full off and channel 4 full on + + cout << "Turning channel 3 off, and channel 4 on." << endl; + cout << "All other channels will be PWM'd at a 50% duty cycle." << endl; + + leds->ledFullOff(3, true); + leds->ledFullOn(4, true); + + // now, just sleep for 5 seconds, reset channels 3 and 4, and exit. + cout << "Sleeping for 5 seconds..." << endl; + + sleep(5); + + cout << "Exiting..." << endl; + + // clear the bits we set earlier + leds->ledFullOff(3, false); + leds->ledFullOn(4, false); + +//! [Interesting] + + delete leds; + return 0; +} diff --git a/examples/javascript/pca9685.js b/examples/javascript/pca9685.js new file mode 100644 index 00000000..3768f8e7 --- /dev/null +++ b/examples/javascript/pca9685.js @@ -0,0 +1,85 @@ +/*jslint node:true, vars:true, bitwise:true, unparam:true */ +/*jshint unused:true */ +/* +* Author: Zion Orent +* 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. +*/ + +function exit() +{ + console.log("Exiting"); + + if (myLEDController_obj) + { + // clear the bits we set earlier + myLEDController_obj.ledFullOff(3, false); + myLEDController_obj.ledFullOn(4, false); + } + myLEDController_obj = null; + if (LEDController_lib) + { + LEDController_lib.cleanUp(); + LEDController_lib = null; + } + process.exit(0); +} + +// The pca9685 is an led controller. +// It's being used in this case to drive motors. +var LEDController_lib = require('jsupm_pca9685'); + +var I2CBus = LEDController_lib.PCA9685_I2C_BUS; +var I2CAddr = LEDController_lib.PCA9685_DEFAULT_I2C_ADDR; +// Instantiate an PCA9685 on I2C bus 0 +var myLEDController_obj = new LEDController_lib.PCA9685(I2CBus, I2CAddr); + +// put device to sleep +myLEDController_obj.setModeSleep(true); + +// setup a period of 50Hz +myLEDController_obj.setPrescaleFromHz(50); + +// wake device up +myLEDController_obj.setModeSleep(false); + +// Setup a 50% duty cycle -- on time at 0, off time at 2048 (4096 / 2) +// Set for all channels +var LEDNum = LEDController_lib.PCA9685_ALL_LED; +myLEDController_obj.ledOnTime(LEDNum, 0); +myLEDController_obj.ledOffTime(LEDNum, 2048); + +// but, turn channel 3 full off and channel 4 full on +console.log("Turning channel 3 off, and channel 4 on."); +console.log("All other channels will be PWM'd at a 50% duty cycle."); + +myLEDController_obj.ledFullOff(3, true); +myLEDController_obj.ledFullOn(4, true); + + // now, just sleep for 5 seconds, reset channels 3 and 4, and exit. +console.log("Sleeping for 5 seconds..."); + +setTimeout(exit, 5000); + +process.on('SIGINT', function() +{ + exit(); +}); diff --git a/src/pca9685/CMakeLists.txt b/src/pca9685/CMakeLists.txt new file mode 100644 index 00000000..621148f2 --- /dev/null +++ b/src/pca9685/CMakeLists.txt @@ -0,0 +1,5 @@ +set (libname "pca9685") +set (libdescription "upm pca9685 I2C 16ch, 12b pwm, LED controller") +set (module_src ${libname}.cxx) +set (module_h ${libname}.h) +upm_module_init() diff --git a/src/pca9685/jsupm_pca9685.i b/src/pca9685/jsupm_pca9685.i new file mode 100644 index 00000000..63515d01 --- /dev/null +++ b/src/pca9685/jsupm_pca9685.i @@ -0,0 +1,9 @@ +%module jsupm_pca9685 +%include "../upm.i" +%include "cpointer.i" + +%{ + #include "pca9685.h" +%} + +%include "pca9685.h" diff --git a/src/pca9685/pca9685.cxx b/src/pca9685/pca9685.cxx new file mode 100644 index 00000000..d274c218 --- /dev/null +++ b/src/pca9685/pca9685.cxx @@ -0,0 +1,283 @@ +/* + * Author: Jon Trulson + * 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. + */ + +#include +#include +#include +#include + +#include "pca9685.h" + +using namespace upm; +using namespace std; + + +PCA9685::PCA9685(int bus, uint8_t address) +{ + m_addr = address; + + // setup our i2c link + if ( !(m_i2c = mraa_i2c_init(bus)) ) + { + cerr << "PCA9685: mraa_i2c_init() failed." << endl; + return; + } + + mraa_result_t rv; + + if ( (rv = mraa_i2c_address(m_i2c, m_addr)) != MRAA_SUCCESS) + { + cerr << "PCA9685: Could not initialize i2c bus. " << endl; + mraa_result_print(rv); + return; + } + + // enable auto-increment mode by default + enableAutoIncrement(true); + + // enable restart by default. + enableRestart(true); +} + +PCA9685::~PCA9685() +{ + setModeSleep(true); + mraa_i2c_stop(m_i2c); +} + +bool PCA9685::writeByte(uint8_t reg, uint8_t byte) +{ + mraa_result_t rv = mraa_i2c_write_byte_data(m_i2c, byte, reg); + + if (rv != MRAA_SUCCESS) + { + cerr << __FUNCTION__ << ": mraa_i2c_write_byte() failed." << endl; + mraa_result_print(rv); + return false; + } + + return true; +} + +bool PCA9685::writeWord(uint8_t reg, uint16_t word) +{ + mraa_result_t rv = mraa_i2c_write_word_data(m_i2c, word, reg); + + if (rv != MRAA_SUCCESS) + { + cerr << __FUNCTION__ << ": mraa_i2c_write_word() failed." << endl; + mraa_result_print(rv); + return false; + } + + return true; +} + +uint8_t PCA9685::readByte(uint8_t reg) +{ + return mraa_i2c_read_byte_data(m_i2c, reg); +} + +uint16_t PCA9685::readWord(uint8_t reg) +{ + return mraa_i2c_read_word_data(m_i2c, reg); +} + +bool PCA9685::setModeSleep(bool sleep) +{ + uint8_t mode1 = readByte(REG_MODE1); + uint8_t restartBit = mode1 & MODE1_RESTART; + + if (sleep) + mode1 |= MODE1_SLEEP; + else + mode1 &= ~MODE1_SLEEP; + + // if we are waking up, then preserve but don't write restart bit if set + if (!sleep && restartBit) + mode1 &= ~MODE1_RESTART; + + bool rv = writeByte(REG_MODE1, mode1); + + if (!rv) + { + cerr << __FUNCTION__ << ": write to MODE1 failed." << endl; + return rv; + } + + // Need a delay of 500us after turning sleep mode off for the oscillator + // to stabilize + if (!sleep) + usleep(500); + + // now check to see if we want to (and can) restart when waking up + if (restartBit && m_restartEnabled && !sleep) + { + mode1 |= restartBit; + rv = writeByte(REG_MODE1, mode1); + } + + return rv; +} + +bool PCA9685::enableAutoIncrement(bool ai) +{ + uint8_t mode1 = readByte(REG_MODE1); + + if (ai) + mode1 |= MODE1_AI; + else + mode1 &= ~MODE1_AI; + + return writeByte(REG_MODE1, mode1); +} + +bool PCA9685::ledFullOn(uint8_t led, bool val) +{ + if (led > 15 && (led != PCA9685_ALL_LED)) + { + cerr << __FUNCTION__ << ": led value must be between 0-15 or " + << "PCA9685_ALL_LED (255)" << endl; + return false; + } + + // figure out the register offset (*_ON_H) + uint8_t regoff; + + if (led == PCA9685_ALL_LED) + regoff = REG_ALL_LED_ON_H; + else + regoff = REG_LED0_ON_L + (led * 4) + 1; + + uint8_t bits = readByte(regoff); + + if (val) + bits |= ((1 << 4) & 0xff); + else + bits &= ~((1 << 4) & 0xff); + + return writeByte(regoff, bits); +} + +bool PCA9685::ledFullOff(uint8_t led, bool val) +{ + if (led > 15 && (led != PCA9685_ALL_LED)) + { + cerr << __FUNCTION__ << ": led value must be between 0-15 or " + << "PCA9685_ALL_LED (255)" << endl; + return false; + } + + // figure out the register offset (*_OFF_H) + uint8_t regoff; + + if (led == PCA9685_ALL_LED) + regoff = REG_ALL_LED_OFF_H; + else + regoff = REG_LED0_ON_L + (led * 4) + 3; + + uint8_t bits = readByte(regoff); + + if (val) + bits |= ((1 << 4) & 0xff); + else + bits &= ~((1 << 4) & 0xff); + + return writeByte(regoff, bits); +} + +bool PCA9685::ledOnTime(uint8_t led, uint16_t time) +{ + if (led > 15 && (led != PCA9685_ALL_LED)) + { + cerr << __FUNCTION__ << ": led value must be between 0-15 or " + << "PCA9685_ALL_LED (255)" << endl; + return false; + } + + if (time > 4095) + { + cerr << __FUNCTION__ << ": time value must be between 0-4095" << endl; + return false; + } + + // figure out the register offset (*_ON_L) + uint8_t regoff; + + if (led == PCA9685_ALL_LED) + regoff = REG_ALL_LED_ON_L; + else + regoff = REG_LED0_ON_L + (led * 4); + + // we need to preserve the full ON bit in *_ON_H + uint8_t onbit = (readByte(regoff + 1) & 0x40); + + time = (time & 0x0fff) | (onbit << 8); + + return writeWord(regoff, time); +} + +bool PCA9685::ledOffTime(uint8_t led, uint16_t time) +{ + if (led > 15 && (led != PCA9685_ALL_LED)) + { + cerr << __FUNCTION__ << ": led value must be between 0-15 or " + << "PCA9685_ALL_LED (255)" << endl; + return false; + } + + if (time > 4095) + { + cerr << __FUNCTION__ << ": time value must be between 0-4095" << endl; + return false; + } + + // figure out the register offset (*_OFF_L) + uint8_t regoff; + + if (led == PCA9685_ALL_LED) + regoff = REG_ALL_LED_OFF_L; + else + regoff = REG_LED0_ON_L + (led * 4) + 2; + + // we need to preserve the full OFF bit in *_OFF_H + uint8_t offbit = (readByte(regoff + 1) & 0x40); + + time = (time & 0x0fff) | (offbit << 8); + + return writeWord(regoff, time); +} + +bool PCA9685::setPrescale(uint8_t prescale) +{ + // This will be ignored if the device isn't in SLEEP mode + return writeByte(REG_PRESCALE, prescale); +} + +bool PCA9685::setPrescaleFromHz(float hz, float oscFreq) +{ + float prescale = round( oscFreq / (4096.0 * hz) ) - 1; + + return setPrescale(uint8_t(prescale)); +} diff --git a/src/pca9685/pca9685.h b/src/pca9685/pca9685.h new file mode 100644 index 00000000..26b876ab --- /dev/null +++ b/src/pca9685/pca9685.h @@ -0,0 +1,315 @@ +/* + * Author: Jon Trulson + * 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 +#include +#include + +#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 UPM module for the PCA9685 16 channel, 12 bit pwm LED controller. + * @defgroup pca9685 libupm-pca9685 + */ + + /** + * @brief C++ 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 step motors and 2 servo + * motors. + * + * This module was tested with the Adafruit Motor Shield v2.3 + * + * @ingroup i2c pca9685 + * @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 the address for this device + */ + PCA9685(int bus, uint8_t address = PCA9685_DEFAULT_I2C_ADDR); + + /** + * PCA9685 Destructor + */ + ~PCA9685(); + + /** + * Write byte value into 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); + + /** + * Write word value at 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); + + /** + * Read byte value from register + * + * @param reg register location to read from + * @return value at specified register + */ + uint8_t readByte(uint8_t reg); + + /** + * Read word value from 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 at specified register + */ + uint16_t readWord(uint8_t reg); + + /** + * Put the device into or out of Sleep mode. The device is always + * in sleep mode upon power up. + * + * @param sleep true to put into sleep mode, false to take out of + * sleep mode. + * @return true if successful + */ + bool setModeSleep(bool sleep); + + /** + * set or clear the FULL ON bit for a given LED + * + * @param led led number, valid values: 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); + + /** + * set or clear the FULL OFF bit for a given LED. If the FULL ON + * bit is also set, then FULL OFF has precendence. + * + * @param led led number, valid values: 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); + + /** + * set the LED on time (0-4095). See the pca9685 datasheet for details. + * + * @param led led number, valid values: 0-15, PCA9685_ALL_LED + * @param time the 12 bit value at which point the LED will turn on + * @return true if successful + */ + bool ledOnTime(uint8_t led, uint16_t time); + + /** + * set the LED off time (0-4095). See the pca9685 datasheet for details. + * + * @param led led number, valid values: 0-15, PCA9685_ALL_LED + * @param time the 12 bit value at which point the LED will turn off + * @return true if successful + */ + bool ledOffTime(uint8_t led, uint16_t time); + + /** + * set the prescale value. See the pca9685 datasheet for + * details. The prescale can only be set when the device is in + * sleep mode. + * + * @param prescale the prescale value + * @return true if successful + */ + bool setPrescale(uint8_t prescale); + + /** + * set the prescale value based on a desired frequency in hz. The + * prescale can only be set when the device is in sleep mode. + * + * @param hz the desired frequency in hz + * @param oscFreq the oscillator frequency, defaul 25Mhz + * @return true if successful + */ + bool setPrescaleFromHz(float hz, + float oscFreq=PCA9685_INTERNAL_OSC); + + /** + * enable or disable the RESTART capability of the controller + * + * @param enabled true to enable restart, false to disable. + * Default is enabled. + * + * @return true if successful + */ + bool enableRestart(bool enabled) { m_restartEnabled = enabled; }; + + private: + /** + * Enable 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 auto-increment of i2c regs, false otherwise + */ + bool enableAutoIncrement(bool ai); + + bool m_restartEnabled; + mraa_i2c_context m_i2c; + uint8_t m_addr; + }; +} + + diff --git a/src/pca9685/pyupm_pca9685.i b/src/pca9685/pyupm_pca9685.i new file mode 100644 index 00000000..86faaf96 --- /dev/null +++ b/src/pca9685/pyupm_pca9685.i @@ -0,0 +1,13 @@ +%module pyupm_pca9685 +%include "../upm.i" + +%feature("autodoc", "3"); + +#ifdef DOXYGEN +%include "pca9685_doc.i" +#endif + +%include "pca9685.h" +%{ + #include "pca9685.h" +%}