@@ -11,6 +11,7 @@ import (
1111 "errors"
1212
1313 "tinygo.org/x/drivers"
14+ "tinygo.org/x/drivers/pixel"
1415)
1516
1617type Model uint8
@@ -20,12 +21,23 @@ type Model uint8
2021// Deprecated: use drivers.Rotation instead.
2122type Rotation = drivers.Rotation
2223
24+ // Pixel formats supported by the st7735 driver.
25+ type Color interface {
26+ pixel.RGB444BE | pixel.RGB565BE
27+
28+ pixel.BaseColor
29+ }
30+
2331var (
2432 errOutOfBounds = errors .New ("rectangle coordinates outside display area" )
2533)
2634
2735// 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 {
2941 bus drivers.SPI
3042 dcPin machine.Pin
3143 resetPin machine.Pin
@@ -39,7 +51,7 @@ type Device struct {
3951 batchLength int16
4052 model Model
4153 isBGR bool
42- batchData [] uint8
54+ batchData pixel. Image [ T ] // "image" with width, height of (batchLength, 1)
4355}
4456
4557// Config is the configuration for the display
@@ -54,11 +66,17 @@ type Config struct {
5466
5567// New creates a new ST7735 connection. The SPI wire must already be configured.
5668func 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 ] {
5775 dcPin .Configure (machine.PinConfig {Mode : machine .PinOutput })
5876 resetPin .Configure (machine.PinConfig {Mode : machine .PinOutput })
5977 csPin .Configure (machine.PinConfig {Mode : machine .PinOutput })
6078 blPin .Configure (machine.PinConfig {Mode : machine .PinOutput })
61- return Device {
79+ return DeviceOf [ T ] {
6280 bus : bus ,
6381 dcPin : dcPin ,
6482 resetPin : resetPin ,
@@ -68,7 +86,7 @@ func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin machine.Pin) Device {
6886}
6987
7088// Configure initializes the display with default configuration
71- func (d * Device ) Configure (cfg Config ) {
89+ func (d * DeviceOf [ T ] ) Configure (cfg Config ) {
7290 d .model = cfg .Model
7391 if cfg .Width != 0 {
7492 d .width = cfg .Width
@@ -93,7 +111,7 @@ func (d *Device) Configure(cfg Config) {
93111 d .batchLength = d .height
94112 }
95113 d .batchLength += d .batchLength & 1
96- d .batchData = make ([] uint8 , d .batchLength * 2 )
114+ d .batchData = pixel . NewImage [ T ]( int ( d .batchLength ), 1 )
97115
98116 // reset the device
99117 d .resetPin .High ()
@@ -142,8 +160,16 @@ func (d *Device) Configure(cfg Config) {
142160 d .Data (0xEE )
143161 d .Command (VMCTR1 )
144162 d .Data (0x0E )
163+
164+ // Set the color format depending on the generic type.
145165 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+ }
147173
148174 if d .model == GREENTAB {
149175 d .InvertColors (false )
@@ -204,12 +230,12 @@ func (d *Device) Configure(cfg Config) {
204230}
205231
206232// 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 {
208234 return nil
209235}
210236
211237// 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 ) {
213239 w , h := d .Size ()
214240 if x < 0 || y < 0 || x >= w || y >= h {
215241 return
@@ -218,7 +244,7 @@ func (d *Device) SetPixel(x int16, y int16, c color.RGBA) {
218244}
219245
220246// 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 ) {
222248 if d .rotation == drivers .Rotation0 || d .rotation == drivers .Rotation180 {
223249 x += d .columnOffset
224250 y += d .rowOffset
@@ -234,7 +260,7 @@ func (d *Device) setWindow(x, y, w, h int16) {
234260}
235261
236262// 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 ) {
238264 // TODO: this code is broken, see the st7789 and ili9341 implementations for
239265 // how to do this correctly.
240266 d .Command (VSCRDEF )
@@ -246,46 +272,40 @@ func (d *Device) SetScrollArea(topFixedArea, bottomFixedArea int16) {
246272}
247273
248274// SetScroll sets the vertical scroll address of the display.
249- func (d * Device ) SetScroll (line int16 ) {
275+ func (d * DeviceOf [ T ] ) SetScroll (line int16 ) {
250276 d .Command (VSCRSADD )
251277 d .Tx ([]uint8 {uint8 (line >> 8 ), uint8 (line )}, false )
252278}
253279
254280// SpotScroll returns the display to its normal state
255- func (d * Device ) StopScroll () {
281+ func (d * DeviceOf [ T ] ) StopScroll () {
256282 d .Command (NORON )
257283}
258284
259285// 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 {
261287 k , i := d .Size ()
262288 if x < 0 || y < 0 || width <= 0 || height <= 0 ||
263289 x >= k || (x + width ) > k || y >= i || (y + height ) > i {
264290 return errors .New ("rectangle coordinates outside display area" )
265291 }
266292 d .setWindow (x , y , width , height )
267- c565 := RGBATo565 (c )
268- c1 := uint8 (c565 >> 8 )
269- c2 := uint8 (c565 )
270293
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 ))
275295 i = width * height
276296 for i > 0 {
277297 if i >= d .batchLength {
278- d .Tx (d .batchData , false )
298+ d .Tx (d .batchData . RawBuffer () , false )
279299 } else {
280- d .Tx (d .batchData [: i * 2 ] , false )
300+ d .Tx (d .batchData . Rescale ( int ( i ), 1 ). RawBuffer () , false )
281301 }
282302 i -= d .batchLength
283303 }
284304 return nil
285305}
286306
287307// 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 {
289309 k , i := d .Size ()
290310 if x < 0 || y < 0 || w <= 0 || h <= 0 ||
291311 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 {
297317}
298318
299319// 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 {
301321 k , l := d .Size ()
302322 if x < 0 || y < 0 || width <= 0 || height <= 0 ||
303323 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
315335 for k > 0 {
316336 for i := int16 (0 ); i < d .batchLength ; i ++ {
317337 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 ))
323340 }
324341 }
325342 if k >= d .batchLength {
326- d .Tx (d .batchData , false )
343+ d .Tx (d .batchData . RawBuffer () , false )
327344 } else {
328- d .Tx (d .batchData [: k * 2 ] , false )
345+ d .Tx (d .batchData . Rescale ( int ( k ), 1 ). RawBuffer () , false )
329346 }
330347 k -= d .batchLength
331348 offset += d .batchLength
@@ -334,23 +351,23 @@ func (d *Device) FillRectangleWithBuffer(x, y, width, height int16, buffer []col
334351}
335352
336353// 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 ) {
338355 if y0 > y1 {
339356 y0 , y1 = y1 , y0
340357 }
341358 d .FillRectangle (x , y0 , 1 , y1 - y0 + 1 , c )
342359}
343360
344361// 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 ) {
346363 if x0 > x1 {
347364 x0 , x1 = x1 , x0
348365 }
349366 d .FillRectangle (x0 , y , x1 - x0 + 1 , 1 , c )
350367}
351368
352369// 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 ) {
354371 if d .rotation == drivers .Rotation0 || d .rotation == drivers .Rotation180 {
355372 d .FillRectangle (0 , 0 , d .width , d .height , c )
356373 } else {
@@ -359,12 +376,12 @@ func (d *Device) FillScreen(c color.RGBA) {
359376}
360377
361378// Rotation returns the currently configured rotation.
362- func (d * Device ) Rotation () drivers.Rotation {
379+ func (d * DeviceOf [ T ] ) Rotation () drivers.Rotation {
363380 return d .rotation
364381}
365382
366383// 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 {
368385 d .rotation = rotation
369386 madctl := uint8 (0 )
370387 switch rotation % 4 {
@@ -386,31 +403,31 @@ func (d *Device) SetRotation(rotation drivers.Rotation) error {
386403}
387404
388405// Command sends a command to the display
389- func (d * Device ) Command (command uint8 ) {
406+ func (d * DeviceOf [ T ] ) Command (command uint8 ) {
390407 d .Tx ([]byte {command }, true )
391408}
392409
393410// Command sends a data to the display
394- func (d * Device ) Data (data uint8 ) {
411+ func (d * DeviceOf [ T ] ) Data (data uint8 ) {
395412 d .Tx ([]byte {data }, false )
396413}
397414
398415// 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 ) {
400417 d .dcPin .Set (! isCommand )
401418 d .bus .Tx (data , nil )
402419}
403420
404421// 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 ) {
406423 if d .rotation == drivers .Rotation0 || d .rotation == drivers .Rotation180 {
407424 return d .width , d .height
408425 }
409426 return d .height , d .width
410427}
411428
412429// EnableBacklight enables or disables the backlight
413- func (d * Device ) EnableBacklight (enable bool ) {
430+ func (d * DeviceOf [ T ] ) EnableBacklight (enable bool ) {
414431 if enable {
415432 d .blPin .High ()
416433 } else {
@@ -421,7 +438,7 @@ func (d *Device) EnableBacklight(enable bool) {
421438// Set the sleep mode for this LCD panel. When sleeping, the panel uses a lot
422439// less power. The LCD won't display an image anymore, but the memory contents
423440// will be kept.
424- func (d * Device ) Sleep (sleepEnabled bool ) error {
441+ func (d * DeviceOf [ T ] ) Sleep (sleepEnabled bool ) error {
425442 if sleepEnabled {
426443 // Shut down LCD panel.
427444 d .Command (SLPIN )
@@ -437,7 +454,7 @@ func (d *Device) Sleep(sleepEnabled bool) error {
437454}
438455
439456// InverColors inverts the colors of the screen
440- func (d * Device ) InvertColors (invert bool ) {
457+ func (d * DeviceOf [ T ] ) InvertColors (invert bool ) {
441458 if invert {
442459 d .Command (INVON )
443460 } else {
@@ -446,14 +463,6 @@ func (d *Device) InvertColors(invert bool) {
446463}
447464
448465// IsBGR changes the color mode (RGB/BGR)
449- func (d * Device ) IsBGR (bgr bool ) {
466+ func (d * DeviceOf [ T ] ) IsBGR (bgr bool ) {
450467 d .isBGR = bgr
451468}
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