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 }