123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185 |
- --[[
- 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
|