mcp23017-promise
Version:
MCP23017 library with promises.
336 lines • 13 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const async_i2c_bus_1 = require("async-i2c-bus");
exports.A0 = 1 << 0;
exports.A1 = 1 << 1;
exports.A2 = 1 << 2;
exports.A3 = 1 << 3;
exports.A4 = 1 << 4;
exports.A5 = 1 << 5;
exports.A6 = 1 << 6;
exports.A7 = 1 << 7;
exports.B0 = 1 << 8;
exports.B1 = 1 << 9;
exports.B2 = 1 << 10;
exports.B3 = 1 << 11;
exports.B4 = 1 << 12;
exports.B5 = 1 << 13;
exports.B6 = 1 << 14;
exports.B7 = 1 << 15;
const PINS_A = 0xFF;
const PINS_B = 0xFF00;
/** Direction of the I/O pins */
var Direction;
(function (Direction) {
/** Output pin */
Direction[Direction["OUT"] = 0] = "OUT";
/** Input pin without pull-up */
Direction[Direction["IN"] = 1] = "IN";
/** Input pin with pull-up */
Direction[Direction["IN_UP"] = 2] = "IN_UP";
})(Direction = exports.Direction || (exports.Direction = {}));
/** Interupt mode */
var InteruptMode;
(function (InteruptMode) {
/** No interupt (used to reset) */
InteruptMode[InteruptMode["INT_None"] = 0] = "INT_None";
/** Interupt when not one */
InteruptMode[InteruptMode["INT_One"] = 1] = "INT_One";
/** Interupt when not zero */
InteruptMode[InteruptMode["INT_Zero"] = 2] = "INT_Zero";
/** Interupt on change */
InteruptMode[InteruptMode["INT_Change"] = 3] = "INT_Change";
})(InteruptMode = exports.InteruptMode || (exports.InteruptMode = {}));
/** Pin value */
var Level;
(function (Level) {
/** Low or 0 */
Level[Level["LOW"] = 0] = "LOW";
/** High or 1 */
Level[Level["HIGH"] = 1] = "HIGH";
})(Level = exports.Level || (exports.Level = {}));
exports.IOCON_INTPOL = 1 << 1;
exports.IOCON_ODR = 1 << 2;
exports.IOCON_HAEN = 1 << 3;
exports.IOCON_DISSLW = 1 << 4;
exports.IOCON_SEQOP = 1 << 5;
exports.IOCON_MIRROR = 1 << 6;
exports.IOCON_BANK = 1 << 7;
const bankSeparate = {
DEFVAL: { A: 0x03, B: 0x13 },
GPINTEN: { A: 0x02, B: 0x12 },
GPIO: { A: 0x09, B: 0x19 },
GPPU: { A: 0x06, B: 0x16 },
INTCAP: { A: 0x08, B: 0x18 },
INTCON: { A: 0x04, B: 0x14 },
INTF: { A: 0x07, B: 0x17 },
IOCON: 0x05,
IODIR: { A: 0x00, B: 0x10 },
IPOL: { A: 0x01, B: 0x11 },
OLAT: { A: 0x0A, B: 0x1A },
};
const bankInterleaved = {
DEFVAL: { A: 0x06, B: 0x07 },
GPINTEN: { A: 0x04, B: 0x05 },
GPIO: { A: 0x12, B: 0x13 },
GPPU: { A: 0x0C, B: 0x0D },
INTCAP: { A: 0x10, B: 0x11 },
INTCON: { A: 0x08, B: 0x09 },
INTF: { A: 0x0E, B: 0x0F },
IOCON: 0x0A,
IODIR: { A: 0x00, B: 0x01 },
IPOL: { A: 0x02, B: 0x03 },
OLAT: { A: 0x14, B: 0x15 },
};
/** This class drives a single MCP23017 GPIO extender */
class MCP23017 {
/** Constructor.
*
* @param bus an instance of i2c bus. May be closed at this point
* @param options options for the driver.
*/
constructor(bus, options = {}) {
this.device = async_i2c_bus_1.Device({ bus, address: options.address || 0x20 });
this.separate = options.separate || false;
this.bank = this.separate ? bankSeparate : bankInterleaved;
this.cache = Buffer.allocUnsafe(0);
}
/** Initialize the class
*
* Must be called with an open bus
*/
init() {
return __awaiter(this, void 0, void 0, function* () {
this.cache = yield this.initCache();
});
}
/** Reads the configuration byte of the chip
* @return the value of the configuration as a byte in a promise.
*/
readConfigByte() {
return this.device.readByte(this.bank.IOCON);
}
/** Change the way register banks are organize.
* @param separate set to true for registers organized as two
* independent banks and false for interleaved (default)
* @return a promisse fullfilled when the operation is performed.
*/
setSeparate(separate) {
return __awaiter(this, void 0, void 0, function* () {
const iocon = this.bank.IOCON;
if (this.separate !== separate) {
this.separate = separate;
this.bank = this.separate ? bankSeparate : bankInterleaved;
const ioconVal = yield this.device.readByte(iocon);
yield this.writeByte(iocon, ioconVal ^ exports.IOCON_BANK);
this.cache = yield this.initCache();
}
});
}
/** Sets the direction of a set of GPI/O pins and eventually pull-up status.
* @param pins: the set of pins (number used as a bit set)
* @param direction: the direction of the pin (can be IN OUT or IN_UP)
* @return a promisse fullfilled when the operation is performed.
*/
setDirection(pins, direction) {
return __awaiter(this, void 0, void 0, function* () {
const iodir = this.readCache(this.bank.IODIR);
const newIodir = direction === Direction.OUT ? iodir & ~pins : iodir | pins;
const changesIodir = newIodir ^ iodir;
yield this.writeBanks(newIodir, changesIodir, this.bank.IODIR);
if (direction !== Direction.OUT) {
const pullup = this.readCache(this.bank.GPPU);
const newPullup = (direction === Direction.IN_UP ? pullup
| pins : pullup & ~pins);
const changesPullup = pullup ^ newPullup;
yield this.writeBanks(newPullup, changesPullup, this.bank.GPPU);
}
});
}
/** Set the polarity of the output (ie XOR with it)
* @param polarity of each pin as a bit array
*/
setPolarity(pins) {
return __awaiter(this, void 0, void 0, function* () {
const ipol = this.readCache(this.bank.IPOL);
const changes = ipol ^ pins;
yield this.writeBanks(pins, changes, this.bank.IPOL);
});
}
/** Reads the GPIO
* @return the value of the GPIO Pin in a promise.
*/
read() {
return __awaiter(this, void 0, void 0, function* () {
return yield this.readBanks(true, true, this.bank.GPIO);
});
}
/** Modify the GPIO
* @param set: bits to set as a bit field
* @param unset: bits to set as a bit field
*/
write(set, unset) {
return __awaiter(this, void 0, void 0, function* () {
const gpio = this.readCache(this.bank.OLAT);
const newGpio = (gpio | set) & ~unset;
const changesGpio = gpio ^ newGpio;
yield this.writeBanks(newGpio, changesGpio, this.bank.OLAT);
});
}
/** Register changes to the interupt handling
* @param intDefs: a list of interupt handling description that
* will be (dis)activated
* @param handler: an optional interupt handler that was in place
* @return the new interupt handler.
*/
register(intDefs, handler = {}) {
return __awaiter(this, void 0, void 0, function* () {
function addDef(intState, intDef) {
switch (intDef.mode) {
case InteruptMode.INT_None:
intState.inten &= ~intDef.pin;
delete intState.handler[intDef.pin];
break;
case InteruptMode.INT_Change:
intState.inten |= intDef.pin;
intState.intcon &= ~intDef.pin;
if (intDef.callback) {
intState.handler[intDef.pin] = intDef.callback;
}
break;
case InteruptMode.INT_Zero:
intState.inten |= intDef.pin;
intState.intcon |= intDef.pin;
intState.defval &= ~intDef.pin;
if (intDef.callback) {
intState.handler[intDef.pin] = intDef.callback;
}
break;
case InteruptMode.INT_One:
intState.inten |= intDef.pin;
intState.intcon |= intDef.pin;
intState.defval |= intDef.pin;
if (intDef.callback) {
intState.handler[intDef.pin] = intDef.callback;
}
break;
}
return intState;
}
const inten = this.readCache(this.bank.GPINTEN);
const intcon = this.readCache(this.bank.INTCON);
const defval = this.readCache(this.bank.DEFVAL);
const state = intDefs.reduce(addDef, { inten, intcon, defval, handler });
yield this.writeBanks(state.defval, defval ^ state.defval, this.bank.DEFVAL);
yield this.writeBanks(state.defval, intcon ^ state.intcon, this.bank.INTCON);
yield this.writeBanks(state.defval, inten ^ state.inten, this.bank.GPINTEN);
return state.handler;
});
}
/** Handle interupts received.
* @param handler: an interupt handler built during the registration.
*/
handle(handler) {
return __awaiter(this, void 0, void 0, function* () {
const intf = yield this.readBanks(true, true, this.bank.INTF);
const intcap = yield this.readBanks(true, true, this.bank.INTCAP);
let mask = 1;
for (let pin = 0; pin < 16; pin++) {
if (mask & intf) {
const callback = handler[pin];
if (callback) {
callback(mask & intcap ? Level.HIGH : Level.LOW);
}
}
mask <<= 1;
}
});
}
readCache(regs) {
return this.cache[regs.A] | (this.cache[regs.B] << 8);
}
initCache() {
return __awaiter(this, void 0, void 0, function* () {
const buffer = Buffer.alloc(0x1B);
if (this.separate) {
const bufA = yield this.readBuffer(0, 0xA);
const bufB = yield this.readBuffer(0x10, 0xA);
bufA.copy(buffer, 0);
bufB.copy(buffer, 0x10);
}
else {
const buf = yield this.readBuffer(0, 0x16);
buf.copy(buffer, 0);
}
return buffer;
});
}
readBuffer(register, len) {
return __awaiter(this, void 0, void 0, function* () {
const buffer = Buffer.alloc(len);
const lenRes = yield this.device.readI2cBlock(register, len, buffer);
if (lenRes !== len) {
throw new Error("Bad length");
}
return buffer;
});
}
writeByte(register, byte) {
return __awaiter(this, void 0, void 0, function* () {
yield this.device.writeByte(register, byte);
this.cache[register] = byte;
});
}
writeWord(register, word) {
return __awaiter(this, void 0, void 0, function* () {
yield this.device.writeWord(register, word);
this.cache[register] = word & 0xff;
this.cache[register + 1] = (word >> 8) & 0xff;
});
}
writeBanks(pins, changes, regs) {
return __awaiter(this, void 0, void 0, function* () {
const needsA = (changes & PINS_A) !== 0;
const needsB = (changes & PINS_B) !== 0;
if (this.separate) {
if (needsA) {
yield this.writeByte(regs.A, pins & PINS_A);
}
if (needsB) {
yield this.writeByte(regs.B, (pins & PINS_B) >> 8);
}
}
else {
yield this.writeWord(regs.A, pins & 0xFFFF);
}
});
}
readBanks(needsA, needsB, regs) {
return __awaiter(this, void 0, void 0, function* () {
if (this.separate) {
let valA = 0;
if (needsA) {
valA = yield this.device.readByte(regs.A);
}
let valB = 0;
if (needsB) {
valB = yield this.device.readByte(regs.B);
}
return valA | (valB << 8);
}
else {
return yield this.device.readWord(regs.A);
}
});
}
}
exports.MCP23017 = MCP23017;
//# sourceMappingURL=index.js.map