forked from hybridgroup/gobot
/
ssd1306_driver.go
337 lines (286 loc) · 8.53 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
package i2c
import (
"errors"
"image"
"gobot.io/x/gobot"
)
const ssd1306I2CAddress = 0x3c
const ssd1306Width = 128
const ssd1306Height = 64
const ssd1306PageSize = 8
const ssd1306SetMemoryAddressingMode = 0x20
const ssd1306SetComOutput0 = 0xC0
const ssd1306SetComOutput1 = 0xC1
const ssd1306SetComOutput2 = 0xC2
const ssd1306SetComOutput3 = 0xC3
const ssd1306SetComOutput4 = 0xC4
const ssd1306SetComOutput5 = 0xC5
const ssd1306SetComOutput6 = 0xC6
const ssd1306SetComOutput7 = 0xC7
const ssd1306SetComOutput8 = 0xC8
const ssd1306ColumnAddr = 0x21
const ssd1306PageAddr = 0x22
const ssd1306SetContrast = 0x81
const ssd1306SetSegmentRemap0 = 0xA0
const ssd1306SetSegmentRemap127 = 0xA1
const ssd1306DisplayOnResumeToRAM = 0xA4
const ssd1306SetDisplayNormal = 0xA6
const ssd1306SetDisplayInverse = 0xA7
const ssd1306SetDisplayOff = 0xAE
const ssd1306SetDisplayOn = 0xAF
const ssd1306ContinuousHScrollRight = 0x26
const ssd1306ContinuousHScrollLeft = 0x27
const ssd1306ContinuousVHScrollRight = 0x29
const ssd1306ContinuousVHScrollLeft = 0x2A
const ssd1306StopScroll = 0x2E
const ssd1306StartScroll = 0x2F
const ssd1306SetStartLine = 0x40
const ssd1306ChargePumpSetting = 0x8D
const ssd1306SetDisplayClock = 0xD5
const ssd1306SetMultiplexRatio = 0xA8
const ssd1306SetComPins = 0xDA
const ssd1306SetDisplayOffset = 0xD3
const ssd1306SetPrechargePeriod = 0xD9
const ssd1306SetVComDeselectLevel = 0xDB
var ssd1306InitSequence []byte = []byte{
ssd1306SetDisplayNormal,
ssd1306SetDisplayOff,
ssd1306SetDisplayClock, 0x80, // the suggested ratio 0x80
ssd1306SetMultiplexRatio, 0x3F,
ssd1306SetDisplayOffset, 0x0, //no offset
ssd1306SetStartLine | 0x0, //SETSTARTLINE
ssd1306ChargePumpSetting, 0x14,
ssd1306SetMemoryAddressingMode, 0x00, //0x0 act like ks0108
ssd1306SetSegmentRemap0,
ssd1306SetComOutput0,
ssd1306SetComPins, 0x12, //COMSCANDEC
ssd1306SetContrast, 0xCF,
ssd1306SetPrechargePeriod, 0xF1,
ssd1306SetVComDeselectLevel, 0x40,
ssd1306DisplayOnResumeToRAM,
ssd1306SetDisplayNormal,
ssd1306StopScroll,
ssd1306SetSegmentRemap0,
ssd1306SetSegmentRemap127,
ssd1306SetComOutput8,
ssd1306SetMemoryAddressingMode, 0x00,
ssd1306SetContrast, 0xff,
}
// DisplayBuffer represents the display buffer intermediate memory
type DisplayBuffer struct {
Width, Height int
buffer []byte
}
// NewDisplayBuffer creates a new DisplayBuffer
func NewDisplayBuffer(Width, Height int) *DisplayBuffer {
s := &DisplayBuffer{
Width: Width,
Height: Height,
}
s.buffer = make([]byte, s.Size())
return s
}
// Size returns the memory size of the display buffer
func (s *DisplayBuffer) Size() int {
return (s.Width * s.Height) / ssd1306PageSize
}
// Clear the contents of the display buffer
func (s *DisplayBuffer) Clear() {
s.buffer = make([]byte, s.Size())
}
// Set sets the x, y pixel with c color
func (s *DisplayBuffer) Set(x, y, c int) {
idx := x + (y/ssd1306PageSize)*s.Width
bit := uint(y) % ssd1306PageSize
if c == 0 {
s.buffer[idx] &= ^(1 << bit)
} else {
s.buffer[idx] |= (1 << bit)
}
}
// SSD1306Driver is a Gobot Driver for a SSD1306 Display
type SSD1306Driver struct {
name string
connector Connector
connection Connection
Config
gobot.Commander
DisplayWidth int
DisplayHeight 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
// WithDisplayWidth(int): width of display (defaults to 128)
// WithDisplayHeight(int): height of display (defaults to 64)
//
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,
}
for _, option := range options {
option(s)
}
s.Buffer = NewDisplayBuffer(s.DisplayWidth, s.DisplayHeight)
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 starts the Driver up, and writes start command
func (s *SSD1306Driver) Start() (err error) {
bus := s.GetBusOrDefault(s.connector.GetDefaultBus())
address := s.GetAddressOrDefault(ssd1306I2CAddress)
s.connection, err = s.connector.GetConnection(address, bus)
if err != nil {
return
}
s.Init()
s.On()
return
}
// Halt returns true if device is halted successfully
func (s *SSD1306Driver) Halt() (err error) { 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 {
// TODO: return error for trying to set DisplayWidth for non-SSD1306Driver
return
}
}
}
// 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 {
// TODO: return error for trying to set DisplayHeight for non-SSD1306Driver
return
}
}
}
// Init turns display on
func (s *SSD1306Driver) Init() (err error) {
s.Off()
s.commands(ssd1306InitSequence)
s.commands([]byte{ssd1306ColumnAddr, 0, // Start at 0,
byte(s.Buffer.Width) - 1, // End at last column (127?)
})
s.commands([]byte{ssd1306PageAddr, 0, // Start at 0,
(byte(s.Buffer.Height) / ssd1306PageSize) - 1, // End at page 7
})
return nil
}
// On turns display on
func (s *SSD1306Driver) On() (err error) {
return s.command(ssd1306SetDisplayOn)
}
// Off turns display off
func (s *SSD1306Driver) Off() (err error) {
return s.command(ssd1306SetDisplayOff)
}
// Clear clears
func (s *SSD1306Driver) Clear() (err error) {
s.Buffer.Clear()
return nil
}
// Set sets a pixel
func (s *SSD1306Driver) Set(x, y, c int) {
s.Buffer.Set(x, y, c)
}
// Reset sends the memory buffer to the display
func (s *SSD1306Driver) Reset() (err error) {
s.Off()
s.Clear()
s.On()
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) {
// Write the buffer
_, err = s.connection.Write(append([]byte{0x40}, s.Buffer.buffer...))
return err
}
// 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) {
_, err = s.connection.Write([]byte{0x80, b})
return
}
// commands sends a command sequence
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
}