forked from hybridgroup/gobot
/
ssd1306_driver.go
426 lines (392 loc) · 11.6 KB
/
ssd1306_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
package spi
import (
"errors"
"fmt"
"image"
"time"
"gobot.io/x/gobot"
"gobot.io/x/gobot/drivers/gpio"
)
const (
// default values
ssd1306Width = 128
ssd1306Height = 64
ssd1306DcPin = "16" // for raspberry pi
ssd1306RstPin = "18" // for raspberry pi
ssd1306ExternalVcc = false
ssd1306SetStartLine = 0x40
// fundamental commands
ssd1306SetContrast = 0x81
ssd1306DisplayOnResumeToRAM = 0xA4
ssd1306DisplayOnResume = 0xA5
ssd1306SetDisplayNormal = 0xA6
ssd1306SetDisplayInverse = 0xA7
ssd1306SetDisplayOff = 0xAE
ssd1306SetDisplayOn = 0xAF
// scrolling commands
ssd1306RightHorizontalScroll = 0x26
ssd1306LeftHorizontalScroll = 0x27
ssd1306VerticalAndRightHorizontalScroll = 0x29
ssd1306VerticalAndLeftHorizontalScroll = 0x2A
ssd1306DeactivateScroll = 0x2E
ssd1306ActivateScroll = 0x2F
ssd1306SetVerticalScrollArea = 0xA3
// addressing settings commands
ssd1306SetMemoryAddressingMode = 0x20
ssd1306ColumnAddr = 0x21
ssd1306PageAddr = 0x22
// hardware configuration commands
ssd1306SetSegmentRemap0 = 0xA0
ssd1306SetSegmentRemap127 = 0xA1
ssd1306SetMultiplexRatio = 0xA8
ssd1306ComScanInc = 0xC0
ssd1306ComScanDec = 0xC8
ssd1306SetDisplayOffset = 0xD3
ssd1306SetComPins = 0xDA
// timing and driving scheme commands
ssd1306SetDisplayClock = 0xD5
ssd1306SetPrechargePeriod = 0xD9
ssd1306SetVComDeselectLevel = 0xDB
ssd1306NOOP = 0xE3
// charge pump command
ssd1306ChargePumpSetting = 0x8D
)
// ssd1306Init configures the ssd1306 based on the options passed in when the driver was created
func (s *SSD1306Driver) ssd1306Init() {
s.command(ssd1306SetDisplayOff)
s.command(ssd1306SetDisplayClock)
if s.DisplayHeight == 16 {
s.command(0x60)
} else {
s.command(0x80)
}
s.command(ssd1306SetMultiplexRatio)
s.command(uint8(s.DisplayHeight) - 1)
s.command(ssd1306SetDisplayOffset)
s.command(0x0)
s.command(ssd1306SetStartLine)
s.command(0x0)
s.command(ssd1306ChargePumpSetting)
if s.ExternalVcc {
s.command(0x10)
} else {
s.command(0x14)
}
s.command(ssd1306SetMemoryAddressingMode)
s.command(0x00)
s.command(ssd1306SetSegmentRemap0)
s.command(0x01)
s.command(ssd1306ComScanInc)
s.command(ssd1306SetComPins)
if s.DisplayHeight == 64 {
s.command(0x12)
} else {
s.command(0x02)
}
s.command(ssd1306SetContrast)
if s.DisplayHeight == 64 {
if s.ExternalVcc {
s.command(0x9F)
} else {
s.command(0xCF)
}
} else {
s.command(0x8F)
}
s.command(ssd1306SetPrechargePeriod)
if s.ExternalVcc {
s.command(0x22)
} else {
s.command(0xF1)
}
s.command(ssd1306SetVComDeselectLevel)
s.command(0x40)
s.command(ssd1306DisplayOnResumeToRAM)
s.command(ssd1306SetDisplayNormal)
s.command(ssd1306DeactivateScroll)
s.command(ssd1306SetDisplayOn)
}
// DisplayBuffer represents the display buffer intermediate memory
type DisplayBuffer struct {
width, height, pageSize int
buffer []byte
}
// NewDisplayBuffer creates a new DisplayBuffer
func NewDisplayBuffer(width, height, pageSize int) *DisplayBuffer {
s := &DisplayBuffer{
width: width,
height: height,
pageSize: pageSize,
}
s.buffer = make([]byte, s.Size())
return s
}
// Size returns the memory size of the display buffer
func (d *DisplayBuffer) Size() int {
return (d.width * d.height) / d.pageSize
}
// Clear the contents of the display buffer
func (d *DisplayBuffer) Clear() {
d.buffer = make([]byte, d.Size())
}
// SetPixel sets the x, y pixel with c color
func (d *DisplayBuffer) SetPixel(x, y, c int) {
idx := x + (y/d.pageSize)*d.width
bit := uint(y) % uint(d.pageSize)
if c == 0 {
d.buffer[idx] &= ^(1 << bit)
} else {
d.buffer[idx] |= (1 << bit)
}
}
// Set sets the display buffer with the given buffer
func (d *DisplayBuffer) Set(buf []byte) {
d.buffer = buf
}
// SSD1306Driver is a Gobot Driver for a SSD1306 Display
type SSD1306Driver struct {
name string
connector Connector
connection Connection
dcDriver *gpio.DirectPinDriver
rstDriver *gpio.DirectPinDriver
pageSize int
DisplayWidth int
DisplayHeight int
DCPin string
RSTPin string
ExternalVcc bool
buffer *DisplayBuffer
Config
gobot.Commander
}
// NewSSD1306Driver creates a new SSD1306Driver.
//
// Params:
// conn Connector - the Adaptor to use with this Driver
//
// Optional params:
// spi.WithBus(int): bus to use with this driver
// spi.WithChip(int): chip to use with this driver
// spi.WithMode(int): mode to use with this driver
// spi.WithBits(int): number of bits to use with this driver
// spi.WithSpeed(int64): speed in Hz to use with this driver
// spi.WithDisplayWidth(int): width of display (defaults to 128)
// spi.WithDisplayHeight(int): height of display (defaults to 64)
// spi.WithDCPin(string): gpio pin number connected to dc pin on display (defaults to "16")
// spi.WithRstPin(string): gpio pin number connected to rst pin on display (defaults to "18")
// spi.WithExternalVCC(bool): set to true if using external vcc (defaults to false)
//
func NewSSD1306Driver(a gobot.Adaptor, options ...func(Config)) *SSD1306Driver {
// cast adaptor to spi connector since we also need the adaptor for gpio
b, ok := a.(Connector)
if !ok {
panic("unable to get gobot connector for ssd1306")
}
s := &SSD1306Driver{
name: gobot.DefaultName("SSD1306"),
Commander: gobot.NewCommander(),
connector: b,
DisplayWidth: ssd1306Width,
DisplayHeight: ssd1306Height,
DCPin: ssd1306DcPin,
RSTPin: ssd1306RstPin,
ExternalVcc: ssd1306ExternalVcc,
Config: NewConfig(),
}
for _, option := range options {
option(s)
}
s.dcDriver = gpio.NewDirectPinDriver(a, s.DCPin)
s.rstDriver = gpio.NewDirectPinDriver(a, s.RSTPin)
s.pageSize = s.DisplayHeight / 8
s.buffer = NewDisplayBuffer(s.DisplayWidth, s.DisplayHeight, s.pageSize)
s.AddCommand("Display", func(params map[string]interface{}) interface{} {
err := s.Display()
return map[string]interface{}{"err": err}
})
s.AddCommand("On", func(params map[string]interface{}) interface{} {
err := s.On()
return map[string]interface{}{"err": err}
})
s.AddCommand("Off", func(params map[string]interface{}) interface{} {
err := s.Off()
return map[string]interface{}{"err": err}
})
s.AddCommand("Clear", func(params map[string]interface{}) interface{} {
err := s.Clear()
return map[string]interface{}{"err": err}
})
s.AddCommand("SetContrast", func(params map[string]interface{}) interface{} {
contrast := byte(params["contrast"].(byte))
err := s.SetContrast(contrast)
return map[string]interface{}{"err": err}
})
s.AddCommand("Set", func(params map[string]interface{}) interface{} {
x := int(params["x"].(int))
y := int(params["y"].(int))
c := int(params["c"].(int))
s.Set(x, y, c)
return nil
})
return s
}
// Name returns the Name for the Driver
func (s *SSD1306Driver) Name() string { return s.name }
// SetName sets the Name for the Driver
func (s *SSD1306Driver) SetName(n string) { s.name = n }
// Connection returns the connection for the Driver
func (s *SSD1306Driver) Connection() gobot.Connection { return s.connector.(gobot.Connection) }
// Start sets up the needed connection, and initialized the device.
func (s *SSD1306Driver) Start() (err error) {
bus := s.GetBusOrDefault(s.connector.GetSpiDefaultBus())
chip := s.GetChipOrDefault(s.connector.GetSpiDefaultChip())
mode := s.GetModeOrDefault(s.connector.GetSpiDefaultMode())
bits := s.GetBitsOrDefault(s.connector.GetSpiDefaultBits())
maxSpeed := s.GetSpeedOrDefault(s.connector.GetSpiDefaultMaxSpeed())
s.connection, err = s.connector.GetSpiConnection(bus, chip, mode, bits, maxSpeed)
if err != nil {
return err
}
s.ssd1306Init()
return
}
// Halt returns true if device is halted successfully.
func (s *SSD1306Driver) Halt() (err error) {
s.Reset()
s.Off()
return nil
}
// WithDisplayWidth option sets the SSD1306Driver DisplayWidth option.
func WithDisplayWidth(val int) func(Config) {
return func(c Config) {
d, ok := c.(*SSD1306Driver)
if ok {
d.DisplayWidth = val
} else {
panic("unable to set display width for ssd1306")
}
}
}
// WithDisplayHeight option sets the SSD1306Driver DisplayHeight option.
func WithDisplayHeight(val int) func(Config) {
return func(c Config) {
d, ok := c.(*SSD1306Driver)
if ok {
d.DisplayHeight = val
} else {
panic("unable to set display height for ssd1306")
}
}
}
// WithDCPin option sets the SSD1306Driver DC Pin option.
func WithDCPin(val string) func(Config) {
return func(c Config) {
d, ok := c.(*SSD1306Driver)
if ok {
d.DCPin = val
} else {
panic("unable to set dc pin for ssd1306")
}
}
}
// WithRstPin option sets the SSD1306Driver RST pin option.
func WithRstPin(val string) func(Config) {
return func(c Config) {
d, ok := c.(*SSD1306Driver)
if ok {
d.RSTPin = val
} else {
panic("unable to set rst pin for ssd1306")
}
}
}
// WithExternalVCC option sets the SSD1306Driver external vcc option.
func WithExternalVCC(val bool) func(Config) {
return func(c Config) {
d, ok := c.(*SSD1306Driver)
if ok {
d.ExternalVcc = val
} else {
panic("unable to set rst pin for ssd1306")
}
}
}
// On turns on the display.
func (s *SSD1306Driver) On() (err error) {
return s.command(ssd1306SetDisplayOn)
}
// Off turns off the display.
func (s *SSD1306Driver) Off() (err error) {
return s.command(ssd1306SetDisplayOff)
}
// Clear clears the display buffer.
func (s *SSD1306Driver) Clear() (err error) {
s.buffer.Clear()
return nil
}
// Set sets a pixel in the display buffer.
func (s *SSD1306Driver) Set(x, y, c int) {
s.buffer.SetPixel(x, y, c)
}
// Reset re-initializes the device to a clean state.
func (s *SSD1306Driver) Reset() (err error) {
s.rstDriver.DigitalWrite(1)
time.Sleep(10 * time.Millisecond)
s.rstDriver.DigitalWrite(0)
time.Sleep(10 * time.Millisecond)
s.rstDriver.DigitalWrite(1)
return nil
}
// SetBufferAndDisplay sets the display buffer with the given buffer and displays the image.
func (s *SSD1306Driver) SetBufferAndDisplay(buf []byte) (err error) {
s.buffer.Set(buf)
return s.Display()
}
// SetContrast sets the display contrast (0-255).
func (s *SSD1306Driver) SetContrast(contrast byte) (err error) {
if contrast < 0 || contrast > 255 {
return fmt.Errorf("contrast value must be between 0-255")
}
if err = s.command(ssd1306SetContrast); err != nil {
return err
}
return s.command(contrast)
}
// Display sends the memory buffer to the display.
func (s *SSD1306Driver) Display() (err error) {
s.command(ssd1306ColumnAddr)
s.command(0)
s.command(uint8(s.DisplayWidth) - 1)
s.command(ssd1306PageAddr)
s.command(0)
s.command(uint8(s.pageSize) - 1)
if err = s.dcDriver.DigitalWrite(1); err != nil {
return err
}
return s.connection.Tx(append([]byte{0x40}, s.buffer.buffer...), nil)
}
// ShowImage takes a standard Go image and shows it on the display in monochrome.
func (s *SSD1306Driver) ShowImage(img image.Image) (err error) {
if img.Bounds().Dx() != s.DisplayWidth || img.Bounds().Dy() != s.DisplayHeight {
return errors.New("Image must match the display width and height")
}
s.Clear()
for y, w, h := 0, img.Bounds().Dx(), img.Bounds().Dy(); y < h; y++ {
for x := 0; x < w; x++ {
c := img.At(x, y)
if r, g, b, _ := c.RGBA(); r > 0 || g > 0 || b > 0 {
s.Set(x, y, 1)
}
}
}
return s.Display()
}
// command sends a unique command
func (s *SSD1306Driver) command(b byte) (err error) {
if err = s.dcDriver.DigitalWrite(0); err != nil {
return err
}
err = s.connection.Tx([]byte{b}, nil)
return err
}