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

     1  // Package epd2in9 implements a driver for Waveshare 2.9in black and white e-paper device.
     2  //
     3  // Note: this is for the V1 device (using IL3820), the V2 device uses a different chipset.
     4  //
     5  // Datasheets:
     6  //
     7  //	https://www.waveshare.com/w/upload/e/e6/2.9inch_e-Paper_Datasheet.pdf
     8  //	https://www.smart-prototyping.com/image/data/9_Modules/EinkDisplay/GDE029A1/IL3820.pdf
     9  //
    10  // This implementation is essentially a copy of the 2in13 driver with the correct LUTs and
    11  // default size for the 2.9in device.
    12  package epd2in9 // import "tinygo.org/x/drivers/waveshare-epd/epd2in9"
    13  
    14  import (
    15  	"image/color"
    16  	"machine"
    17  	"time"
    18  
    19  	"tinygo.org/x/drivers"
    20  )
    21  
    22  type Config struct {
    23  	Width        int16 // Width is the display resolution
    24  	Height       int16
    25  	LogicalWidth int16    // LogicalWidth must be a multiple of 8 and same size or bigger than Width
    26  	Rotation     Rotation // Rotation is clock-wise
    27  }
    28  
    29  type Device struct {
    30  	bus          drivers.SPI
    31  	cs           machine.Pin
    32  	dc           machine.Pin
    33  	rst          machine.Pin
    34  	busy         machine.Pin
    35  	logicalWidth int16
    36  	width        int16
    37  	height       int16
    38  	buffer       []uint8
    39  	bufferLength uint32
    40  	rotation     Rotation
    41  }
    42  
    43  type Rotation uint8
    44  
    45  // Look up table for full updates
    46  var lutFullUpdate = [30]uint8{
    47  	0x50, 0xAA, 0x55, 0xAA, 0x11, 0x00,
    48  	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    49  	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    50  	0x00, 0x00, 0xFF, 0xFF, 0x1F, 0x00,
    51  	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    52  }
    53  
    54  // Look up table for partial updates, faster but there will be some ghosting
    55  var lutPartialUpdate = [30]uint8{
    56  	0x10, 0x18, 0x18, 0x08, 0x18, 0x18,
    57  	0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
    58  	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    59  	0x00, 0x00, 0x13, 0x14, 0x44, 0x12,
    60  	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    61  }
    62  
    63  // New returns a new epd2in9 driver. Pass in a fully configured SPI bus.
    64  func New(bus drivers.SPI, csPin, dcPin, rstPin, busyPin machine.Pin) Device {
    65  	csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
    66  	dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
    67  	rstPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
    68  	busyPin.Configure(machine.PinConfig{Mode: machine.PinInput})
    69  	return Device{
    70  		bus:  bus,
    71  		cs:   csPin,
    72  		dc:   dcPin,
    73  		rst:  rstPin,
    74  		busy: busyPin,
    75  	}
    76  }
    77  
    78  // Configure sets up the device.
    79  func (d *Device) Configure(cfg Config) {
    80  	if cfg.LogicalWidth != 0 {
    81  		d.logicalWidth = cfg.LogicalWidth
    82  	} else {
    83  		d.logicalWidth = 128
    84  	}
    85  	if cfg.Width != 0 {
    86  		d.width = cfg.Width
    87  	} else {
    88  		d.width = 128
    89  	}
    90  	if cfg.Height != 0 {
    91  		d.height = cfg.Height
    92  	} else {
    93  		d.height = 296
    94  	}
    95  	d.rotation = cfg.Rotation
    96  	d.bufferLength = (uint32(d.logicalWidth) * uint32(d.height)) / 8
    97  	d.buffer = make([]uint8, d.bufferLength)
    98  	for i := uint32(0); i < d.bufferLength; i++ {
    99  		d.buffer[i] = 0xFF
   100  	}
   101  
   102  	d.cs.Low()
   103  	d.dc.Low()
   104  	d.rst.Low()
   105  
   106  	d.Reset()
   107  
   108  	d.SendCommand(DRIVER_OUTPUT_CONTROL)
   109  	d.SendData(uint8((d.height - 1) & 0xFF))
   110  	d.SendData(uint8(((d.height - 1) >> 8) & 0xFF))
   111  	d.SendData(0x00) // GD = 0; SM = 0; TB = 0;
   112  	d.SendCommand(BOOSTER_SOFT_START_CONTROL)
   113  	d.SendData(0xD7)
   114  	d.SendData(0xD6)
   115  	d.SendData(0x9D)
   116  	d.SendCommand(WRITE_VCOM_REGISTER)
   117  	d.SendData(0xA8) // VCOM 7C
   118  	d.SendCommand(SET_DUMMY_LINE_PERIOD)
   119  	d.SendData(0x1A) // 4 dummy lines per gate
   120  	d.SendCommand(SET_GATE_TIME)
   121  	d.SendData(0x08) // 2us per line
   122  	d.SendCommand(DATA_ENTRY_MODE_SETTING)
   123  	d.SendData(0x03) // X increment; Y increment
   124  
   125  	d.SetLUT(true)
   126  }
   127  
   128  // Reset resets the device
   129  func (d *Device) Reset() {
   130  	d.rst.Low()
   131  	time.Sleep(200 * time.Millisecond)
   132  	d.rst.High()
   133  	time.Sleep(200 * time.Millisecond)
   134  }
   135  
   136  // DeepSleep puts the display into deepsleep
   137  func (d *Device) DeepSleep() {
   138  	d.SendCommand(DEEP_SLEEP_MODE)
   139  	d.WaitUntilIdle()
   140  }
   141  
   142  // SendCommand sends a command to the display
   143  func (d *Device) SendCommand(command uint8) {
   144  	d.sendDataCommand(true, command)
   145  }
   146  
   147  // SendData sends a data byte to the display
   148  func (d *Device) SendData(data uint8) {
   149  	d.sendDataCommand(false, data)
   150  }
   151  
   152  // sendDataCommand sends image data or a command to the screen
   153  func (d *Device) sendDataCommand(isCommand bool, data uint8) {
   154  	if isCommand {
   155  		d.dc.Low()
   156  	} else {
   157  		d.dc.High()
   158  	}
   159  	d.cs.Low()
   160  	d.bus.Transfer(data)
   161  	d.cs.High()
   162  }
   163  
   164  // SetLUT sets the look up tables for full or partial updates
   165  func (d *Device) SetLUT(fullUpdate bool) {
   166  	d.SendCommand(WRITE_LUT_REGISTER)
   167  	if fullUpdate {
   168  		for i := 0; i < 30; i++ {
   169  			d.SendData(lutFullUpdate[i])
   170  		}
   171  	} else {
   172  		for i := 0; i < 30; i++ {
   173  			d.SendData(lutPartialUpdate[i])
   174  		}
   175  	}
   176  }
   177  
   178  // SetPixel modifies the internal buffer in a single pixel.
   179  // The display have 2 colors: black and white
   180  // We use RGBA(0,0,0, 255) as white (transparent)
   181  // Anything else as black
   182  func (d *Device) SetPixel(x int16, y int16, c color.RGBA) {
   183  	x, y = d.xy(x, y)
   184  	if x < 0 || x >= d.logicalWidth || y < 0 || y >= d.height {
   185  		return
   186  	}
   187  	byteIndex := (int32(x) + int32(y)*int32(d.logicalWidth)) / 8
   188  	if c.R == 0 && c.G == 0 && c.B == 0 { // TRANSPARENT / WHITE
   189  		d.buffer[byteIndex] |= 0x80 >> uint8(x%8)
   190  	} else { // WHITE / EMPTY
   191  		d.buffer[byteIndex] &^= 0x80 >> uint8(x%8)
   192  	}
   193  }
   194  
   195  // Display sends the buffer to the screen.
   196  func (d *Device) Display() error {
   197  	d.setMemoryArea(0, 0, d.logicalWidth-1, d.height-1)
   198  	for j := int16(0); j < d.height; j++ {
   199  		d.setMemoryPointer(0, j)
   200  		d.SendCommand(WRITE_RAM)
   201  		for i := int16(0); i < d.logicalWidth/8; i++ {
   202  			d.SendData(d.buffer[i+j*(d.logicalWidth/8)])
   203  		}
   204  	}
   205  
   206  	d.SendCommand(DISPLAY_UPDATE_CONTROL_2)
   207  	d.SendData(0xC4)
   208  	d.SendCommand(MASTER_ACTIVATION)
   209  	d.SendCommand(TERMINATE_FRAME_READ_WRITE)
   210  	return nil
   211  }
   212  
   213  // ClearDisplay erases the device SRAM
   214  func (d *Device) ClearDisplay() {
   215  	d.setMemoryArea(0, 0, d.logicalWidth-1, d.height-1)
   216  	d.setMemoryPointer(0, 0)
   217  	d.SendCommand(WRITE_RAM)
   218  	for i := uint32(0); i < d.bufferLength; i++ {
   219  		d.SendData(0xFF)
   220  	}
   221  	d.Display()
   222  }
   223  
   224  // setMemoryArea sets the area of the display that will be updated
   225  func (d *Device) setMemoryArea(x0 int16, y0 int16, x1 int16, y1 int16) {
   226  	d.SendCommand(SET_RAM_X_ADDRESS_START_END_POSITION)
   227  	d.SendData(uint8((x0 >> 3) & 0xFF))
   228  	d.SendData(uint8((x1 >> 3) & 0xFF))
   229  	d.SendCommand(SET_RAM_Y_ADDRESS_START_END_POSITION)
   230  	d.SendData(uint8(y0 & 0xFF))
   231  	d.SendData(uint8((y0 >> 8) & 0xFF))
   232  	d.SendData(uint8(y1 & 0xFF))
   233  	d.SendData(uint8((y1 >> 8) & 0xFF))
   234  }
   235  
   236  // setMemoryPointer moves the internal pointer to the speficied coordinates
   237  func (d *Device) setMemoryPointer(x int16, y int16) {
   238  	d.SendCommand(SET_RAM_X_ADDRESS_COUNTER)
   239  	d.SendData(uint8((x >> 3) & 0xFF))
   240  	d.SendCommand(SET_RAM_Y_ADDRESS_COUNTER)
   241  	d.SendData(uint8(y & 0xFF))
   242  	d.SendData(uint8((y >> 8) & 0xFF))
   243  	d.WaitUntilIdle()
   244  }
   245  
   246  // WaitUntilIdle waits until the display is ready
   247  func (d *Device) WaitUntilIdle() {
   248  	for d.busy.Get() {
   249  		time.Sleep(100 * time.Millisecond)
   250  	}
   251  }
   252  
   253  // IsBusy returns the busy status of the display
   254  func (d *Device) IsBusy() bool {
   255  	return d.busy.Get()
   256  }
   257  
   258  // ClearBuffer sets the buffer to 0xFF (white)
   259  func (d *Device) ClearBuffer() {
   260  	for i := uint32(0); i < d.bufferLength; i++ {
   261  		d.buffer[i] = 0xFF
   262  	}
   263  }
   264  
   265  // Size returns the current size of the display.
   266  func (d *Device) Size() (w, h int16) {
   267  	if d.rotation == ROTATION_90 || d.rotation == ROTATION_270 {
   268  		return d.height, d.logicalWidth
   269  	}
   270  	return d.logicalWidth, d.height
   271  }
   272  
   273  // SetRotation changes the rotation (clock-wise) of the device
   274  func (d *Device) SetRotation(rotation Rotation) {
   275  	d.rotation = rotation
   276  }
   277  
   278  // xy chages the coordinates according to the rotation
   279  func (d *Device) xy(x, y int16) (int16, int16) {
   280  	switch d.rotation {
   281  	case NO_ROTATION:
   282  		return x, y
   283  	case ROTATION_90:
   284  		return d.width - y - 1, x
   285  	case ROTATION_180:
   286  		return d.width - x - 1, d.height - y - 1
   287  	case ROTATION_270:
   288  		return y, d.height - x - 1
   289  	}
   290  	return x, y
   291  }