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 }