@@ -6,7 +6,6 @@ package ssd1306 // import "tinygo.org/x/drivers/ssd1306"
6
6
import (
7
7
"errors"
8
8
"image/color"
9
- "machine"
10
9
"time"
11
10
12
11
"tinygo.org/x/drivers"
@@ -22,16 +21,15 @@ type ResetValue [2]byte
22
21
23
22
// Device wraps I2C or SPI connection.
24
23
type Device struct {
25
- bus Buser
26
- buffer []byte
27
- width int16
28
- height int16
29
- bufferSize int16
30
- vccState VccMode
31
- canReset bool
32
- resetCol ResetValue
33
- resetPage ResetValue
34
- rotation drivers.Rotation
24
+ bus Buser
25
+ buffer []byte
26
+ width int16
27
+ height int16
28
+ vccState VccMode
29
+ canReset bool
30
+ resetCol ResetValue
31
+ resetPage ResetValue
32
+ rotation drivers.Rotation
35
33
}
36
34
37
35
// Config is the configuration for the display
@@ -50,51 +48,14 @@ type Config struct {
50
48
Rotation drivers.Rotation
51
49
}
52
50
53
- type I2CBus struct {
54
- wire drivers.I2C
55
- Address uint16
56
- }
57
-
58
- type SPIBus struct {
59
- wire drivers.SPI
60
- dcPin machine.Pin
61
- resetPin machine.Pin
62
- csPin machine.Pin
63
- }
64
-
65
51
type Buser interface {
66
- configure () error
67
- tx ( data [] byte , isCommand bool ) error
68
- setAddress ( address uint16 ) error
52
+ configure (address uint16 , size int16 ) [] byte // configure the bus with the given configuration and return the buffer to use
53
+ command ( cmd uint8 ) error // send a command to the display
54
+ flush ( ) error // send the data in the buffer to the display
69
55
}
70
56
71
57
type VccMode uint8
72
58
73
- // NewI2C creates a new SSD1306 connection. The I2C wire must already be configured.
74
- func NewI2C (bus drivers.I2C ) Device {
75
- return Device {
76
- bus : & I2CBus {
77
- wire : bus ,
78
- Address : Address ,
79
- },
80
- }
81
- }
82
-
83
- // NewSPI creates a new SSD1306 connection. The SPI wire must already be configured.
84
- func NewSPI (bus drivers.SPI , dcPin , resetPin , csPin machine.Pin ) Device {
85
- dcPin .Configure (machine.PinConfig {Mode : machine .PinOutput })
86
- resetPin .Configure (machine.PinConfig {Mode : machine .PinOutput })
87
- csPin .Configure (machine.PinConfig {Mode : machine .PinOutput })
88
- return Device {
89
- bus : & SPIBus {
90
- wire : bus ,
91
- dcPin : dcPin ,
92
- resetPin : resetPin ,
93
- csPin : csPin ,
94
- },
95
- }
96
- }
97
-
98
59
// Configure initializes the display with default configuration
99
60
func (d * Device ) Configure (cfg Config ) {
100
61
var zeroReset ResetValue
@@ -108,9 +69,6 @@ func (d *Device) Configure(cfg Config) {
108
69
} else {
109
70
d .height = 64
110
71
}
111
- if cfg .Address != 0 {
112
- d .bus .setAddress (cfg .Address )
113
- }
114
72
if cfg .VccState != 0 {
115
73
d .vccState = cfg .VccState
116
74
} else {
@@ -126,77 +84,75 @@ func (d *Device) Configure(cfg Config) {
126
84
} else {
127
85
d .resetPage = ResetValue {0 , uint8 (d .height / 8 ) - 1 }
128
86
}
129
- d .bufferSize = d .width * d .height / 8
130
- d .buffer = make ([]byte , d .bufferSize + 1 ) // +1 for the I2C command byte
131
87
d .canReset = cfg .Address != 0 || d .width != 128 || d .height != 64 // I2C or not 128x64
132
88
133
- d .bus .configure ()
89
+ d .buffer = d . bus .configure (cfg . Address , d . width * d . height / 8 )
134
90
135
91
time .Sleep (100 * time .Nanosecond )
136
- d .Command (DISPLAYOFF )
137
- d .Command (SETDISPLAYCLOCKDIV )
138
- d .Command (0x80 )
139
- d .Command (SETMULTIPLEX )
140
- d .Command (uint8 (d .height - 1 ))
141
- d .Command (SETDISPLAYOFFSET )
142
- d .Command (0x0 )
143
- d .Command (SETSTARTLINE | 0x0 )
144
- d .Command (CHARGEPUMP )
92
+ d .bus . command (DISPLAYOFF )
93
+ d .bus . command (SETDISPLAYCLOCKDIV )
94
+ d .bus . command (0x80 )
95
+ d .bus . command (SETMULTIPLEX )
96
+ d .bus . command (uint8 (d .height - 1 ))
97
+ d .bus . command (SETDISPLAYOFFSET )
98
+ d .bus . command (0x0 )
99
+ d .bus . command (SETSTARTLINE | 0x0 )
100
+ d .bus . command (CHARGEPUMP )
145
101
if d .vccState == EXTERNALVCC {
146
- d .Command (0x10 )
102
+ d .bus . command (0x10 )
147
103
} else {
148
- d .Command (0x14 )
104
+ d .bus . command (0x14 )
149
105
}
150
- d .Command (MEMORYMODE )
151
- d .Command (0x00 )
106
+ d .bus . command (MEMORYMODE )
107
+ d .bus . command (0x00 )
152
108
153
109
d .SetRotation (cfg .Rotation )
154
110
155
111
if (d .width == 128 && d .height == 64 ) || (d .width == 64 && d .height == 48 ) { // 128x64 or 64x48
156
- d .Command (SETCOMPINS )
157
- d .Command (0x12 )
158
- d .Command (SETCONTRAST )
112
+ d .bus . command (SETCOMPINS )
113
+ d .bus . command (0x12 )
114
+ d .bus . command (SETCONTRAST )
159
115
if d .vccState == EXTERNALVCC {
160
- d .Command (0x9F )
116
+ d .bus . command (0x9F )
161
117
} else {
162
- d .Command (0xCF )
118
+ d .bus . command (0xCF )
163
119
}
164
120
} else if d .width == 128 && d .height == 32 { // 128x32
165
- d .Command (SETCOMPINS )
166
- d .Command (0x02 )
167
- d .Command (SETCONTRAST )
168
- d .Command (0x8F )
121
+ d .bus . command (SETCOMPINS )
122
+ d .bus . command (0x02 )
123
+ d .bus . command (SETCONTRAST )
124
+ d .bus . command (0x8F )
169
125
} else if d .width == 96 && d .height == 16 { // 96x16
170
- d .Command (SETCOMPINS )
171
- d .Command (0x2 )
172
- d .Command (SETCONTRAST )
126
+ d .bus . command (SETCOMPINS )
127
+ d .bus . command (0x2 )
128
+ d .bus . command (SETCONTRAST )
173
129
if d .vccState == EXTERNALVCC {
174
- d .Command (0x10 )
130
+ d .bus . command (0x10 )
175
131
} else {
176
- d .Command (0xAF )
132
+ d .bus . command (0xAF )
177
133
}
178
134
} else {
179
135
// fail silently, it might work
180
136
println ("there's no configuration for this display's size" )
181
137
}
182
138
183
- d .Command (SETPRECHARGE )
139
+ d .bus . command (SETPRECHARGE )
184
140
if d .vccState == EXTERNALVCC {
185
- d .Command (0x22 )
141
+ d .bus . command (0x22 )
186
142
} else {
187
- d .Command (0xF1 )
143
+ d .bus . command (0xF1 )
188
144
}
189
- d .Command (SETVCOMDETECT )
190
- d .Command (0x40 )
191
- d .Command (DISPLAYALLON_RESUME )
192
- d .Command (NORMALDISPLAY )
193
- d .Command (DEACTIVATE_SCROLL )
194
- d .Command (DISPLAYON )
145
+ d .bus . command (SETVCOMDETECT )
146
+ d .bus . command (0x40 )
147
+ d .bus . command (DISPLAYALLON_RESUME )
148
+ d .bus . command (NORMALDISPLAY )
149
+ d .bus . command (DEACTIVATE_SCROLL )
150
+ d .bus . command (DISPLAYON )
195
151
}
196
152
197
153
// ClearBuffer clears the image buffer
198
154
func (d * Device ) ClearBuffer () {
199
- for i := int16 ( 0 ) ; i < d . bufferSize ; i ++ {
155
+ for i := 0 ; i < len ( d . buffer ) ; i ++ {
200
156
d .buffer [i ] = 0
201
157
}
202
158
}
@@ -214,15 +170,15 @@ func (d *Device) Display() error {
214
170
// In the 128x64 (SPI) screen resetting to 0x0 after 128 times corrupt the buffer
215
171
// Since we're printing the whole buffer, avoid resetting it in this case
216
172
if d .canReset {
217
- d .Command (COLUMNADDR )
218
- d .Command (d .resetCol [0 ])
219
- d .Command (d .resetCol [1 ])
220
- d .Command (PAGEADDR )
221
- d .Command (d .resetPage [0 ])
222
- d .Command (d .resetPage [1 ])
173
+ d .bus . command (COLUMNADDR )
174
+ d .bus . command (d .resetCol [0 ])
175
+ d .bus . command (d .resetCol [1 ])
176
+ d .bus . command (PAGEADDR )
177
+ d .bus . command (d .resetPage [0 ])
178
+ d .bus . command (d .resetPage [1 ])
223
179
}
224
180
225
- return d .Tx ( d . buffer , false )
181
+ return d .bus . flush ( )
226
182
}
227
183
228
184
// SetPixel enables or disables a pixel in the buffer
@@ -251,12 +207,10 @@ func (d *Device) GetPixel(x int16, y int16) bool {
251
207
252
208
// SetBuffer changes the whole buffer at once
253
209
func (d * Device ) SetBuffer (buffer []byte ) error {
254
- if int16 ( len (buffer )) != d . bufferSize {
210
+ if len (buffer ) != len ( d . buffer ) {
255
211
return errBufferSize
256
212
}
257
- for i := int16 (0 ); i < d .bufferSize ; i ++ {
258
- d .buffer [i ] = buffer [i ]
259
- }
213
+ copy (d .buffer , buffer )
260
214
return nil
261
215
}
262
216
@@ -265,72 +219,6 @@ func (d *Device) GetBuffer() []byte {
265
219
return d .buffer
266
220
}
267
221
268
- // Command sends a command to the display
269
- func (d * Device ) Command (command uint8 ) {
270
- d .buffer [1 ] = command // The second byte is the actual command
271
- d .bus .tx (d .buffer [0 :2 ], true )
272
- }
273
-
274
- // setAddress sets the address to the I2C bus
275
- func (b * I2CBus ) setAddress (address uint16 ) error {
276
- b .Address = address
277
- return nil
278
- }
279
-
280
- // setAddress does nothing, but it's required to avoid reflection
281
- func (b * SPIBus ) setAddress (address uint16 ) error {
282
- // do nothing
283
- println ("trying to Configure an address on a SPI device" )
284
- return nil
285
- }
286
-
287
- // configure does nothing, but it's required to avoid reflection
288
- func (b * I2CBus ) configure () error { return nil }
289
-
290
- // configure configures some pins with the SPI bus
291
- func (b * SPIBus ) configure () error {
292
- b .csPin .Low ()
293
- b .dcPin .Low ()
294
- b .resetPin .Low ()
295
-
296
- b .resetPin .High ()
297
- time .Sleep (1 * time .Millisecond )
298
- b .resetPin .Low ()
299
- time .Sleep (10 * time .Millisecond )
300
- b .resetPin .High ()
301
-
302
- return nil
303
- }
304
-
305
- // Tx sends data to the display
306
- func (d * Device ) Tx (data []byte , isCommand bool ) error {
307
- return d .bus .tx (data , isCommand )
308
- }
309
-
310
- // tx sends data to the display (I2CBus implementation)
311
- func (b * I2CBus ) tx (data []byte , isCommand bool ) error {
312
- if isCommand {
313
- data [0 ] = 0x00 // Command mode
314
- } else {
315
- data [0 ] = 0x40 // Data mode
316
- }
317
- return b .wire .Tx (uint16 (b .Address ), data , nil )
318
- }
319
-
320
- // tx sends data to the display (SPIBus implementation)
321
- func (b * SPIBus ) tx (data []byte , isCommand bool ) error {
322
- b .csPin .High ()
323
- if isCommand {
324
- b .dcPin .Low ()
325
- } else {
326
- b .dcPin .High ()
327
- }
328
- b .csPin .Low ()
329
- err := b .wire .Tx (data [1 :], nil ) // The first byte is reserved for I2C communcation, strip it
330
- b .csPin .High ()
331
- return err
332
- }
333
-
334
222
// Size returns the current size of the display.
335
223
func (d * Device ) Size () (w , h int16 ) {
336
224
return d .width , d .height
@@ -362,15 +250,15 @@ func (d *Device) SetRotation(rotation drivers.Rotation) error {
362
250
d .rotation = rotation
363
251
switch d .rotation {
364
252
case drivers .Rotation0 :
365
- d .Command (SEGREMAP | 0x1 ) // Reverse horizontal mapping
366
- d .Command (COMSCANDEC ) // Reverse vertical mapping
253
+ d .bus . command (SEGREMAP | 0x1 ) // Reverse horizontal mapping
254
+ d .bus . command (COMSCANDEC ) // Reverse vertical mapping
367
255
case drivers .Rotation180 :
368
- d .Command (SEGREMAP ) // Normal horizontal mapping
369
- d .Command (COMSCANINC ) // Normal vertical mapping
256
+ d .bus . command (SEGREMAP ) // Normal horizontal mapping
257
+ d .bus . command (COMSCANINC ) // Normal vertical mapping
370
258
// nothing to do
371
259
default :
372
- d .Command (SEGREMAP | 0x1 ) // Reverse horizontal mapping
373
- d .Command (COMSCANDEC ) // Reverse vertical mapping
260
+ d .bus . command (SEGREMAP | 0x1 ) // Reverse horizontal mapping
261
+ d .bus . command (COMSCANDEC ) // Reverse vertical mapping
374
262
}
375
263
return nil
376
264
}
@@ -380,9 +268,9 @@ func (d *Device) SetRotation(rotation drivers.Rotation) error {
380
268
// should be kept.
381
269
func (d * Device ) Sleep (sleepEnabled bool ) error {
382
270
if sleepEnabled {
383
- d .Command (DISPLAYOFF )
271
+ d .bus . command (DISPLAYOFF )
384
272
} else {
385
- d .Command (DISPLAYON )
273
+ d .bus . command (DISPLAYON )
386
274
}
387
275
return nil
388
276
}
0 commit comments