184 lines
5.4 KiB
Lua
184 lines
5.4 KiB
Lua
|
local bit = bit --luacheck: read globals bit
|
||
|
-- metatable
|
||
|
local LiquidCrystal = {}
|
||
|
LiquidCrystal.__index = LiquidCrystal
|
||
|
|
||
|
-- commands
|
||
|
local LCD_CLEARDISPLAY = 0x01
|
||
|
local LCD_RETURNHOME = 0x02
|
||
|
local LCD_ENTRYMODESET = 0x04
|
||
|
local LCD_DISPLAYCONTROL = 0x08
|
||
|
local LCD_CURSORSHIFT = 0x10
|
||
|
local LCD_FUNCTIONSET = 0x20
|
||
|
local LCD_SETCGRAMADDR = 0x40
|
||
|
local LCD_SETDDRAMADDR = 0x80
|
||
|
|
||
|
-- flags for display entry mode
|
||
|
-- local LCD_ENTRYRIGHT = 0x00
|
||
|
local LCD_ENTRYLEFT = 0x02
|
||
|
local LCD_ENTRYSHIFTINCREMENT = 0x01
|
||
|
-- local LCD_ENTRYSHIFTDECREMENT = 0x00
|
||
|
|
||
|
-- flags for display on/off control
|
||
|
local LCD_DISPLAYON = 0x04
|
||
|
-- local LCD_DISPLAYOFF = 0x00
|
||
|
local LCD_CURSORON = 0x02
|
||
|
-- local LCD_CURSOROFF = 0x00
|
||
|
local LCD_BLINKON = 0x01
|
||
|
-- local LCD_BLINKOFF = 0x00
|
||
|
|
||
|
-- flags for display/cursor shift
|
||
|
local LCD_DISPLAYMOVE = 0x08
|
||
|
local LCD_CURSORMOVE = 0x00
|
||
|
local LCD_MOVERIGHT = 0x04
|
||
|
local LCD_MOVELEFT = 0x00
|
||
|
|
||
|
-- flags for function set
|
||
|
local LCD_8BITMODE = 0x10
|
||
|
local LCD_4BITMODE = 0x00
|
||
|
local LCD_2LINE = 0x08
|
||
|
local LCD_1LINE = 0x00
|
||
|
local LCD_5x10DOTS = 0x04
|
||
|
local LCD_5x8DOTS = 0x00
|
||
|
|
||
|
|
||
|
function LiquidCrystal:autoscroll(on)
|
||
|
if on then
|
||
|
self._displaymode = bit.bor(self._displaymode, LCD_ENTRYSHIFTINCREMENT)
|
||
|
else
|
||
|
self._displaymode = bit.band(self._displaymode, bit.bnot(LCD_ENTRYSHIFTINCREMENT))
|
||
|
end
|
||
|
return self:_command(bit.bor(LCD_ENTRYMODESET, self._displaymode))
|
||
|
end
|
||
|
|
||
|
function LiquidCrystal:blink(on)
|
||
|
if on then
|
||
|
self._displaycontrol = bit.bor(self._displaycontrol, LCD_BLINKON)
|
||
|
else
|
||
|
self._displaycontrol = bit.band(self._displaycontrol, bit.bnot(LCD_BLINKON))
|
||
|
end
|
||
|
return self:_command(bit.bor(LCD_DISPLAYCONTROL, self._displaycontrol))
|
||
|
end
|
||
|
|
||
|
function LiquidCrystal:clear() return self:_command(LCD_CLEARDISPLAY) end
|
||
|
|
||
|
function LiquidCrystal:cursorLeft()
|
||
|
return self:_command(bit.bor(LCD_CURSORSHIFT, LCD_CURSORMOVE, LCD_MOVELEFT))
|
||
|
end
|
||
|
|
||
|
function LiquidCrystal:cursorMove(col, row)
|
||
|
return self:_command(bit.bor(LCD_SETDDRAMADDR, col + (row and (self._offsets[row] - 1) or 0)))
|
||
|
end
|
||
|
|
||
|
function LiquidCrystal:cursor(on)
|
||
|
if on then
|
||
|
self._displaycontrol = bit.bor(self._displaycontrol, LCD_CURSORON)
|
||
|
else
|
||
|
self._displaycontrol = bit.band(self._displaycontrol, bit.bnot(LCD_CURSORON))
|
||
|
end
|
||
|
return self:_command(bit.bor(LCD_DISPLAYCONTROL, self._displaycontrol))
|
||
|
end
|
||
|
|
||
|
function LiquidCrystal:cursorRight()
|
||
|
return self:_command(bit.bor(LCD_CURSORSHIFT, LCD_CURSORMOVE, LCD_MOVERIGHT))
|
||
|
end
|
||
|
|
||
|
function LiquidCrystal:customChar(index, bytes)
|
||
|
local pos = self:position()
|
||
|
self:_command(bit.bor(LCD_SETCGRAMADDR,
|
||
|
bit.lshift(bit.band(self._eightdots and index or bit.clear(index, 0),
|
||
|
0x7), 3)))
|
||
|
for i=1,(self._eightdots and 8 or 11) do self:_write(bytes[i] or 0) end
|
||
|
self:cursorMove(pos)
|
||
|
end
|
||
|
|
||
|
function LiquidCrystal:display(on)
|
||
|
if on then
|
||
|
self._displaycontrol = bit.bor(self._displaycontrol, LCD_DISPLAYON)
|
||
|
else
|
||
|
self._displaycontrol = bit.band(self._displaycontrol, bit.bnot(LCD_DISPLAYON))
|
||
|
end
|
||
|
return self:_command(bit.bor(LCD_DISPLAYCONTROL, self._displaycontrol))
|
||
|
end
|
||
|
|
||
|
function LiquidCrystal:home() return self:_command(LCD_RETURNHOME) end
|
||
|
|
||
|
function LiquidCrystal:leftToRight()
|
||
|
self._displaymode = bit.bor(self._displaymode, LCD_ENTRYLEFT)
|
||
|
return self:_command(bit.bor(LCD_ENTRYMODESET, self._displaymode))
|
||
|
end
|
||
|
|
||
|
function LiquidCrystal:readCustom(index)
|
||
|
local pos = self:position()
|
||
|
local data = {}
|
||
|
self:_command(bit.bor(LCD_SETCGRAMADDR,
|
||
|
bit.lshift(bit.band(self._eightdots and index or bit.clear(index, 0),
|
||
|
0x7), 3)))
|
||
|
for i=1,(self._eightdots and 8 or 11) do data[i] = self:read() end
|
||
|
self:cursorMove(pos)
|
||
|
return data
|
||
|
end
|
||
|
|
||
|
function LiquidCrystal:rightToLeft()
|
||
|
self._displaymode = bit.band(self._displaymode, bit.bnot(LCD_ENTRYLEFT))
|
||
|
self:_command(bit.bor(LCD_ENTRYMODESET, self._displaymode))
|
||
|
end
|
||
|
|
||
|
function LiquidCrystal:scrollLeft()
|
||
|
return self:_command(bit.bor(LCD_CURSORSHIFT, LCD_DISPLAYMOVE, LCD_MOVELEFT))
|
||
|
end
|
||
|
|
||
|
function LiquidCrystal:scrollRight()
|
||
|
return self:_command(bit.bor(LCD_CURSORSHIFT, LCD_DISPLAYMOVE, LCD_MOVERIGHT))
|
||
|
end
|
||
|
|
||
|
function LiquidCrystal:write(...)
|
||
|
for _, x in ipairs({...}) do
|
||
|
if type(x) == "number" then
|
||
|
self:_write(x)
|
||
|
end
|
||
|
if type(x) == "string" then
|
||
|
for i=1,#x do
|
||
|
self:_write(string.byte(x, i))
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
|
||
|
return function (backend, onelinemode, eightdotsmode, column_width)
|
||
|
local self = {}
|
||
|
setmetatable(self, LiquidCrystal)
|
||
|
|
||
|
-- copy out backend functions, to avoid a long-lived table
|
||
|
self._command = backend.command
|
||
|
self.busy = backend.busy
|
||
|
self.position = backend.position
|
||
|
self._write = backend.write
|
||
|
self.read = backend.read
|
||
|
self.backlight = backend.backlight
|
||
|
|
||
|
-- defaults
|
||
|
self._displaycontrol = 0
|
||
|
self._displaymode = 0
|
||
|
|
||
|
self._eightdots = eightdotsmode
|
||
|
self._offsets = {0, 0x40}
|
||
|
if column_width ~= nil then
|
||
|
self._offsets[3] = 0 + column_width
|
||
|
self._offsets[4] = 0x40 + column_width
|
||
|
end
|
||
|
|
||
|
self:_command(bit.bor(LCD_FUNCTIONSET,
|
||
|
bit.bor(
|
||
|
backend.fourbits and LCD_4BITMODE or LCD_8BITMODE,
|
||
|
onelinemode and LCD_1LINE or LCD_2LINE,
|
||
|
eightdotsmode and LCD_5x8DOTS or LCD_5x10DOTS)))
|
||
|
self:_command(bit.bor(LCD_ENTRYMODESET, self._displaymode))
|
||
|
|
||
|
self:display(true)
|
||
|
self:clear()
|
||
|
|
||
|
return self
|
||
|
end
|