uci-mcp/lib/mcp23008-17.js

246 lines
7.8 KiB
JavaScript

'use strict'
const Device = require('uci-dev').Device,
Port = require('uci-gpio').Port,
_u = require('uci-utils')
class MCP23008 extends Device {
constructor(busObj, i2cAddress, opts = {}) {
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'
opts.pids = opts.pids ? opts.pids : opts.pidsA
this.ports.A = new Port(opts)
this.chip_config = opts.chip_config // TODO allow opts.chip_config to be a byte instead of a string pointer
this.ports.A.interrupt = opts.interruptA ? opts.interruptA : opts.interrupt
if (this.ports.A.interrupt) { this.ports.A.interrupt.hook = 'A' }
} // end constructor
init() {
// console.log('chip configuration', chip_config.cmd, chipSetByte())
// console.log(super.write.toString())
let jobs = [this.writeChipCfg(this.chip_config)] // configure chip
for (let port in this.ports) {
if (this.inter(port)) {
// console.log('interrupt init', this.inter(port).init().toString())
// console.log('interrupt', this.inter(port).init)
jobs.push(this.inter(port).init()) // set up listeners if interrupts exits
// jobs.push(this.inter(port).init.bind(this.inter(port))())
}
}
jobs.push(this.writePinsCfg())
return _u.pSeries(jobs)
}
start() {
let starts = []
for (let port in this.ports) {
if (this.inter(port)) {
starts.push(
this.read(portReg(0x08, port))
.then(_u.pDelay(100)) // give enough time for mcp to reset its interupt
.then(() => {
return this.inter(port).start()
.then(() => {
return Promise.resolve(`interrupt port ${port} started`)
})
})
)
}
}
return Promise.all(starts)
}
pin(id) { return this.ports.A.pin(id) } // get a reference to a particular pin's object
pid(address) { return this.ports.A.pid(address) } // return pin id for a given address on a port
portByPin(id) {
if (this.ports.A.pin(id)) { return 'A' }
return false
}
inter(port = 'A') {
return this.ports[port].interrupt
}
writeChipCfg(cfg = 'default') {
let setting = chip_config[cfg]
let byte = _u.byteFormat(setting.val, { in: setting.fmt,
out: 'DEC'
})
return super.write(chip_config.cmd, byte)
.then(() => Promise.resolve(`mcp chip config: reg ${chip_config.cmd} byte ${byte} written`))
}
// 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]
if (port === 'B') { reg = reg + 0x10 } // TODO 0x10 should be based on chip config
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]
}
//console.log(`port ${port} - setting ${setting} - reg ${reg} - byte ${byte}`)
jobs.push(
super.write(reg, byte).then(() => Promise.resolve(`config: wrote ${byte} to register ${reg} on port ${port}`))
)
}
}
return _u.pSeries(jobs)
} // end writePinsCfg
// reads all pins in all ports
readPins(cmd = 'gpio', fmt) {
let jobs = []
let reg = registers.pin_cmd[cmd]
for (let port in this.ports) {
jobs.push(() => super.read(reg))
reg += 0x10
}
return _u.pSeries(jobs)
}
pinRead(id, opts) {
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')
}
console.log('id of fired pin', id)
console.log('port of fired pin', mcpdev.portByPin(id))
return mcpdev.read(portReg(reg, mcpdev.portByPin(id))).then(resp => {
resp = _u.byteFormat(resp, { in: 'DEC',
out: 'ARY'
})
let addr = gpio.adr.toFmt('ARY')
// console.log(addr)
// console.log(resp)
resolve(_u.sum(_u.and(addr, resp))) // resolve 1 or 0
}).catch(err => reject(err)) // end Promise
}) // end promise
} // end pinRead
pinWrite(id, opts) {
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'
opts.pids = opts.pidsB
this.ports.B = new Port(opts)
this.ports.B.interrupt = opts.interruptB ? opts.interruptB : opts.interrupt
if (this.ports.B.interrupt) { this.ports.B.interrupt.hook = 'B' }
}
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)
}
pid(address, port) {
if (!port) {
return this.ports.A.pid(address)
}
return this.ports[port].pid(address)
}
portByPin(id) {
console.log('pin id in portbypin', id)
if (this.ports.A.pin(id)) { return 'A' }
if (this.ports.B.pin(id)) { return 'B' }
return false
}
} // end MCP23017 Class
module.exports.MCP23017 = MCP23017
function portReg(reg, port) {
if (port === 'B') { return reg += 0x10 } else { return reg }
}
/* 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
*/
// Chip Configuration to be used with Register See Page 18 of 23017 doc
let chip_config = {
// byte: ['NULL','INTPOL','ODR','HAEN','DISSLW','SEQOP','MIRROR','BANK'] // see page 18 of 23017 datasheet for 8 setting details
cmd: 0x0A, // IOCON.BANK=0 (msb) at powerup so need to use 0x0A, if set to 1 then use
default: {
val: '10100010', // Split Banks port A + 0x10 = Port B,(ignored by 23008), Sequential operation disabled, active high=pulldown
fmt: 'STR'
},
oneint: {
val: '11100100', // same as default execpt int pins connected
fmt: 'STR'
}
}
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
}
}