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

     1  // Package epd2in13 implements a driver for Waveshare 2.13in black and white e-paper device.
     2  //
     3  // Datasheet: https://www.waveshare.com/w/upload/e/e6/2.13inch_e-Paper_Datasheet.pdf
     4  package epd2in13 // import "tinygo.org/x/drivers/waveshare-epd/epd2in13"
     5  
     6  import (
     7  	"errors"
     8  	"image/color"
     9  	"machine"
    10  	"time"
    11  
    12  	"tinygo.org/x/drivers"
    13  )
    14  
    15  type Config struct {
    16  	Width        int16 // Width is the display resolution
    17  	Height       int16
    18  	LogicalWidth int16 // LogicalWidth must be a multiple of 8 and same size or bigger than Width
    19  	Rotation     drivers.Rotation
    20  }
    21  
    22  type Device struct {
    23  	bus          drivers.SPI
    24  	cs           machine.Pin
    25  	dc           machine.Pin
    26  	rst          machine.Pin
    27  	busy         machine.Pin
    28  	logicalWidth int16
    29  	width        int16
    30  	height       int16
    31  	buffer       []uint8
    32  	bufferLength uint32
    33  	rotation     drivers.Rotation
    34  }
    35  
    36  // Deprecated: use drivers.Rotation instead.
    37  type Rotation = drivers.Rotation
    38  
    39  // Look up table for full updates
    40  var lutFullUpdate = [30]uint8{
    41  	0x22, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x11,
    42  	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    43  	0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E,
    44  	0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
    45  }
    46  
    47  // Look up table for partial updates, faster but there will be some ghosting
    48  var lutPartialUpdate = [30]uint8{
    49  	0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    50  	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    51  	0x0F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    52  	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    53  }
    54  
    55  // New returns a new epd2in13x driver. Pass in a fully configured SPI bus.
    56  func New(bus drivers.SPI, csPin, dcPin, rstPin, busyPin machine.Pin) Device {
    57  	csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
    58  	dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
    59  	rstPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
    60  	busyPin.Configure(machine.PinConfig{Mode: machine.PinInput})
    61  	return Device{
    62  		bus:  bus,
    63  		cs:   csPin,
    64  		dc:   dcPin,
    65  		rst:  rstPin,
    66  		busy: busyPin,
    67  	}
    68  }
    69  
    70  // Configure sets up the device.
    71  func (d *Device) Configure(cfg Config) {
    72  	if cfg.LogicalWidth != 0 {
    73  		d.logicalWidth = cfg.LogicalWidth
    74  	} else {
    75  		d.logicalWidth = 128
    76  	}
    77  	if cfg.Width != 0 {
    78  		d.width = cfg.Width
    79  	} else {
    80  		d.width = 122
    81  	}
    82  	if cfg.Height != 0 {
    83  		d.height = cfg.Height
    84  	} else {
    85  		d.height = 250
    86  	}
    87  	d.rotation = cfg.Rotation
    88  	d.bufferLength = (uint32(d.logicalWidth) * uint32(d.height)) / 8
    89  	d.buffer = make([]uint8, d.bufferLength)
    90  	for i := uint32(0); i < d.bufferLength; i++ {
    91  		d.buffer[i] = 0xFF
    92  	}
    93  
    94  	d.cs.Low()
    95  	d.dc.Low()
    96  	d.rst.Low()
    97  
    98  	d.Reset()
    99  
   100  	d.SendCommand(DRIVER_OUTPUT_CONTROL)
   101  	d.SendData(uint8((d.height - 1) & 0xFF))
   102  	d.SendData(uint8(((d.height - 1) >> 8) & 0xFF))
   103  	d.SendData(0x00) // GD = 0; SM = 0; TB = 0;
   104  	d.SendCommand(BOOSTER_SOFT_START_CONTROL)
   105  	d.SendData(0xD7)
   106  	d.SendData(0xD6)
   107  	d.SendData(0x9D)
   108  	d.SendCommand(WRITE_VCOM_REGISTER)
   109  	d.SendData(0xA8) // VCOM 7C
   110  	d.SendCommand(SET_DUMMY_LINE_PERIOD)
   111  	d.SendData(0x1A) // 4 dummy lines per gate
   112  	d.SendCommand(SET_GATE_TIME)
   113  	d.SendData(0x08) // 2us per line
   114  	d.SendCommand(DATA_ENTRY_MODE_SETTING)
   115  	d.SendData(0x03) // X increment; Y increment
   116  
   117  	d.SetLUT(true)
   118  }
   119  
   120  // Reset resets the device
   121  func (d *Device) Reset() {
   122  	d.rst.Low()
   123  	time.Sleep(200 * time.Millisecond)
   124  	d.rst.High()
   125  	time.Sleep(200 * time.Millisecond)
   126  }
   127  
   128  // DeepSleep puts the display into deepsleep
   129  func (d *Device) DeepSleep() {
   130  	d.SendCommand(DEEP_SLEEP_MODE)
   131  	d.WaitUntilIdle()
   132  }
   133  
   134  // Set the sleep mode of the panel. The display will still show its contents,
   135  // but will go into a lower-power state.
   136  func (d *Device) Sleep(sleepEnabled bool) error {
   137  	if sleepEnabled {
   138  		d.DeepSleep()
   139  	} else {
   140  		d.Reset()
   141  	}
   142  	return nil
   143  }
   144  
   145  // SendCommand sends a command to the display
   146  func (d *Device) SendCommand(command uint8) {
   147  	d.sendDataCommand(true, command)
   148  }
   149  
   150  // SendData sends a data byte to the display
   151  func (d *Device) SendData(data uint8) {
   152  	d.sendDataCommand(false, data)
   153  }
   154  
   155  // sendDataCommand sends image data or a command to the screen
   156  func (d *Device) sendDataCommand(isCommand bool, data uint8) {
   157  	if isCommand {
   158  		d.dc.Low()
   159  	} else {
   160  		d.dc.High()
   161  	}
   162  	d.cs.Low()
   163  	d.bus.Transfer(data)
   164  	d.cs.High()
   165  }
   166  
   167  // SetLUT sets the look up tables for full or partial updates
   168  func (d *Device) SetLUT(fullUpdate bool) {
   169  	d.SendCommand(WRITE_LUT_REGISTER)
   170  	if fullUpdate {
   171  		for i := 0; i < 30; i++ {
   172  			d.SendData(lutFullUpdate[i])
   173  		}
   174  	} else {
   175  		for i := 0; i < 30; i++ {
   176  			d.SendData(lutPartialUpdate[i])
   177  		}
   178  	}
   179  }
   180  
   181  // SetPixel modifies the internal buffer in a single pixel.
   182  // The display have 2 colors: black and white. We use a very simple cutoff to
   183  // determine whether a pixel is black or white (darker colors are black, lighter
   184  // colors are white).
   185  func (d *Device) SetPixel(x int16, y int16, c color.RGBA) {
   186  	x, y = d.xy(x, y)
   187  	if x < 0 || x >= d.logicalWidth || y < 0 || y >= d.height {
   188  		return
   189  	}
   190  	byteIndex := (x + y*d.logicalWidth) / 8
   191  	// Very simle black/white split.
   192  	// This isn't very accurate (especially for sRGB colors) but is close enough
   193  	// to the truth that it probably doesn't matter much - especially on an
   194  	// e-paper display.
   195  	if int(c.R)+int(c.G)+int(c.B) > 128*3 { // light, convert to white
   196  		d.buffer[byteIndex] |= 0x80 >> uint8(x%8)
   197  	} else { // dark, convert to black
   198  		d.buffer[byteIndex] &^= 0x80 >> uint8(x%8)
   199  	}
   200  }
   201  
   202  // Display sends the buffer to the screen.
   203  func (d *Device) Display() error {
   204  	d.setMemoryArea(0, 0, d.logicalWidth-1, d.height-1)
   205  	for j := int16(0); j < d.height; j++ {
   206  		d.setMemoryPointer(0, j)
   207  		d.SendCommand(WRITE_RAM)
   208  		for i := int16(0); i < d.logicalWidth/8; i++ {
   209  			d.SendData(d.buffer[i+j*(d.logicalWidth/8)])
   210  		}
   211  	}
   212  
   213  	d.SendCommand(DISPLAY_UPDATE_CONTROL_2)
   214  	d.SendData(0xC4)
   215  	d.SendCommand(MASTER_ACTIVATION)
   216  	d.SendCommand(TERMINATE_FRAME_READ_WRITE)
   217  	return nil
   218  }
   219  
   220  // DisplayRect sends only an area of the buffer to the screen.
   221  // The rectangle points need to be a multiple of 8 in the screen.
   222  // They might not work as expected if the screen is rotated.
   223  func (d *Device) DisplayRect(x int16, y int16, width int16, height int16) error {
   224  	x, y = d.xy(x, y)
   225  	if x < 0 || y < 0 || x >= d.logicalWidth || y >= d.height || width < 0 || height < 0 {
   226  		return errors.New("wrong rectangle")
   227  	}
   228  	if d.rotation == drivers.Rotation90 {
   229  		width, height = height, width
   230  		x -= width
   231  	} else if d.rotation == drivers.Rotation180 {
   232  		x -= width - 1
   233  		y -= height - 1
   234  	} else if d.rotation == drivers.Rotation270 {
   235  		width, height = height, width
   236  		y -= height
   237  	}
   238  	x &= 0xF8
   239  	width &= 0xF8
   240  	width = x + width // reuse variables
   241  	if width >= d.logicalWidth {
   242  		width = d.logicalWidth
   243  	}
   244  	height = y + height
   245  	if height > d.height {
   246  		height = d.height
   247  	}
   248  	d.setMemoryArea(x, y, width, height)
   249  	x = x / 8
   250  	width = width / 8
   251  	for ; y < height; y++ {
   252  		d.setMemoryPointer(8*x, y)
   253  		d.SendCommand(WRITE_RAM)
   254  		for i := int16(x); i < width; i++ {
   255  			d.SendData(d.buffer[i+y*d.logicalWidth/8])
   256  		}
   257  	}
   258  
   259  	d.SendCommand(DISPLAY_UPDATE_CONTROL_2)
   260  	d.SendData(0xC4)
   261  	d.SendCommand(MASTER_ACTIVATION)
   262  	d.SendCommand(TERMINATE_FRAME_READ_WRITE)
   263  	return nil
   264  }
   265  
   266  // ClearDisplay erases the device SRAM
   267  func (d *Device) ClearDisplay() {
   268  	d.setMemoryArea(0, 0, d.logicalWidth-1, d.height-1)
   269  	d.setMemoryPointer(0, 0)
   270  	d.SendCommand(WRITE_RAM)
   271  	for i := uint32(0); i < d.bufferLength; i++ {
   272  		d.SendData(0xFF)
   273  	}
   274  	d.Display()
   275  }
   276  
   277  // setMemoryArea sets the area of the display that will be updated
   278  func (d *Device) setMemoryArea(x0 int16, y0 int16, x1 int16, y1 int16) {
   279  	d.SendCommand(SET_RAM_X_ADDRESS_START_END_POSITION)
   280  	d.SendData(uint8((x0 >> 3) & 0xFF))
   281  	d.SendData(uint8((x1 >> 3) & 0xFF))
   282  	d.SendCommand(SET_RAM_Y_ADDRESS_START_END_POSITION)
   283  	d.SendData(uint8(y0 & 0xFF))
   284  	d.SendData(uint8((y0 >> 8) & 0xFF))
   285  	d.SendData(uint8(y1 & 0xFF))
   286  	d.SendData(uint8((y1 >> 8) & 0xFF))
   287  }
   288  
   289  // setMemoryPointer moves the internal pointer to the speficied coordinates
   290  func (d *Device) setMemoryPointer(x int16, y int16) {
   291  	d.SendCommand(SET_RAM_X_ADDRESS_COUNTER)
   292  	d.SendData(uint8((x >> 3) & 0xFF))
   293  	d.SendCommand(SET_RAM_Y_ADDRESS_COUNTER)
   294  	d.SendData(uint8(y & 0xFF))
   295  	d.SendData(uint8((y >> 8) & 0xFF))
   296  	d.WaitUntilIdle()
   297  }
   298  
   299  // WaitUntilIdle waits until the display is ready
   300  func (d *Device) WaitUntilIdle() {
   301  	for d.busy.Get() {
   302  		time.Sleep(100 * time.Millisecond)
   303  	}
   304  }
   305  
   306  // IsBusy returns the busy status of the display
   307  func (d *Device) IsBusy() bool {
   308  	return d.busy.Get()
   309  }
   310  
   311  // ClearBuffer sets the buffer to 0xFF (white)
   312  func (d *Device) ClearBuffer() {
   313  	for i := uint32(0); i < d.bufferLength; i++ {
   314  		d.buffer[i] = 0xFF
   315  	}
   316  }
   317  
   318  // Size returns the current size of the display.
   319  func (d *Device) Size() (w, h int16) {
   320  	if d.rotation == drivers.Rotation90 || d.rotation == drivers.Rotation270 {
   321  		return d.height, d.logicalWidth
   322  	}
   323  	return d.logicalWidth, d.height
   324  }
   325  
   326  // Rotation returns the current rotation of the device.
   327  func (d *Device) Rotation() drivers.Rotation {
   328  	return d.rotation
   329  }
   330  
   331  // SetRotation changes the rotation of the device.
   332  func (d *Device) SetRotation(rotation drivers.Rotation) error {
   333  	d.rotation = rotation
   334  	return nil
   335  }
   336  
   337  // xy chages the coordinates according to the rotation
   338  func (d *Device) xy(x, y int16) (int16, int16) {
   339  	switch d.rotation {
   340  	case drivers.Rotation0:
   341  		return x, y
   342  	case drivers.Rotation90:
   343  		return d.width - y - 1, x
   344  	case drivers.Rotation180:
   345  		return d.width - x - 1, d.height - y - 1
   346  	case drivers.Rotation270:
   347  		return y, d.height - x - 1
   348  	}
   349  	return x, y
   350  }