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

     1  // Package epd4in2 implements a driver for Waveshare 4.2in black and white e-paper device.
     2  //
     3  // Derived from:
     4  //
     5  //	https://github.com/tinygo-org/drivers/tree/master/waveshare-epd
     6  //	https://github.com/waveshare/e-Paper/blob/master/Arduino/epd4in2/epd4in2.cpp
     7  //
     8  // Datasheet: https://www.waveshare.com/wiki/4.2inch_e-Paper_Module
     9  package epd4in2
    10  
    11  import (
    12  	"image/color"
    13  	"machine"
    14  	"time"
    15  
    16  	"tinygo.org/x/drivers"
    17  )
    18  
    19  type Config struct {
    20  	Width        int16 // Width is the display resolution
    21  	Height       int16
    22  	LogicalWidth int16    // LogicalWidth must be a multiple of 8 and same size or bigger than Width
    23  	Rotation     Rotation // Rotation is clock-wise
    24  }
    25  
    26  type Device struct {
    27  	bus          drivers.SPI
    28  	cs           machine.Pin
    29  	dc           machine.Pin
    30  	rst          machine.Pin
    31  	busy         machine.Pin
    32  	logicalWidth int16
    33  	width        int16
    34  	height       int16
    35  	buffer       []uint8
    36  	bufferLength uint32
    37  	rotation     Rotation
    38  }
    39  
    40  type Rotation uint8
    41  
    42  // New returns a new epd4in2 driver. Pass in a fully configured SPI bus.
    43  func New(bus drivers.SPI, csPin, dcPin, rstPin, busyPin machine.Pin) Device {
    44  	csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
    45  	dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
    46  	rstPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
    47  	busyPin.Configure(machine.PinConfig{Mode: machine.PinInput})
    48  	return Device{
    49  		bus:  bus,
    50  		cs:   csPin,
    51  		dc:   dcPin,
    52  		rst:  rstPin,
    53  		busy: busyPin,
    54  	}
    55  }
    56  
    57  // Configure sets up the device.
    58  func (d *Device) Configure(cfg Config) {
    59  	if cfg.LogicalWidth != 0 {
    60  		d.logicalWidth = cfg.LogicalWidth
    61  	} else {
    62  		d.logicalWidth = EPD_WIDTH
    63  	}
    64  	if cfg.Width != 0 {
    65  		d.width = cfg.Width
    66  	} else {
    67  		d.width = EPD_WIDTH
    68  	}
    69  	if cfg.Height != 0 {
    70  		d.height = cfg.Height
    71  	} else {
    72  		d.height = EPD_HEIGHT
    73  	}
    74  	d.rotation = cfg.Rotation
    75  	d.bufferLength = (uint32(d.logicalWidth) * uint32(d.height)) / 8
    76  	d.buffer = make([]uint8, d.bufferLength)
    77  	for i := uint32(0); i < d.bufferLength; i++ {
    78  		d.buffer[i] = 0xFF
    79  	}
    80  
    81  	d.cs.Low()
    82  	d.dc.Low()
    83  	d.rst.Low()
    84  
    85  	d.Reset()
    86  	d.SendCommand(POWER_SETTING)
    87  	d.SendData(0x03) // VDS_EN, VDG_EN
    88  	d.SendData(0x00) // VCOM_HV, VGHL_LV[1], VGHL_LV[0]
    89  	d.SendData(0x2b) // VDH
    90  	d.SendData(0x2b) // VDL
    91  	d.SendData(0xff) // VDHR
    92  	d.SendCommand(BOOSTER_SOFT_START)
    93  	d.SendData(0x17)
    94  	d.SendData(0x17)
    95  	d.SendData(0x17) //07 0f 17 1f 27 2F 37 2f
    96  	d.SendCommand(POWER_ON)
    97  	d.WaitUntilIdle()
    98  	d.SendCommand(PANEL_SETTING)
    99  	d.SendData(0xbf) // KW-BF   KWR-AF  BWROTP 0f
   100  	d.SendData(0x0b)
   101  	d.SendCommand(PLL_CONTROL)
   102  	d.SendData(0x3c) // 3A 100HZ   29 150Hz 39 200HZ  31 171HZ
   103  }
   104  
   105  // Reset resets the device
   106  func (d *Device) Reset() {
   107  	d.rst.Low()
   108  	time.Sleep(200 * time.Millisecond)
   109  	d.rst.High()
   110  	time.Sleep(200 * time.Millisecond)
   111  }
   112  
   113  // DeepSleep puts the display into deepsleep
   114  func (d *Device) DeepSleep() {
   115  	d.SendCommand(VCOM_AND_DATA_INTERVAL_SETTING)
   116  	d.SendData(0x17)              //border floating
   117  	d.SendCommand(VCM_DC_SETTING) //VCOM to 0V
   118  	d.SendCommand(PANEL_SETTING)
   119  	time.Sleep(100 * time.Millisecond)
   120  
   121  	d.SendCommand(POWER_SETTING) //VG&VS to 0V fast
   122  	d.SendData(0x00)
   123  	d.SendData(0x00)
   124  	d.SendData(0x00)
   125  	d.SendData(0x00)
   126  	d.SendData(0x00)
   127  	time.Sleep(100 * time.Millisecond)
   128  
   129  	d.SendCommand(POWER_OFF) //power off
   130  	d.WaitUntilIdle()
   131  	d.SendCommand(DEEP_SLEEP) //deep sleep
   132  	d.SendData(0xA5)
   133  }
   134  
   135  // SendCommand sends a command to the display
   136  func (d *Device) SendCommand(command uint8) {
   137  	d.sendDataCommand(true, command)
   138  }
   139  
   140  // SendData sends a data byte to the display
   141  func (d *Device) SendData(data uint8) {
   142  	d.sendDataCommand(false, data)
   143  }
   144  
   145  // sendDataCommand sends image data or a command to the screen
   146  func (d *Device) sendDataCommand(isCommand bool, data uint8) {
   147  	if isCommand {
   148  		d.dc.Low()
   149  	} else {
   150  		d.dc.High()
   151  	}
   152  	d.cs.Low()
   153  	d.bus.Transfer(data)
   154  	d.cs.High()
   155  }
   156  
   157  // SetLUT sets the look up tables for full or partial updates
   158  func (d *Device) SetLUT() {
   159  	lut_vcom0 := []uint8{
   160  		0x00, 0x17, 0x00, 0x00, 0x00, 0x02,
   161  		0x00, 0x17, 0x17, 0x00, 0x00, 0x02,
   162  		0x00, 0x0A, 0x01, 0x00, 0x00, 0x01,
   163  		0x00, 0x0E, 0x0E, 0x00, 0x00, 0x02,
   164  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   165  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   166  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   167  		0x00, 0x00, // 44 bytes, unlike the others
   168  	}
   169  	lut_ww := []uint8{
   170  		0x40, 0x17, 0x00, 0x00, 0x00, 0x02,
   171  		0x90, 0x17, 0x17, 0x00, 0x00, 0x02,
   172  		0x40, 0x0A, 0x01, 0x00, 0x00, 0x01,
   173  		0xA0, 0x0E, 0x0E, 0x00, 0x00, 0x02,
   174  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   175  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   176  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   177  	}
   178  	lut_bw := []uint8{
   179  		0x40, 0x17, 0x00, 0x00, 0x00, 0x02,
   180  		0x90, 0x17, 0x17, 0x00, 0x00, 0x02,
   181  		0x40, 0x0A, 0x01, 0x00, 0x00, 0x01,
   182  		0xA0, 0x0E, 0x0E, 0x00, 0x00, 0x02,
   183  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   184  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   185  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   186  	}
   187  	lut_bb := []uint8{
   188  		0x80, 0x17, 0x00, 0x00, 0x00, 0x02,
   189  		0x90, 0x17, 0x17, 0x00, 0x00, 0x02,
   190  		0x80, 0x0A, 0x01, 0x00, 0x00, 0x01,
   191  		0x50, 0x0E, 0x0E, 0x00, 0x00, 0x02,
   192  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   193  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   194  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   195  	}
   196  	lut_wb := []uint8{
   197  		0x80, 0x17, 0x00, 0x00, 0x00, 0x02,
   198  		0x90, 0x17, 0x17, 0x00, 0x00, 0x02,
   199  		0x80, 0x0A, 0x01, 0x00, 0x00, 0x01,
   200  		0x50, 0x0E, 0x0E, 0x00, 0x00, 0x02,
   201  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   202  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   203  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   204  	}
   205  
   206  	d.SendCommand(LUT_FOR_VCOM) //vcom
   207  	for count := 0; count < 44; count++ {
   208  		d.SendData(lut_vcom0[count])
   209  	}
   210  
   211  	d.SendCommand(LUT_WHITE_TO_WHITE) //ww --
   212  	for count := 0; count < 42; count++ {
   213  		d.SendData(lut_ww[count])
   214  	}
   215  
   216  	d.SendCommand(LUT_BLACK_TO_WHITE) //bw r
   217  	for count := 0; count < 42; count++ {
   218  		d.SendData(lut_bw[count])
   219  	}
   220  
   221  	d.SendCommand(LUT_WHITE_TO_BLACK) //wb w
   222  	for count := 0; count < 42; count++ {
   223  		d.SendData(lut_bb[count])
   224  	}
   225  
   226  	d.SendCommand(LUT_BLACK_TO_BLACK) //bb b
   227  	for count := 0; count < 42; count++ {
   228  		d.SendData(lut_wb[count])
   229  	}
   230  }
   231  
   232  // SetPixel modifies the internal buffer in a single pixel.
   233  // The display have 2 colors: black and white
   234  // We use RGBA(0,0,0, 255) as white (transparent)
   235  // Anything else as black
   236  func (d *Device) SetPixel(x int16, y int16, c color.RGBA) {
   237  	x, y = d.xy(x, y)
   238  	if x < 0 || x >= d.logicalWidth || y < 0 || y >= d.height {
   239  		return
   240  	}
   241  	byteIndex := (uint32(x) + uint32(y)*uint32(d.logicalWidth)) / 8
   242  	if c.R == 0 && c.G == 0 && c.B == 0 { // TRANSPARENT / WHITE
   243  		d.buffer[byteIndex] |= 0x80 >> uint8(x%8)
   244  	} else { // WHITE / EMPTY
   245  		d.buffer[byteIndex] &^= 0x80 >> uint8(x%8)
   246  	}
   247  }
   248  
   249  // Display sends the buffer to the screen.
   250  func (d *Device) Display() error {
   251  	d.SendCommand(RESOLUTION_SETTING)
   252  	d.SendData(uint8(d.height >> 8))
   253  	d.SendData(uint8(d.logicalWidth & 0xff))
   254  	d.SendData(uint8(d.height >> 8))
   255  	d.SendData(uint8(d.height & 0xff))
   256  
   257  	d.SendCommand(VCM_DC_SETTING)
   258  	d.SendData(0x12)
   259  
   260  	d.SendCommand(VCOM_AND_DATA_INTERVAL_SETTING)
   261  	d.SendCommand(0x97) //VBDF 17|D7 VBDW 97  VBDB 57  VBDF F7  VBDW 77  VBDB 37  VBDR B7
   262  
   263  	d.SendCommand(DATA_START_TRANSMISSION_1)
   264  	var i int16
   265  	for i = 0; i < d.logicalWidth/8*d.height; i++ {
   266  		d.SendData(0xFF) // bit set: white, bit reset: black
   267  	}
   268  	time.Sleep(2 * time.Millisecond)
   269  	d.SendCommand(DATA_START_TRANSMISSION_2)
   270  	for i = 0; i < d.logicalWidth/8*d.height; i++ {
   271  		d.SendData(d.buffer[i])
   272  	}
   273  	time.Sleep(2 * time.Millisecond)
   274  
   275  	d.SetLUT()
   276  
   277  	d.SendCommand(DISPLAY_REFRESH)
   278  	time.Sleep(100 * time.Millisecond)
   279  	d.WaitUntilIdle()
   280  
   281  	return nil
   282  }
   283  
   284  // ClearDisplay erases the device SRAM
   285  func (d *Device) ClearDisplay() {
   286  	d.SendCommand(RESOLUTION_SETTING)
   287  	d.SendData(uint8(d.height >> 8))
   288  	d.SendData(uint8(d.logicalWidth & 0xff))
   289  	d.SendData(uint8(d.height >> 8))
   290  	d.SendData(uint8(d.height & 0xff))
   291  
   292  	d.SendCommand(DATA_START_TRANSMISSION_1)
   293  	time.Sleep(2 * time.Millisecond)
   294  	var i int16
   295  	for i = 0; i < d.logicalWidth/8*d.height; i++ {
   296  		d.SendData(0xFF)
   297  	}
   298  	time.Sleep(2 * time.Millisecond)
   299  	d.SendCommand(DATA_START_TRANSMISSION_2)
   300  	time.Sleep(2 * time.Millisecond)
   301  	for i = 0; i < d.logicalWidth/8*d.height; i++ {
   302  		d.SendData(0xFF)
   303  	}
   304  	time.Sleep(2 * time.Millisecond)
   305  
   306  	d.SetLUT()
   307  	d.SendCommand(DISPLAY_REFRESH)
   308  	time.Sleep(100 * time.Millisecond)
   309  	d.WaitUntilIdle()
   310  }
   311  
   312  // WaitUntilIdle waits until the display is ready
   313  func (d *Device) WaitUntilIdle() {
   314  	for d.busy.Get() {
   315  		time.Sleep(100 * time.Millisecond)
   316  	}
   317  }
   318  
   319  // IsBusy returns the busy status of the display
   320  func (d *Device) IsBusy() bool {
   321  	return d.busy.Get()
   322  }
   323  
   324  // ClearBuffer sets the buffer to 0xFF (white)
   325  func (d *Device) ClearBuffer() {
   326  	for i := uint32(0); i < d.bufferLength; i++ {
   327  		d.buffer[i] = 0xFF
   328  	}
   329  }
   330  
   331  // Size returns the current size of the display.
   332  func (d *Device) Size() (w, h int16) {
   333  	if d.rotation == ROTATION_90 || d.rotation == ROTATION_270 {
   334  		return d.height, d.logicalWidth
   335  	}
   336  	return d.logicalWidth, d.height
   337  }
   338  
   339  // SetRotation changes the rotation (clock-wise) of the device
   340  func (d *Device) SetRotation(rotation Rotation) {
   341  	d.rotation = rotation
   342  }
   343  
   344  // xy chages the coordinates according to the rotation
   345  func (d *Device) xy(x, y int16) (int16, int16) {
   346  	switch d.rotation {
   347  	case NO_ROTATION:
   348  		return x, y
   349  	case ROTATION_90:
   350  		return d.width - y - 1, x
   351  	case ROTATION_180:
   352  		return d.width - x - 1, d.height - y - 1
   353  	case ROTATION_270:
   354  		return y, d.height - x - 1
   355  	}
   356  	return x, y
   357  }