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 }