@@ -6,7 +6,6 @@ package ssd1306 // import "tinygo.org/x/drivers/ssd1306"
66import (
77 "errors"
88 "image/color"
9- "machine"
109 "time"
1110
1211 "tinygo.org/x/drivers"
@@ -22,16 +21,15 @@ type ResetValue [2]byte
2221
2322// Device wraps I2C or SPI connection.
2423type 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
3533}
3634
3735// Config is the configuration for the display
@@ -50,51 +48,14 @@ type Config struct {
5048 Rotation drivers.Rotation
5149}
5250
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-
6551type 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
6955}
7056
7157type VccMode uint8
7258
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-
9859// Configure initializes the display with default configuration
9960func (d * Device ) Configure (cfg Config ) {
10061 var zeroReset ResetValue
@@ -108,9 +69,6 @@ func (d *Device) Configure(cfg Config) {
10869 } else {
10970 d .height = 64
11071 }
111- if cfg .Address != 0 {
112- d .bus .setAddress (cfg .Address )
113- }
11472 if cfg .VccState != 0 {
11573 d .vccState = cfg .VccState
11674 } else {
@@ -126,77 +84,75 @@ func (d *Device) Configure(cfg Config) {
12684 } else {
12785 d .resetPage = ResetValue {0 , uint8 (d .height / 8 ) - 1 }
12886 }
129- d .bufferSize = d .width * d .height / 8
130- d .buffer = make ([]byte , d .bufferSize + 1 ) // +1 for the I2C command byte
13187 d .canReset = cfg .Address != 0 || d .width != 128 || d .height != 64 // I2C or not 128x64
13288
133- d .bus .configure ()
89+ d .buffer = d . bus .configure (cfg . Address , d . width * d . height / 8 )
13490
13591 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 )
145101 if d .vccState == EXTERNALVCC {
146- d .Command (0x10 )
102+ d .bus . command (0x10 )
147103 } else {
148- d .Command (0x14 )
104+ d .bus . command (0x14 )
149105 }
150- d .Command (MEMORYMODE )
151- d .Command (0x00 )
106+ d .bus . command (MEMORYMODE )
107+ d .bus . command (0x00 )
152108
153109 d .SetRotation (cfg .Rotation )
154110
155111 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 )
159115 if d .vccState == EXTERNALVCC {
160- d .Command (0x9F )
116+ d .bus . command (0x9F )
161117 } else {
162- d .Command (0xCF )
118+ d .bus . command (0xCF )
163119 }
164120 } 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 )
169125 } 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 )
173129 if d .vccState == EXTERNALVCC {
174- d .Command (0x10 )
130+ d .bus . command (0x10 )
175131 } else {
176- d .Command (0xAF )
132+ d .bus . command (0xAF )
177133 }
178134 } else {
179135 // fail silently, it might work
180136 println ("there's no configuration for this display's size" )
181137 }
182138
183- d .Command (SETPRECHARGE )
139+ d .bus . command (SETPRECHARGE )
184140 if d .vccState == EXTERNALVCC {
185- d .Command (0x22 )
141+ d .bus . command (0x22 )
186142 } else {
187- d .Command (0xF1 )
143+ d .bus . command (0xF1 )
188144 }
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 )
195151}
196152
197153// ClearBuffer clears the image buffer
198154func (d * Device ) ClearBuffer () {
199- for i := int16 ( 0 ) ; i < d . bufferSize ; i ++ {
155+ for i := 0 ; i < len ( d . buffer ) ; i ++ {
200156 d .buffer [i ] = 0
201157 }
202158}
@@ -214,15 +170,15 @@ func (d *Device) Display() error {
214170 // In the 128x64 (SPI) screen resetting to 0x0 after 128 times corrupt the buffer
215171 // Since we're printing the whole buffer, avoid resetting it in this case
216172 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 ])
223179 }
224180
225- return d .Tx ( d . buffer , false )
181+ return d .bus . flush ( )
226182}
227183
228184// SetPixel enables or disables a pixel in the buffer
@@ -251,12 +207,10 @@ func (d *Device) GetPixel(x int16, y int16) bool {
251207
252208// SetBuffer changes the whole buffer at once
253209func (d * Device ) SetBuffer (buffer []byte ) error {
254- if int16 ( len (buffer )) != d . bufferSize {
210+ if len (buffer ) != len ( d . buffer ) {
255211 return errBufferSize
256212 }
257- for i := int16 (0 ); i < d .bufferSize ; i ++ {
258- d .buffer [i ] = buffer [i ]
259- }
213+ copy (d .buffer , buffer )
260214 return nil
261215}
262216
@@ -265,72 +219,6 @@ func (d *Device) GetBuffer() []byte {
265219 return d .buffer
266220}
267221
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-
334222// Size returns the current size of the display.
335223func (d * Device ) Size () (w , h int16 ) {
336224 return d .width , d .height
@@ -362,15 +250,15 @@ func (d *Device) SetRotation(rotation drivers.Rotation) error {
362250 d .rotation = rotation
363251 switch d .rotation {
364252 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
367255 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
370258 // nothing to do
371259 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
374262 }
375263 return nil
376264}
@@ -380,9 +268,9 @@ func (d *Device) SetRotation(rotation drivers.Rotation) error {
380268// should be kept.
381269func (d * Device ) Sleep (sleepEnabled bool ) error {
382270 if sleepEnabled {
383- d .Command (DISPLAYOFF )
271+ d .bus . command (DISPLAYOFF )
384272 } else {
385- d .Command (DISPLAYON )
273+ d .bus . command (DISPLAYON )
386274 }
387275 return nil
388276}
0 commit comments