@@ -11,6 +11,7 @@ import (
11
11
"errors"
12
12
13
13
"tinygo.org/x/drivers"
14
+ "tinygo.org/x/drivers/pixel"
14
15
)
15
16
16
17
type Model uint8
@@ -20,12 +21,23 @@ type Model uint8
20
21
// Deprecated: use drivers.Rotation instead.
21
22
type Rotation = drivers.Rotation
22
23
24
+ // Pixel formats supported by the st7735 driver.
25
+ type Color interface {
26
+ pixel.RGB444BE | pixel.RGB565BE
27
+
28
+ pixel.BaseColor
29
+ }
30
+
23
31
var (
24
32
errOutOfBounds = errors .New ("rectangle coordinates outside display area" )
25
33
)
26
34
27
35
// Device wraps an SPI connection.
28
- type Device struct {
36
+ type Device = DeviceOf [pixel.RGB565BE ]
37
+
38
+ // DeviceOf is a generic version of Device, which supports different pixel
39
+ // formats.
40
+ type DeviceOf [T Color ] struct {
29
41
bus drivers.SPI
30
42
dcPin machine.Pin
31
43
resetPin machine.Pin
@@ -39,7 +51,7 @@ type Device struct {
39
51
batchLength int16
40
52
model Model
41
53
isBGR bool
42
- batchData [] uint8
54
+ batchData pixel. Image [ T ] // "image" with width, height of (batchLength, 1)
43
55
}
44
56
45
57
// Config is the configuration for the display
@@ -54,11 +66,17 @@ type Config struct {
54
66
55
67
// New creates a new ST7735 connection. The SPI wire must already be configured.
56
68
func New (bus drivers.SPI , resetPin , dcPin , csPin , blPin machine.Pin ) Device {
69
+ return NewOf [pixel.RGB565BE ](bus , resetPin , dcPin , csPin , blPin )
70
+ }
71
+
72
+ // NewOf creates a new ST7735 connection with a particular pixel format. The SPI
73
+ // wire must already be configured.
74
+ func NewOf [T Color ](bus drivers.SPI , resetPin , dcPin , csPin , blPin machine.Pin ) DeviceOf [T ] {
57
75
dcPin .Configure (machine.PinConfig {Mode : machine .PinOutput })
58
76
resetPin .Configure (machine.PinConfig {Mode : machine .PinOutput })
59
77
csPin .Configure (machine.PinConfig {Mode : machine .PinOutput })
60
78
blPin .Configure (machine.PinConfig {Mode : machine .PinOutput })
61
- return Device {
79
+ return DeviceOf [ T ] {
62
80
bus : bus ,
63
81
dcPin : dcPin ,
64
82
resetPin : resetPin ,
@@ -68,7 +86,7 @@ func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin machine.Pin) Device {
68
86
}
69
87
70
88
// Configure initializes the display with default configuration
71
- func (d * Device ) Configure (cfg Config ) {
89
+ func (d * DeviceOf [ T ] ) Configure (cfg Config ) {
72
90
d .model = cfg .Model
73
91
if cfg .Width != 0 {
74
92
d .width = cfg .Width
@@ -93,7 +111,7 @@ func (d *Device) Configure(cfg Config) {
93
111
d .batchLength = d .height
94
112
}
95
113
d .batchLength += d .batchLength & 1
96
- d .batchData = make ([] uint8 , d .batchLength * 2 )
114
+ d .batchData = pixel . NewImage [ T ]( int ( d .batchLength ), 1 )
97
115
98
116
// reset the device
99
117
d .resetPin .High ()
@@ -142,8 +160,16 @@ func (d *Device) Configure(cfg Config) {
142
160
d .Data (0xEE )
143
161
d .Command (VMCTR1 )
144
162
d .Data (0x0E )
163
+
164
+ // Set the color format depending on the generic type.
145
165
d .Command (COLMOD )
146
- d .Data (0x05 )
166
+ var zeroColor T
167
+ switch any (zeroColor ).(type ) {
168
+ case pixel.RGB444BE :
169
+ d .Data (0x03 ) // 12 bits per pixel
170
+ default :
171
+ d .Data (0x05 ) // 16 bits per pixel
172
+ }
147
173
148
174
if d .model == GREENTAB {
149
175
d .InvertColors (false )
@@ -204,12 +230,12 @@ func (d *Device) Configure(cfg Config) {
204
230
}
205
231
206
232
// Display does nothing, there's no buffer as it might be too big for some boards
207
- func (d * Device ) Display () error {
233
+ func (d * DeviceOf [ T ] ) Display () error {
208
234
return nil
209
235
}
210
236
211
237
// SetPixel sets a pixel in the screen
212
- func (d * Device ) SetPixel (x int16 , y int16 , c color.RGBA ) {
238
+ func (d * DeviceOf [ T ] ) SetPixel (x int16 , y int16 , c color.RGBA ) {
213
239
w , h := d .Size ()
214
240
if x < 0 || y < 0 || x >= w || y >= h {
215
241
return
@@ -218,7 +244,7 @@ func (d *Device) SetPixel(x int16, y int16, c color.RGBA) {
218
244
}
219
245
220
246
// setWindow prepares the screen to be modified at a given rectangle
221
- func (d * Device ) setWindow (x , y , w , h int16 ) {
247
+ func (d * DeviceOf [ T ] ) setWindow (x , y , w , h int16 ) {
222
248
if d .rotation == drivers .Rotation0 || d .rotation == drivers .Rotation180 {
223
249
x += d .columnOffset
224
250
y += d .rowOffset
@@ -234,7 +260,7 @@ func (d *Device) setWindow(x, y, w, h int16) {
234
260
}
235
261
236
262
// SetScrollWindow sets an area to scroll with fixed top and bottom parts of the display
237
- func (d * Device ) SetScrollArea (topFixedArea , bottomFixedArea int16 ) {
263
+ func (d * DeviceOf [ T ] ) SetScrollArea (topFixedArea , bottomFixedArea int16 ) {
238
264
// TODO: this code is broken, see the st7789 and ili9341 implementations for
239
265
// how to do this correctly.
240
266
d .Command (VSCRDEF )
@@ -246,46 +272,40 @@ func (d *Device) SetScrollArea(topFixedArea, bottomFixedArea int16) {
246
272
}
247
273
248
274
// SetScroll sets the vertical scroll address of the display.
249
- func (d * Device ) SetScroll (line int16 ) {
275
+ func (d * DeviceOf [ T ] ) SetScroll (line int16 ) {
250
276
d .Command (VSCRSADD )
251
277
d .Tx ([]uint8 {uint8 (line >> 8 ), uint8 (line )}, false )
252
278
}
253
279
254
280
// SpotScroll returns the display to its normal state
255
- func (d * Device ) StopScroll () {
281
+ func (d * DeviceOf [ T ] ) StopScroll () {
256
282
d .Command (NORON )
257
283
}
258
284
259
285
// FillRectangle fills a rectangle at a given coordinates with a color
260
- func (d * Device ) FillRectangle (x , y , width , height int16 , c color.RGBA ) error {
286
+ func (d * DeviceOf [ T ] ) FillRectangle (x , y , width , height int16 , c color.RGBA ) error {
261
287
k , i := d .Size ()
262
288
if x < 0 || y < 0 || width <= 0 || height <= 0 ||
263
289
x >= k || (x + width ) > k || y >= i || (y + height ) > i {
264
290
return errors .New ("rectangle coordinates outside display area" )
265
291
}
266
292
d .setWindow (x , y , width , height )
267
- c565 := RGBATo565 (c )
268
- c1 := uint8 (c565 >> 8 )
269
- c2 := uint8 (c565 )
270
293
271
- for i = 0 ; i < d .batchLength ; i ++ {
272
- d .batchData [i * 2 ] = c1
273
- d .batchData [i * 2 + 1 ] = c2
274
- }
294
+ d .batchData .FillSolidColor (pixel .NewColor [T ](c .R , c .G , c .B ))
275
295
i = width * height
276
296
for i > 0 {
277
297
if i >= d .batchLength {
278
- d .Tx (d .batchData , false )
298
+ d .Tx (d .batchData . RawBuffer () , false )
279
299
} else {
280
- d .Tx (d .batchData [: i * 2 ] , false )
300
+ d .Tx (d .batchData . Rescale ( int ( i ), 1 ). RawBuffer () , false )
281
301
}
282
302
i -= d .batchLength
283
303
}
284
304
return nil
285
305
}
286
306
287
307
// DrawRGBBitmap8 copies an RGB bitmap to the internal buffer at given coordinates
288
- func (d * Device ) DrawRGBBitmap8 (x , y int16 , data []uint8 , w , h int16 ) error {
308
+ func (d * DeviceOf [ T ] ) DrawRGBBitmap8 (x , y int16 , data []uint8 , w , h int16 ) error {
289
309
k , i := d .Size ()
290
310
if x < 0 || y < 0 || w <= 0 || h <= 0 ||
291
311
x >= k || (x + w ) > k || y >= i || (y + h ) > i {
@@ -297,7 +317,7 @@ func (d *Device) DrawRGBBitmap8(x, y int16, data []uint8, w, h int16) error {
297
317
}
298
318
299
319
// FillRectangle fills a rectangle at a given coordinates with a buffer
300
- func (d * Device ) FillRectangleWithBuffer (x , y , width , height int16 , buffer []color.RGBA ) error {
320
+ func (d * DeviceOf [ T ] ) FillRectangleWithBuffer (x , y , width , height int16 , buffer []color.RGBA ) error {
301
321
k , l := d .Size ()
302
322
if x < 0 || y < 0 || width <= 0 || height <= 0 ||
303
323
x >= k || (x + width ) > k || y >= l || (y + height ) > l {
@@ -315,17 +335,14 @@ func (d *Device) FillRectangleWithBuffer(x, y, width, height int16, buffer []col
315
335
for k > 0 {
316
336
for i := int16 (0 ); i < d .batchLength ; i ++ {
317
337
if offset + i < l {
318
- c565 := RGBATo565 (buffer [offset + i ])
319
- c1 := uint8 (c565 >> 8 )
320
- c2 := uint8 (c565 )
321
- d .batchData [i * 2 ] = c1
322
- d .batchData [i * 2 + 1 ] = c2
338
+ c := buffer [offset + i ]
339
+ d .batchData .Set (int (i ), 0 , pixel .NewColor [T ](c .R , c .G , c .B ))
323
340
}
324
341
}
325
342
if k >= d .batchLength {
326
- d .Tx (d .batchData , false )
343
+ d .Tx (d .batchData . RawBuffer () , false )
327
344
} else {
328
- d .Tx (d .batchData [: k * 2 ] , false )
345
+ d .Tx (d .batchData . Rescale ( int ( k ), 1 ). RawBuffer () , false )
329
346
}
330
347
k -= d .batchLength
331
348
offset += d .batchLength
@@ -334,23 +351,23 @@ func (d *Device) FillRectangleWithBuffer(x, y, width, height int16, buffer []col
334
351
}
335
352
336
353
// DrawFastVLine draws a vertical line faster than using SetPixel
337
- func (d * Device ) DrawFastVLine (x , y0 , y1 int16 , c color.RGBA ) {
354
+ func (d * DeviceOf [ T ] ) DrawFastVLine (x , y0 , y1 int16 , c color.RGBA ) {
338
355
if y0 > y1 {
339
356
y0 , y1 = y1 , y0
340
357
}
341
358
d .FillRectangle (x , y0 , 1 , y1 - y0 + 1 , c )
342
359
}
343
360
344
361
// DrawFastHLine draws a horizontal line faster than using SetPixel
345
- func (d * Device ) DrawFastHLine (x0 , x1 , y int16 , c color.RGBA ) {
362
+ func (d * DeviceOf [ T ] ) DrawFastHLine (x0 , x1 , y int16 , c color.RGBA ) {
346
363
if x0 > x1 {
347
364
x0 , x1 = x1 , x0
348
365
}
349
366
d .FillRectangle (x0 , y , x1 - x0 + 1 , 1 , c )
350
367
}
351
368
352
369
// FillScreen fills the screen with a given color
353
- func (d * Device ) FillScreen (c color.RGBA ) {
370
+ func (d * DeviceOf [ T ] ) FillScreen (c color.RGBA ) {
354
371
if d .rotation == drivers .Rotation0 || d .rotation == drivers .Rotation180 {
355
372
d .FillRectangle (0 , 0 , d .width , d .height , c )
356
373
} else {
@@ -359,12 +376,12 @@ func (d *Device) FillScreen(c color.RGBA) {
359
376
}
360
377
361
378
// Rotation returns the currently configured rotation.
362
- func (d * Device ) Rotation () drivers.Rotation {
379
+ func (d * DeviceOf [ T ] ) Rotation () drivers.Rotation {
363
380
return d .rotation
364
381
}
365
382
366
383
// SetRotation changes the rotation of the device (clock-wise)
367
- func (d * Device ) SetRotation (rotation drivers.Rotation ) error {
384
+ func (d * DeviceOf [ T ] ) SetRotation (rotation drivers.Rotation ) error {
368
385
d .rotation = rotation
369
386
madctl := uint8 (0 )
370
387
switch rotation % 4 {
@@ -386,31 +403,31 @@ func (d *Device) SetRotation(rotation drivers.Rotation) error {
386
403
}
387
404
388
405
// Command sends a command to the display
389
- func (d * Device ) Command (command uint8 ) {
406
+ func (d * DeviceOf [ T ] ) Command (command uint8 ) {
390
407
d .Tx ([]byte {command }, true )
391
408
}
392
409
393
410
// Command sends a data to the display
394
- func (d * Device ) Data (data uint8 ) {
411
+ func (d * DeviceOf [ T ] ) Data (data uint8 ) {
395
412
d .Tx ([]byte {data }, false )
396
413
}
397
414
398
415
// Tx sends data to the display
399
- func (d * Device ) Tx (data []byte , isCommand bool ) {
416
+ func (d * DeviceOf [ T ] ) Tx (data []byte , isCommand bool ) {
400
417
d .dcPin .Set (! isCommand )
401
418
d .bus .Tx (data , nil )
402
419
}
403
420
404
421
// Size returns the current size of the display.
405
- func (d * Device ) Size () (w , h int16 ) {
422
+ func (d * DeviceOf [ T ] ) Size () (w , h int16 ) {
406
423
if d .rotation == drivers .Rotation0 || d .rotation == drivers .Rotation180 {
407
424
return d .width , d .height
408
425
}
409
426
return d .height , d .width
410
427
}
411
428
412
429
// EnableBacklight enables or disables the backlight
413
- func (d * Device ) EnableBacklight (enable bool ) {
430
+ func (d * DeviceOf [ T ] ) EnableBacklight (enable bool ) {
414
431
if enable {
415
432
d .blPin .High ()
416
433
} else {
@@ -421,7 +438,7 @@ func (d *Device) EnableBacklight(enable bool) {
421
438
// Set the sleep mode for this LCD panel. When sleeping, the panel uses a lot
422
439
// less power. The LCD won't display an image anymore, but the memory contents
423
440
// will be kept.
424
- func (d * Device ) Sleep (sleepEnabled bool ) error {
441
+ func (d * DeviceOf [ T ] ) Sleep (sleepEnabled bool ) error {
425
442
if sleepEnabled {
426
443
// Shut down LCD panel.
427
444
d .Command (SLPIN )
@@ -437,7 +454,7 @@ func (d *Device) Sleep(sleepEnabled bool) error {
437
454
}
438
455
439
456
// InverColors inverts the colors of the screen
440
- func (d * Device ) InvertColors (invert bool ) {
457
+ func (d * DeviceOf [ T ] ) InvertColors (invert bool ) {
441
458
if invert {
442
459
d .Command (INVON )
443
460
} else {
@@ -446,14 +463,6 @@ func (d *Device) InvertColors(invert bool) {
446
463
}
447
464
448
465
// IsBGR changes the color mode (RGB/BGR)
449
- func (d * Device ) IsBGR (bgr bool ) {
466
+ func (d * DeviceOf [ T ] ) IsBGR (bgr bool ) {
450
467
d .isBGR = bgr
451
468
}
452
-
453
- // RGBATo565 converts a color.RGBA to uint16 used in the display
454
- func RGBATo565 (c color.RGBA ) uint16 {
455
- r , g , b , _ := c .RGBA ()
456
- return uint16 ((r & 0xF800 ) +
457
- ((g & 0xFC00 ) >> 5 ) +
458
- ((b & 0xF800 ) >> 11 ))
459
- }
0 commit comments