tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/gc9a01/gc9a01.go (about)

     1  // Package gc9a01 implements a driver for the gc9a01 LCD round display
     2  //
     3  // Datasheet: https://www.waveshare.com/w/upload/5/5e/GC9A01A.pdf
     4  package gc9a01 // import "tinygo.org/x/drivers/gc9a01"
     5  
     6  import (
     7  	"image/color"
     8  	"machine"
     9  	"time"
    10  
    11  	"errors"
    12  
    13  	"tinygo.org/x/drivers"
    14  )
    15  
    16  // Rotation controls the rotation used by the display.
    17  type Orientation uint8
    18  
    19  // FrameRate controls the frame rate used by the display.
    20  type FrameRate uint8
    21  
    22  // Device wraps an SPI connection.
    23  type Device struct {
    24  	bus             drivers.SPI
    25  	dcPin           machine.Pin
    26  	resetPin        machine.Pin
    27  	csPin           machine.Pin
    28  	blPin           machine.Pin
    29  	width           int16
    30  	height          int16
    31  	columnOffsetCfg int16
    32  	rowOffsetCfg    int16
    33  	columnOffset    int16
    34  	rowOffset       int16
    35  	frameRate       FrameRate
    36  	isBGR           bool
    37  	vSyncLines      int16
    38  	orientation     Orientation
    39  	batchLength     int16
    40  	batchData       []uint8
    41  }
    42  
    43  // Config is the configuration for the display
    44  type Config struct {
    45  	Orientation  Orientation
    46  	RowOffset    int16
    47  	ColumnOffset int16
    48  	FrameRate    FrameRate
    49  	VSyncLines   int16
    50  	Width        int16
    51  	Height       int16
    52  }
    53  
    54  // New creates a new ST7789 connection. The SPI wire must already be configured.
    55  func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin machine.Pin) Device {
    56  	resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
    57  	dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
    58  	csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
    59  	blPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
    60  	return Device{
    61  		bus:      bus,
    62  		resetPin: resetPin,
    63  		dcPin:    dcPin,
    64  		csPin:    csPin,
    65  		blPin:    blPin,
    66  	}
    67  }
    68  
    69  // Reset the Device
    70  func (d *Device) Reset() {
    71  	d.resetPin.High()
    72  	time.Sleep(100 * time.Millisecond)
    73  	d.resetPin.Low()
    74  	time.Sleep(100 * time.Millisecond)
    75  	d.resetPin.High()
    76  	time.Sleep(100 * time.Millisecond)
    77  
    78  }
    79  
    80  // Sets Device configuration for screen orientation using the initialzation values
    81  func (d *Device) SetDeviceOrientation() {
    82  
    83  	var MemoryAccessReg uint8
    84  
    85  	//Get GRAM and LCD width and height
    86  	if d.orientation == HORIZONTAL {
    87  		MemoryAccessReg = 0xc8
    88  	} else {
    89  		MemoryAccessReg = 0x68
    90  	}
    91  
    92  	// Set the read / write scan direction of the frame memory
    93  	d.Command(MADCTR)
    94  	//0x08 set RGB
    95  	d.Command(MemoryAccessReg)
    96  }
    97  
    98  // setWindow prepares the screen to be modified at a given rectangle
    99  func (d *Device) setWindow(x, y, w, h int16) {
   100  	if d.orientation == HORIZONTAL {
   101  		x += d.columnOffset
   102  		y += d.rowOffset
   103  	} else {
   104  		x += d.rowOffset
   105  		y += d.columnOffset
   106  	}
   107  	d.Tx([]uint8{CASET}, true)
   108  	d.Tx([]uint8{uint8(x >> 8), uint8(x), uint8((x + w - 1) >> 8), uint8(x + w - 1)}, false)
   109  	d.Tx([]uint8{RASET}, true)
   110  	d.Tx([]uint8{uint8(y >> 8), uint8(y), uint8((y + h - 1) >> 8), uint8(y + h - 1)}, false)
   111  	d.Command(RAMWR)
   112  }
   113  
   114  // FillScreen fills the screen with a given color
   115  func (d *Device) FillScreen(c color.RGBA) {
   116  	d.FillRectangle(0, 0, d.height, d.width, c)
   117  }
   118  
   119  // FillRectangle fills a rectangle at a given coordinates with a color
   120  func (d *Device) FillRectangle(x, y, width, height int16, c color.RGBA) error {
   121  	k, j := d.Size()
   122  	var i int32
   123  	if x < 0 || y < 0 || width <= 0 || height <= 0 ||
   124  		x >= k || (x+width) > k || y >= j || (y+height) > j {
   125  		return errors.New("rectangle coordinates outside display area")
   126  	}
   127  	d.setWindow(x, y, width, height)
   128  	c565 := RGBATo565(c)
   129  	c1 := uint8(c565 >> 8)
   130  	c2 := uint8(c565)
   131  
   132  	for i = 0; i < int32(d.batchLength); i++ {
   133  		d.batchData[i*2] = c1
   134  		d.batchData[i*2+1] = c2
   135  	}
   136  	i = int32(width) * int32(height)
   137  	batchLength := int32(d.batchLength)
   138  	for i > 0 {
   139  		if i >= batchLength {
   140  			d.Tx(d.batchData, false)
   141  		} else {
   142  			d.Tx(d.batchData[:i*2], false)
   143  		}
   144  		i -= batchLength
   145  	}
   146  	return nil
   147  }
   148  
   149  // Display sends the whole buffer to the screen
   150  func (d *Device) Display() error {
   151  	return nil
   152  }
   153  
   154  // FillRectangleWithBuffer fills buffer with a rectangle at a given coordinates.
   155  func (d *Device) FillRectangleWithBuffer(x, y, width, height int16, buffer []color.RGBA) error {
   156  	h, w := d.Size()
   157  	if x < 0 || y < 0 || width <= 0 || height <= 0 ||
   158  		x >= h || (x+width) > h || y >= w || (y+height) > w {
   159  		return errors.New("rectangle coordinates outside display area")
   160  	}
   161  	k := int32(width) * int32(height)
   162  	l := int32(len(buffer))
   163  	if k != l {
   164  		return errors.New("buffer length does not match with rectangle size")
   165  	}
   166  
   167  	d.setWindow(x, y, width, height)
   168  
   169  	offset := int32(0)
   170  	batchLength := int32(d.batchLength)
   171  	for k > 0 {
   172  		for i := int32(0); i < batchLength; i++ {
   173  			if offset+i < l {
   174  				c565 := RGBATo565(buffer[offset+i])
   175  				c1 := uint8(c565 >> 8)
   176  				c2 := uint8(c565)
   177  				d.batchData[i*2] = c1
   178  				d.batchData[i*2+1] = c2
   179  			}
   180  		}
   181  		if k >= batchLength {
   182  			d.Tx(d.batchData, false)
   183  		} else {
   184  			d.Tx(d.batchData[:k*2], false)
   185  		}
   186  		k -= batchLength
   187  		offset += batchLength
   188  	}
   189  	return nil
   190  }
   191  
   192  // DrawFastVLine draws a vertical line faster than using SetPixel
   193  func (d *Device) DrawFastVLine(x, y0, y1 int16, c color.RGBA) {
   194  	if y0 > y1 {
   195  		y0, y1 = y1, y0
   196  	}
   197  	d.FillRectangle(x, y0, 1, y1-y0+1, c)
   198  }
   199  
   200  // DrawFastHLine draws a horizontal line faster than using SetPixel
   201  func (d *Device) DrawFastHLine(x0, x1, y int16, c color.RGBA) {
   202  	if x0 > x1 {
   203  		x0, x1 = x1, x0
   204  	}
   205  	d.FillRectangle(x0, y, x1-x0+1, 1, c)
   206  }
   207  
   208  // SetPixel sets a pixel in the screen
   209  func (d *Device) SetPixel(x, y int16, c color.RGBA) {
   210  	w, h := d.Size()
   211  	if x < 0 || y < 0 || x >= w || y >= h {
   212  		return
   213  	}
   214  	d.FillRectangle(x, y, 1, 1, c)
   215  }
   216  
   217  // Command sends a command to the display.
   218  func (d *Device) Command(command uint8) {
   219  	d.Tx([]byte{command}, true)
   220  }
   221  
   222  // Data sends data to the display.
   223  func (d *Device) Data(data uint8) {
   224  	d.Tx([]byte{data}, false)
   225  }
   226  
   227  // Tx sends data to the display
   228  func (d *Device) Tx(data []byte, isCommand bool) {
   229  	d.dcPin.Set(!isCommand)
   230  	d.bus.Tx(data, nil)
   231  }
   232  
   233  // Rx reads data from the display
   234  func (d *Device) Rx(command uint8, data []byte) {
   235  	d.dcPin.Low()
   236  	d.csPin.Low()
   237  	d.bus.Transfer(command)
   238  	d.dcPin.High()
   239  	for i := range data {
   240  		data[i], _ = d.bus.Transfer(0xFF)
   241  	}
   242  	d.csPin.High()
   243  }
   244  
   245  // Size returns the current size of the display.
   246  func (d *Device) Size() (w, h int16) {
   247  	return d.height, d.width
   248  }
   249  
   250  // EnableBacklight enables or disables the backlight
   251  func (d *Device) EnableBacklight(enable bool) {
   252  	if enable {
   253  		d.blPin.High()
   254  	} else {
   255  		d.blPin.Low()
   256  	}
   257  }
   258  
   259  // InvertColors inverts the colors of the screen
   260  func (d *Device) InvertColors(invert bool) {
   261  	if invert {
   262  		d.Command(INVON)
   263  	} else {
   264  		d.Command(INVOFF)
   265  	}
   266  }
   267  
   268  // IsBGR changes the color mode (RGB/BGR)
   269  func (d *Device) IsBGR(bgr bool) {
   270  	d.isBGR = bgr
   271  }
   272  
   273  // SetScrollArea sets an area to scroll with fixed top and bottom parts of the display.
   274  func (d *Device) SetScrollArea(topFixedArea, bottomFixedArea int16) {
   275  	d.Command(VSCRDEF)
   276  	d.Tx([]uint8{
   277  		uint8(topFixedArea >> 8), uint8(topFixedArea),
   278  		uint8(d.height - topFixedArea - bottomFixedArea>>8), uint8(d.height - topFixedArea - bottomFixedArea),
   279  		uint8(bottomFixedArea >> 8), uint8(bottomFixedArea)},
   280  		false)
   281  }
   282  
   283  // SetScroll sets the vertical scroll address of the display.
   284  func (d *Device) SetScroll(line int16) {
   285  	d.Command(VSCRSADD)
   286  	d.Tx([]uint8{uint8(line >> 8), uint8(line)}, false)
   287  }
   288  
   289  // StopScroll returns the display to its normal state.
   290  func (d *Device) StopScroll() {
   291  	d.Command(NORON)
   292  }
   293  
   294  // RGBATo565 converts a color.RGBA to uint16 used in the display
   295  func RGBATo565(c color.RGBA) uint16 {
   296  	r, g, b, _ := c.RGBA()
   297  	return uint16((r & 0xF800) +
   298  		((g & 0xFC00) >> 5) +
   299  		((b & 0xF800) >> 11))
   300  }
   301  
   302  // Configure initializes the display with default configuration
   303  func (d *Device) Configure(cfg Config) {
   304  	if cfg.Width != 0 {
   305  		d.width = cfg.Width
   306  	} else {
   307  		d.width = 240
   308  	}
   309  	if cfg.Height != 0 {
   310  		d.height = cfg.Height
   311  	} else {
   312  		d.height = 240
   313  	}
   314  
   315  	d.orientation = cfg.Orientation
   316  	d.rowOffsetCfg = cfg.RowOffset
   317  	d.columnOffsetCfg = cfg.ColumnOffset
   318  	d.batchLength = d.width
   319  
   320  	if cfg.VSyncLines >= 2 && cfg.VSyncLines <= 254 {
   321  		d.vSyncLines = cfg.VSyncLines
   322  	} else {
   323  		d.vSyncLines = 16
   324  	}
   325  	d.batchLength += d.batchLength & 1
   326  	d.batchData = make([]uint8, d.batchLength*2)
   327  
   328  	// Reset the device
   329  	d.Reset()
   330  
   331  	// Set Device Attributes
   332  	d.SetDeviceOrientation()
   333  
   334  	// Common initialization
   335  	d.Command(0xEF)
   336  	d.Command(0xEB)
   337  	d.Data(0x14)
   338  
   339  	d.Command(INTEN1)
   340  	d.Command(0xEF)
   341  
   342  	d.Command(0xEB)
   343  	d.Data(0x14)
   344  
   345  	d.Command(0x84)
   346  	d.Data(0x40)
   347  
   348  	d.Command(0x85)
   349  	d.Data(0xFF)
   350  
   351  	d.Command(0x86)
   352  	d.Data(0xFF)
   353  
   354  	d.Command(0x87)
   355  	d.Data(0xFF)
   356  
   357  	d.Command(0x88)
   358  	d.Data(0x0A)
   359  
   360  	d.Command(0x89)
   361  	d.Data(0x21)
   362  
   363  	d.Command(0x8A)
   364  	d.Data(0x00)
   365  
   366  	d.Command(0x8B)
   367  	d.Data(0x80)
   368  
   369  	d.Command(0x8C)
   370  	d.Data(0x01)
   371  
   372  	d.Command(0x8D)
   373  	d.Data(0x01)
   374  
   375  	d.Command(0x8E)
   376  	d.Data(0xFF)
   377  
   378  	d.Command(0x8F)
   379  	d.Data(0xFF)
   380  
   381  	d.Command(DISFNCTL)
   382  	d.Data(0x00)
   383  	d.Data(0x20)
   384  
   385  	d.Command(MADCTR)
   386  	d.Data(0x08) //Set as vertical screen
   387  
   388  	d.Command(COLMOD)
   389  	d.Data(0x05)
   390  
   391  	d.Command(0x90)
   392  	d.Data(0x08)
   393  	d.Data(0x08)
   394  	d.Data(0x08)
   395  	d.Data(0x08)
   396  
   397  	d.Command(0xBD)
   398  	d.Data(0x06)
   399  
   400  	d.Command(0xBC)
   401  	d.Data(0x00)
   402  
   403  	d.Command(0xFF)
   404  	d.Data(0x60)
   405  	d.Data(0x01)
   406  	d.Data(0x04)
   407  
   408  	d.Command(PWCTR3)
   409  	d.Data(0x13)
   410  	d.Command(PWCTR4)
   411  	d.Data(0x13)
   412  
   413  	d.Command(0xC9)
   414  	d.Data(0x22)
   415  
   416  	d.Command(0xBE)
   417  	d.Data(0x11)
   418  
   419  	d.Command(0xE1)
   420  	d.Data(0x10)
   421  	d.Data(0x0E)
   422  
   423  	d.Command(0xDF)
   424  	d.Data(0x21)
   425  	d.Data(0x0c)
   426  	d.Data(0x02)
   427  
   428  	d.Command(GMSET1)
   429  	d.Data(0x45)
   430  	d.Data(0x09)
   431  	d.Data(0x08)
   432  	d.Data(0x08)
   433  	d.Data(0x26)
   434  	d.Data(0x2A)
   435  
   436  	d.Command(GMSET2)
   437  	d.Data(0x43)
   438  	d.Data(0x70)
   439  	d.Data(0x72)
   440  	d.Data(0x36)
   441  	d.Data(0x37)
   442  	d.Data(0x6F)
   443  
   444  	d.Command(GMSET3)
   445  	d.Data(0x45)
   446  	d.Data(0x09)
   447  	d.Data(0x08)
   448  	d.Data(0x08)
   449  	d.Data(0x26)
   450  	d.Data(0x2A)
   451  
   452  	d.Command(GMSET4)
   453  	d.Data(0x43)
   454  	d.Data(0x70)
   455  	d.Data(0x72)
   456  	d.Data(0x36)
   457  	d.Data(0x37)
   458  	d.Data(0x6F)
   459  
   460  	d.Command(0xED)
   461  	d.Data(0x1B)
   462  	d.Data(0x0B)
   463  
   464  	d.Command(0xAE)
   465  	d.Data(0x77)
   466  
   467  	d.Command(0xCD)
   468  	d.Data(0x63)
   469  
   470  	d.Command(0x70)
   471  	d.Data(0x07)
   472  	d.Data(0x07)
   473  	d.Data(0x04)
   474  	d.Data(0x0E)
   475  	d.Data(0x0F)
   476  	d.Data(0x09)
   477  	d.Data(0x07)
   478  	d.Data(0x08)
   479  	d.Data(0x03)
   480  
   481  	d.Command(FRMCTL)
   482  	d.Data(0x34)
   483  
   484  	d.Command(0x62)
   485  	d.Data(0x18)
   486  	d.Data(0x0D)
   487  	d.Data(0x71)
   488  	d.Data(0xED)
   489  	d.Data(0x70)
   490  	d.Data(0x70)
   491  	d.Data(0x18)
   492  	d.Data(0x0F)
   493  	d.Data(0x71)
   494  	d.Data(0xEF)
   495  	d.Data(0x70)
   496  	d.Data(0x70)
   497  
   498  	d.Command(0x63)
   499  	d.Data(0x18)
   500  	d.Data(0x11)
   501  	d.Data(0x71)
   502  	d.Data(0xF1)
   503  	d.Data(0x70)
   504  	d.Data(0x70)
   505  	d.Data(0x18)
   506  	d.Data(0x13)
   507  	d.Data(0x71)
   508  	d.Data(0xF3)
   509  	d.Data(0x70)
   510  	d.Data(0x70)
   511  
   512  	d.Command(0x64)
   513  	d.Data(0x28)
   514  	d.Data(0x29)
   515  	d.Data(0xF1)
   516  	d.Data(0x01)
   517  	d.Data(0xF1)
   518  	d.Data(0x00)
   519  	d.Data(0x07)
   520  
   521  	d.Command(0x66)
   522  	d.Data(0x3C)
   523  	d.Data(0x00)
   524  	d.Data(0xCD)
   525  	d.Data(0x67)
   526  	d.Data(0x45)
   527  	d.Data(0x45)
   528  	d.Data(0x10)
   529  	d.Data(0x00)
   530  	d.Data(0x00)
   531  	d.Data(0x00)
   532  
   533  	d.Command(0x67)
   534  	d.Data(0x00)
   535  	d.Data(0x3C)
   536  	d.Data(0x00)
   537  	d.Data(0x00)
   538  	d.Data(0x00)
   539  	d.Data(0x01)
   540  	d.Data(0x54)
   541  	d.Data(0x10)
   542  	d.Data(0x32)
   543  	d.Data(0x98)
   544  
   545  	d.Command(0x74)
   546  	d.Data(0x10)
   547  	d.Data(0x85)
   548  	d.Data(0x80)
   549  	d.Data(0x00)
   550  	d.Data(0x00)
   551  	d.Data(0x4E)
   552  	d.Data(0x00)
   553  
   554  	d.Command(0x98)
   555  	d.Data(0x3e)
   556  	d.Data(0x07)
   557  
   558  	d.Command(TEON)
   559  	d.Command(0x21)
   560  
   561  	d.Command(SLPOUT)
   562  	time.Sleep(120 * time.Millisecond)
   563  	d.Command(DISPON)
   564  	time.Sleep(20 * time.Millisecond)
   565  
   566  	d.blPin.High()
   567  }