mcp23017.lua 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. --[[
  2. This Lua module provides access to the MCP23017 module.
  3. The MCP23017 is a port expander and provides 16 channels for inputs and outputs.
  4. Up to 8 devices (128 channels) are possible by the configurable address (A0 - A2 Pins).
  5. Due to the 16 channels, 2 bytes are required for switching outputs or reading input signals. These are A and B.
  6. A single pin can be set or a whole byte.
  7. The numbering of the individual pins starts at 0 and ends with 7.
  8. The numbers are for each register GPIO A and GPIO B.
  9. The module requires `i2c` and `bit` C module built into firmware.
  10. @author Marcel P. | Plomi.net
  11. @github https://github.com/plomi-net
  12. @version 1.0.0
  13. ]]
  14. local i2c, string, issetBit, setBit, clearBit, error =
  15. i2c, string, bit.isset, bit.set, bit.clear, error
  16. local isINPUT, isGPB, isHIGH = true, true, true
  17. -- registers (not used registers are commented out)
  18. local MCP23017_IODIRA = 0x00
  19. local MCP23017_IODIRB = 0x01
  20. local MCP23017_DEFVALA = 0x06
  21. local MCP23017_DEFVALB = 0x07
  22. local MCP23017_GPIOA = 0x12
  23. local MCP23017_GPIOB = 0x13
  24. --[[
  25. local MCP23017_IPOLA = 0x02
  26. local MCP23017_IPOLB = 0x03
  27. local MCP23017_GPINTENA = 0x04
  28. local MCP23017_GPINTENB = 0x05
  29. local MCP23017_DEFVALA = 0x06
  30. local MCP23017_DEFVALB = 0x07
  31. local MCP23017_INTCONA = 0x08
  32. local MCP23017_INTCONB = 0x09
  33. local MCP23017_IOCON = 0x0A
  34. local MCP23017_IOCON2 = 0x0B
  35. local MCP23017_GPPUA = 0x0C
  36. local MCP23017_GPPUB = 0x0D
  37. local MCP23017_INTFA = 0x0E
  38. local MCP23017_INTFB = 0x0F
  39. local MCP23017_INTCAPA = 0x10
  40. local MCP23017_INTCAPB = 0x11
  41. local MCP23017_OLATA = 0x14
  42. local MCP23017_OLATB = 0x15
  43. ]]
  44. -- metatable
  45. local mcp23017 = {
  46. INPUT = isINPUT,
  47. OUTPUT = not isINPUT,
  48. GPA = not isGPB,
  49. GPB = isGPB,
  50. HIGH = isHIGH,
  51. LOW = not isHIGH
  52. }
  53. mcp23017.__index = mcp23017
  54. -- check device is available on address
  55. local function checkDevice(address, i2cId)
  56. i2c.start(i2cId)
  57. local response = i2c.address(i2cId, address, i2c.TRANSMITTER)
  58. i2c.stop(i2cId)
  59. return response
  60. end
  61. -- write byte
  62. local function writeByte(address, i2cId, registerAddr, val)
  63. i2c.start(i2cId)
  64. i2c.address(i2cId, address, i2c.TRANSMITTER)
  65. i2c.write(i2cId, registerAddr)
  66. i2c.write(i2cId, val)
  67. i2c.stop(i2cId)
  68. end
  69. -- read byte
  70. local function readByte(address, i2cId, registerAddr)
  71. i2c.start(i2cId)
  72. i2c.address(i2cId, address, i2c.TRANSMITTER)
  73. i2c.write(i2cId, registerAddr)
  74. i2c.stop(i2cId)
  75. i2c.start(i2cId)
  76. i2c.address(i2cId, address, i2c.RECEIVER)
  77. local data = i2c.read(i2cId, 1)
  78. i2c.stop(i2cId)
  79. return string.byte(data)
  80. end
  81. -- check pin is in range
  82. local function checkPinIsInRange(pin)
  83. if pin > 7 or pin < 0 then
  84. error("MCP23017 the pin must be between 0 and 7")
  85. end
  86. return pin
  87. end
  88. local function reset(address, i2cId)
  89. writeByte(address, i2cId, MCP23017_IODIRA, 0xFF)
  90. writeByte(address, i2cId, MCP23017_IODIRB, 0xFF)
  91. end
  92. -- setup device
  93. local function setup(address, i2cId)
  94. -- check device address (0x20 to 0x27)
  95. if (address < 32 or address > 39) then
  96. error("MCP23017 address is out of range")
  97. end
  98. if (checkDevice(address, i2cId) ~= true) then
  99. error("MCP23017 device on " .. string.format('0x%02X', address) .. " not found")
  100. else
  101. reset(address, i2cId)
  102. return 1
  103. end
  104. end
  105. return function(address, i2cId)
  106. local self = setmetatable({}, mcp23017)
  107. if setup(address, i2cId) then
  108. self.writeIODIR = function(sf, bReg, newByte) -- luacheck: no unused
  109. writeByte(address, i2cId,
  110. bReg == isGPB and MCP23017_IODIRB or MCP23017_IODIRA,
  111. newByte)
  112. end
  113. self.writeGPIO = function(sf, bReg, newByte) -- luacheck: no unused
  114. writeByte(address, i2cId,
  115. bReg == isGPB and MCP23017_GPIOB or MCP23017_GPIOA, newByte)
  116. end
  117. self.readGPIO = function(sf, bReg) -- luacheck: no unused
  118. return readByte(address, i2cId, -- upvals
  119. bReg == isGPB and MCP23017_GPIOB or MCP23017_GPIOA)
  120. end
  121. -- read pin input
  122. self.getPinState = function(sf, bReg, pin) -- luacheck: no unused
  123. return issetBit(readByte(address, i2cId,
  124. bReg == isGPB and MCP23017_GPIOB or MCP23017_GPIOA),
  125. checkPinIsInRange(pin))
  126. end
  127. -- set pin to low or high
  128. self.setPin = function(sf, bReg, pin, state) -- luacheck: no unused
  129. local inReq = bReg == isGPB and MCP23017_GPIOB or MCP23017_GPIOA
  130. local inPin = checkPinIsInRange(pin)
  131. local response = readByte(address, i2cId, inReq)
  132. writeByte(address, i2cId, inReq,
  133. state == isHIGH and setBit(response, inPin) or clearBit(response, inPin))
  134. return true
  135. end
  136. -- set mode for a pin
  137. self.setMode = function(sf, bReg, pin, mode) -- luacheck: no unused
  138. local inReq = bReg == isGPB and MCP23017_IODIRB or MCP23017_IODIRA
  139. local inPin = checkPinIsInRange(pin)
  140. local response = readByte(address, i2cId, inReq)
  141. writeByte(address, i2cId, inReq,
  142. mode == isINPUT and setBit(response, inPin) or clearBit(response, inPin))
  143. return true
  144. end
  145. -- reset gpio mode
  146. self.reset = function(sf) -- luacheck: no unused
  147. reset(address, i2cId)
  148. end
  149. -- setup internal pullup
  150. self.setInternalPullUp = function(sf, bReg, iByte) -- luacheck: no unused
  151. writeByte(address, i2cId,
  152. bReg == isGPB and MCP23017_DEFVALB or MCP23017_DEFVALA, iByte)
  153. end
  154. return self
  155. end
  156. return nil
  157. end