2017-01-12 20:05:20 -08:00
|
|
|
'use strict'
|
2017-01-16 21:41:42 -08:00
|
|
|
const Device = require('uci-dev').Device,
|
2017-01-13 19:51:55 -08:00
|
|
|
Port = require('uci-gpio').Port,
|
2017-01-12 20:05:20 -08:00
|
|
|
_u = require('uci-utils')
|
|
|
|
|
|
|
|
class MCP23008 extends Device {
|
2017-01-18 20:31:53 -08:00
|
|
|
constructor(busObj, i2cAddress, opts = {}) {
|
2017-01-12 20:05:20 -08:00
|
|
|
super(busObj, i2cAddress, opts)
|
|
|
|
// opts could pass in array of custom pin config, or single for all, or anything
|
|
|
|
// this.registers = registers // load in register database
|
|
|
|
this.ports = {}
|
|
|
|
opts.portID = 'A'
|
|
|
|
this.ports.A = new Port(opts)
|
2017-01-18 20:31:53 -08:00
|
|
|
this.chip_config = opts.chip_config
|
2017-01-21 12:22:18 -08:00
|
|
|
this.ports.A.interrupt = opts.interruptA ? opts.interruptA : opts.interrupt
|
|
|
|
|
2017-01-12 20:05:20 -08:00
|
|
|
} // end constructor
|
|
|
|
|
|
|
|
init() {
|
2017-01-16 16:26:28 -08:00
|
|
|
// console.log('chip configuration', chip_config.cmd, chipSetByte())
|
|
|
|
// console.log(super.write.toString())
|
2017-01-21 12:22:18 -08:00
|
|
|
let jobs = [super.write(chip_config.cmd, chipSetByte(this.chip_config))] // configure chip
|
|
|
|
for (let port in this.ports) {
|
|
|
|
if (this.ports[port].interrupt) { jobs.push(this.ports[port].interrupt.init()) }
|
|
|
|
}
|
|
|
|
jobs.push(this.writePinsCfg())
|
|
|
|
return _u.pSeries(jobs)
|
2017-01-12 20:05:20 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
pin(id) { return this.ports.A.pin(id) } // get a reference to a particular pin's object
|
|
|
|
|
2017-01-21 12:22:18 -08:00
|
|
|
interrupt(port = 'A') { return this.ports[port].interrupt }
|
|
|
|
|
2017-01-12 20:05:20 -08:00
|
|
|
// pin configurations should already be set before calling
|
|
|
|
writePinsCfg() {
|
|
|
|
let jobs = [];
|
|
|
|
for (let port in this.ports) {
|
|
|
|
for (let setting in registers.pin_config) {
|
|
|
|
let reg = registers.pin_config[setting]
|
2017-01-18 20:31:53 -08:00
|
|
|
if (port === 'B') { reg = reg + 0x10 } // TODO 0x10 should be based on chip config
|
2017-01-12 20:05:20 -08:00
|
|
|
let byte = 0;
|
|
|
|
let pins = this.ports[port].allPins
|
|
|
|
for (let i = 0; i < 8; i++) {
|
|
|
|
let pin = pins[i]
|
|
|
|
byte += pin.address.toFmt('DEC') * pin.cfg[setting]
|
|
|
|
}
|
2017-01-16 16:26:28 -08:00
|
|
|
// console.log(`port ${port} - setting ${setting} - reg ${reg} - byte ${byte}`)
|
2017-01-12 20:05:20 -08:00
|
|
|
jobs.push(
|
2017-01-21 12:22:18 -08:00
|
|
|
super.write(reg, byte) //.then(() => console.log(`done writing config`))
|
2017-01-12 20:05:20 -08:00
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
2017-01-16 21:41:42 -08:00
|
|
|
return _u.pSeries(jobs)
|
2017-01-12 20:05:20 -08:00
|
|
|
} // end writePinsCfg
|
|
|
|
|
2017-01-15 17:59:50 -08:00
|
|
|
// reads all pins in all ports
|
2017-01-16 16:26:28 -08:00
|
|
|
readPins(cmd, fmt) {
|
2017-01-15 17:59:50 -08:00
|
|
|
let jobs = []
|
|
|
|
cmd = cmd ? cmd : 'gpio'
|
|
|
|
let reg = registers.pin_cmd[cmd]
|
|
|
|
for (let port in this.ports) {
|
|
|
|
jobs.push(() => super.read(reg))
|
|
|
|
reg += 0x10
|
|
|
|
}
|
2017-01-16 21:41:42 -08:00
|
|
|
return _u.pSeries(jobs)
|
2017-01-15 17:59:50 -08:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-01-12 20:05:20 -08:00
|
|
|
pinRead(id, opts) {
|
2017-01-15 17:59:50 -08:00
|
|
|
let mcpdev = this;
|
|
|
|
return new Promise(function (resolve, reject) {
|
|
|
|
let cmd = opts.cmd ? opts.cmd : 'gpio'
|
|
|
|
let fmt = opts.fmt ? opts.fmt : { in: 'PLC', out: 'PLC' }
|
|
|
|
let reg = registers.pin_cmd[cmd]
|
|
|
|
let gpio = mcpdev.pin(id)
|
|
|
|
if (!gpio) {
|
|
|
|
reject('no pin found for given id')
|
|
|
|
}
|
|
|
|
if (gpio.port === 'B') {
|
|
|
|
reg = reg + 16
|
|
|
|
}
|
|
|
|
// call device class read (response is always decimal)
|
|
|
|
console.log('port - reg', gpio.port, reg)
|
|
|
|
return mcpdev.read(reg).then(resp => {
|
|
|
|
resp = _u.byteFormat(resp, { in: 'DEC',
|
|
|
|
out: 'ARY'
|
|
|
|
})
|
|
|
|
let addr = gpio.pin.address.toFmt('ARY')
|
|
|
|
console.log(addr)
|
|
|
|
console.log(resp)
|
|
|
|
resolve(_u.sum(_u.and(addr, resp))) // resolve 1 or 0
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.then(state => console.log(`pin state ${state}`))
|
|
|
|
.catch(err => console.log(err)) // end Promise
|
|
|
|
} // end pinRead
|
|
|
|
|
|
|
|
pinWrite(id, opts) {
|
2017-01-12 20:05:20 -08:00
|
|
|
let mcpdev = this;
|
|
|
|
return new Promise(function (resolve, reject) {
|
|
|
|
let cmd = opts.cmd ? opts.cmd : 'gpio'
|
|
|
|
let reg = registers.pin_cmd[cmd]
|
|
|
|
let gpio = mcpdev.pin(id, opts.port)
|
|
|
|
if (!gpio) {
|
|
|
|
reject('no pin found for given id')
|
|
|
|
}
|
|
|
|
if (gpio.port === 'B') {
|
|
|
|
reg = reg + 16
|
|
|
|
}
|
|
|
|
// call device class read
|
|
|
|
console.log('port - reg', gpio.port, reg)
|
|
|
|
return mcpdev.read(reg).then(resp => {
|
|
|
|
resp = _u.byteFormat(resp, { in: 'DEC',
|
|
|
|
out: 'ARY'
|
|
|
|
})
|
|
|
|
let addr = gpio.pin.address.toFmt('ARY')
|
|
|
|
console.log(addr)
|
|
|
|
console.log(resp)
|
|
|
|
resolve(_u.sum(_u.and(addr, resp))) // resolve 1 or 0
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.then(state => console.log(`pin state ${state}`))
|
|
|
|
.catch(err => console.log(err)) // end Promise
|
|
|
|
} // end pinRead
|
|
|
|
|
|
|
|
} // end 23008
|
|
|
|
module.exports.MCP23008 = MCP23008
|
|
|
|
|
|
|
|
class MCP23017 extends MCP23008 {
|
|
|
|
constructor(busObj, i2cAddress, opts) {
|
|
|
|
super(busObj, i2cAddress, opts)
|
|
|
|
// add a second port
|
|
|
|
opts.portID = 'B'
|
|
|
|
this.ports.B = new Port(opts)
|
2017-01-21 12:22:18 -08:00
|
|
|
this.ports.B.interrupt = opts.interruptB ? opts.interruptB : opts.interrupt
|
|
|
|
|
2017-01-12 20:05:20 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
pin(id, port) {
|
|
|
|
if (!port) {
|
|
|
|
return this.ports.A.pin(id) ? this.ports.A.pin(id) : this.ports.B.pin(id)
|
|
|
|
}
|
|
|
|
return this.ports[port].pin(id)
|
|
|
|
}
|
|
|
|
|
|
|
|
} // end MCP23017 Class
|
|
|
|
module.exports.MCP23017 = MCP23017
|
|
|
|
|
|
|
|
/* ww1.microchip.com/downloads/en/DeviceDoc/21952b.pdf
|
|
|
|
* or see MCP23017.pdf and MCP 23008.pdf in /docs
|
|
|
|
* see table 1.5 and details in sections 1.6.x following
|
|
|
|
* !!! for 23017 MUST initialize with bit 7 "BANK" of IOCON = 1 for the addresses below
|
|
|
|
* then for all registers add 16 (0x10) to each reg address to talk to PortB pins
|
|
|
|
* this will make reg addresses be equilvant for 23008 and PortA of 23017
|
|
|
|
* reg addresses in the config objects are all in Hexidecminal
|
|
|
|
*/
|
2017-01-18 20:31:53 -08:00
|
|
|
// Chip Configuration to be used with Register See Page 18 of 23017 doc
|
2017-01-12 20:05:20 -08:00
|
|
|
let chip_config = {
|
|
|
|
// byte: ['NULL','INTPOL','ODR','HAEN','DISSLW','SEQOP','MIRROR','BANK'] // see page 18 of 23017 datasheet for 8 setting details
|
2017-01-18 20:31:53 -08:00
|
|
|
cmd: 0x0A, // IOCON.BANK=0 (msb) at powerup so need to use 0x0A, if set to 1 then use
|
2017-01-12 20:05:20 -08:00
|
|
|
default: {
|
2017-01-18 20:31:53 -08:00
|
|
|
val: '10000000', // int pins not connected, port A + 0x10 = Port B -- ignored by 23008
|
2017-01-12 20:05:20 -08:00
|
|
|
fmt: 'STR'
|
|
|
|
},
|
2017-01-18 20:31:53 -08:00
|
|
|
oneint: {
|
|
|
|
val: '11000000', // int pins connected, port A + 0x10 = Port B -- ignored by 23008
|
2017-01-12 20:05:20 -08:00
|
|
|
fmt: 'STR'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function chipSetByte(setting = 'default') {
|
|
|
|
setting = chip_config[setting]
|
|
|
|
return _u.byteFormat(setting.val, { in: setting.fmt,
|
|
|
|
out: 'DEC'
|
|
|
|
})
|
|
|
|
}
|
|
|
|
let registers = {
|
|
|
|
pin_config: {
|
|
|
|
dir: 0,
|
|
|
|
ivrt: 1,
|
|
|
|
pullup: 6,
|
|
|
|
intr: 2,
|
|
|
|
usedef: 4,
|
|
|
|
defval: 3,
|
|
|
|
},
|
|
|
|
pin_cmd: {
|
|
|
|
intf: 7, // readonly
|
|
|
|
intcap: 8, // readonly
|
|
|
|
gpio: 9, // read/write
|
|
|
|
olat: 10 // read only
|
|
|
|
}
|
|
|
|
}
|