diff --git a/examples/waveshare-epd/epd7in5v2/.tool-versions b/examples/waveshare-epd/epd7in5v2/.tool-versions new file mode 100644 index 000000000..db5d8ee5b --- /dev/null +++ b/examples/waveshare-epd/epd7in5v2/.tool-versions @@ -0,0 +1 @@ +golang 1.23.4 diff --git a/examples/waveshare-epd/epd7in5v2/main.go b/examples/waveshare-epd/epd7in5v2/main.go new file mode 100644 index 000000000..90c29b7be --- /dev/null +++ b/examples/waveshare-epd/epd7in5v2/main.go @@ -0,0 +1,64 @@ +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/waveshare-epd/epd7in5v2" +) + +// example for writing to a e-ink display +func main() { + // Pins are for an Adafruit Feather nRF52840 Express + bus := machine.SPI0 + cs := machine.D5 + dc := machine.D6 + rst := machine.D9 + busy := machine.D10 + pwr := machine.D11 + + panel := epd7in5v2.New(cs, rst, dc, busy, pwr, bus) + err := panel.Configure(machine.SPI0_SCK_PIN, machine.SPI0_SDO_PIN) + if err != nil { + panic(err) + } + + panel.Reset() + + err = panel.Init() + if err != nil { + panic(err) + } + + buf := epd7in5v2.NewImageBuffer() + + // Draw some stripes + var color byte + for y := 0; y < epd7in5v2.HEIGHT; y++ { + if y/60%2 == 0 { + color = 0xFF + } else { + color = 0x00 + } + for x := 0; x < epd7in5v2.BYTE_WIDTH; x++ { + buf[y*epd7in5v2.BYTE_WIDTH+x] = color + } + } + + err = panel.Draw(buf) + if err != nil { + panic(err) + } + + time.Sleep(5 * time.Second) + + err = panel.Clear() + if err != nil { + panic(err) + } + + err = panel.DeepSleep() + if err != nil { + panic(err) + } +} diff --git a/smoketest.sh b/smoketest.sh index f21e2be55..20f44a3e8 100755 --- a/smoketest.sh +++ b/smoketest.sh @@ -140,6 +140,7 @@ tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/tmc2209/mai tinygo build -size short -o ./build/test.hex -target=pico ./examples/tmc5160/main.go tinygo build -size short -o ./build/test.uf2 -target=nicenano ./examples/sharpmem/main.go tinygo build -size short -o ./build/test.hex -target=feather-nrf52840 ./examples/max6675/main.go +tinygo build -size short -o ./build/test.uf2 -target=feather-nrf52840 ./examples/waveshare-epd/epd7in5v2/main.go # network examples (espat) tinygo build -size short -o ./build/test.hex -target=challenger-rp2040 ./examples/net/ntpclient/ # network examples (wifinina) diff --git a/waveshare-epd/epd7in5v2/epd7in5v2.go b/waveshare-epd/epd7in5v2/epd7in5v2.go new file mode 100644 index 000000000..495619f99 --- /dev/null +++ b/waveshare-epd/epd7in5v2/epd7in5v2.go @@ -0,0 +1,226 @@ +package epd7in5v2 + +import ( + "machine" + "time" +) + +// Waveshare 7.5 inch e-ink display v2 +// https://www.waveshare.com/product/displays/e-paper/epaper-1/7.5inch-e-paper-hat.htm +// +// Currently only supporting 2 color display, and full display updates. +// +// https://files.waveshare.com/upload/6/60/7.5inch_e-Paper_V2_Specification.pdf +// https://github.com/waveshareteam/e-Paper/tree/4822c075f5df714f88b02e10c336b4eeff7e603e/Arduino/epd7in5_V2 + +const WIDTH int = 800 +const HEIGHT int = 480 +const BYTE_WIDTH int = WIDTH / 8 + +type EPD7in5_V2 struct { + cs machine.Pin + rst machine.Pin + dc machine.Pin + bsy machine.Pin + pwr machine.Pin + bus machine.SPI +} + +func New(cs, rst, dc, bsy, pwr machine.Pin, bus machine.SPI) *EPD7in5_V2 { + return &EPD7in5_V2{ + cs: cs, + rst: rst, + dc: dc, + bsy: bsy, + pwr: pwr, + bus: bus, + } +} + +func (e *EPD7in5_V2) Configure(sck, sdo machine.Pin) error { + e.bsy.Configure(machine.PinConfig{Mode: machine.PinInput}) + e.cs.Configure(machine.PinConfig{Mode: machine.PinOutput}) + e.rst.Configure(machine.PinConfig{Mode: machine.PinOutput}) + e.dc.Configure(machine.PinConfig{Mode: machine.PinOutput}) + e.pwr.Configure(machine.PinConfig{Mode: machine.PinOutput}) + e.pwr.High() + + return e.bus.Configure(machine.SPIConfig{ + Frequency: 2_000_000, + SCK: sck, + SDO: sdo, + LSBFirst: false, + Mode: 0, + }) +} + +func (e *EPD7in5_V2) Command(cmd byte, data []byte) error { + e.dc.Low() + e.cs.Low() + _, err := e.bus.Transfer(cmd) + e.cs.High() + + if err != nil || data == nil { + return err + } + + return e.Data(data) +} + +func (e *EPD7in5_V2) Data(data []byte) error { + e.dc.High() + e.cs.Low() + err := e.bus.Tx(data, []byte{}) + e.cs.High() + return err +} + +func (e *EPD7in5_V2) waitUntilIdle() error { + var err error + for { + if err = e.Command(0x71, nil); err != nil { + return err + } + if e.bsy.Get() { + break + } + time.Sleep(10 * time.Millisecond) + } + time.Sleep(20 * time.Millisecond) + return nil +} + +func (e *EPD7in5_V2) Reset() { + e.rst.High() + time.Sleep(20 * time.Millisecond) + e.rst.Low() + time.Sleep(20 * time.Millisecond) + e.rst.High() + time.Sleep(20 * time.Millisecond) +} + +func (e *EPD7in5_V2) Init() error { + var err error + + // power setting + if err = e.Command(0x01, []byte{0x17, 0x17, 0x3F, 0x3F, 0x11}); err != nil { + return err + } + + // VCOM DC setting + if err = e.Command(0x82, []byte{0x24}); err != nil { + return err + } + + // booster setting + if err = e.Command(0x06, []byte{0x27, 0x27, 0x2F, 0x17}); err != nil { + return err + } + + // OSC setting + if err = e.Command(0x30, []byte{0x06}); err != nil { + return err + } + + // power on + if err = e.Command(0x04, nil); err != nil { + return err + } + time.Sleep(100 * time.Millisecond) + if err = e.waitUntilIdle(); err != nil { + return err + } + + // panel setting + if err = e.Command(0x00, []byte{0x3F}); err != nil { + return err + } + + // resolution setting + if err = e.Command(0x61, []byte{0x03, 0x20, 0x01, 0xE0}); err != nil { + return err + } + + // dual SPI mode (off) + if err = e.Command(0x15, []byte{0x00}); err != nil { + return err + } + + // VCOM and data interval + if err = e.Command(0x50, []byte{0x10, 0x00}); err != nil { + return err + } + + // tcon setting + if err = e.Command(0x60, []byte{0x22}); err != nil { + return err + } + + // Gate/Source Start Setting + if err = e.Command(0x65, []byte{0x00, 0x00, 0x00, 0x00}); err != nil { + return err + } + + // Set LUT + if err = e.Command(0x20, []byte{ + 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x01, 0x00, 0x0F, 0x01, 0x0F, 0x01, 0x02, 0x00, 0x0F, + 0x0F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }); err != nil { + return err + } + + if err = e.Command(0x21, []byte{ + 0x10, 0x0F, 0x0F, 0x00, 0x00, 0x01, 0x84, 0x0F, 0x01, 0x0F, 0x01, 0x02, 0x20, 0x0F, + 0x0F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }); err != nil { + return err + } + + if err = e.Command(0x22, []byte{ + 0x10, 0x0F, 0x0F, 0x00, 0x00, 0x01, 0x84, 0x0F, 0x01, 0x0F, 0x01, 0x02, 0x20, 0x0F, + 0x0F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }); err != nil { + return err + } + + if err = e.Command(0x23, []byte{ + 0x80, 0x0F, 0x0F, 0x00, 0x00, 0x01, 0x84, 0x0F, 0x01, 0x0F, 0x01, 0x02, 0x40, 0x0F, + 0x0F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }); err != nil { + return err + } + + return e.Command(0x24, []byte{ + 0x80, 0x0F, 0x0F, 0x00, 0x00, 0x01, 0x84, 0x0F, 0x01, 0x0F, 0x01, 0x02, 0x40, 0x0F, + 0x0F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }) +} + +func (e *EPD7in5_V2) Draw(img []byte) error { + var err error + if err = e.Command(0x13, img); err != nil { + return err + } + if err = e.Command(0x12, nil); err != nil { + return err + } + time.Sleep(100 * time.Millisecond) + return e.waitUntilIdle() +} + +func (e *EPD7in5_V2) Clear() error { + return e.Draw(make([]byte, BYTE_WIDTH*HEIGHT)) +} + +func (e *EPD7in5_V2) DeepSleep() error { + return e.Command(0x07, nil) +} + +func NewImageBuffer() []byte { + return make([]byte, BYTE_WIDTH*HEIGHT) +}