0.1.0 MAX14661 multiplexer I2C

This commit is contained in:
rob tillaart 2021-06-03 09:31:17 +02:00
parent 826094199f
commit b1cd76d5f9
16 changed files with 971 additions and 0 deletions

View File

@ -0,0 +1,7 @@
compile:
# Choosing to run compilation tests on 2 different Arduino platforms
platforms:
- uno
- leonardo
- due
- zero

View File

@ -0,0 +1,13 @@
name: Arduino-lint
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: arduino/arduino-lint-action@v1
with:
library-manager: update
compliance: strict

View File

@ -0,0 +1,13 @@
---
name: Arduino CI
on: [push, pull_request]
jobs:
arduino_ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: Arduino-CI/action@master
# Arduino-CI/action@v0.1.1

View File

@ -0,0 +1,18 @@
name: JSON check
on:
push:
paths:
- '**.json'
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: json-syntax-check
uses: limitusus/json-syntax-check@v1
with:
pattern: "\\.json$"

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021-2021 Rob Tillaart
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.

View File

@ -0,0 +1,309 @@
//
// FILE: MAX14661.cpp
// AUTHOR: Rob Tillaart
// DATE: 2021-01-29
// VERSION: 0.1.0
// PURPOSE: Arduino library for MAX14661 16 channel I2C multiplexer
// URL: https://github.com/RobTillaart/MAX14661
//
// HISTORY:
// 0.1.00 2021-01-29 initial version
//
#include "MAX14661.h"
// registers
#define MAX14661_DIR0 0x00
#define MAX14661_DIR1 0x01
#define MAX14661_DIR2 0x02
#define MAX14661_DIR3 0x03
#define MAX14661_SHDW0 0x10
#define MAX14661_SHDw1 0x11
#define MAX14661_SHDw2 0x12
#define MAX14661_SHDw3 0x13
#define MAX14661_CMD_A 0x14
#define MAX14661_CMD_B 0x15
MAX14661::MAX14661(const uint8_t deviceAddress, TwoWire *wire)
{
_address = deviceAddress;
_wire = wire;
}
#if defined (ESP8266) || defined(ESP32)
bool MAX14661::begin(uint8_t dataPin, uint8_t clockPin)
{
_wire = &Wire;
if ((dataPin < 255) && (clockPin < 255))
{
_wire->begin(dataPin, clockPin);
} else {
_wire->begin();
}
_error = 0;
if (! isConnected()) return false;
return true;
}
#endif
bool MAX14661::begin()
{
_wire->begin();
_error = 0;
if (! isConnected()) return false;
return true;
}
bool MAX14661::isConnected()
{
_wire->beginTransmission(_address);
return ( _wire->endTransmission() == 0);
}
/////////////////////////////////////////////////////////
//
// PAIR INTERFACE
//
bool MAX14661::openChannel(uint8_t channel)
{
if (channel > 15) return false;
uint8_t ch = channel;
uint8_t reg = MAX14661_DIR0;
if (ch > 7)
{
reg = MAX14661_DIR1;
ch -= 8;
}
uint8_t mask = readRegister(reg);
mask |= (1 << ch);
writeRegister(reg, mask);
reg += 2;
mask = readRegister(reg);
mask |= (1 << ch);
writeRegister(reg, mask);
return true;
}
bool MAX14661::closeChannel(uint8_t channel)
{
if (channel > 15) return false;
uint8_t ch = channel;
uint8_t reg = MAX14661_DIR0;
if (ch > 7)
{
reg = MAX14661_DIR1;
ch -= 8;
}
uint8_t mask = readRegister(reg);
mask &= ~(1 << ch);
writeRegister(reg, mask);
reg += 2;
mask = readRegister(reg);
mask &= ~(1 << ch);
writeRegister(reg, mask);
return true;
}
// assumption both A and B are in same state
bool MAX14661::isOpenChannel(uint8_t channel)
{
if (channel > 15) return false;
uint8_t ch = channel;
uint8_t reg = MAX14661_DIR0;
if (ch > 7)
{
reg = MAX14661_DIR1;
ch -= 8;
}
uint8_t mask = readRegister(reg);
return (mask & (1 << ch)) > 0;
}
void MAX14661::openAllChannels()
{
setChannels(0xFFFF);
}
void MAX14661::closeAllChannels()
{
setChannels(0);
}
void MAX14661::setChannels(uint16_t mask)
{
writeRegister(MAX14661_DIR0, mask & 0x00FF);
writeRegister(MAX14661_DIR1, (mask & 0xFF00) >> 8);
writeRegister(MAX14661_DIR2, mask & 0x00FF);
writeRegister(MAX14661_DIR3, (mask & 0xFF00) >> 8);
}
// assumption both A and B are in same state
uint16_t MAX14661::getChannels()
{
uint16_t channels = readRegister(MAX14661_DIR1) << 8;
channels |= readRegister(MAX14661_DIR0);
return channels;
}
/////////////////////////////////////////////////////////
//
// MUX INTERFACE
//
void MAX14661::MUXA(uint8_t channel)
{
uint8_t ch = channel;
if (channel > 15) ch = 0x10;
writeRegister(MAX14661_CMD_A, ch);
}
uint8_t MAX14661::getMUXA()
{
uint8_t ch = readRegister(MAX14661_CMD_A);
if ((ch & 0x10) == 0) return (ch & 0x0F);
return 255;
}
void MAX14661::MUXB(uint8_t channel)
{
uint8_t ch = channel;
if (channel > 15) ch = 0x10;
writeRegister(MAX14661_CMD_B, ch);
}
uint8_t MAX14661::getMUXB()
{
uint8_t ch = readRegister(MAX14661_CMD_B);
if ((ch & 0x10) == 0) return (ch & 0x0F);
return 255;
}
/////////////////////////////////////////////////////////
//
// FULL CONTROL
//
bool MAX14661::openA(uint8_t channel)
{
if (channel > 15) return false;
uint8_t ch = channel;
uint8_t reg = MAX14661_DIR0;
if (ch > 7)
{
reg = MAX14661_DIR1;
ch -= 8;
}
uint8_t mask = readRegister(reg);
mask |= (1 << ch);
writeRegister(reg, mask);
return true;
}
bool MAX14661::openB(uint8_t channel)
{
if (channel > 15) return false;
uint8_t ch = channel;
uint8_t reg = MAX14661_DIR2;
if (ch > 7)
{
reg = MAX14661_DIR3;
ch -= 8;
}
uint8_t mask = readRegister(reg);
mask |= (1 << ch);
writeRegister(reg, mask);
return true;
}
bool MAX14661::closeA(uint8_t channel)
{
if (channel > 15) return false;
uint8_t ch = channel;
uint8_t reg = MAX14661_DIR0;
if (ch > 7)
{
reg = MAX14661_DIR1;
ch -= 8;
}
uint8_t mask = readRegister(reg);
mask &= ~(1 << ch);
writeRegister(reg, mask);
return true;
}
bool MAX14661::closeB(uint8_t channel)
{
if (channel > 15) return false;
uint8_t ch = channel;
uint8_t reg = MAX14661_DIR2;
if (ch > 7)
{
reg = MAX14661_DIR3;
ch -= 8;
}
uint8_t mask = readRegister(reg);
mask &= ~(1 << ch);
writeRegister(reg, mask);
return true;
}
/////////////////////////////////////////////////////////
//
// LOW LEVEL CONTROL
//
uint8_t MAX14661::readRegister(uint8_t reg)
{
_wire->beginTransmission(_address);
_wire->write(reg);
_error = _wire->endTransmission();
if (_error != 0) return 0;
if (_wire->requestFrom(_address, (uint8_t)1) != 1)
{
_error = -1;
return 0;
}
return _wire->read();
}
int MAX14661::writeRegister(uint8_t reg, uint8_t value)
{
_wire->beginTransmission(_address);
_wire->write(reg);
_wire->write(value);
_error = _wire->endTransmission();
return _error;
}
int MAX14661::lastError()
{
int e = _error;
_error = 0;
return e;
}
// -- END OF FILE --

View File

@ -0,0 +1,106 @@
#pragma once
//
// FILE: MAX14661.h
// AUTHOR: Rob Tillaart
// DATE: 2021-01-29
// VERSION: 0.1.0
// PURPOSE: Arduino library for MAX14661 16 channel I2C multiplexer
// URL: https://github.com/RobTillaart/MAX14661
//
// - initial version has only support for opening and closing channels
// by means of the direct access register.
// - no support for shadow registers
#include "Arduino.h"
#include "Wire.h"
#define MAX14661_LIB_VERSION (F("0.1.0"))
class MAX14661
{
public:
explicit MAX14661(const uint8_t deviceAddress, TwoWire *wire = &Wire);
#if defined (ESP8266) || defined(ESP32)
bool begin(uint8_t sda, uint8_t scl);
#endif
bool begin();
bool isConnected();
//
// PAIR INTERFACE
// - keeps A and B line in sync, ideal for an I2C bus
// - returns false if channel nr > 15
//
// open ==> connect
bool openChannel(uint8_t channel);
// close ==> disconnect
bool closeChannel(uint8_t channel);
// returns true if channel is opened
bool isOpenChannel(uint8_t channel);
// open A and B lines of all channels
void openAllChannels();
// closes A and B lines of all channels
void closeAllChannels();
// set channels with a bit mask
void setChannels(uint16_t mask);
// returns channel state as bit mask
uint16_t getChannels();
//
//
// SHADOW INTERFACE
//
// TODO reg 0x10 ..0x13
//
// openShadow(channel)
// closeShadow(channel)
// sync () - reg 14 / 15 10001
//
// MUX INTERFACE
// - allows only one channel simultaneously
//
void MUXA(uint8_t channel); // 0..15, else ==> off
uint8_t getMUXA();
void MUXB(uint8_t channel); // 0..15, else ==> off
uint8_t getMUXB();
//
// FULL CONTROL PER A B LINE
// - selective open and close A and B
// - returns false if channel nr > 15
//
bool openA(uint8_t channel);
bool openB(uint8_t channel);
bool closeA(uint8_t channel);
bool closeB(uint8_t channel);
// LOW LEVEL CONTROL
uint8_t readRegister(uint8_t reg);
int writeRegister(uint8_t reg, uint8_t value);
int lastError();
private:
uint8_t _address;
TwoWire* _wire;
int _error;
// cache direct registers?
};
// -- END OF FILE --

View File

@ -0,0 +1,116 @@
[![Arduino CI](https://github.com/RobTillaart/MAX14661/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/MAX14661/blob/master/LICENSE)
[![GitHub release](https://img.shields.io/github/release/RobTillaart/MAX14661.svg?maxAge=3600)](https://github.com/RobTillaart/MAX14661/releases)
# MAX14661
Arduino library for MAX14661 multiplexer with I2C interface.
## Description
The MAX14661 multiplexer is a 16x2 channel multiplexer.
It has 2 lines A and B which can be
connected up to 16 A lines and 16 B lines.
The device can be controlled by SPI or I2C.
This library implements only an I2C interface
## Interface
The library provides 3 kinds of interfaces.
Mixing these interfaces is allowed but definitely not advised as
especially the PAIR interface assumes that A and B selections
are kept in sync.
So depending on your application choose the interface you want to use.
### Constructor
- **MAX14661(deviceAddress, TwoWire \*wire = &Wire)** Constructor with device address,
and optional the Wire interface as parameter.
- **bool begin()** initializes the wire interface
- **bool begin(sda, scl)** idem, for the ESP32 where one can choose the I2C pins.
- **bool isConnected()** checks if the address is visable on the I2C bus
### PAIR interface
The functions in this interface part all work symmetrical on the A and B line. They are managed as a PAIR. So this is ideal e.g. to multiplex an I2C bus.
The interface allows to have multiple lines A/B open in parallel.
// open ==> connect
- **bool openChannel(uint8_t channel)** connects A/B to output channel
- **bool closeChannel(uint8_t channel)** disconnects channel from A/B
- **bool isOpenChannel(uint8_t channel)** returns true if connected to A/B
- **void openAllChannels()** connects all channels
- **void closeAllChannels()** disconnects all channels
- **void setChannels(uint16_t mask)** connect multiple channels with a bit mask.
- **uint16_t getChannels()** returns a bit mask of the channels connected.
### SHADOW interface
not implemented yet.
Allows to prepare which channels should be selected and set them all at once.
### MUX interface
The MUX interface allows one channel to be open at a time.
- **void MUXA(uint8_t channel)** if channel < 16 only that channel will be selected. All other values will select no channel.
- **uint8_t getMUXA()** returns the selected channel. 255 means none selected.
- **void MUXB(uint8_t channel)** if channel < 16 only that channel will be selected. All other values will select no channel.
- **uint8_t getMUXB()** returns the selected channel. 255 means none selected.
### FULL CONTROL interface
full control per channel, any combination is possible.
Use with care as these can interfere e.g. with the PAIR interface.
All functions return false if channel > 15.
- **bool openA(uint8_t channel)** idem
- **bool openB(uint8_t channel)** idem
- **bool closeA(uint8_t channel)** idem
- **bool closeB(uint8_t channel)** idem
### LOW LEVEL CONTROL interface
Check datasheet for these values of the registers.
- **uint8_t readRegister(uint8_t reg)** read low level device register.
- **int writeRegister(uint8_t reg, uint8_t value)** write value to device register.
### Misc
- **lastError()** returns the last error, limited to I2C for now.
## Error codes
to be elaborated
## TODO
- test test
- write unit tests.
- SHADOW interface
- error handling
- improve documentation
- initial value param for begin()?
## Operation
See examples

Binary file not shown.

View File

@ -0,0 +1,79 @@
//
// FILE: MAX14661_MUX.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: demo PAIR interface
// DATE: 2021-01-29
// URL: https://github.com/RobTillaart/MAX14661
//
#include "Wire.h"
#include "MAX14661.h"
MAX14661 mux(0x62);
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.println(MAX14661_LIB_VERSION);
if (mux.begin() == false)
{
Serial.println("Could not find MAX14661");
while (1);
}
test1();
test2();
test3();
}
void test1()
{
Serial.println("\nTEST 1");
for (int ch = 0; ch < 16; ch++)
{
mux.MUXA(ch);
Serial.println(mux.getMUXA(), HEX);
}
mux.MUXA(255);
Serial.println(mux.getMUXB(), HEX);
}
void test2()
{
Serial.println("\nTEST 1");
for (int ch = 0; ch < 16; ch++)
{
mux.MUXB(ch);
Serial.println(mux.getMUXB(), HEX);
}
mux.MUXB(255);
Serial.println(mux.getMUXB(), HEX);
}
void test3()
{
Serial.println("\nTEST 1");
for (int ch = 0; ch < 16; ch++)
{
mux.MUXB(ch);
Serial.println(mux.getMUXB(), HEX);
}
mux.MUXB(255);
Serial.println(mux.getMUXB(), HEX);
}
void loop()
{
}
// -- END OF FILE --

View File

@ -0,0 +1,102 @@
//
// FILE: MAX14661_PAIR.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: demo PAIR interface
// DATE: 2021-01-29
// URL: https://github.com/RobTillaart/MAX14661
//
#include "Wire.h"
#include "MAX14661.h"
MAX14661 mux(0x4C); // 0x4C..0x4F
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.println(MAX14661_LIB_VERSION);
if (mux.begin() == false)
{
Serial.println("Could not find MAX14661");
while(1);
}
test1();
test2();
test3();
test4();
test5();
}
void test1()
{
Serial.println("\nTEST 1");
for (int ch = 0; ch < 16; ch++)
{
mux.openChannel(ch);
Serial.println(mux.getChannels(), HEX);
}
mux.closeAllChannels();
Serial.println(mux.getChannels(), HEX);
}
void test2()
{
Serial.println("\nTEST 2");
for (int ch = 0; ch < 16; ch++)
{
mux.openChannel(ch);
Serial.println(mux.getChannels(), HEX);
mux.closeChannel(ch);
}
mux.closeAllChannels();
Serial.println(mux.getChannels(), HEX);
}
void test3()
{
Serial.println("\nTEST 3");
Serial.println(mux.getChannels(), HEX);
mux.openAllChannels();
mux.closeAllChannels();
Serial.println(mux.getChannels(), HEX);
}
void test4()
{
Serial.println("\nTEST 4");
Serial.println(mux.getChannels(), HEX);
mux.openAllChannels();
Serial.println(mux.getChannels(), HEX);
mux.closeAllChannels();
Serial.println(mux.getChannels(), HEX);
}
void test5()
{
Serial.println("\nTEST 5");
for (int i = 0; i < 10; i++)
{
uint16_t mask = random(65535);
mux.setChannels(mask);
Serial.println(mux.getChannels(), HEX);
}
mux.closeAllChannels();
Serial.println(mux.getChannels(), HEX);
}
void loop()
{
}
// -- END OF FILE --

View File

@ -0,0 +1,55 @@
//
// FILE: MAX14661_isConnected.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: test MAX14661 lib
// DATE: 2021-01-29
// URL: https://github.com/RobTillaart/MAX14661
//
// test behavior when device is not connected and reconnected again.
// E.g. simulate loose wires..
#include "Wire.h"
#include "MAX14661.h"
MAX14661 mux(0x4C); // 0x4C..0x4F
bool connected = false;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.println(MAX14661_LIB_VERSION);
if (mux.begin() == false)
{
Serial.println("Could not find MAX14661");
}
Serial.println("end of setup()");
}
void loop()
{
if (!connected && mux.isConnected())
{
connected = true;
Serial.print(millis());
Serial.println("\tconnected");
}
if (connected && !mux.isConnected())
{
connected = false;
}
delay(1000);
}
// -- END OF FILE --

View File

@ -0,0 +1,38 @@
# Syntax Coloring Map for MAX14661
# Datatypes (KEYWORD1)
MAX14661 KEYWORD1
# Methods and Functions (KEYWORD2)
begin KEYWORD2
isConnected KEYWORD2
openChannel KEYWORD2
closeChannel KEYWORD2
isOpenChannel KEYWORD2
openAllChannels KEYWORD2
closeAllChannels KEYWORD2
setChannels KEYWORD2
getChannels KEYWORD2
MUXA KEYWORD2
getMUXA KEYWORD2
MUXB KEYWORD2
getMUXB KEYWORD2
openA KEYWORD2
openB KEYWORD2
closeA KEYWORD2
closeB KEYWORD2
readRegister KEYWORD2
writeRegister KEYWORD2
lastError KEYWORD2
# Constants ( LITERAL1)
MAX14661_LIB_VERSION LITERAL1

View File

@ -0,0 +1,22 @@
{
"name": "MAX14661",
"keywords": "I2C, multiplexer",
"description": "Arduino library for MAX14661 16 channel I2C multiplexer",
"authors":
[
{
"name": "Rob Tillaart",
"email": "Rob.Tillaart@gmail.com",
"maintainer": true
}
],
"repository":
{
"type": "git",
"url": "https://github.com/RobTillaart/MAX14661.git"
},
"version":"0.1.0",
"license": "MIT",
"frameworks": "arduino",
"platforms": "*"
}

View File

@ -0,0 +1,11 @@
name=MAX14661
version=0.1.0
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Arduino library for MAX14661 16 channel I2C multiplexer
paragraph=
category=Signal Input/Output
url=https://github.com/RobTillaart/MAX14661.git
architectures=*
includes=MAX14661.h
depends=

View File

@ -0,0 +1,61 @@
//
// FILE: unit_test_001.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// DATE: 2021-06-02
// PURPOSE: unit tests for the MAX14661 multiplexer
// https://github.com/RobTillaart/MAX14661
// https://github.com/Arduino-CI/arduino_ci/blob/master/REFERENCE.md
//
// supported assertions
// ----------------------------
// assertEqual(expected, actual); // a == b
// assertNotEqual(unwanted, actual); // a != b
// assertComparativeEquivalent(expected, actual); // abs(a - b) == 0 or (!(a > b) && !(a < b))
// assertComparativeNotEquivalent(unwanted, actual); // abs(a - b) > 0 or ((a > b) || (a < b))
// assertLess(upperBound, actual); // a < b
// assertMore(lowerBound, actual); // a > b
// assertLessOrEqual(upperBound, actual); // a <= b
// assertMoreOrEqual(lowerBound, actual); // a >= b
// assertTrue(actual);
// assertFalse(actual);
// assertNull(actual);
// // special cases for floats
// assertEqualFloat(expected, actual, epsilon); // fabs(a - b) <= epsilon
// assertNotEqualFloat(unwanted, actual, epsilon); // fabs(a - b) >= epsilon
// assertInfinity(actual); // isinf(a)
// assertNotInfinity(actual); // !isinf(a)
// assertNAN(arg); // isnan(a)
// assertNotNAN(arg); // !isnan(a)
#include <ArduinoUnitTests.h>
#include "Arduino.h"
#include "MAX14661.h"
unittest_setup()
{
}
unittest_teardown()
{
}
unittest(test_constructor)
{
fprintf(stderr, "VERSION: %s\n", MAX14661_LIB_VERSION);
MAX14661 MUX(0x4C);
MUX.begin();
}
unittest_main()
// --------