diff --git a/examples/waveshare-epd/epd2in13-pizero/main.go b/examples/waveshare-epd/epd2in13-pizero/main.go new file mode 100644 index 00000000..df3b693a --- /dev/null +++ b/examples/waveshare-epd/epd2in13-pizero/main.go @@ -0,0 +1,178 @@ +// Demo for the Waveshare 2.13in e-Paper HAT V1 on a Waveshare RP2040-PiZero. +// +// Panel version. +// +// This demo is only for the V1 panel, which uses the IL3820 controller. It +// uses the epd2in13 driver, which sends the IL3820 init sequence and a 30 +// byte LUT. Later panels use the SSD1680 controller, which needs a different +// init sequence and a 153 byte LUT, so this demo does not drive them. +// +// Identify the panel from the date code on the ribbon cable. V1 shipped from +// 2015, V2 from 2019 and V3 and V4 from 2021. +// +// V1 (IL3820, 122x250) works, this demo +// V2 (SSD1680, 122x250) not supported here, try the epd2in9v2 driver +// V3 (SSD1680, 122x250) not supported here, try the epd2in9v2 driver +// V4 (SSD1680, 122x250) not supported here, try the epd2in9v2 driver +// 2.13in B, C and D variants (three colour or flexible) not supported here, +// use epd2in13x for the B and C variants +// +// Board support. +// +// The pin numbers below are only correct for the RP2040-PiZero. Any other +// board needs its own mapping. +// +// Waveshare RP2040-PiZero tested, works +// Raspberry Pi Pico and Pico W untested, needs its own pin mapping +// because it has no 40 pin header +// Other RP2040 boards untested, needs its own pin mapping +// Raspberry Pi 40 pin SBC headers not applicable, this is TinyGo firmware +// for a microcontroller, not Linux +// +// The RP2040-PiZero has a Raspberry Pi 40 pin header. Waveshare exchanges +// GPIO10 and GPIO11 on the header so that RP2040 SPI1 SCK and TX align with +// the Raspberry Pi SCLK and MOSI positions. +// Schematic: https://files.waveshare.com/wiki/RP2040-PiZero/RP2040-PiZero.pdf +// +// Header to RP2040 GPIO for the e-Paper HAT signals: +// +// pin 11 RST GPIO17 +// pin 18 BUSY GPIO24 +// pin 19 MOSI GPIO11 (SPI1 SDO) +// pin 22 DC GPIO25 +// pin 23 SCLK GPIO10 (SPI1 SCK) +// pin 24 CS GPIO8 +// +// Build and flash: +// +// tinygo flash -target=pico ./examples/waveshare-epd/epd2in13-pizero +package main + +import ( + "image/color" + "machine" + "time" + + "tinygo.org/x/drivers/waveshare-epd/epd2in13" +) + +const ( + epdWidth = 122 + epdHeight = 250 +) + +var ( + black = color.RGBA{0, 0, 0, 255} + white = color.RGBA{255, 255, 255, 255} + + display epd2in13.Device +) + +func main() { + // Wait for the USB serial console to attach so the log is not lost. + time.Sleep(3 * time.Second) + + err := machine.SPI1.Configure(machine.SPIConfig{ + Frequency: 4000000, + SCK: machine.GPIO10, + SDO: machine.GPIO11, + SDI: machine.GPIO12, + Mode: 0, + }) + if err != nil { + println("SPI1 configure failed:", err.Error()) + return + } + + display = epd2in13.New(machine.SPI1, machine.GPIO8, machine.GPIO25, machine.GPIO17, machine.GPIO24) + display.Configure(epd2in13.Config{ + Width: epdWidth, + Height: epdHeight, + }) + + println("clear the display") + display.ClearBuffer() + display.ClearDisplay() + display.WaitUntilIdle() + + rotations := []struct { + name string + r epd2in13.Rotation + }{ + {"0", epd2in13.NO_ROTATION}, + {"90", epd2in13.ROTATION_90}, + {"180", epd2in13.ROTATION_180}, + {"270", epd2in13.ROTATION_270}, + } + for _, rot := range rotations { + println("draw the test pattern at rotation", rot.name) + display.SetRotation(rot.r) + w, h := display.Size() + display.ClearBuffer() + drawTestPattern(w, h) + display.Display() + display.WaitUntilIdle() + time.Sleep(3 * time.Second) + } + + println("done, the display holds the image without power") + display.DeepSleep() + + for { + time.Sleep(time.Second) + } +} + +// drawTestPattern draws a border, two diagonals, a solid black block and a +// grey scale ramp, scaled to the given width and height. A correct panel +// shows all four. Missing or shifted parts point to a wrong pin, a wrong +// panel version or a bad ribbon connection. +func drawTestPattern(w, h int16) { + fill(0, 0, w, h, white) + + // Border, 2 pixels wide. + fill(0, 0, w, 2, black) + fill(0, h-2, w, 2, black) + fill(0, 0, 2, h, black) + fill(w-2, 0, 2, h, black) + + // Diagonals across the full panel. + for y := int16(0); y < h; y++ { + x := int16(int32(y) * int32(w) / int32(h)) + display.SetPixel(x, y, black) + display.SetPixel(w-1-x, y, black) + } + + // Solid black block. This shows the darkest level the panel can reach. + // Sized as a fraction of the panel so it fits in both orientations. + blockSize := h / 6 + fill(8, 8, blockSize, blockSize, black) + + // Dither ramp from light to dark in eight steps, packed into whatever + // vertical space remains below the block so it fits in both + // orientations, including the shorter 122 pixel dimension. + rampTop := 8 + blockSize + 8 + rampHeight := h - rampTop - 8 + bandHeight := rampHeight / 8 + if bandHeight < 1 { + bandHeight = 1 + } + for step := int16(0); step < 8; step++ { + y0 := rampTop + step*bandHeight + for y := y0; y < y0+bandHeight && y < h-8; y++ { + for x := int16(10); x < w-10; x++ { + if (x+y*3)%8 < step+1 { + display.SetPixel(x, y, black) + } + } + } + } +} + +func fill(x, y, w, h int16, c color.RGBA) { + for i := x; i < x+w; i++ { + for j := y; j < y+h; j++ { + display.SetPixel(i, j, c) + } + } +} diff --git a/waveshare-epd/epd2in13/epd2in13.go b/waveshare-epd/epd2in13/epd2in13.go index e49fd427..1511e1fa 100644 --- a/waveshare-epd/epd2in13/epd2in13.go +++ b/waveshare-epd/epd2in13/epd2in13.go @@ -318,9 +318,9 @@ func (d *Device) ClearBuffer() { // Size returns the current size of the display. func (d *Device) Size() (w, h int16) { if d.rotation == drivers.Rotation90 || d.rotation == drivers.Rotation270 { - return d.height, d.logicalWidth + return d.height, d.width } - return d.logicalWidth, d.height + return d.width, d.height } // Rotation returns the current rotation of the device.