From 5b35a3fb05bf426013bb641a182c32c226db54c5 Mon Sep 17 00:00:00 2001 From: hansmi Date: Sat, 11 Dec 2021 00:19:15 +0100 Subject: [PATCH] waveshare2in13v2: Deduplicate and simplify logic for sending data (#33) Dev.Clear: Avoid writing one beyond the end of the line. Also build the row data only once before sending it for each line. Dev.Draw, Dev.DrawPartial: Deduplicate logic for iterating over pixels and simplify it such that a whole row of bits is built before sending them in one go. Tested using a Waveshare e-Paper 2.13in V2 display. Signed-off-by: Michael Hanselmann --- waveshare2in13v2/waveshare213v2.go | 120 ++++++++++-------------- waveshare2in13v2/waveshare213v2_test.go | 38 ++++++++ 2 files changed, 87 insertions(+), 71 deletions(-) create mode 100644 waveshare2in13v2/waveshare213v2_test.go diff --git a/waveshare2in13v2/waveshare213v2.go b/waveshare2in13v2/waveshare213v2.go index 99baaa3..628ec9a 100644 --- a/waveshare2in13v2/waveshare213v2.go +++ b/waveshare2in13v2/waveshare213v2.go @@ -5,6 +5,7 @@ package waveshare2in13v2 import ( + "bytes" "fmt" "image" "image/color" @@ -124,6 +125,12 @@ var EPD2in13v2 = Opts{ }, } +// dataDimensions returns the size in terms of bytes needed to fill the +// display. +func dataDimensions(opts *Opts) (int, int) { + return opts.Height, (opts.Width + 7) / 8 +} + func (eh *errorHandler) rstOut(l gpio.Level) { if eh.err != nil { return @@ -307,21 +314,16 @@ func (d *Dev) Init(partialUpdate PartialUpdate) error { // Clear clears the display. func (d *Dev) Clear(color byte) error { - linewidth := 0 - if d.opts.Width%8 == 0 { - linewidth = d.opts.Width / 8 - } else { - linewidth = d.opts.Width/8 + 1 - } + rows, cols := dataDimensions(d.opts) + data := bytes.Repeat([]byte{color}, cols) + if err := d.sendCommand([]byte{writeRAMBW}); err != nil { return err } - for i := 0; i <= d.opts.Height; i++ { - for i := 0; i <= linewidth; i++ { - if err := d.sendData([]byte{color}); err != nil { - return err - } + for y := 0; y < rows; y++ { + if err := d.sendData(data); err != nil { + return err } } @@ -338,32 +340,44 @@ func (d *Dev) Bounds() image.Rectangle { return image.Rect(0, 0, d.opts.Width, d.opts.Height) } +func (d *Dev) sendImage(cmd []byte, dstRect image.Rectangle, src *image1bit.VerticalLSB) error { + // TODO: Handle dstRect not matching the device bounds. + + if err := d.setMemoryPointer(0, 0); err != nil { + return err + } + + eh := errorHandler{d: *d} + eh.sendCommand(cmd) + + rows, cols := dataDimensions(d.opts) + + for y := 0; y < rows; y++ { + data := make([]byte, cols) + + for x := 0; x < cols; x++ { + for bit := 0; bit < 8; bit++ { + if src.BitAt((x*8)+bit, y) { + data[x] |= 0x80 >> bit + } + } + } + + eh.sendData(data) + } + + return eh.err +} + // Draw draws the given image to the display. func (d *Dev) Draw(dstRect image.Rectangle, src image.Image, srcPts image.Point) error { next := image1bit.NewVerticalLSB(dstRect) draw.Src.Draw(next, dstRect, src, srcPts) - var byteToSend byte = 0x00 - for y := 0; y <= d.opts.Height; y++ { - if err := d.setMemoryPointer(0, y); err != nil { - return err - } - if err := d.sendCommand([]byte{writeRAMBW}); err != nil { - return err - } - for x := 0; x <= d.opts.Width; x++ { - bit := next.BitAt(x, y) - if bit { - byteToSend |= 0x80 >> (uint32(x) % 8) - } - if x%8 == 7 { - if err := d.sendData([]byte{byteToSend}); err != nil { - return err - } - byteToSend = 0x00 - } - } + if err := d.sendImage([]byte{writeRAMBW}, dstRect, next); err != nil { + return err } + return d.turnOnDisplay() } @@ -372,48 +386,12 @@ func (d *Dev) DrawPartial(dstRect image.Rectangle, src image.Image, srcPts image next := image1bit.NewVerticalLSB(dstRect) draw.Src.Draw(next, dstRect, src, srcPts) - var byteToSend byte = 0x00 - for y := 0; y < d.opts.Height; y++ { - if err := d.setMemoryPointer(0, y); err != nil { - return err - } - if err := d.sendCommand([]byte{writeRAMBW}); err != nil { - return err - } - for x := 0; x < d.opts.Width; x++ { - bit := next.BitAt(x, y) - if bit { - byteToSend |= 0x80 >> (uint32(x) % 8) - } - if x%8 == 7 { - if err := d.sendData([]byte{byteToSend}); err != nil { - return err - } - byteToSend = 0x00 - } - } + if err := d.sendImage([]byte{writeRAMBW}, dstRect, next); err != nil { + return err } - byteToSend = 0x00 - for y := 0; y < d.opts.Height; y++ { - if err := d.setMemoryPointer(0, y); err != nil { - return err - } - if err := d.sendCommand([]byte{writeRAMRed}); err != nil { - return err - } - for x := 0; x < d.opts.Width; x++ { - bit := next.BitAt(x, y) - if bit { - byteToSend |= 0x80 >> (uint32(x) % 8) - } - if x%8 == 7 { - if err := d.sendData([]byte{^byteToSend}); err != nil { - return err - } - byteToSend = 0x00 - } - } + if err := d.sendImage([]byte{writeRAMRed}, dstRect, next); err != nil { + return err } return d.turnOnDisplay() diff --git a/waveshare2in13v2/waveshare213v2_test.go b/waveshare2in13v2/waveshare213v2_test.go new file mode 100644 index 0000000..bf21784 --- /dev/null +++ b/waveshare2in13v2/waveshare213v2_test.go @@ -0,0 +1,38 @@ +// Copyright 2021 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package waveshare2in13v2 + +import ( + "fmt" + "testing" +) + +func TestDataDimensions(t *testing.T) { + for _, tc := range []struct { + opts *Opts + wantHeight int + wantWidth int + }{ + {opts: &Opts{Width: 0, Height: 0}}, + { + opts: &Opts{Height: 48, Width: 16}, + wantHeight: 48, + wantWidth: 2, + }, + { + opts: &Opts{Height: 250, Width: 122}, + wantHeight: 250, + wantWidth: 16, + }, + } { + t.Run(fmt.Sprintf("%+v", *tc.opts), func(t *testing.T) { + gotHeight, gotWidth := dataDimensions(tc.opts) + + if !(gotHeight == tc.wantHeight && gotWidth == tc.wantWidth) { + t.Errorf("dataDimensions(%#v) returned %d, %d; want %d, %d", tc.opts, gotHeight, gotWidth, tc.wantHeight, tc.wantWidth) + } + }) + } +}