mirror of https://github.com/periph/devices
epd: add ePaper display support (#295)
parent
2352b96bc1
commit
2a2fa553a3
@ -0,0 +1,18 @@
|
|||||||
|
// Copyright 2018 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 epd controls Waveshare e-paper series displays.
|
||||||
|
//
|
||||||
|
// More details
|
||||||
|
//
|
||||||
|
// Datasheets
|
||||||
|
//
|
||||||
|
// https://www.waveshare.com/w/upload/e/e6/2.13inch_e-Paper_Datasheet.pdf
|
||||||
|
//
|
||||||
|
// Product page:
|
||||||
|
//
|
||||||
|
// 2.13 Inch version: https://www.waveshare.com/wiki/2.13inch_e-Paper_HAT
|
||||||
|
//
|
||||||
|
// 1.54 inch version: https://www.waveshare.com/wiki/1.54inch_e-Paper_Module
|
||||||
|
package epd
|
||||||
@ -0,0 +1,470 @@
|
|||||||
|
// Copyright 2018 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 epd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"image/draw"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"periph.io/x/periph/host/rpi"
|
||||||
|
|
||||||
|
"periph.io/x/periph/conn"
|
||||||
|
"periph.io/x/periph/conn/display"
|
||||||
|
"periph.io/x/periph/conn/gpio"
|
||||||
|
"periph.io/x/periph/conn/physic"
|
||||||
|
"periph.io/x/periph/conn/spi"
|
||||||
|
"periph.io/x/periph/devices/ssd1306/image1bit"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EPD commands
|
||||||
|
const (
|
||||||
|
driverOutputControl byte = 0x01
|
||||||
|
boosterSoftStartControl byte = 0x0C
|
||||||
|
gateScanStartPosition byte = 0x0F
|
||||||
|
deepSleepMode byte = 0x10
|
||||||
|
dataEntryModeSetting byte = 0x11
|
||||||
|
swReset byte = 0x12
|
||||||
|
temperatureSensorControl byte = 0x1A
|
||||||
|
masterActivation byte = 0x20
|
||||||
|
displayUpdateControl1 byte = 0x21
|
||||||
|
displayUpdateControl2 byte = 0x22
|
||||||
|
writeRAM byte = 0x24
|
||||||
|
writeVcomRegister byte = 0x2C
|
||||||
|
writeLutRegister byte = 0x32
|
||||||
|
setDummyLinePeriod byte = 0x3A
|
||||||
|
setGateTime byte = 0x3B
|
||||||
|
borderWaveformControl byte = 0x3C
|
||||||
|
setRAMXAddressStartEndPosition byte = 0x44
|
||||||
|
setRAMYAddressStartEndPosition byte = 0x45
|
||||||
|
setRAMXAddressCounter byte = 0x4E
|
||||||
|
setRAMYAddressCounter byte = 0x4F
|
||||||
|
terminateFrameReadWrite byte = 0xFF
|
||||||
|
)
|
||||||
|
|
||||||
|
// LUT contains the display specific waveform for the pixel programming of the display.
|
||||||
|
type LUT []byte
|
||||||
|
|
||||||
|
// PartialUpdate represents if updates to the display should be full or partial.
|
||||||
|
type PartialUpdate bool
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Full LUT config to update all the display
|
||||||
|
Full PartialUpdate = false
|
||||||
|
// Partial LUT config only a part of the display
|
||||||
|
Partial PartialUpdate = true
|
||||||
|
)
|
||||||
|
|
||||||
|
// EPD2in13 is the config for the 2.13 inch display.
|
||||||
|
var EPD2in13 = Opts{
|
||||||
|
W: 128,
|
||||||
|
H: 250,
|
||||||
|
FullUpdate: LUT{
|
||||||
|
0x22, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x11,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E,
|
||||||
|
0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
},
|
||||||
|
PartialUpdate: LUT{
|
||||||
|
0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x0F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// EPD1in54 is the config for the 1.54 inch display.
|
||||||
|
var EPD1in54 = Opts{
|
||||||
|
W: 200,
|
||||||
|
H: 200,
|
||||||
|
FullUpdate: LUT{
|
||||||
|
0x02, 0x02, 0x01, 0x11, 0x12, 0x12, 0x22, 0x22,
|
||||||
|
0x66, 0x69, 0x69, 0x59, 0x58, 0x99, 0x99, 0x88,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xF8, 0xB4, 0x13, 0x51,
|
||||||
|
0x35, 0x51, 0x51, 0x19, 0x01, 0x00,
|
||||||
|
},
|
||||||
|
PartialUpdate: LUT{
|
||||||
|
0x10, 0x18, 0x18, 0x08, 0x18, 0x18, 0x08, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x13, 0x14, 0x44, 0x12,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Opts defines the options for the ePaper Device.
|
||||||
|
type Opts struct {
|
||||||
|
W int
|
||||||
|
H int
|
||||||
|
FullUpdate LUT
|
||||||
|
PartialUpdate LUT
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSPI returns a Dev object that communicates over SPI to a E-Paper display controller.
|
||||||
|
func NewSPI(p spi.Port, dc, cs, rst gpio.PinOut, busy gpio.PinIO, opts *Opts) (*Dev, error) {
|
||||||
|
if dc == gpio.INVALID {
|
||||||
|
return nil, errors.New("epd: use nil for dc to use 3-wire mode, do not use gpio.INVALID")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := dc.Out(gpio.Low); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := p.Connect(5*physic.MegaHertz, spi.Mode0, 8)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
d := &Dev{
|
||||||
|
c: c,
|
||||||
|
dc: dc,
|
||||||
|
cs: cs,
|
||||||
|
rst: rst,
|
||||||
|
busy: busy,
|
||||||
|
update: Full,
|
||||||
|
opts: opts,
|
||||||
|
rect: image.Rect(0, 0, opts.W, opts.H),
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Reset()
|
||||||
|
|
||||||
|
if err := d.Init(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSPIHat returns a Dev object that communicates over SPI
|
||||||
|
// and have the default config for the e-paper hat for raspberry pi
|
||||||
|
func NewSPIHat(p spi.Port, opts *Opts) (*Dev, error) {
|
||||||
|
dc := rpi.P1_22
|
||||||
|
cs := rpi.P1_24
|
||||||
|
rst := rpi.P1_11
|
||||||
|
busy := rpi.P1_18
|
||||||
|
return NewSPI(p, dc, cs, rst, busy, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dev is an open handle to the display controller.
|
||||||
|
type Dev struct {
|
||||||
|
// Communication
|
||||||
|
c conn.Conn
|
||||||
|
dc gpio.PinOut
|
||||||
|
cs gpio.PinOut
|
||||||
|
rst gpio.PinOut
|
||||||
|
busy gpio.PinIO
|
||||||
|
|
||||||
|
// Display size controlled by the e-paper display.
|
||||||
|
rect image.Rectangle
|
||||||
|
|
||||||
|
update PartialUpdate
|
||||||
|
opts *Opts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dev) String() string {
|
||||||
|
return fmt.Sprintf("epd.Dev{%s, %s, %s}", d.c, d.dc, d.rect.Max)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ColorModel implements display.Drawer.
|
||||||
|
// It is a one bit color model, as implemented by image1bit.Bit.
|
||||||
|
func (d *Dev) ColorModel() color.Model {
|
||||||
|
return image1bit.BitModel
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bounds implements display.Drawer. Min is guaranteed to be {0, 0}.
|
||||||
|
func (d *Dev) Bounds() image.Rectangle {
|
||||||
|
return d.rect
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw implements display.Drawer.
|
||||||
|
func (d *Dev) Draw(r image.Rectangle, src image.Image, sp image.Point) error {
|
||||||
|
xStart := sp.X
|
||||||
|
yStart := sp.Y
|
||||||
|
imageW := r.Dx() & 0xF8
|
||||||
|
imageH := r.Dy()
|
||||||
|
w := d.rect.Dx()
|
||||||
|
h := d.rect.Dy()
|
||||||
|
|
||||||
|
xEnd := xStart + imageW - 1
|
||||||
|
if xStart+imageW >= w {
|
||||||
|
xEnd = w - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
yEnd := yStart + imageH - 1
|
||||||
|
if yStart+imageH >= h {
|
||||||
|
yEnd = h - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
d.setMemoryArea(xStart, yStart, xEnd, yEnd)
|
||||||
|
|
||||||
|
next := image1bit.NewVerticalLSB(d.rect)
|
||||||
|
draw.Src.Draw(next, r, src, sp)
|
||||||
|
var byteToSend byte = 0x00
|
||||||
|
for y := yStart; y < yEnd+1; y++ {
|
||||||
|
d.setMemoryPointer(xStart, y)
|
||||||
|
if err := d.sendCommand([]byte{writeRAM}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for x := xStart; x < xEnd+1; x++ {
|
||||||
|
bit := next.BitAt(x-xStart, y-yStart)
|
||||||
|
if bit {
|
||||||
|
byteToSend |= 0x80 >> (uint32(x) % 8)
|
||||||
|
}
|
||||||
|
if x%8 == 7 {
|
||||||
|
if err := d.sendData([]byte{byteToSend}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
byteToSend = 0x00
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearFrameMemory clear the frame memory with the specified color.
|
||||||
|
// this won't update the display.
|
||||||
|
func (d *Dev) ClearFrameMemory(color byte) error {
|
||||||
|
w := d.rect.Dx()
|
||||||
|
h := d.rect.Dy()
|
||||||
|
d.setMemoryArea(0, 0, w-1, h-1)
|
||||||
|
d.setMemoryPointer(0, 0)
|
||||||
|
if err := d.sendCommand([]byte{writeRAM}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// send the color data
|
||||||
|
for i := 0; i < (w / 8 * h); i++ {
|
||||||
|
if err := d.sendData([]byte{color}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisplayFrame update the display.
|
||||||
|
//
|
||||||
|
// There are 2 memory areas embedded in the e-paper display but once
|
||||||
|
// this function is called, the next action of SetFrameMemory or ClearFrame
|
||||||
|
// will set the other memory area.
|
||||||
|
func (d *Dev) DisplayFrame() error {
|
||||||
|
if err := d.sendCommand([]byte{displayUpdateControl2}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.sendData([]byte{byte(0xC4)}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.sendCommand([]byte{masterActivation}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.sendCommand([]byte{terminateFrameReadWrite}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
d.waitUntilIdle()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Halt turns off the display.
|
||||||
|
func (d *Dev) Halt() error {
|
||||||
|
return d.ClearFrameMemory(0xFF)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sleep after this command is transmitted, the chip would enter the
|
||||||
|
// deep-sleep mode to save power.
|
||||||
|
//
|
||||||
|
// The deep sleep mode would return to standby by hardware reset.
|
||||||
|
// You can use Reset() to awaken and Init to re-initialize the device.
|
||||||
|
func (d *Dev) Sleep() error {
|
||||||
|
if err := d.sendCommand([]byte{deepSleepMode}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
d.waitUntilIdle()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init initialize the display config. This method is already called when creating
|
||||||
|
// a device using NewSPI and NewSPIHat methods.
|
||||||
|
//
|
||||||
|
// It should be only used when you put the device to sleep and need to re-init the device.
|
||||||
|
func (d *Dev) Init() error {
|
||||||
|
if err := d.sendCommand([]byte{driverOutputControl}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.sendData([]byte{byte((d.opts.H - 1) & 0xFF)}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.sendData([]byte{byte(((d.opts.H - 1) >> 8) & 0xFF)}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.sendData([]byte{byte(0x00)}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.sendCommand([]byte{boosterSoftStartControl}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.sendData([]byte{byte(0xD7)}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.sendData([]byte{byte(0xD6)}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.sendData([]byte{byte(0x9D)}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.sendCommand([]byte{writeVcomRegister}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.sendData([]byte{byte(0xA8)}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.sendCommand([]byte{setDummyLinePeriod}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.sendData([]byte{byte(0x1A)}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.sendCommand([]byte{setGateTime}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.sendData([]byte{byte(0x08)}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.sendCommand([]byte{dataEntryModeSetting}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.sendData([]byte{byte(0x03)}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.setLut(Full); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset can be also used to awaken the device
|
||||||
|
func (d *Dev) Reset() {
|
||||||
|
d.rst.Out(gpio.Low)
|
||||||
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
d.rst.Out(gpio.High)
|
||||||
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dev) setMemoryPointer(x, y int) error {
|
||||||
|
if err := d.sendCommand([]byte{setRAMXAddressCounter}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// x point must be the multiple of 8 or the last 3 bits will be ignored
|
||||||
|
if err := d.sendData([]byte{byte((x >> 3) & 0xFF)}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.sendCommand([]byte{setRAMYAddressCounter}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := d.sendData([]byte{byte(y & 0xFF)}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := d.sendData([]byte{byte((y >> 8) & 0xFF)}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
d.waitUntilIdle()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dev) waitUntilIdle() {
|
||||||
|
for d.busy.Read() == gpio.High {
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dev) setMemoryArea(xStart, yStart, xEnd, yEnd int) error {
|
||||||
|
if err := d.sendCommand([]byte{setRAMXAddressStartEndPosition}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.sendData([]byte{byte((xStart >> 3) & 0xFF)}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := d.sendData([]byte{byte((xEnd >> 3) & 0xFF)}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.sendCommand([]byte{setRAMYAddressStartEndPosition}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := d.sendData([]byte{byte(yStart & 0xFF)}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := d.sendData([]byte{byte((yStart >> 8) & 0xFF)}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := d.sendData([]byte{byte(yEnd & 0xFF)}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := d.sendData([]byte{byte((yEnd >> 8) & 0xFF)}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dev) setLut(update PartialUpdate) error {
|
||||||
|
d.update = update
|
||||||
|
lut := d.opts.FullUpdate
|
||||||
|
if d.update == Partial {
|
||||||
|
lut = d.opts.PartialUpdate
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.sendCommand([]byte{writeLutRegister}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range lut {
|
||||||
|
d.sendData([]byte{lut[i]})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dev) sendData(c []byte) error {
|
||||||
|
if err := d.dc.Out(gpio.High); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.c.Tx(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dev) sendCommand(c []byte) error {
|
||||||
|
if err := d.dc.Out(gpio.Low); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.c.Tx(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ display.Drawer = &Dev{}
|
||||||
@ -0,0 +1,123 @@
|
|||||||
|
// Copyright 2018 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 epd_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"periph.io/x/periph/experimental/devices/epd"
|
||||||
|
|
||||||
|
"periph.io/x/periph/conn/spi/spireg"
|
||||||
|
|
||||||
|
"periph.io/x/periph/devices/ssd1306/image1bit"
|
||||||
|
"periph.io/x/periph/host"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Example() {
|
||||||
|
// Make sure periph is initialized.
|
||||||
|
if _, err := host.Init(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use spireg SPI bus registry to find the first available SPI bus.
|
||||||
|
b, err := spireg.Open("")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer b.Close()
|
||||||
|
|
||||||
|
dev, err := epd.NewSPIHat(b, &epd.EPD2in13) // Display config and size
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to initialize epd: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw on it.
|
||||||
|
img := image1bit.NewVerticalLSB(dev.Bounds())
|
||||||
|
// Note: this code is commented out so periph does not depend on:
|
||||||
|
// "golang.org/x/image/font"
|
||||||
|
// "golang.org/x/image/font/basicfont"
|
||||||
|
// "golang.org/x/image/math/fixed"
|
||||||
|
//
|
||||||
|
// f := basicfont.Face7x13
|
||||||
|
// drawer := font.Drawer{
|
||||||
|
// Dst: img,
|
||||||
|
// Src: &image.Uniform{image1bit.On},
|
||||||
|
// Face: f,
|
||||||
|
// Dot: fixed.P(0, img.Bounds().Dy()-1-f.Descent),
|
||||||
|
// }
|
||||||
|
// drawer.DrawString("Hello from periph!")
|
||||||
|
|
||||||
|
if err := dev.Draw(dev.Bounds(), img, image.Point{}); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
dev.DisplayFrame() // After drawing on the display, you have to show the frame
|
||||||
|
}
|
||||||
|
|
||||||
|
func Example_other() {
|
||||||
|
// Make sure periph is initialized.
|
||||||
|
if _, err := host.Init(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use spireg SPI bus registry to find the first available SPI bus.
|
||||||
|
b, err := spireg.Open("")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer b.Close()
|
||||||
|
|
||||||
|
dev, err := epd.NewSPIHat(b, &epd.EPD2in13) // Display config and size
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to initialize epd: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var img image.Image
|
||||||
|
// Note: this code is commented out so periph does not depend on:
|
||||||
|
// "github.com/fogleman/gg"
|
||||||
|
// "github.com/golang/freetype/truetype"
|
||||||
|
// "golang.org/x/image/font/gofont/goregular"
|
||||||
|
//
|
||||||
|
// bounds := dev.Bounds()
|
||||||
|
// w := bounds.Dx()
|
||||||
|
// h := bounds.Dy()
|
||||||
|
// dc := gg.NewContext(w, h)
|
||||||
|
// im, err := gg.LoadPNG("gopher.png")
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// dc.SetRGB(1, 1, 1)
|
||||||
|
// dc.Clear()
|
||||||
|
// dc.SetRGB(0, 0, 0)
|
||||||
|
// dc.Rotate(gg.Radians(90))
|
||||||
|
// dc.Translate(0.0, -float64(h/2))
|
||||||
|
// font, err := truetype.Parse(goregular.TTF)
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// face := truetype.NewFace(font, &truetype.Options{
|
||||||
|
// Size: 16,
|
||||||
|
// })
|
||||||
|
// dc.SetFontFace(face)
|
||||||
|
// text := "Hello from periph!"
|
||||||
|
// tw, th := dc.MeasureString(text)
|
||||||
|
// dc.DrawImage(im, 120, 30)
|
||||||
|
// padding := 8.0
|
||||||
|
// dc.DrawRoundedRectangle(padding*2, padding*2, tw+padding*2, th+padding, 10)
|
||||||
|
// dc.Stroke()
|
||||||
|
// dc.DrawString(text, padding*3, padding*2+th)
|
||||||
|
// for i := 0; i < 10; i++ {
|
||||||
|
// dc.DrawCircle(float64(30+(10*i)), 100, 5)
|
||||||
|
// }
|
||||||
|
// for i := 0; i < 10; i++ {
|
||||||
|
// dc.DrawRectangle(float64(30+(10*i)), 80, 5, 5)
|
||||||
|
// }
|
||||||
|
// dc.Fill()
|
||||||
|
// img = dc.Image()
|
||||||
|
if err := dev.Draw(dev.Bounds(), img, image.Point{}); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
dev.DisplayFrame() // After drawing on the display, you have to show the frame
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue