-
Notifications
You must be signed in to change notification settings - Fork 14
/
lcd-hd44780.lua
271 lines (231 loc) · 8.73 KB
/
lcd-hd44780.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
---
-- Lua module to use a hd44780 compatible LCD display with the Raspberry Pi GPIO interface.
-- @module GPIO.lcd-hd44780
--
-- based on code from lrvick, LiquidCrystal and Adafruit
--
-- lrvic - `https://github.com/lrvick/raspi-hd44780/blob/master/hd44780.py`
--
-- LiquidCrystal - `https://github.com/arduino/Arduino/blob/master/libraries/LiquidCrystal/LiquidCrystal.cpp`
--
-- Adafruit - `https://github.com/adafruit/Adafruit-Raspberry-Pi-Python-Code/blob/master/Adafruit_CharLCD/Adafruit_CharLCD.py`
--
-- @copyright (c); Adafruit for the Python library, Thijs Schreijer for the Lua migration
-- Load some modules
local GPIO = require("GPIO")
local bit32 = bit32 or require("bit32")
pcall(require, "socket") -- try and load LuaSocket for the sleep function
-- create some shortcuts
local bor, band, bnot, btest = bit32.bor, bit32.band, bit32.bnot, bit32.btest
local M = {} -- module table to export
-- commands
M.LCD_CLEARDISPLAY = 0x01
M.LCD_RETURNHOME = 0x02
M.LCD_ENTRYMODESET = 0x04
M.LCD_DISPLAYCONTROL = 0x08
M.LCD_CURSORSHIFT = 0x10
M.LCD_FUNCTIONSET = 0x20
M.LCD_SETCGRAMADDR = 0x40
M.LCD_SETDDRAMADDR = 0x80
-- flags for display entry mode
M.LCD_ENTRYRIGHT = 0x00
M.LCD_ENTRYLEFT = 0x02
M.LCD_ENTRYSHIFTINCREMENT = 0x01
M.LCD_ENTRYSHIFTDECREMENT = 0x00
-- flags for display on/off control
M.LCD_DISPLAYON = 0x04
M.LCD_DISPLAYOFF = 0x00
M.LCD_CURSORON = 0x02
M.LCD_CURSOROFF = 0x00
M.LCD_BLINKON = 0x01
M.LCD_BLINKOFF = 0x00
-- flags for display/cursor shift
M.LCD_DISPLAYMOVE = 0x08
M.LCD_CURSORMOVE = 0x00
-- flags for display/cursor shift
M.LCD_DISPLAYMOVE = 0x08
M.LCD_CURSORMOVE = 0x00
M.LCD_MOVERIGHT = 0x04
M.LCD_MOVELEFT = 0x00
-- flags for function set
M.LCD_8BITMODE = 0x10
M.LCD_4BITMODE = 0x00
M.LCD_2LINE = 0x08
M.LCD_1LINE = 0x00
M.LCD_5x10DOTS = 0x04
M.LCD_5x8DOTS = 0x00
-- actually writes a byte of data to the display unit.
-- a byte is writen as 2x 4bits
-- @param self the LCD object to use
-- @param bits a byte value (0-255) containing the bits to write
-- @param char_mode a boolean (optional) indicating whether character mode is to be used (true), or command mode (false)
local function write4bits(self, bits, char_mode)
if char_mode then char_mode=true else char_mode=false end
M.delayMicroseconds(1000) -- 1000 microsecond sleep
GPIO.output(self.pin_rs, char_mode)
for n = 1, 2 do
for i, pin in ipairs(self.pin_db) do
local bit = (2-n)*4 + (i-1)
local val = (btest(bits, 2^bit))
GPIO.output(pin, val)
--print(" ", pin, val, "i="..i, "n="..n)
end
self:pulseEnable()
end
end
---
-- Sets the display size.
-- @param self display object
-- @param cols nr of columns
-- @param lines nr of lines
local function begin(self, cols, lines)
if (lines > 1) then
self.numlines = lines
self.displayfunction = bor(self.displayfunction, M.LCD_2LINE)
self.currline = 0
end
end
local function home(self)
write4bits(self, M.LCD_RETURNHOME) -- set cursor position to zero
M.delayMicroseconds(3000) -- this command takes a long time!
end
local function clear(self)
write4bits(self, M.LCD_CLEARDISPLAY) -- command to clear display
M.delayMicroseconds(3000) -- 3000 microsecond sleep, clearing the display takes a long time
end
local row_offsets = { 0x00, 0x40, 0x14, 0x54 }
local function setCursor(self, col, row)
if row > self.numlines then
row = self.numlines - 1 -- we count rows starting w/0
end
--TODO does python index from 0 ???
write4bits(self, bor(M.LCD_SETDDRAMADDR, (col + row_offsets[row])))
end
local function noDisplay(self)
self.displaycontrol = band(self.displaycontrol, bnot(M.LCD_DISPLAYON))
write4bits(bor(self, M.LCD_DISPLAYCONTROL, self.displaycontrol))
end
local function display(self)
self.displaycontrol = bor(self.displaycontrol, M.LCD_DISPLAYON)
write4bits(self, bor(M.LCD_DISPLAYCONTROL, self.displaycontrol))
end
local function noCursor(self)
self.displaycontrol = band(self.displaycontrol, bnot(M.LCD_CURSORON))
write4bits(self, bor(M.LCD_DISPLAYCONTROL, self.displaycontrol))
end
local function cursor(self)
self.displaycontrol = bor(self.displaycontrol, M.LCD_CURSORON)
write4bits(self, bor(M.LCD_DISPLAYCONTROL, self.displaycontrol))
end
local function noBlink(self)
self.displaycontrol = band(self.displaycontrol, bnot(M.LCD_BLINKON))
write4bits(self, bor(M.LCD_DISPLAYCONTROL, self.displaycontrol))
end
local function blink(self)
self.displaycontrol = bor(self.displaycontrol, M.LCD_BLINKON)
write4bits(self, bor(M.LCD_DISPLAYCONTROL, self.displaycontrol))
end
local function scrollDisplayLeft(self)
write4bits(self, bor(M.LCD_CURSORSHIFT, M.LCD_DISPLAYMOVE, M.LCD_MOVELEFT))
end
local function scrollDisplayRight(self)
write4bits(self, bor(M.LCD_CURSORSHIFT, M.LCD_DISPLAYMOVE, M.LCD_MOVERIGHT))
end
local function leftToRight(self)
self.displaymode = bor(self.displaycontrol, M.LCD_ENTRYLEFT)
write4bits(self, bor(M.LCD_ENTRYMODESET, self.displaymode))
end
local function rightToLeft(self)
self.displaymode = band(self.displaymode, bnot(M.LCD_ENTRYLEFT))
write4bits(self, bor(M.LCD_ENTRYMODESET, self.displaymode))
end
local function autoScroll(self)
self.displaymode = bor(self.displaycontrol, M.LCD_ENTRYSHIFTINCREMENT)
write4bits(self, bor(M.LCD_ENTRYMODESET, self.displaymode))
end
local function noAutoScroll(self)
self.displaymode = band(self.displaymode, bnot(M.LCD_ENTRYSHIFTINCREMENT))
write4bits(self, bor(M.LCD_ENTRYMODESET, self.displaymode))
end
local function pulseEnable(self)
GPIO.output(self.pin_e, false)
M.delayMicroseconds(1) -- 1 microsecond pause - enable pulse must be > 450ns
GPIO.output(self.pin_e, true)
M.delayMicroseconds(1) -- 1 microsecond pause - enable pulse must be > 450ns
GPIO.output(self.pin_e, false)
M.delayMicroseconds(37) -- commands need > 37us to settle
end
local function message(self, text)
text = text:gsub("\n", string.char(0xC0))
for i = 1, #text do
local c = text:byte(i)
write4bits(self, c, (c ~= 0xC0)) -- newline should pass 'false'
end
end
---
-- This function will sleep for a number om micro (NOT milli!) seconds.
-- On first call it will replace itself with a new implementation based on LuaSocket
-- or the OS. NOTE: the OS version is really slow!
-- @param microseconds number of microseconds to sleep
function M.delayMicroseconds(microseconds)
local sleep = (package.loaded.socket or {}).sleep
if sleep then
-- use LuaSocket sleep function
M.delayMicroseconds = function(microseconds)
sleep(microseconds/1000000)
end
else
-- LuaSocket sleep function not found, so go and use OS
M.delayMicroseconds = function(microseconds)
os.execute(string.format("sleep %.6f", microseconds/1000000))
end
end
return M.delayMicroseconds(microseconds) -- invoke the new implementation
end
--- Creates a new display object with its pin configuration.
-- @param pin_rs pin number for rs (according to current pin numbering scheme)
-- @param pin_e pin number for e (according to current pin numbering scheme)
-- @param pin_db table/list with 4 pin numbers for data (according to current pin numbering scheme)
-- @return New display object
function M.initialize(pin_rs, pin_e, pin_db)
local self = {
pin_rs = pin_rs,
pin_e = pin_e,
pin_db = { pin_db[1], pin_db[2], pin_db[3], pin_db[4] }
}
GPIO.setup(self.pin_rs, GPIO.OUT)
GPIO.setup(self.pin_e, GPIO.OUT)
for _, pin in ipairs(self.pin_db) do
GPIO.setup(pin, GPIO.OUT)
end
self.displaycontrol = bor(M.LCD_DISPLAYON, M.LCD_CURSOROFF, M.LCD_BLINKOFF)
self.displayfunction = bor(M.LCD_4BITMODE, M.LCD_1LINE, M.LCD_5x8DOTS, M.LCD_2LINE)
self.displaymode = bor(M.LCD_ENTRYLEFT, M.LCD_ENTRYSHIFTDECREMENT)
self.begin = begin
self.home = home
self.clear = clear
self.setCursor = setCursor
self.noDisplay = noDisplay
self.display = display
self.noCursor = noCursor
self.cursor = cursor
self.noBlink = noBlink
self.blink = blink
self.scrollDisplayLeft = scrollDisplayLeft
self.scrollDisplayRight = scrollDisplayRight
self.leftToRight = leftToRight
self.rightToLeft = rightToLeft
self.autoScroll = autoScroll
self.noAutoScroll = noAutoScroll
self.pulseEnable = pulseEnable
self.message = message
write4bits(self, 0x33) -- initialization
write4bits(self, 0x32) -- initialization
write4bits(self, 0x28) -- 2 line 5x7 matrix
write4bits(self, 0x0C) -- turn cursor off 0x0E to enable cursor
write4bits(self, 0x06) -- shift cursor right
write4bits(self, bor(M.LCD_ENTRYMODESET, self.displaymode)) -- set the entry mode
self:clear()
return self
end
return M