Skip to content

Commit ef34c13

Browse files
ncwconejoninja
authored andcommitted
hd44780: add a mode to work with boards where the RW pin is grounded
On some HD44780 boards (eg the Keyestudio LCD1602 expansion shield), the RW pin isn't brought out and is permanently grounded. This means that the board can't be read from, and in particular the busy status can't be read. This patch adapts the package to work with boards like these. To signal this to the package, set the RW pin to machine.NoPin. The package will then disallow all reading and use adjustable timing based writing. The timing can be adjusted the configuration.
1 parent c64d792 commit ef34c13

File tree

2 files changed

+71
-10
lines changed

2 files changed

+71
-10
lines changed

hd44780/gpio.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,16 @@ func (g *GPIO) SetCommandMode(set bool) {
5757
}
5858
}
5959

60+
// WriteOnly is true if you passed rw in as machine.NoPin
61+
func (g *GPIO) WriteOnly() bool {
62+
return g.rw == machine.NoPin
63+
}
64+
6065
// Write writes len(data) bytes from data to display driver
6166
func (g *GPIO) Write(data []byte) (n int, err error) {
62-
g.rw.Low()
67+
if !g.WriteOnly() {
68+
g.rw.Low()
69+
}
6370
for _, d := range data {
6471
g.write(d)
6572
n++
@@ -89,6 +96,9 @@ func (g *GPIO) Read(data []byte) (n int, err error) {
8996
if len(data) == 0 {
9097
return 0, errors.New("length greater than 0 is required")
9198
}
99+
if g.WriteOnly() {
100+
return 0, errors.New("Read not supported if RW not wired")
101+
}
92102
g.rw.High()
93103
g.reconfigureGPIOMode(machine.PinInput)
94104
for i := 0; i < len(data); i++ {

hd44780/hd44780.go

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,23 @@ import (
1111
"time"
1212
)
1313

14+
const (
15+
// These are the default execution times for the Clear and
16+
// Home commands and everything else.
17+
//
18+
// These are used if RW is passed as machine.NoPin and ignored
19+
// otherwise.
20+
//
21+
// They are set conservatively here and can be tweaked in the
22+
// Config structure.
23+
DefaultClearHomeTime = 80 * time.Millisecond
24+
DefaultInstrExecTime = 80 * time.Microsecond
25+
)
26+
1427
type Buser interface {
1528
io.ReadWriter
1629
SetCommandMode(set bool)
30+
WriteOnly() bool
1731
}
1832

1933
type Device struct {
@@ -28,21 +42,28 @@ type Device struct {
2842

2943
cursor cursor
3044
busyStatus []byte
45+
46+
clearHomeTime time.Duration // time clear/home instructions might take
47+
instrExecTime time.Duration // time all other instructions might take
3148
}
3249

3350
type cursor struct {
3451
x, y uint8
3552
}
3653

3754
type Config struct {
38-
Width int16
39-
Height int16
40-
CursorBlink bool
41-
CursorOnOff bool
42-
Font uint8
55+
Width int16
56+
Height int16
57+
CursorBlink bool
58+
CursorOnOff bool
59+
Font uint8
60+
ClearHomeTime time.Duration // time clear/home instructions might take - use 0 for the default
61+
InstrExecTime time.Duration // time all other instructions might take - use 0 for the default
4362
}
4463

4564
// NewGPIO4Bit returns 4bit data length HD44780 driver. Datapins are LCD DB pins starting from DB4 to DB7
65+
//
66+
// If your device has RW set permanently to ground then pass in rw as machine.NoPin
4667
func NewGPIO4Bit(dataPins []machine.Pin, e, rs, rw machine.Pin) (Device, error) {
4768
const fourBitMode = 4
4869
if len(dataPins) != fourBitMode {
@@ -52,6 +73,8 @@ func NewGPIO4Bit(dataPins []machine.Pin, e, rs, rw machine.Pin) (Device, error)
5273
}
5374

5475
// NewGPIO8Bit returns 8bit data length HD44780 driver. Datapins are LCD DB pins starting from DB0 to DB7
76+
//
77+
// If your device has RW set permanently to ground then pass in rw as machine.NoPin
5578
func NewGPIO8Bit(dataPins []machine.Pin, e, rs, rw machine.Pin) (Device, error) {
5679
const eightBitMode = 8
5780
if len(dataPins) != eightBitMode {
@@ -68,6 +91,8 @@ func (d *Device) Configure(cfg Config) error {
6891
if d.width == 0 || d.height == 0 {
6992
return errors.New("width and height must be set")
7093
}
94+
d.clearHomeTime = cfg.ClearHomeTime
95+
d.instrExecTime = cfg.InstrExecTime
7196
memoryMap := uint8(ONE_LINE)
7297
if d.height > 1 {
7398
memoryMap = TWO_LINE
@@ -186,7 +211,7 @@ func (d *Device) SendCommand(command byte) {
186211
d.bus.SetCommandMode(true)
187212
d.bus.Write([]byte{command})
188213

189-
for d.Busy() {
214+
for d.busy(command == DISPLAY_CLEAR || command == CURSOR_HOME) {
190215
}
191216
}
192217

@@ -195,7 +220,7 @@ func (d *Device) sendData(data byte) {
195220
d.bus.SetCommandMode(false)
196221
d.bus.Write([]byte{data})
197222

198-
for d.Busy() {
223+
for d.busy(false) {
199224
}
200225
}
201226

@@ -207,13 +232,39 @@ func (d *Device) CreateCharacter(cgramAddr uint8, data []byte) {
207232
}
208233
}
209234

210-
// Busy returns true when hd447890 is busy
211-
func (d *Device) Busy() bool {
235+
// busy returns true when hd447890 is busy
236+
// or after the timeout specified
237+
func (d *Device) busy(longDelay bool) bool {
238+
if d.bus.WriteOnly() {
239+
// Can't read busy flag if write only, so sleep a bit then return
240+
if longDelay {
241+
// Note that we sleep like this so the default
242+
// time.Sleep is time.Sleep(constant) as
243+
// time.Sleep(variable) doesn't seem to work on AVR yet
244+
if d.clearHomeTime != 0 {
245+
time.Sleep(d.clearHomeTime)
246+
} else {
247+
time.Sleep(DefaultClearHomeTime)
248+
}
249+
} else {
250+
if d.instrExecTime != 0 {
251+
time.Sleep(d.instrExecTime)
252+
} else {
253+
time.Sleep(DefaultInstrExecTime)
254+
}
255+
}
256+
return false
257+
}
212258
d.bus.SetCommandMode(true)
213259
d.bus.Read(d.busyStatus)
214260
return (d.busyStatus[0] & BUSY) > 0
215261
}
216262

263+
// Busy returns true when hd447890 is busy
264+
func (d *Device) Busy() bool {
265+
return d.busy(false)
266+
}
267+
217268
// Size returns the current size of the display.
218269
func (d *Device) Size() (w, h int16) {
219270
return int16(d.width), int16(d.height)

0 commit comments

Comments
 (0)