mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-09-10 16:49:32 +00:00
examples: add 2.13in e-Paper V1 demo for RP2040-PiZero
Exercise all four SetRotation modes in the demo. Fix epd2in13.Size(), which reported the padded logical buffer width instead of the true panel width at rotation 90 and 270. This clipped the last few rows on those rotations because the driver bounds check used the true width while Size() reported the padded width. Scale the dither ramp and solid block to the panel height instead of fixed pixel offsets, so they fit inside the shorter 122 pixel dimension at rotation 90 and 270.
This commit is contained in:
committed by
Daniel Esteban
parent
4f6f6bc66f
commit
3b4cdece03
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user