liquidcrystal.lua 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. local bit = bit --luacheck: read globals bit
  2. -- metatable
  3. local LiquidCrystal = {}
  4. LiquidCrystal.__index = LiquidCrystal
  5. -- commands
  6. local LCD_CLEARDISPLAY = 0x01
  7. local LCD_RETURNHOME = 0x02
  8. local LCD_ENTRYMODESET = 0x04
  9. local LCD_DISPLAYCONTROL = 0x08
  10. local LCD_CURSORSHIFT = 0x10
  11. local LCD_FUNCTIONSET = 0x20
  12. local LCD_SETCGRAMADDR = 0x40
  13. local LCD_SETDDRAMADDR = 0x80
  14. -- flags for display entry mode
  15. -- local LCD_ENTRYRIGHT = 0x00
  16. local LCD_ENTRYLEFT = 0x02
  17. local LCD_ENTRYSHIFTINCREMENT = 0x01
  18. -- local LCD_ENTRYSHIFTDECREMENT = 0x00
  19. -- flags for display on/off control
  20. local LCD_DISPLAYON = 0x04
  21. -- local LCD_DISPLAYOFF = 0x00
  22. local LCD_CURSORON = 0x02
  23. -- local LCD_CURSOROFF = 0x00
  24. local LCD_BLINKON = 0x01
  25. -- local LCD_BLINKOFF = 0x00
  26. -- flags for display/cursor shift
  27. local LCD_DISPLAYMOVE = 0x08
  28. local LCD_CURSORMOVE = 0x00
  29. local LCD_MOVERIGHT = 0x04
  30. local LCD_MOVELEFT = 0x00
  31. -- flags for function set
  32. local LCD_8BITMODE = 0x10
  33. local LCD_4BITMODE = 0x00
  34. local LCD_2LINE = 0x08
  35. local LCD_1LINE = 0x00
  36. local LCD_5x10DOTS = 0x04
  37. local LCD_5x8DOTS = 0x00
  38. function LiquidCrystal:autoscroll(on)
  39. if on then
  40. self._displaymode = bit.bor(self._displaymode, LCD_ENTRYSHIFTINCREMENT)
  41. else
  42. self._displaymode = bit.band(self._displaymode, bit.bnot(LCD_ENTRYSHIFTINCREMENT))
  43. end
  44. return self:_command(bit.bor(LCD_ENTRYMODESET, self._displaymode))
  45. end
  46. function LiquidCrystal:blink(on)
  47. if on then
  48. self._displaycontrol = bit.bor(self._displaycontrol, LCD_BLINKON)
  49. else
  50. self._displaycontrol = bit.band(self._displaycontrol, bit.bnot(LCD_BLINKON))
  51. end
  52. return self:_command(bit.bor(LCD_DISPLAYCONTROL, self._displaycontrol))
  53. end
  54. function LiquidCrystal:clear() return self:_command(LCD_CLEARDISPLAY) end
  55. function LiquidCrystal:cursorLeft()
  56. return self:_command(bit.bor(LCD_CURSORSHIFT, LCD_CURSORMOVE, LCD_MOVELEFT))
  57. end
  58. function LiquidCrystal:cursorMove(col, row)
  59. return self:_command(bit.bor(LCD_SETDDRAMADDR, col + (row and (self._offsets[row] - 1) or 0)))
  60. end
  61. function LiquidCrystal:cursor(on)
  62. if on then
  63. self._displaycontrol = bit.bor(self._displaycontrol, LCD_CURSORON)
  64. else
  65. self._displaycontrol = bit.band(self._displaycontrol, bit.bnot(LCD_CURSORON))
  66. end
  67. return self:_command(bit.bor(LCD_DISPLAYCONTROL, self._displaycontrol))
  68. end
  69. function LiquidCrystal:cursorRight()
  70. return self:_command(bit.bor(LCD_CURSORSHIFT, LCD_CURSORMOVE, LCD_MOVERIGHT))
  71. end
  72. function LiquidCrystal:customChar(index, bytes)
  73. local pos = self:position()
  74. self:_command(bit.bor(LCD_SETCGRAMADDR,
  75. bit.lshift(bit.band(self._eightdots and index or bit.clear(index, 0),
  76. 0x7), 3)))
  77. for i=1,(self._eightdots and 8 or 11) do self:_write(bytes[i] or 0) end
  78. self:cursorMove(pos)
  79. end
  80. function LiquidCrystal:display(on)
  81. if on then
  82. self._displaycontrol = bit.bor(self._displaycontrol, LCD_DISPLAYON)
  83. else
  84. self._displaycontrol = bit.band(self._displaycontrol, bit.bnot(LCD_DISPLAYON))
  85. end
  86. return self:_command(bit.bor(LCD_DISPLAYCONTROL, self._displaycontrol))
  87. end
  88. function LiquidCrystal:home() return self:_command(LCD_RETURNHOME) end
  89. function LiquidCrystal:leftToRight()
  90. self._displaymode = bit.bor(self._displaymode, LCD_ENTRYLEFT)
  91. return self:_command(bit.bor(LCD_ENTRYMODESET, self._displaymode))
  92. end
  93. function LiquidCrystal:readCustom(index)
  94. local pos = self:position()
  95. local data = {}
  96. self:_command(bit.bor(LCD_SETCGRAMADDR,
  97. bit.lshift(bit.band(self._eightdots and index or bit.clear(index, 0),
  98. 0x7), 3)))
  99. for i=1,(self._eightdots and 8 or 11) do data[i] = self:read() end
  100. self:cursorMove(pos)
  101. return data
  102. end
  103. function LiquidCrystal:rightToLeft()
  104. self._displaymode = bit.band(self._displaymode, bit.bnot(LCD_ENTRYLEFT))
  105. self:_command(bit.bor(LCD_ENTRYMODESET, self._displaymode))
  106. end
  107. function LiquidCrystal:scrollLeft()
  108. return self:_command(bit.bor(LCD_CURSORSHIFT, LCD_DISPLAYMOVE, LCD_MOVELEFT))
  109. end
  110. function LiquidCrystal:scrollRight()
  111. return self:_command(bit.bor(LCD_CURSORSHIFT, LCD_DISPLAYMOVE, LCD_MOVERIGHT))
  112. end
  113. function LiquidCrystal:write(...)
  114. for _, x in ipairs({...}) do
  115. if type(x) == "number" then
  116. self:_write(x)
  117. end
  118. if type(x) == "string" then
  119. for i=1,#x do
  120. self:_write(string.byte(x, i))
  121. end
  122. end
  123. end
  124. end
  125. return function (backend, onelinemode, eightdotsmode, column_width)
  126. local self = {}
  127. setmetatable(self, LiquidCrystal)
  128. -- copy out backend functions, to avoid a long-lived table
  129. self._command = backend.command
  130. self.busy = backend.busy
  131. self.position = backend.position
  132. self._write = backend.write
  133. self.read = backend.read
  134. self.backlight = backend.backlight
  135. -- defaults
  136. self._displaycontrol = 0
  137. self._displaymode = 0
  138. self._eightdots = eightdotsmode
  139. self._offsets = {0, 0x40}
  140. if column_width ~= nil then
  141. self._offsets[3] = 0 + column_width
  142. self._offsets[4] = 0x40 + column_width
  143. end
  144. self:_command(bit.bor(LCD_FUNCTIONSET,
  145. bit.bor(
  146. backend.fourbits and LCD_4BITMODE or LCD_8BITMODE,
  147. onelinemode and LCD_1LINE or LCD_2LINE,
  148. eightdotsmode and LCD_5x8DOTS or LCD_5x10DOTS)))
  149. self:_command(bit.bor(LCD_ENTRYMODESET, self._displaymode))
  150. self:display(true)
  151. self:clear()
  152. return self
  153. end