tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/waveshare-epd/epd2in13x/epd2in13x.go (about) 1 // Package epd2in13x implements a driver for Waveshare 2.13in (B & C versions) tri-color e-paper device. 2 // 3 // Datasheet: https://www.waveshare.com/w/upload/d/d3/2.13inch-e-paper-b-Specification.pdf 4 package epd2in13x // import "tinygo.org/x/drivers/waveshare-epd/epd2in13x" 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 17 Height int16 18 NumColors uint8 19 } 20 21 type Device struct { 22 bus drivers.SPI 23 cs machine.Pin 24 dc machine.Pin 25 rst machine.Pin 26 busy machine.Pin 27 width int16 28 height int16 29 buffer [][]uint8 30 bufferLength uint32 31 } 32 33 type Color uint8 34 35 // New returns a new epd2in13x driver. Pass in a fully configured SPI bus. 36 func New(bus drivers.SPI, csPin, dcPin, rstPin, busyPin machine.Pin) Device { 37 csPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) 38 dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) 39 rstPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) 40 busyPin.Configure(machine.PinConfig{Mode: machine.PinInput}) 41 return Device{ 42 bus: bus, 43 cs: csPin, 44 dc: dcPin, 45 rst: rstPin, 46 busy: busyPin, 47 } 48 } 49 50 // Configure sets up the device. 51 func (d *Device) Configure(cfg Config) { 52 if cfg.Width != 0 { 53 d.width = cfg.Width 54 } else { 55 d.width = 104 56 } 57 if cfg.Height != 0 { 58 d.height = cfg.Height 59 } else { 60 d.height = 212 61 } 62 if cfg.NumColors == 0 { 63 cfg.NumColors = 3 64 } else if cfg.NumColors == 1 { 65 cfg.NumColors = 2 66 } 67 d.bufferLength = (uint32(d.width) * uint32(d.height)) / 8 68 d.buffer = make([][]uint8, cfg.NumColors-1) 69 for i := range d.buffer { 70 d.buffer[i] = make([]uint8, d.bufferLength) 71 } 72 for i := range d.buffer { 73 for j := uint32(0); j < d.bufferLength; j++ { 74 d.buffer[i][j] = 0xFF 75 } 76 } 77 78 d.cs.Low() 79 d.dc.Low() 80 d.rst.Low() 81 82 d.Reset() 83 84 d.SendCommand(BOOSTER_SOFT_START) 85 d.SendData(0x17) 86 d.SendData(0x17) 87 d.SendData(0x17) 88 d.SendCommand(POWER_ON) 89 d.WaitUntilIdle() 90 d.SendCommand(PANEL_SETTING) 91 d.SendData(0x8F) 92 d.SendCommand(VCOM_AND_DATA_INTERVAL_SETTING) 93 d.SendData(0x37) 94 d.SendCommand(RESOLUTION_SETTING) 95 d.SendData(uint8(d.width)) 96 d.SendData(0x00) 97 d.SendData(uint8(d.height)) 98 } 99 100 // Reset resets the device 101 func (d *Device) Reset() { 102 d.rst.Low() 103 time.Sleep(200 * time.Millisecond) 104 d.rst.High() 105 time.Sleep(200 * time.Millisecond) 106 } 107 108 // DeepSleep puts the display into deepsleep 109 func (d *Device) DeepSleep() { 110 d.SendCommand(POWER_OFF) 111 d.WaitUntilIdle() 112 d.SendCommand(DEEP_SLEEP) 113 d.SendData(0xA5) 114 } 115 116 // SendCommand sends a command to the display 117 func (d *Device) SendCommand(command uint8) { 118 d.sendDataCommand(true, command) 119 } 120 121 // SendData sends a data byte to the display 122 func (d *Device) SendData(data uint8) { 123 d.sendDataCommand(false, data) 124 } 125 126 // sendDataCommand sends image data or a command to the screen 127 func (d *Device) sendDataCommand(isCommand bool, data uint8) { 128 if isCommand { 129 d.dc.Low() 130 } else { 131 d.dc.High() 132 } 133 d.cs.Low() 134 d.bus.Transfer(data) 135 d.cs.High() 136 } 137 138 // SetPixel modifies the internal buffer in a single pixel. 139 // The display have 3 colors: black, white and a third color that could be red or yellow 140 // We use RGBA(0,0,0, 255) as white (transparent) 141 // RGBA(1-255,0,0,255) as colored (red or yellow) 142 // Anything else as black 143 func (d *Device) SetPixel(x int16, y int16, c color.RGBA) { 144 if x < 0 || x >= d.width || y < 0 || y >= d.height { 145 return 146 } 147 if c.R != 0 && c.G == 0 && c.B == 0 { // COLORED 148 d.SetEPDPixel(x, y, COLORED) 149 } else if c.G != 0 || c.B != 0 { // BLACK 150 d.SetEPDPixel(x, y, BLACK) 151 } else { // WHITE / EMPTY 152 d.SetEPDPixel(x, y, WHITE) 153 } 154 } 155 156 // SetEPDPixel modifies the internal buffer in a single pixel. 157 func (d *Device) SetEPDPixel(x int16, y int16, c Color) { 158 if x < 0 || x >= d.width || y < 0 || y >= d.height { 159 return 160 } 161 byteIndex := (x + y*d.width) / 8 162 if c == WHITE { 163 d.buffer[BLACK-1][byteIndex] |= 0x80 >> uint8(x%8) 164 d.buffer[COLORED-1][byteIndex] |= 0x80 >> uint8(x%8) 165 } else if c == COLORED { 166 d.buffer[BLACK-1][byteIndex] |= 0x80 >> uint8(x%8) 167 d.buffer[COLORED-1][byteIndex] &^= 0x80 >> uint8(x%8) 168 } else { // BLACK 169 d.buffer[COLORED-1][byteIndex] |= 0x80 >> uint8(x%8) 170 d.buffer[BLACK-1][byteIndex] &^= 0x80 >> uint8(x%8) 171 } 172 } 173 174 // Display sends the buffer (if any) to the screen. 175 func (d *Device) Display() error { 176 d.SendCommand(DATA_START_TRANSMISSION_1) // black 177 time.Sleep(2 * time.Millisecond) 178 for i := uint32(0); i < d.bufferLength; i++ { 179 d.SendData(d.buffer[BLACK-1][i]) 180 } 181 time.Sleep(2 * time.Millisecond) 182 d.SendCommand(DATA_START_TRANSMISSION_2) // red 183 time.Sleep(2 * time.Millisecond) 184 for i := uint32(0); i < d.bufferLength; i++ { 185 d.SendData(d.buffer[COLORED-1][i]) 186 } 187 time.Sleep(2 * time.Millisecond) 188 d.SendCommand(DISPLAY_REFRESH) 189 return nil 190 } 191 192 // SetDisplayRect sends a rectangle of data at specific coordinates to the device SRAM directly 193 func (d *Device) SetDisplayRect(buffer [][]uint8, x int16, y int16, w int16, h int16) error { 194 if w%8 != 0 { 195 return errors.New("rectangle width needs to be a multiple of 8") 196 } 197 for i := range buffer { 198 if int16(len(buffer[i])) < (w/8)*h { 199 return errors.New("buffer has the wrong size") 200 } 201 } 202 d.SendCommand(PARTIAL_IN) 203 d.SendCommand(PARTIAL_WINDOW) 204 d.SendData(uint8(x) & 0xF8) 205 d.SendData(((uint8(x) & 0xF8) + uint8(w) - 1) | 0x07) 206 d.SendData(uint8(y) >> 8) 207 d.SendData(uint8(y) & 0xFF) 208 d.SendData(uint8(y+h-1) >> 8) 209 d.SendData(uint8(y+h-1) & 0xFF) 210 d.SendData(0x01) 211 time.Sleep(2 * time.Millisecond) 212 d.SendCommand(DATA_START_TRANSMISSION_1) 213 for i := int16(0); i < (w/8)*h; i++ { 214 d.SendData(buffer[BLACK-1][i]) 215 } 216 time.Sleep(2 * time.Millisecond) 217 if len(buffer) > 1 { 218 d.SendCommand(DATA_START_TRANSMISSION_2) 219 for i := int16(0); i < (w/8)*h; i++ { 220 d.SendData(buffer[COLORED-1][i]) 221 } 222 time.Sleep(2 * time.Millisecond) 223 } 224 d.SendCommand(PARTIAL_OUT) 225 return nil 226 } 227 228 // SetDisplayRectColor sends a rectangle of data at specific coordinates to the device SRAM directly 229 func (d *Device) SetDisplayRectColor(buffer []uint8, x int16, y int16, w int16, h int16, c Color) error { 230 if w%8 != 0 { 231 return errors.New("rectangle width needs to be a multiple of 8") 232 } 233 if int16(len(buffer)) < (w/8)*h { 234 return errors.New("buffer has the wrong size") 235 } 236 if c == WHITE { 237 return errors.New("wrong color") 238 } 239 d.SendCommand(PARTIAL_IN) 240 d.SendCommand(PARTIAL_WINDOW) 241 d.SendData(uint8(x) & 0xF8) 242 d.SendData(((uint8(x) & 0xF8) + uint8(w) - 1) | 0x07) 243 d.SendData(uint8(y) >> 8) 244 d.SendData(uint8(y) & 0xFF) 245 d.SendData(uint8(y+h-1) >> 8) 246 d.SendData(uint8(y+h-1) & 0xFF) 247 d.SendData(0x01) 248 time.Sleep(2 * time.Millisecond) 249 if c == COLORED { 250 d.SendCommand(DATA_START_TRANSMISSION_2) 251 } else { 252 d.SendCommand(DATA_START_TRANSMISSION_1) 253 } 254 for i := int16(0); i < (w/8)*h; i++ { 255 d.SendData(buffer[i]) 256 } 257 time.Sleep(2 * time.Millisecond) 258 d.SendCommand(PARTIAL_OUT) 259 return nil 260 } 261 262 // ClearDisplay erases the device SRAM 263 func (d *Device) ClearDisplay() { 264 d.SendCommand(DATA_START_TRANSMISSION_1) // black 265 time.Sleep(2 * time.Millisecond) 266 for i := uint32(0); i < d.bufferLength; i++ { 267 d.SendData(0xFF) 268 } 269 time.Sleep(2 * time.Millisecond) 270 d.SendCommand(DATA_START_TRANSMISSION_2) // red 271 time.Sleep(2 * time.Millisecond) 272 for i := uint32(0); i < d.bufferLength; i++ { 273 d.SendData(0xFF) 274 } 275 time.Sleep(2 * time.Millisecond) 276 } 277 278 // WaitUntilIdle waits until the display is ready 279 func (d *Device) WaitUntilIdle() { 280 for !d.busy.Get() { 281 time.Sleep(100 * time.Millisecond) 282 } 283 } 284 285 // IsBusy returns the busy status of the display 286 func (d *Device) IsBusy() bool { 287 return d.busy.Get() 288 } 289 290 // ClearBuffer sets the buffer to 0xFF (white) 291 func (d *Device) ClearBuffer() { 292 for i := uint8(0); i < uint8(len(d.buffer)); i++ { 293 for j := uint32(0); j < d.bufferLength; j++ { 294 d.buffer[i][j] = 0xFF 295 } 296 } 297 } 298 299 // Size returns the current size of the display. 300 func (d *Device) Size() (w, h int16) { 301 return d.width, d.height 302 }