mcp23017.lua 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  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
  4. outputs. Up to 8 devices (128 channels) are possible by the configurable
  5. address (A0 - A2 Pins).
  6. Due to the 16 channels, 2 bytes are required for switching outputs or
  7. reading input signals. These are A and B. A single pin can be set or a
  8. whole byte.
  9. The numbering of the individual pins starts at 0 and ends with 7. The
  10. numbers are for each register GPIO A and GPIO B.
  11. The module requires `i2c` and `bit` C module built into firmware.
  12. For register name <-> number mapping, see the MCP23017 data sheet, table
  13. 3-1 on page 12 (as of Revision C, July 2016). This module uses the
  14. IOCON.BANK=0 numbering (the default) throughout.
  15. @author Marcel P. | Plomi.net
  16. @github https://github.com/plomi-net
  17. @version 1.0.0
  18. ]]
  19. local i2c, string, issetBit, setBit, clearBit =
  20. i2c, string, bit.isset, bit.set, bit.clear
  21. -- metatable
  22. local mcp23017 = {
  23. -- convenience parameter enumeration names
  24. INPUT = true,
  25. OUTPUT = false,
  26. GPA = false,
  27. GPB = true,
  28. HIGH = true,
  29. LOW = false
  30. }
  31. mcp23017.__index = mcp23017
  32. -- check device is available on address
  33. local function checkDevice(address, i2cId)
  34. i2c.start(i2cId)
  35. local response = i2c.address(i2cId, address, i2c.TRANSMITTER)
  36. i2c.stop(i2cId)
  37. return response
  38. end
  39. -- write byte
  40. local function writeByte(address, i2cId, registerAddr, val)
  41. i2c.start(i2cId)
  42. i2c.address(i2cId, address, i2c.TRANSMITTER)
  43. i2c.write(i2cId, registerAddr)
  44. i2c.write(i2cId, val)
  45. i2c.stop(i2cId)
  46. end
  47. -- read byte
  48. local function readByte(address, i2cId, registerAddr)
  49. i2c.start(i2cId)
  50. i2c.address(i2cId, address, i2c.TRANSMITTER)
  51. i2c.write(i2cId, registerAddr)
  52. i2c.stop(i2cId)
  53. i2c.start(i2cId)
  54. i2c.address(i2cId, address, i2c.RECEIVER)
  55. local data = i2c.read(i2cId, 1)
  56. i2c.stop(i2cId)
  57. return string.byte(data)
  58. end
  59. -- check pin is in range
  60. local function checkPinIsInRange(pin)
  61. if pin > 7 or pin < 0 then
  62. error("MCP23017 the pin must be between 0 and 7")
  63. end
  64. return pin
  65. end
  66. function mcp23017:writeIODIR(bReg, newByte)
  67. writeByte(self.address, self.i2cId,
  68. bReg and 0x1 --[[IODIRB register]] or 0x0 --[[IODIRA register]], newByte)
  69. end
  70. function mcp23017:writeGPIO(bReg, newByte)
  71. writeByte(self.address, self.i2cId,
  72. bReg and 0x13 --[[GPIOB register]] or 0x12 --[[GPIOA register]], newByte)
  73. end
  74. function mcp23017:readGPIO(bReg)
  75. return readByte(self.address, self.i2cId,
  76. bReg and 0x13 --[[GPIOB register]] or 0x12 --[[GPIOA register]])
  77. end
  78. -- read pin input
  79. function mcp23017:getPinState(bReg, pin)
  80. return issetBit(readByte(self.address, self.i2cId,
  81. bReg and 0x13 --[[GPIOB register]] or 0x12 --[[GPIOA register]]),
  82. checkPinIsInRange(pin))
  83. end
  84. -- set pin to low or high
  85. function mcp23017:setPin(bReg, pin, state)
  86. local a, i = self.address, self.i2cId
  87. local inReq = bReg and 0x13 --[[GPIOB register]] or 0x12 --[[GPIOA register]]
  88. local inPin = checkPinIsInRange(pin)
  89. local response = readByte(a, i, inReq)
  90. writeByte(a, i, inReq,
  91. state and setBit(response, inPin) or clearBit(response, inPin))
  92. return true
  93. end
  94. -- set mode for a pin
  95. function mcp23017:setMode(bReg, pin, mode)
  96. local a, i = self.address, self.i2cId
  97. local inReq = bReg and 0x1 --[[IODIRB register]] or 0x0 --[[IODIRA register]]
  98. local inPin = checkPinIsInRange(pin)
  99. local response = readByte(a, i, inReq)
  100. writeByte(a, i, inReq,
  101. mode and setBit(response, inPin) or clearBit(response, inPin))
  102. return true
  103. end
  104. -- reset gpio mode
  105. function mcp23017:reset()
  106. local a, i = self.address, self.i2cId
  107. writeByte(a, i, 0x0 --[[IODIRA register]], 0xFF)
  108. writeByte(a, i, 0x1 --[[IODIRB register]], 0xFF)
  109. end
  110. -- setup internal pullup
  111. function mcp23017:setInternalPullUp(bReg, iByte)
  112. writeByte(self.address, self.i2cId,
  113. bReg and 0x7 --[[DEFVALB register]] or 0x6 --[[DEFVALA register]], iByte)
  114. end
  115. return function(address, i2cId)
  116. local self = setmetatable({}, mcp23017)
  117. -- check device address (0x20 to 0x27)
  118. if (address < 32 or address > 39) then
  119. error("MCP23017 address is out of range")
  120. end
  121. if (checkDevice(address, i2cId) ~= true) then
  122. error("MCP23017 device on " .. string.format('0x%02X', address) .. " not found")
  123. end
  124. self.address = address
  125. self.i2cId = i2cId
  126. self:reset()
  127. return self
  128. end