diff --git a/docs/images/tca9548a.jpg b/docs/images/tca9548a.jpg new file mode 100644 index 00000000..30adfdbe Binary files /dev/null and b/docs/images/tca9548a.jpg differ diff --git a/examples/c++/tca9548a.cxx b/examples/c++/tca9548a.cxx new file mode 100644 index 00000000..df29ad2f --- /dev/null +++ b/examples/c++/tca9548a.cxx @@ -0,0 +1,103 @@ +/* + * Author: Gerard Vidal + * 2017 IFE ENS de Lyon + * + * + * 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 +#include +#include "tca9548a.hpp" + +using namespace std; +using namespace upm; + +/* + * This examble is built to multiplex channel 2 and Channel 4 + * + */ + +void +showAllPorts(upm::TCA9548A *multiplex) { + int i; + char ports [80]; + std::string convert; + for (i = 0; i<8; i++) { + if (multiplex->getPort(i)) + strcat (ports, "1." ); + else + strcat (ports,"0."); + } + cout << "Ports: " << ports; +} + +int main(int argc, char **argv) +{ + + // Instantiate a TCA9548A instance of i2c multiplexer + upm::TCA9548A *multiplex = new upm::TCA9548A(0); + + //Clean + multiplex->disableAllPorts(); + cout << "All ports disabled."; + + // Open i2c bus on multiplexer channel 4 + multiplex->setPort(4,multiplex->ENABLED,multiplex->EXCLUSIVE); + cout << "Port 4 enabled with exclusive access."; + showAllPorts(multiplex); + + // Close com with channel 4 + multiplex->disableAllPorts(); + + // Open i2c bus on multiplexer channel 2 + multiplex->setPort(2,multiplex->ENABLED,multiplex->EXCLUSIVE); + cout << "Port 2 enabled with exclusive access."; + showAllPorts(multiplex); + + //close com with channel 2 + multiplex->disableAllPorts(); + + // Open 2 i2c bus on multiplexer channel 4 and channel2 + multiplex->setPort(4,multiplex->ENABLED,multiplex->EXCLUSIVE); + multiplex->setPort(2,multiplex->ENABLED,multiplex->INCLUSIVE); + cout << "Port 4 enabled with exclusive access."; + cout << "Port 2 enabled with exclusive access."; + showAllPorts(multiplex); + + //close com with all channels + multiplex->disableAllPorts(); + + // Open i2c bus on all multiplexer channels + multiplex->enableAllPorts(); + cout << "All ports enabled."; + showAllPorts(multiplex); + + //close com with all channels + multiplex->disableAllPorts(); + + delete multiplex; + + return 0; +} diff --git a/examples/javascript/tca9548.js b/examples/javascript/tca9548.js new file mode 100644 index 00000000..7fbf18ec --- /dev/null +++ b/examples/javascript/tca9548.js @@ -0,0 +1,75 @@ +/* +* Author: Keelan Lightfoot +* 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. +*/ + +/* +* On its own, a i2c switch doesn't really do anything, so the best I can do is +* flips some ports on and off to demonstrate the capabilities of the library. +*/ + +var tca = require('jsupm_tca9548a'); + +// Assumes default bus and address +var sw = new tca.TCA9548A(tca.TCA9548A_I2C_BUS, + tca.TCA9548A_DEFAULT_ADDR); + +// Start with clean slate +sw.disableAllPorts(); +console.log("All ports disabled."); +showAllPorts(); + +// Enable port 4 +sw.setPort(4, tca.TCA9548A.ENABLED, tca.TCA9548A.EXCLUSIVE); +console.log("Port 4 enabled with exclusive access."); +showAllPorts(); + +// Enable port 5 +sw.setPort(5, tca.TCA9548A.ENABLED, tca.TCA9548A.INCLUSIVE); +console.log("Port 5 enabled with inclusive access."); +showAllPorts(); + +// Disable port 5 +sw.setPort(5, tca.TCA9548A.DISABLED, tca.TCA9548A.INCLUSIVE); +console.log("Port 5 disabled with inclusive access."); +showAllPorts(); + +// Enable all ports +sw.enableAllPorts(); +console.log("All ports enabled."); +showAllPorts(); + +// Clean up our mess, in an oblique way. +sw.setPort(4, tca.TCA9548A.DISABLED, tca.TCA9548A.EXCLUSIVE); +console.log("Port 4 disabled with exclusive access."); +showAllPorts(); + +// fin +process.exit(0); + +function showAllPorts() { + var ports = ""; + for (var i = 0; i<8; i++) { + ports += (sw.getPort(i)?i:"."); + } + console.log("Ports: "+ports); +} diff --git a/src/tca9548a/CMakeLists.txt b/src/tca9548a/CMakeLists.txt new file mode 100644 index 00000000..c4e091fe --- /dev/null +++ b/src/tca9548a/CMakeLists.txt @@ -0,0 +1,5 @@ +set (libname "tca9548a") +set (libdescription "tca9548a i2c multiplexer 8 channels") +set (module_src ${libname}.cxx) +set (module_hpp ${libname}.hpp) +upm_module_init(mraa) diff --git a/src/tca9548a/javaupm_tca9548a.i b/src/tca9548a/javaupm_tca9548a.i new file mode 100644 index 00000000..afe28f04 --- /dev/null +++ b/src/tca9548a/javaupm_tca9548a.i @@ -0,0 +1,19 @@ +%module javaupm_tca9548a +%include "../upm.i" + +%{ + #include "tca9548a.hpp" +%} + +%include "tca9548a.hpp" + +%pragma(java) jniclasscode=%{ + static { + try { + System.loadLibrary("javaupm_tca9548a"); + } catch (UnsatisfiedLinkError e) { + System.err.println("Native code library failed to load. \n" + e); + System.exit(1); + } + } +%} diff --git a/src/tca9548a/jsupm_tca9548a.i b/src/tca9548a/jsupm_tca9548a.i new file mode 100644 index 00000000..12490eed --- /dev/null +++ b/src/tca9548a/jsupm_tca9548a.i @@ -0,0 +1,8 @@ +%module jsupm_tca9548a +%include "../upm.i" + +%{ + #include "tca9548a.hpp" +%} + +%include "tca9548a.hpp" diff --git a/src/tca9548a/pyupm_tca9548a.i b/src/tca9548a/pyupm_tca9548a.i new file mode 100644 index 00000000..1c1ea818 --- /dev/null +++ b/src/tca9548a/pyupm_tca9548a.i @@ -0,0 +1,11 @@ +// Include doxygen-generated documentation +%include "pyupm_doxy2swig.i" +%module pyupm_tca9548a +%include "../upm.i" + +%feature("autodoc", "3"); + +%include "tca9548a.hpp" +%{ + #include "tca9548a.hpp" +%} diff --git a/src/tca9548a/tca9548a.cxx b/src/tca9548a/tca9548a.cxx new file mode 100644 index 00000000..2c02de80 --- /dev/null +++ b/src/tca9548a/tca9548a.cxx @@ -0,0 +1,138 @@ +/* + * Author: Gerard Vidal + * Copyright (c) 2017 IFE-ENS-Lyon + * Author: Keelan Lightfoot + * 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 "tca9548a.hpp" +#include + +#include +#include +#include +#include +#include + +using namespace upm; + +TCA9548A::TCA9548A (int bus, uint8_t address){ + m_name = "tca9548a"; + if(!(i2c = new mraa::I2c(bus))){ + throw std::invalid_argument(std::string(__FUNCTION__) + +": I2c.init() failed"); + return; + } + + if((i2c->address(address) != mraa::SUCCESS)){ + throw std::invalid_argument(std::string(__FUNCTION__) + + ": I2c.address() failed"); + return; + } + + if(i2c->frequency( mraa::I2C_FAST) != mraa::SUCCESS){ + syslog(LOG_WARNING, + "%s: I2c.frequency(I2C_FAST) failed, using default speed", + std::string(__FUNCTION__).c_str()); + } +} + +TCA9548A::~TCA9548A (){ + delete i2c; +} + + +bool +TCA9548A::getPort(int port) { + uint8_t config; + // Check range + if (!validPort(port)) { + throw std::invalid_argument(std::string(__FUNCTION__) + + ": port index out of range"); + return false; + } + // Get current port configuration + config = getPortConfig(); + // Check if the bit is set + return (config & (0x01 << port)); +} + +void +TCA9548A::setPort(int port, TCA9548A_PORT_STATE state, + TCA9548A_PORT_MODE mode) { + uint8_t config; + // Check range + if (!validPort(port)) { + throw std::invalid_argument(std::string(__FUNCTION__) + + ": port index out of range"); + return; + } + // If we're enabling more than one port at a time, we need to do a + // read-modify-write. + if (mode == INCLUSIVE) { + config = getPortConfig(); + } else { + config = TCA9548A_NO_PORTS; + } + + // Convert port number to control byte + if (state == ENABLED) { + config |= (0x01 << port); + } else { + config &= ~(0x01 << port); + } + // Set port + setPortConfig(config); +} + +void +TCA9548A::disableAllPorts() { + // Turn off all ports + setPortConfig(TCA9548A_NO_PORTS); +} + +void +TCA9548A::enableAllPorts() { + // Turn on all ports + setPortConfig(TCA9548A_ALL_PORTS); +} + +//Private functions + +uint8_t +TCA9548A::getPortConfig() { + return i2c->readByte(); +} + +void +TCA9548A::setPortConfig(uint8_t config) { + if(i2c->writeByte(config) != mraa::SUCCESS) { + throw std::runtime_error(std::string(__FUNCTION__) + + ": I2c.write() failed"); + return; + } +} + +bool +TCA9548A::validPort(int port) { + return (port >= TCA9548A_MIN_PORT && port <= TCA9548A_MAX_PORT); +} diff --git a/src/tca9548a/tca9548a.hpp b/src/tca9548a/tca9548a.hpp new file mode 100644 index 00000000..62ae14cc --- /dev/null +++ b/src/tca9548a/tca9548a.hpp @@ -0,0 +1,185 @@ +/* + * Author: Gerard Vidal + * Copyright (c) 2017 IFE-ENS-Lyon + * Author: Keelan Lightfoot + * 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 "mraa.hpp" +#include "mraa/i2c.hpp" + +#define TCA9548A_I2C_BUS 0 +#define TCA9548A_DEFAULT_ADDR 0x70 +#define TCA9548A_NO_PORTS 0x00 +#define TCA9548A_ALL_PORTS 0xFF + +#define TCA9548A_MIN_PORT 0 +#define TCA9548A_MAX_PORT 7 + +namespace upm { + /** + * @brief tca9548a multiplexer library + * @defgroup tca9548a libupm-tca9548a + * @ingroup adafruit i2c + */ + /** + * @library tca9548a + * @sensor TCA9548A + * @comname 1-to-8 I2C Multiplexer Breakout + * @type multiplexer + * @man adafruit + * @con i2c + * @web https://learn.adafruit.com/adafruit-tca9548a-1-to-8-i2c-multiplexer-breakout/overview + * + * @brief API TCA9548A Multiplexer Breakout + * + * The TCA9548A device has eight bidirectional translating switches + * that can be controlled through the I2C bus. The SCL/SDA upstream + * pair fans out to eight downstream pairs, or channels. Any + * individual SCn/SDn channel or combination of channels can be + * selected, determined by the contents of the programmable control + * register. + * + * The TCA9548A Multiplexer Breakout enables to get - up to 8 + * same-address I2C devices hooked up to one microcontroller - or up + * to 8 independent I2C buses. This multiplexer acts as a + * gatekeeper, shuttling the commands to the selected set of I2C + * pins with your command. The TCA9548A multiplexer is interesting + * in that it has an I2C address (0x70 by default) - and you + * basically send it a command to tell it which I2C multiplexed + * output you want to talk to, then you can address the board you + * want to address. You simply write a single byte with the desired + * multiplexed output number to that port, and bam - any future I2C + * packets will get sent to that port. + * + * The TCA9548A Multiplexer provides three pins (A0-A1-A2) that + * enable to change its address in case 0x70 address is used by + * another sensor on the same bus. By connecting one of the Ax pin + * to Vin you change its value from 0 to 1, these values change the + * value of the three first bits of the address : + * + * Address 0b01110-A2-A1-A0 + * No wiring A2=0 A1=0 A0=0 Address 0b01110000 0x70 + * A0 wired A2=0 A1=0 A0=1 Address 0b01110000 0x71 + * A2 wired A2=1 A1=0 A0=0 Address 0b01110100 0x74 + * Any address between 0x70 and 0x77 can be selected. + * + * Tested with Adafriut TCA9548A board. + * + * @image html tca9548a.jpg + * @snippet tca9548.cxx Interesting + */ + class TCA9548A { + + public: + /** + * @enum TCA9548A_PORT_STATE + * @brief boolean enum containing port state + * + * @var TCA9548A_PORT_STATE::DISABLED = 0 + * @var TCA9548A_PORT_STATE::ENABLED = 1 + */ + typedef enum { + DISABLED = 0, + ENABLED = 1 + } TCA9548A_PORT_STATE; + + /** + * @enum TCA9548A_PORT_MODE + * @brief boolean enum containing port access mode + * + * @var TCA9548A_PORT_MODE::EXCLUSIVE = 0 + * @var TCA9548A_PORT_MODE::INCLUSIVE = 1 + */ + typedef enum { + EXCLUSIVE = 0, + INCLUSIVE = 1 + } TCA9548A_PORT_MODE; + + + /** + * TCA9548A constructor + * + * @param address. Device address. Default is 0x70. + */ + TCA9548A (int bus, uint8_t address = 0x70); + + /** + * TCA9548A destructor + */ + ~TCA9548A(); + + /** + * Returns the name of the switch + */ + std::string name() { + return m_name; + } + + /** + * Returns the status of a port as configured in the multiplexer. + * + * @param port Switch port to check + */ + bool getPort(int port); + + /** + * Configure an individual port on the multiplexer + * + * @param port Port to configure + * @param enabled Set to true to enable the port, false to + * disable the port. + * @param exclusive Set to true to disable all other + * ports, false to leave existing port config + * untouched. When exclusive is set to false, an + * additional i2c read is required to read the current + * port setting. + */ + void setPort(int port, TCA9548A_PORT_STATE state, + TCA9548A_PORT_MODE mode); + + /** + * Disables all ports on the multiplexer. + */ + void disableAllPorts(); + + /** + * Enables all ports on the multiplexer. Useful when the + * multiplexer is used to electrically extend the bus + * rather than resolve address conflicts. + */ + void enableAllPorts(); + + private: + std::string m_name; + + mraa::I2c* i2c; + + uint8_t getPortConfig(); + void setPortConfig(uint8_t config); + bool validPort(int port); + }; +}