|
@@ -0,0 +1,185 @@
|
|
|
+--[[
|
|
|
+ This Lua module provides access to the MCP23017 module.
|
|
|
+
|
|
|
+ The MCP23017 is a port expander and provides 16 channels for inputs and outputs.
|
|
|
+ Up to 8 devices (128 channels) are possible by the configurable address (A0 - A2 Pins).
|
|
|
+
|
|
|
+ Due to the 16 channels, 2 bytes are required for switching outputs or reading input signals. These are A and B.
|
|
|
+ A single pin can be set or a whole byte.
|
|
|
+
|
|
|
+ The numbering of the individual pins starts at 0 and ends with 7.
|
|
|
+ The numbers are for each register GPIO A and GPIO B.
|
|
|
+
|
|
|
+ The module requires `i2c` and `bit` C module built into firmware.
|
|
|
+
|
|
|
+ @author Marcel P. | Plomi.net
|
|
|
+ @github https://github.com/plomi-net
|
|
|
+ @version 1.0.0
|
|
|
+]]
|
|
|
+
|
|
|
+local i2c, string, issetBit, setBit, clearBit, error =
|
|
|
+ i2c, string, bit.isset, bit.set, bit.clear, error
|
|
|
+local isINPUT, isGPB, isHIGH = true, true, true
|
|
|
+
|
|
|
+-- registers (not used registers are commented out)
|
|
|
+local MCP23017_IODIRA = 0x00
|
|
|
+local MCP23017_IODIRB = 0x01
|
|
|
+local MCP23017_DEFVALA = 0x06
|
|
|
+local MCP23017_DEFVALB = 0x07
|
|
|
+local MCP23017_GPIOA = 0x12
|
|
|
+local MCP23017_GPIOB = 0x13
|
|
|
+--[[
|
|
|
+local MCP23017_IPOLA = 0x02
|
|
|
+local MCP23017_IPOLB = 0x03
|
|
|
+local MCP23017_GPINTENA = 0x04
|
|
|
+local MCP23017_GPINTENB = 0x05
|
|
|
+local MCP23017_DEFVALA = 0x06
|
|
|
+local MCP23017_DEFVALB = 0x07
|
|
|
+local MCP23017_INTCONA = 0x08
|
|
|
+local MCP23017_INTCONB = 0x09
|
|
|
+local MCP23017_IOCON = 0x0A
|
|
|
+local MCP23017_IOCON2 = 0x0B
|
|
|
+local MCP23017_GPPUA = 0x0C
|
|
|
+local MCP23017_GPPUB = 0x0D
|
|
|
+local MCP23017_INTFA = 0x0E
|
|
|
+local MCP23017_INTFB = 0x0F
|
|
|
+local MCP23017_INTCAPA = 0x10
|
|
|
+local MCP23017_INTCAPB = 0x11
|
|
|
+local MCP23017_OLATA = 0x14
|
|
|
+local MCP23017_OLATB = 0x15
|
|
|
+]]
|
|
|
+
|
|
|
+-- metatable
|
|
|
+local mcp23017 = {
|
|
|
+ INPUT = isINPUT,
|
|
|
+ OUTPUT = not isINPUT,
|
|
|
+ GPA = not isGPB,
|
|
|
+ GPB = isGPB,
|
|
|
+ HIGH = isHIGH,
|
|
|
+ LOW = not isHIGH
|
|
|
+}
|
|
|
+mcp23017.__index = mcp23017
|
|
|
+
|
|
|
+-- check device is available on address
|
|
|
+local function checkDevice(address, i2cId)
|
|
|
+ i2c.start(i2cId)
|
|
|
+ local response = i2c.address(i2cId, address, i2c.TRANSMITTER)
|
|
|
+ i2c.stop(i2cId)
|
|
|
+ return response
|
|
|
+end
|
|
|
+
|
|
|
+-- write byte
|
|
|
+local function writeByte(address, i2cId, registerAddr, val)
|
|
|
+ i2c.start(i2cId)
|
|
|
+ i2c.address(i2cId, address, i2c.TRANSMITTER)
|
|
|
+ i2c.write(i2cId, registerAddr)
|
|
|
+ i2c.write(i2cId, val)
|
|
|
+ i2c.stop(i2cId)
|
|
|
+end
|
|
|
+
|
|
|
+-- read byte
|
|
|
+local function readByte(address, i2cId, registerAddr)
|
|
|
+ i2c.start(i2cId)
|
|
|
+ i2c.address(i2cId, address, i2c.TRANSMITTER)
|
|
|
+ i2c.write(i2cId, registerAddr)
|
|
|
+ i2c.stop(i2cId)
|
|
|
+ i2c.start(i2cId)
|
|
|
+ i2c.address(i2cId, address, i2c.RECEIVER)
|
|
|
+ local data = i2c.read(i2cId, 1)
|
|
|
+ i2c.stop(i2cId)
|
|
|
+ return string.byte(data)
|
|
|
+end
|
|
|
+
|
|
|
+-- check pin is in range
|
|
|
+local function checkPinIsInRange(pin)
|
|
|
+ if pin > 7 or pin < 0 then
|
|
|
+ error("MCP23017 the pin must be between 0 and 7")
|
|
|
+ end
|
|
|
+ return pin
|
|
|
+end
|
|
|
+
|
|
|
+local function reset(address, i2cId)
|
|
|
+ writeByte(address, i2cId, MCP23017_IODIRA, 0xFF)
|
|
|
+ writeByte(address, i2cId, MCP23017_IODIRB, 0xFF)
|
|
|
+end
|
|
|
+
|
|
|
+-- setup device
|
|
|
+local function setup(address, i2cId)
|
|
|
+
|
|
|
+ -- check device address (0x20 to 0x27)
|
|
|
+ if (address < 32 or address > 39) then
|
|
|
+ error("MCP23017 address is out of range")
|
|
|
+ end
|
|
|
+
|
|
|
+ if (checkDevice(address, i2cId) ~= true) then
|
|
|
+ error("MCP23017 device on " .. string.format('0x%02X', address) .. " not found")
|
|
|
+ else
|
|
|
+ reset(address, i2cId)
|
|
|
+ return 1
|
|
|
+ end
|
|
|
+end
|
|
|
+
|
|
|
+return function(address, i2cId)
|
|
|
+ local self = setmetatable({}, mcp23017)
|
|
|
+
|
|
|
+ if setup(address, i2cId) then
|
|
|
+ self.writeIODIR = function(sf, bReg, newByte) -- luacheck: no unused
|
|
|
+ writeByte(address, i2cId,
|
|
|
+ bReg == isGPB and MCP23017_IODIRB or MCP23017_IODIRA,
|
|
|
+ newByte)
|
|
|
+ end
|
|
|
+
|
|
|
+ self.writeGPIO = function(sf, bReg, newByte) -- luacheck: no unused
|
|
|
+ writeByte(address, i2cId,
|
|
|
+ bReg == isGPB and MCP23017_GPIOB or MCP23017_GPIOA, newByte)
|
|
|
+ end
|
|
|
+
|
|
|
+ self.readGPIO = function(sf, bReg) -- luacheck: no unused
|
|
|
+ return readByte(address, i2cId, -- upvals
|
|
|
+ bReg == isGPB and MCP23017_GPIOB or MCP23017_GPIOA)
|
|
|
+ end
|
|
|
+
|
|
|
+ -- read pin input
|
|
|
+ self.getPinState = function(sf, bReg, pin) -- luacheck: no unused
|
|
|
+ return issetBit(readByte(address, i2cId,
|
|
|
+ bReg == isGPB and MCP23017_GPIOB or MCP23017_GPIOA),
|
|
|
+ checkPinIsInRange(pin))
|
|
|
+ end
|
|
|
+
|
|
|
+ -- set pin to low or high
|
|
|
+ self.setPin = function(sf, bReg, pin, state) -- luacheck: no unused
|
|
|
+ local inReq = bReg == isGPB and MCP23017_GPIOB or MCP23017_GPIOA
|
|
|
+ local inPin = checkPinIsInRange(pin)
|
|
|
+ local response = readByte(address, i2cId, inReq)
|
|
|
+ writeByte(address, i2cId, inReq,
|
|
|
+ state == isHIGH and setBit(response, inPin) or clearBit(response, inPin))
|
|
|
+ return true
|
|
|
+ end
|
|
|
+
|
|
|
+ -- set mode for a pin
|
|
|
+ self.setMode = function(sf, bReg, pin, mode) -- luacheck: no unused
|
|
|
+ local inReq = bReg == isGPB and MCP23017_IODIRB or MCP23017_IODIRA
|
|
|
+ local inPin = checkPinIsInRange(pin)
|
|
|
+ local response = readByte(address, i2cId, inReq)
|
|
|
+ writeByte(address, i2cId, inReq,
|
|
|
+ mode == isINPUT and setBit(response, inPin) or clearBit(response, inPin))
|
|
|
+ return true
|
|
|
+ end
|
|
|
+
|
|
|
+ -- reset gpio mode
|
|
|
+ self.reset = function(sf) -- luacheck: no unused
|
|
|
+ reset(address, i2cId)
|
|
|
+ end
|
|
|
+
|
|
|
+ -- setup internal pullup
|
|
|
+ self.setInternalPullUp = function(sf, bReg, iByte) -- luacheck: no unused
|
|
|
+ writeByte(address, i2cId,
|
|
|
+ bReg == isGPB and MCP23017_DEFVALB or MCP23017_DEFVALA, iByte)
|
|
|
+ end
|
|
|
+
|
|
|
+ return self
|
|
|
+ end
|
|
|
+ return nil
|
|
|
+end
|
|
|
+
|
|
|
+
|