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 }