forked from hybridgroup/gobot
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathssd1306_driver.go
427 lines (392 loc) · 11.8 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
427
package i2c
import (
"fmt"
"image"
"gobot.io/x/gobot"
)
// register addresses for the ssd1306
const (
// default values
ssd1306Width = 128
ssd1306Height = 64
ssd1306ExternalVCC = false
ssd1306SetStartLine = 0x40
ssd1306I2CAddress = 0x3c
// fundamental commands
ssd1306SetComOutput0 = 0xC0
ssd1306SetComOutput1 = 0xC1
ssd1306SetComOutput2 = 0xC2
ssd1306SetComOutput3 = 0xC3
ssd1306SetComOutput4 = 0xC4
ssd1306SetComOutput5 = 0xC5
ssd1306SetComOutput6 = 0xC6
ssd1306SetComOutput7 = 0xC7
ssd1306SetComOutput8 = 0xC8
ssd1306SetContrast = 0x81
// scrolling commands
ssd1306ContinuousHScrollRight = 0x26
ssd1306ContinuousHScrollLeft = 0x27
ssd1306ContinuousVHScrollRight = 0x29
ssd1306ContinuousVHScrollLeft = 0x2A
ssd1306StopScroll = 0x2E
ssd1306StartScroll = 0x2F
// adressing settings commands
ssd1306SetMemoryAddressingMode = 0x20
ssd1306ColumnAddr = 0x21
ssd1306PageAddr = 0x22
// hardware configuration commands
ssd1306SetSegmentRemap0 = 0xA0
ssd1306SetSegmentRemap127 = 0xA1
ssd1306DisplayOnResumeToRAM = 0xA4
ssd1306SetDisplayNormal = 0xA6
ssd1306SetDisplayInverse = 0xA7
ssd1306SetDisplayOff = 0xAE
ssd1306SetDisplayOn = 0xAF
// timing and driving scheme commands
ssd1306SetDisplayClock = 0xD5
ssd1306SetPrechargePeriod = 0xD9
ssd1306SetVComDeselectLevel = 0xDB
ssd1306SetMultiplexRatio = 0xA8
ssd1306SetComPins = 0xDA
ssd1306SetDisplayOffset = 0xD3
// charge pump command
ssd1306ChargePumpSetting = 0x8D
)
// SSD1306Init contains the initialization settings for the ssd1306 display.
type SSD1306Init struct {
displayClock byte
multiplexRatio byte
displayOffset byte
startLine byte
chargePumpSetting byte
memoryAddressingMode byte
comPins byte
contrast byte
prechargePeriod byte
vComDeselectLevel byte
}
// GetSequence returns the initialization sequence for the ssd1306 display.
func (i *SSD1306Init) GetSequence() []byte {
return []byte{
ssd1306SetDisplayNormal,
ssd1306SetDisplayOff,
ssd1306SetDisplayClock, i.displayClock,
ssd1306SetMultiplexRatio, i.multiplexRatio,
ssd1306SetDisplayOffset, i.displayOffset,
ssd1306SetStartLine | i.startLine,
ssd1306ChargePumpSetting, i.chargePumpSetting,
ssd1306SetMemoryAddressingMode, i.memoryAddressingMode,
ssd1306SetSegmentRemap0,
ssd1306SetComOutput0,
ssd1306SetComPins, i.comPins,
ssd1306SetContrast, i.contrast,
ssd1306SetPrechargePeriod, i.prechargePeriod,
ssd1306SetVComDeselectLevel, i.vComDeselectLevel,
ssd1306DisplayOnResumeToRAM,
ssd1306SetDisplayNormal,
}
}
// 128x64 init sequence
var ssd1306Init128x64 = &SSD1306Init{
displayClock: 0x80,
multiplexRatio: 0x3F,
displayOffset: 0x00,
startLine: 0x00,
chargePumpSetting: 0x14, // 0x10 if external vcc is set
memoryAddressingMode: 0x00,
comPins: 0x12,
contrast: 0xCF, // 0x9F if external vcc is set
prechargePeriod: 0xF1, // 0x22 if external vcc is set
vComDeselectLevel: 0x40,
}
// 128x32 init sequence
var ssd1306Init128x32 = &SSD1306Init{
displayClock: 0x80,
multiplexRatio: 0x1F,
displayOffset: 0x00,
startLine: 0x00,
chargePumpSetting: 0x14, // 0x10 if external vcc is set
memoryAddressingMode: 0x00,
comPins: 0x02,
contrast: 0x8F, // 0x9F if external vcc is set
prechargePeriod: 0xF1, // 0x22 if external vcc is set
vComDeselectLevel: 0x40,
}
// 96x16 init sequence
var ssd1306Init96x16 = &SSD1306Init{
displayClock: 0x60,
multiplexRatio: 0x0F,
displayOffset: 0x00,
startLine: 0x00,
chargePumpSetting: 0x14, // 0x10 if external vcc is set
memoryAddressingMode: 0x00,
comPins: 0x02,
contrast: 0x8F, // 0x9F if external vcc is set
prechargePeriod: 0xF1, // 0x22 if external vcc is set
vComDeselectLevel: 0x40,
}
// 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 {
d := &DisplayBuffer{
width: width,
height: height,
pageSize: pageSize,
}
d.buffer = make([]byte, d.Size())
return d
}
// 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
Config
gobot.Commander
initSequence *SSD1306Init
displayWidth int
displayHeight int
externalVCC bool
pageSize int
buffer *DisplayBuffer
}
// NewSSD1306Driver creates a new SSD1306Driver.
//
// Params:
// conn Connector - the Adaptor to use with this Driver
//
// Optional params:
// WithBus(int): bus to use with this driver
// WithAddress(int): address to use with this driver
// WithSSD1306DisplayWidth(int): width of display (defaults to 128)
// WithSSD1306DisplayHeight(int): height of display (defaults to 64)
// WithSSD1306ExternalVCC: set true when using an external OLED supply (defaults to false)
//
func NewSSD1306Driver(a Connector, options ...func(Config)) *SSD1306Driver {
s := &SSD1306Driver{
name: gobot.DefaultName("SSD1306"),
Commander: gobot.NewCommander(),
connector: a,
Config: NewConfig(),
displayHeight: ssd1306Height,
displayWidth: ssd1306Width,
externalVCC: ssd1306ExternalVCC,
}
// set options
for _, option := range options {
option(s)
}
// set page size
s.pageSize = 8
// set display buffer
s.buffer = NewDisplayBuffer(s.displayWidth, s.displayHeight, s.pageSize)
// add commands
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{} {
s.Clear()
return map[string]interface{}{}
})
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 starts the Driver up, and writes start command
func (s *SSD1306Driver) Start() (err error) {
// check device size for supported resolutions
switch {
case s.displayWidth == 128 && s.displayHeight == 64:
s.initSequence = ssd1306Init128x64
case s.displayWidth == 128 && s.displayHeight == 32:
s.initSequence = ssd1306Init128x32
case s.displayWidth == 96 && s.displayHeight == 16:
s.initSequence = ssd1306Init96x16
default:
return fmt.Errorf("%dx%d resolution is unsupported, supported resolutions: 128x64, 128x32, 96x16", s.displayWidth, s.displayHeight)
}
// check for external vcc
if s.externalVCC {
s.initSequence.chargePumpSetting = 0x10
s.initSequence.contrast = 0x9F
s.initSequence.prechargePeriod = 0x22
}
bus := s.GetBusOrDefault(s.connector.GetDefaultBus())
address := s.GetAddressOrDefault(ssd1306I2CAddress)
s.connection, err = s.connector.GetConnection(address, bus)
if err != nil {
return err
}
if err = s.Init(); err != nil {
return err
}
if err = s.On(); err != nil {
return err
}
return nil
}
// Halt returns true if device is halted successfully
func (s *SSD1306Driver) Halt() (err error) { return nil }
// WithSSD1306DisplayWidth option sets the SSD1306Driver DisplayWidth option.
func WithSSD1306DisplayWidth(val int) func(Config) {
return func(c Config) {
d, ok := c.(*SSD1306Driver)
if ok {
d.displayWidth = val
}
}
}
// WithSSD1306DisplayHeight option sets the SSD1306Driver DisplayHeight option.
func WithSSD1306DisplayHeight(val int) func(Config) {
return func(c Config) {
d, ok := c.(*SSD1306Driver)
if ok {
d.displayHeight = val
}
}
}
// WithSSD1306ExternalVCC option sets the SSD1306Driver ExternalVCC option.
func WithSSD1306ExternalVCC(val bool) func(Config) {
return func(c Config) {
d, ok := c.(*SSD1306Driver)
if ok {
d.externalVCC = val
}
}
}
// Init initializes the ssd1306 display.
func (s *SSD1306Driver) Init() (err error) {
// turn off screen
if err = s.Off(); err != nil {
return err
}
// run through initialization commands
if err = s.commands(s.initSequence.GetSequence()); err != nil {
return err
}
if err = s.commands([]byte{ssd1306ColumnAddr, 0, byte(s.buffer.width) - 1}); err != nil {
return err
}
if err = s.commands([]byte{ssd1306PageAddr, 0, (byte(s.buffer.height / s.pageSize)) - 1}); err != nil {
return err
}
return nil
}
// 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() {
s.buffer.Clear()
}
// Set sets a pixel in the buffer.
func (s *SSD1306Driver) Set(x, y, c int) {
s.buffer.SetPixel(x, y, c)
}
// Reset clears display.
func (s *SSD1306Driver) Reset() (err error) {
if err = s.Off(); err != nil {
return err
}
s.Clear()
if err = s.On(); err != nil {
return err
}
return nil
}
// SetContrast sets the display contrast.
func (s *SSD1306Driver) SetContrast(contrast byte) (err error) {
err = s.commands([]byte{ssd1306SetContrast, contrast})
return
}
// Display sends the memory buffer to the display.
func (s *SSD1306Driver) Display() (err error) {
_, err = s.connection.Write(append([]byte{0x40}, s.buffer.buffer...))
return err
}
// ShowImage takes a standard Go image and displays it in monochrome.
func (s *SSD1306Driver) ShowImage(img image.Image) (err error) {
if img.Bounds().Dx() != s.displayWidth || img.Bounds().Dy() != s.displayHeight {
return fmt.Errorf("image must match display width and height: %dx%d", s.displayWidth, s.displayHeight)
}
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 command to the ssd1306
func (s *SSD1306Driver) command(b byte) (err error) {
_, err = s.connection.Write([]byte{0x80, b})
return err
}
// commands sends a command sequence to the ssd1306
func (s *SSD1306Driver) commands(commands []byte) (err error) {
var command []byte
for _, d := range commands {
command = append(command, []byte{0x80, d}...)
}
_, err = s.connection.Write(command)
return err
}