Last active
December 17, 2015 13:29
-
-
Save marcboon/5617706 to your computer and use it in GitHub Desktop.
Encapsulates MCP23008 and MCP23017 family of I2C i/o expander for use with electric imp. Emulates the standard Pin class, allows easy moving of i/o from native pins to i/o expander pins.
After creating one or more MCP23008 and/or MCP23017 class instances, user code should only access member functions of the MCP230xxPin class. See example at the …
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Base class for MCP23008 and MCP23017 family of I2C i/o expanders | |
class MCP230xx { | |
BASE_ADDR = 0x20 | |
i2cPort = null | |
i2cAddr = null | |
regs = null | |
constructor(i2cPort, deviceAddr) { | |
this.i2cPort = i2cPort | |
this.i2cAddr = (BASE_ADDR + deviceAddr) << 1 | |
} | |
// Read a byte | |
function read(reg) { | |
local data = i2cPort.read(i2cAddr, format("%c", reg), 1) | |
if(data == null) { | |
server.log("I2C Read Failure") | |
return -1 | |
} | |
return data[0] | |
} | |
// Write a byte | |
function write(reg, data) { | |
i2cPort.write(i2cAddr, format("%c%c", reg, data)) | |
} | |
// Set/clear a bit in a register | |
function writeBit(reg, bitn, level) { | |
local value = read(reg) | |
value = (level == 0) ? (value & ~(1 << bitn)) : (value | (1 << bitn)) | |
write(reg, value) | |
} | |
function setValueForRegister(gpio, reg, value) { | |
writeBit(regs[reg], gpio & 7, value) | |
} | |
function getValueForRegister(gpio, reg) { | |
return (read(regs[reg]) & (1 << (gpio & 7))) ? 1 : 0 | |
} | |
function setDir(gpio, input) { | |
setValueForRegister(gpio, "IODIR", input ? 1 : 0) | |
} | |
function setPullUp(gpio, pull_up) { | |
setValueForRegister(gpio, "GPPU", pull_up ? 1 : 0) | |
} | |
function setPin(gpio, level) { | |
setValueForRegister(gpio, "GPIO", level ? 1 : 0) | |
} | |
function getPin(gpio) { | |
return getValueForRegister(gpio, "GPIO") | |
} | |
} | |
// This class is compatible with the general Pin class | |
class MCP230xxPin { | |
device = null | |
gpio = null | |
regs = null | |
constructor(device, gpio, regs) { | |
this.device = device | |
this.gpio = gpio | |
this.regs = regs | |
} | |
function configure(mode) { | |
device.regs = regs | |
switch(mode) { | |
case DIGITAL_IN: | |
device.setDir(gpio, 1) | |
device.setPullUp(gpio, 0) | |
break | |
case DIGITAL_IN_PULLUP: | |
device.setDir(gpio, 1) | |
device.setPullUp(gpio, 1) | |
break | |
case DIGITAL_OUT: | |
device.setDir(gpio, 0) | |
device.setPullUp(gpio, 0) | |
break | |
default: | |
server.log("MCP230xxPin: Invalid mode") | |
} | |
} | |
function read() { | |
device.regs = regs | |
return device.getPin(gpio) | |
} | |
function write(level) { | |
device.regs = regs | |
device.setPin(gpio, level) | |
} | |
} | |
// Encapsulates a MCP23008 I2C i/o expander | |
class MCP23008 extends MCP230xx { | |
REGS = { | |
IODIR = 0x00 | |
IOPOL = 0x01 | |
GPINTEN = 0x02 | |
DEFVAL = 0x03 | |
INTCON = 0x04 | |
IOCON = 0x05 | |
GPPU = 0x06 | |
INTF = 0x07 | |
INTCAP = 0x08 | |
GPIO = 0x09 | |
OLAT = 0x0A | |
} | |
pin1 = null | |
pin2 = null | |
pin3 = null | |
pin4 = null | |
pin5 = null | |
pin6 = null | |
pin7 = null | |
pin8 = null | |
constructor(i2cPort, deviceAddr) { | |
base.constructor(i2cPort, deviceAddr) | |
for(local gpio = 1; gpio <= 8; gpio++) { | |
this["pin" + gpio] = MCP230xxPin(this, gpio - 1, REGS) | |
} | |
} | |
} | |
// Encapsulates a MCP23017 I2C i/o expander | |
class MCP23017 extends MCP230xx { | |
A = { | |
REGS = { | |
IODIR = 0x00 | |
IOPOL = 0x02 | |
GPINTEN = 0x04 | |
DEFVAL = 0x06 | |
INTCON = 0x08 | |
IOCON = 0x0A | |
GPPU = 0x0C | |
INTF = 0x0E | |
INTCAP = 0x10 | |
GPIO = 0x12 | |
OLAT = 0x14 | |
} | |
pin1 = null | |
pin2 = null | |
pin3 = null | |
pin4 = null | |
pin5 = null | |
pin6 = null | |
pin7 = null | |
pin8 = null | |
} | |
B = { | |
REGS = { | |
IODIR = 0x01 | |
IOPOL = 0x03 | |
GPINTEN = 0x05 | |
DEFVAL = 0x07 | |
INTCON = 0x09 | |
IOCON = 0x0B | |
GPPU = 0x0D | |
INTF = 0x0F | |
INTCAP = 0x11 | |
GPIO = 0x13 | |
OLAT = 0x15 | |
} | |
pin1 = null | |
pin2 = null | |
pin3 = null | |
pin4 = null | |
pin5 = null | |
pin6 = null | |
pin7 = null | |
pin8 = null | |
} | |
constructor(i2cPort, deviceAddr) { | |
base.constructor(i2cPort, deviceAddr) | |
for(local gpio = 1; gpio <= 8; gpio++) { | |
A["pin" + gpio] = MCP230xxPin(this, gpio - 1, A.REGS) | |
B["pin" + gpio] = MCP230xxPin(this, gpio - 1, B.REGS) | |
} | |
} | |
} | |
// Example using two MCP23008 and one MCP23017 on the same I2C bus (using pins 8,9) | |
imp.configure("MCP230xx Demo", [], []) | |
// Configure I2C bus | |
hardware.i2c89.configure(CLOCK_SPEED_100_KHZ) | |
// Create i/o port instances (note: each device on the same bus should have a different device address) | |
port1 <- MCP23008(hardware.i2c89, 0) // pinstrapped to device address 0 | |
port2 <- MCP23008(hardware.i2c89, 1) // pinstrapped to device address 1 | |
port3 <- MCP23017(hardware.i2c89, 2) // pinstrapped to device address 2 | |
// Assign leds to port pins | |
led1 <- port1.pin1 // MCP23008 has pin1 to pin8 | |
led2 <- port2.pin1 // using same pin on the second MCP23008 | |
led3 <- port3.A.pin1 // port3 is a MCP23017, which has A and B ports, both having pin1 to pin8 | |
led4 <- port3.B.pin1 // using same pin on port B | |
// Configure pins as output | |
led1.configure(DIGITAL_OUT) | |
led2.configure(DIGITAL_OUT) | |
led3.configure(DIGITAL_OUT) | |
led4.configure(DIGITAL_OUT) | |
// Demo: running lights | |
function run1() { | |
imp.wakeup(0.1, run2) | |
led1.write(1 - led1.read()) | |
} | |
function run2() { | |
imp.wakeup(0.1, run3) | |
led2.write(1 - led2.read()) | |
} | |
function run3() { | |
imp.wakeup(0.1, run4) | |
led3.write(1 - led3.read()) | |
} | |
function run4() { | |
imp.wakeup(0.1, run1) | |
led4.write(1 - led4.read()) | |
} | |
// Start | |
run1() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment