forked from hybridgroup/gobot
-
Notifications
You must be signed in to change notification settings - Fork 0
/
hd44780_driver.go
513 lines (428 loc) · 12 KB
/
hd44780_driver.go
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
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
package gpio
import (
"errors"
"fmt"
"sync"
"time"
"gobot.io/x/gobot/v2"
)
// Commands for the driver
const (
HD44780_CLEARDISPLAY = 0x01
HD44780_RETURNHOME = 0x02
HD44780_ENTRYMODESET = 0x04
HD44780_DISPLAYCONTROL = 0x08
HD44780_CURSORSHIFT = 0x10
HD44780_FUNCTIONSET = 0x20
HD44780_SETCGRAMADDR = 0x40
HD44780_SETDDRAMADDR = 0x80
HD44780_ENTRYRIGHT = 0x00
HD44780_ENTRYLEFT = 0x02
HD44780_ENTRYSHIFTINCREMENT = 0x01
HD44780_ENTRYSHIFTDECREMENT = 0x00
HD44780_DISPLAYON = 0x04
HD44780_DISPLAYOFF = 0x00
HD44780_CURSORON = 0x02
HD44780_CURSOROFF = 0x00
HD44780_BLINKON = 0x01
HD44780_BLINKOFF = 0x00
HD44780_DISPLAYMOVE = 0x08
HD44780_CURSORMOVE = 0x00
HD44780_MOVERIGHT = 0x04
HD44780_MOVELEFT = 0x00
HD44780_1LINE = 0x00
HD44780_2LINE = 0x08
HD44780_5x8DOTS = 0x00
HD44780_5x10DOTS = 0x04
HD44780_4BITBUS = 0x00
HD44780_8BITBUS = 0x10
)
// Some useful constants for the driver
const (
HD44780_2NDLINEOFFSET = 0x40
)
// HD44780BusMode is the data bus mode
type HD44780BusMode int
// Bus modes of the driver
const (
HD44780_4BITMODE HD44780BusMode = iota + 1
HD44780_8BITMODE
)
// HD44780DataPin are the data bit pins
type HD44780DataPin struct {
D0 string // not used if 4Bit mode
D1 string // not used if 4Bit mode
D2 string // not used if 4Bit mode
D3 string // not used if 4Bit mode
D4 string
D5 string
D6 string
D7 string
}
// HD44780Driver is the gobot driver for the HD44780 LCD controller
// Datasheet: https://www.sparkfun.com/datasheets/LCD/HD44780.pdf
type HD44780Driver struct {
name string
cols int
rows int
rowOffsets [4]int
busMode HD44780BusMode
pinRS *DirectPinDriver
pinEN *DirectPinDriver
pinRW *DirectPinDriver
pinDataBits []*DirectPinDriver
displayCtrl int
displayFunc int
displayMode int
connection gobot.Connection
gobot.Commander
mutex *sync.Mutex // mutex is needed for sequences, like CreateChar(), Write(), Start(), Halt()
}
// NewHD44780Driver return a new HD44780Driver
// a: gobot.Connection
// cols: lcd columns
// rows: lcd rows
// busMode: 4Bit or 8Bit
// pinRS: register select pin
// pinEN: clock enable pin
// pinDataBits: databit pins
func NewHD44780Driver(a gobot.Connection, cols int, rows int, busMode HD44780BusMode, pinRS string, pinEN string, pinDataBits HD44780DataPin) *HD44780Driver {
h := &HD44780Driver{
name: "HD44780Driver",
cols: cols,
rows: rows,
busMode: busMode,
pinRS: NewDirectPinDriver(a, pinRS),
pinEN: NewDirectPinDriver(a, pinEN),
connection: a,
Commander: gobot.NewCommander(),
mutex: &sync.Mutex{},
}
if h.busMode == HD44780_4BITMODE {
h.pinDataBits = make([]*DirectPinDriver, 4)
h.pinDataBits[0] = NewDirectPinDriver(a, pinDataBits.D4)
h.pinDataBits[1] = NewDirectPinDriver(a, pinDataBits.D5)
h.pinDataBits[2] = NewDirectPinDriver(a, pinDataBits.D6)
h.pinDataBits[3] = NewDirectPinDriver(a, pinDataBits.D7)
} else {
h.pinDataBits = make([]*DirectPinDriver, 8)
h.pinDataBits[0] = NewDirectPinDriver(a, pinDataBits.D0)
h.pinDataBits[1] = NewDirectPinDriver(a, pinDataBits.D1)
h.pinDataBits[2] = NewDirectPinDriver(a, pinDataBits.D2)
h.pinDataBits[3] = NewDirectPinDriver(a, pinDataBits.D3)
h.pinDataBits[4] = NewDirectPinDriver(a, pinDataBits.D4)
h.pinDataBits[5] = NewDirectPinDriver(a, pinDataBits.D5)
h.pinDataBits[6] = NewDirectPinDriver(a, pinDataBits.D6)
h.pinDataBits[7] = NewDirectPinDriver(a, pinDataBits.D7)
}
h.rowOffsets[0] = 0x00
h.rowOffsets[1] = HD44780_2NDLINEOFFSET
h.rowOffsets[2] = 0x00 + cols
h.rowOffsets[3] = HD44780_2NDLINEOFFSET + cols
/* TODO : Add commands */
return h
}
// SetRWPin initializes the RW pin
func (h *HD44780Driver) SetRWPin(pinRW string) {
h.mutex.Lock()
defer h.mutex.Unlock()
h.pinRW = NewDirectPinDriver(h.connection, pinRW)
}
// Name returns the HD44780Driver name
func (h *HD44780Driver) Name() string { return h.name }
// SetName sets the HD44780Driver name
func (h *HD44780Driver) SetName(n string) { h.name = n }
// Connection returns the HD44780Driver Connection
func (h *HD44780Driver) Connection() gobot.Connection {
return h.connection
}
// Start initializes the HD44780 LCD controller
// refer to page 45/46 of Hitachi HD44780 datasheet
func (h *HD44780Driver) Start() (err error) {
h.mutex.Lock()
defer h.mutex.Unlock()
for _, bitPin := range h.pinDataBits {
if bitPin.Pin() == "" {
return errors.New("Initialization error")
}
}
time.Sleep(50 * time.Millisecond)
if err := h.activateWriteMode(); err != nil {
return err
}
// for initialization refer to documentation, page 45 and 46
if h.busMode == HD44780_4BITMODE {
if err := h.writeDataPins(0x03); err != nil {
return err
}
time.Sleep(5 * time.Millisecond)
if err := h.writeDataPins(0x03); err != nil {
return err
}
time.Sleep(100 * time.Microsecond)
if err := h.writeDataPins(0x03); err != nil {
return err
}
// no additional delay is necessary now
if err := h.writeDataPins(0x02); err != nil {
return err
}
} else {
if err := h.sendCommand(0x30); err != nil {
return err
}
time.Sleep(5 * time.Millisecond)
if err := h.sendCommand(0x30); err != nil {
return err
}
time.Sleep(100 * time.Microsecond)
if err := h.sendCommand(0x30); err != nil {
return err
}
// no additional delay is necessary now
}
if h.busMode == HD44780_4BITMODE {
h.displayFunc |= HD44780_4BITBUS
} else {
h.displayFunc |= HD44780_8BITBUS
}
if h.rows > 1 {
h.displayFunc |= HD44780_2LINE
} else {
h.displayFunc |= HD44780_1LINE
}
h.displayFunc |= HD44780_5x8DOTS
h.displayCtrl = HD44780_DISPLAYON | HD44780_BLINKOFF | HD44780_CURSOROFF
h.displayMode = HD44780_ENTRYLEFT | HD44780_ENTRYSHIFTDECREMENT
if err := h.sendCommand(HD44780_FUNCTIONSET | h.displayFunc); err != nil {
return err
}
if err := h.sendCommand(HD44780_DISPLAYCONTROL | h.displayCtrl); err != nil {
return err
}
if err := h.clear(); err != nil {
return err
}
if err := h.sendCommand(HD44780_ENTRYMODESET | h.displayMode); err != nil {
return err
}
// see documentation, page 45, 46: the busy flag can't be checked before
return nil
}
// Halt implements the Driver interface
func (h *HD44780Driver) Halt() error {
// mutex: bad characters and device locking can be prevented
// if the last action is finished before return
h.mutex.Lock()
defer h.mutex.Unlock()
return nil
}
// Write output text to the display
func (h *HD44780Driver) Write(message string) (err error) {
h.mutex.Lock()
defer h.mutex.Unlock()
col := 0
if (h.displayMode & HD44780_ENTRYLEFT) == 0 {
col = h.cols - 1
}
row := 0
for _, c := range message {
if c == '\n' {
row++
if err := h.setCursor(col, row); err != nil {
return err
}
continue
}
if err := h.writeChar(int(c)); err != nil {
return err
}
}
return nil
}
// Clear clear the display
func (h *HD44780Driver) Clear() (err error) {
h.mutex.Lock()
defer h.mutex.Unlock()
return h.clear()
}
// Home return cursor to home
func (h *HD44780Driver) Home() (err error) {
h.mutex.Lock()
defer h.mutex.Unlock()
if err := h.sendCommand(HD44780_RETURNHOME); err != nil {
return err
}
time.Sleep(2 * time.Millisecond)
return nil
}
// SetCursor move the cursor to the specified position
func (h *HD44780Driver) SetCursor(col int, row int) (err error) {
h.mutex.Lock()
defer h.mutex.Unlock()
return h.setCursor(col, row)
}
// Display turn the display on and off
func (h *HD44780Driver) Display(on bool) (err error) {
h.mutex.Lock()
defer h.mutex.Unlock()
if on {
h.displayCtrl |= HD44780_DISPLAYON
} else {
h.displayCtrl &= ^HD44780_DISPLAYON
}
return h.sendCommand(HD44780_DISPLAYCONTROL | h.displayCtrl)
}
// Cursor turn the cursor on and off
func (h *HD44780Driver) Cursor(on bool) (err error) {
h.mutex.Lock()
defer h.mutex.Unlock()
if on {
h.displayCtrl |= HD44780_CURSORON
} else {
h.displayCtrl &= ^HD44780_CURSORON
}
return h.sendCommand(HD44780_DISPLAYCONTROL | h.displayCtrl)
}
// Blink turn the blink on and off
func (h *HD44780Driver) Blink(on bool) (err error) {
h.mutex.Lock()
defer h.mutex.Unlock()
if on {
h.displayCtrl |= HD44780_BLINKON
} else {
h.displayCtrl &= ^HD44780_BLINKON
}
return h.sendCommand(HD44780_DISPLAYCONTROL | h.displayCtrl)
}
// ScrollLeft scroll text left
func (h *HD44780Driver) ScrollLeft() (err error) {
h.mutex.Lock()
defer h.mutex.Unlock()
return h.sendCommand(HD44780_CURSORSHIFT | HD44780_DISPLAYMOVE | HD44780_MOVELEFT)
}
// ScrollRight scroll text right
func (h *HD44780Driver) ScrollRight() (err error) {
h.mutex.Lock()
defer h.mutex.Unlock()
return h.sendCommand(HD44780_CURSORSHIFT | HD44780_DISPLAYMOVE | HD44780_MOVERIGHT)
}
// LeftToRight display text from left to right
func (h *HD44780Driver) LeftToRight() (err error) {
h.mutex.Lock()
defer h.mutex.Unlock()
h.displayMode |= HD44780_ENTRYLEFT
return h.sendCommand(HD44780_ENTRYMODESET | h.displayMode)
}
// RightToLeft display text from right to left
func (h *HD44780Driver) RightToLeft() (err error) {
h.mutex.Lock()
defer h.mutex.Unlock()
h.displayMode &= ^HD44780_ENTRYLEFT
return h.sendCommand(HD44780_ENTRYMODESET | h.displayMode)
}
// SendCommand send control command
func (h *HD44780Driver) SendCommand(data int) (err error) {
h.mutex.Lock()
defer h.mutex.Unlock()
return h.sendCommand(data)
}
// WriteChar output a character to the display
func (h *HD44780Driver) WriteChar(data int) (err error) {
h.mutex.Lock()
defer h.mutex.Unlock()
return h.writeChar(data)
}
// CreateChar create custom character
func (h *HD44780Driver) CreateChar(pos int, charMap [8]byte) (err error) {
h.mutex.Lock()
defer h.mutex.Unlock()
if pos > 7 {
return errors.New("can't set a custom character at a position greater than 7")
}
if err := h.sendCommand(HD44780_SETCGRAMADDR | (pos << 3)); err != nil {
return err
}
for i := range charMap {
if err := h.writeChar(int(charMap[i])); err != nil {
return err
}
}
return nil
}
func (h *HD44780Driver) sendCommand(data int) (err error) {
if err := h.activateWriteMode(); err != nil {
return err
}
if err := h.pinRS.Off(); err != nil {
return err
}
if h.busMode == HD44780_4BITMODE {
if err := h.writeDataPins(data >> 4); err != nil {
return err
}
}
return h.writeDataPins(data)
}
func (h *HD44780Driver) writeChar(data int) (err error) {
if err := h.activateWriteMode(); err != nil {
return err
}
if err := h.pinRS.On(); err != nil {
return err
}
if h.busMode == HD44780_4BITMODE {
if err := h.writeDataPins(data >> 4); err != nil {
return err
}
}
return h.writeDataPins(data)
}
func (h *HD44780Driver) clear() (err error) {
if err := h.sendCommand(HD44780_CLEARDISPLAY); err != nil {
return err
}
// clear is time consuming, see documentation for JHD1313
// for lower clock speed it takes more time
time.Sleep(4 * time.Millisecond)
return nil
}
func (h *HD44780Driver) setCursor(col int, row int) (err error) {
if col < 0 || row < 0 || col >= h.cols || row >= h.rows {
return fmt.Errorf("Invalid position value (%d, %d), range (%d, %d)", col, row, h.cols-1, h.rows-1)
}
return h.sendCommand(HD44780_SETDDRAMADDR | col + h.rowOffsets[row])
}
func (h *HD44780Driver) writeDataPins(data int) (err error) {
for i, pin := range h.pinDataBits {
if ((data >> i) & 0x01) == 0x01 {
if err := pin.On(); err != nil {
return err
}
} else {
if err := pin.Off(); err != nil {
return err
}
}
}
return h.fallingEdge()
}
// fallingEdge creates falling edge to trigger data transmission
func (h *HD44780Driver) fallingEdge() (err error) {
if err := h.pinEN.On(); err != nil {
return err
}
time.Sleep(1 * time.Microsecond)
if err := h.pinEN.Off(); err != nil {
return err
}
// fastest write operation at 190kHz mode takes 53 us
time.Sleep(60 * time.Microsecond)
return nil
}
func (h *HD44780Driver) activateWriteMode() (err error) {
if h.pinRW == nil {
return
}
return h.pinRW.Off()
}