mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-26 10:38:41 +00:00
dcf53ac04a
Signed-off-by: deadprogram <ron@hybridgroup.com>
434 lines
8.9 KiB
Go
434 lines
8.9 KiB
Go
// Package epd1in54 implements a driver for Waveshare 1.54in black and white e-paper device.
|
|
//
|
|
// Derived from:
|
|
//
|
|
// https://github.com/tinygo-org/drivers/tree/master/waveshare-epd
|
|
// https://github.com/waveshare/e-Paper/blob/master/Arduino/epd1in54_V2/epd1in54_V2.cpp
|
|
//
|
|
// Datasheet: https://www.waveshare.com/w/upload/e/e5/1.54inch_e-paper_V2_Datasheet.pdf
|
|
package epd1in54
|
|
|
|
import (
|
|
"image/color"
|
|
"machine"
|
|
"time"
|
|
|
|
"tinygo.org/x/drivers"
|
|
"tinygo.org/x/drivers/internal/pin"
|
|
)
|
|
|
|
type Config struct {
|
|
Width int16
|
|
Height int16
|
|
LogicalWidth int16
|
|
Rotation Rotation
|
|
}
|
|
|
|
type Device struct {
|
|
bus *machine.SPI
|
|
cs pin.OutputFunc
|
|
dc pin.OutputFunc
|
|
rst pin.OutputFunc
|
|
isBusy drivers.PinInput
|
|
configurePins func()
|
|
buffer []uint8
|
|
rotation Rotation
|
|
}
|
|
|
|
type Rotation uint8
|
|
|
|
var fullRefresh = [159]uint8{
|
|
0x80, 0x48, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x40, 0x48, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x80, 0x48, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x40, 0x48, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0xA, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x8, 0x1, 0x0, 0x8, 0x1, 0x0, 0x2,
|
|
0xA, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0, 0x0, 0x0,
|
|
0x22, 0x17, 0x41, 0x0, 0x32, 0x20,
|
|
}
|
|
|
|
var partialRefresh = [159]uint8{
|
|
0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x80, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x40, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0, 0x0, 0x0,
|
|
0x02, 0x17, 0x41, 0xB0, 0x32, 0x28,
|
|
}
|
|
|
|
// New returns a new epd1in54 driver. Pass in a fully configured SPI bus.
|
|
func New(bus *machine.SPI, csPin, dcPin, rstPin pin.Output, busyPin pin.Input) Device {
|
|
return Device{
|
|
buffer: make([]uint8, (uint32(Width)*uint32(Height))/8),
|
|
bus: bus,
|
|
cs: csPin.Set,
|
|
dc: dcPin.Set,
|
|
rst: rstPin.Set,
|
|
isBusy: busyPin.Get,
|
|
configurePins: func() {
|
|
pin.ConfigureOutput(csPin)
|
|
pin.ConfigureOutput(dcPin)
|
|
pin.ConfigureOutput(rstPin)
|
|
pin.ConfigureInput(busyPin)
|
|
},
|
|
}
|
|
}
|
|
|
|
func (d *Device) LDirInit(cfg Config) {
|
|
if d.configurePins == nil {
|
|
panic(pin.ErrConfigBeforeInstantiated)
|
|
}
|
|
d.configurePins()
|
|
|
|
d.bus.Configure(machine.SPIConfig{
|
|
Frequency: 2000000,
|
|
Mode: 0,
|
|
LSBFirst: false,
|
|
})
|
|
|
|
d.Reset()
|
|
d.WaitUntilIdle()
|
|
|
|
d.SendCommand(0x12)
|
|
d.WaitUntilIdle()
|
|
|
|
d.SendCommand(0x01)
|
|
d.SendData(0xC7)
|
|
d.SendData(0x00)
|
|
d.SendData(0x00)
|
|
|
|
d.SendCommand(0x11)
|
|
d.SendData(0x03)
|
|
|
|
d.SendCommand(0x44)
|
|
/* x point must be the multiple of 8 or the last 3 bits will be ignored */
|
|
d.SendData((0 >> 3) & 0xFF)
|
|
d.SendData((199 >> 3) & 0xFF)
|
|
|
|
d.SendCommand(0x45)
|
|
d.SendData(0 & 0xFF)
|
|
d.SendData((0 >> 8) & 0xFF)
|
|
d.SendData(199 & 0xFF)
|
|
d.SendData((199 >> 8) & 0xFF)
|
|
|
|
d.SendCommand(0x3C)
|
|
d.SendData(0x01)
|
|
|
|
d.SendCommand(0x18)
|
|
d.SendData(0x80)
|
|
|
|
d.SendCommand(0x22)
|
|
d.SendData(0xB1)
|
|
|
|
d.SendCommand(0x20)
|
|
|
|
d.SendCommand(0x4E)
|
|
d.SendData(0x00)
|
|
|
|
d.SendCommand(0x4F)
|
|
d.SendData(0xC7)
|
|
d.SendData(0x00)
|
|
|
|
d.WaitUntilIdle()
|
|
d.setLUT(fullRefresh)
|
|
}
|
|
|
|
func (d *Device) HDirInit(cfg Config) {
|
|
if d.configurePins == nil {
|
|
panic(pin.ErrConfigBeforeInstantiated)
|
|
}
|
|
d.configurePins()
|
|
|
|
d.bus.Configure(machine.SPIConfig{
|
|
Frequency: 2000000,
|
|
Mode: 0,
|
|
LSBFirst: false,
|
|
})
|
|
|
|
d.Reset()
|
|
d.WaitUntilIdle()
|
|
|
|
d.SendCommand(0x12)
|
|
d.WaitUntilIdle()
|
|
|
|
d.SendCommand(0x01)
|
|
d.SendData(0xC7)
|
|
d.SendData(0x00)
|
|
d.SendData(0x01)
|
|
|
|
d.SendCommand(0x11)
|
|
d.SendData(0x01)
|
|
|
|
d.SendCommand(0x44)
|
|
d.SendData(0x00)
|
|
d.SendData(0x18)
|
|
|
|
d.SendCommand(0x45)
|
|
d.SendData(0xC7)
|
|
d.SendData(0x00)
|
|
d.SendData(0x00)
|
|
d.SendData(0x00)
|
|
|
|
d.SendCommand(0x3C)
|
|
d.SendData(0x01)
|
|
|
|
d.SendCommand(0x18)
|
|
d.SendData(0x80)
|
|
|
|
d.SendCommand(0x22)
|
|
d.SendData(0xB1)
|
|
|
|
d.SendCommand(0x20)
|
|
|
|
d.SendCommand(0x4E)
|
|
d.SendData(0x00)
|
|
|
|
d.SendCommand(0x4F)
|
|
d.SendData(0xC7)
|
|
d.SendData(0x00)
|
|
|
|
d.WaitUntilIdle()
|
|
d.setLUT(fullRefresh)
|
|
}
|
|
|
|
func (d *Device) setLUT(lut [159]uint8) {
|
|
d.SendCommand(0x32)
|
|
for i := 0; i < 153; i++ {
|
|
d.SendData(lut[i])
|
|
}
|
|
d.WaitUntilIdle()
|
|
|
|
d.SendCommand(0x3F)
|
|
d.SendData(lut[153])
|
|
|
|
d.SendCommand(0x03)
|
|
d.SendData(lut[154])
|
|
|
|
d.SendCommand(0x04)
|
|
d.SendData(lut[155])
|
|
d.SendData(lut[156])
|
|
d.SendData(lut[157])
|
|
|
|
d.SendCommand(0x2C)
|
|
d.SendData(lut[158])
|
|
}
|
|
|
|
// Reset resets the display.
|
|
func (d *Device) Reset() {
|
|
d.rst.High()
|
|
time.Sleep(20 * time.Millisecond)
|
|
d.rst.Low()
|
|
time.Sleep(5 * time.Millisecond)
|
|
d.rst.High()
|
|
time.Sleep(20 * time.Millisecond)
|
|
}
|
|
|
|
// SendCommand sends a command to the display
|
|
func (d *Device) SendCommand(command uint8) {
|
|
d.sendDataCommand(true, command)
|
|
}
|
|
|
|
// SendData sends a data byte to the display
|
|
func (d *Device) SendData(data uint8) {
|
|
d.sendDataCommand(false, data)
|
|
}
|
|
|
|
// sendDataCommand sends image data or a command to the screen
|
|
func (d *Device) sendDataCommand(isCommand bool, data uint8) {
|
|
if isCommand {
|
|
d.dc.Low()
|
|
} else {
|
|
d.dc.High()
|
|
}
|
|
d.cs.Low()
|
|
d.bus.Transfer(data)
|
|
d.cs.High()
|
|
}
|
|
|
|
// SetPixel modifies the internal buffer in a single pixel.
|
|
// The display have 2 colors: black and white
|
|
// We use RGBA(0,0,0, 255) as white (transparent)
|
|
// Anything else as black
|
|
func (d *Device) SetPixel(x int16, y int16, c color.RGBA) {
|
|
x, y = d.xy(x, y)
|
|
if x < 0 || x >= Width || y < 0 || y >= Height {
|
|
return
|
|
}
|
|
byteIndex := (uint32(x) + uint32(y)*uint32(Width)) / 8
|
|
if c.R == 0 && c.G == 0 && c.B == 0 { // TRANSPARENT / WHITE
|
|
d.buffer[byteIndex] |= 0x80 >> uint8(x%8)
|
|
} else { // WHITE / EMPTY
|
|
d.buffer[byteIndex] &^= 0x80 >> uint8(x%8)
|
|
}
|
|
}
|
|
|
|
func (d *Device) DisplayImage(image []uint8) {
|
|
var w, h int
|
|
if Width%8 == 0 {
|
|
w = int(Width / 8)
|
|
} else {
|
|
w = int(Width/8 + 1)
|
|
}
|
|
h = int(Height)
|
|
|
|
d.SendCommand(0x24)
|
|
for j := 0; j < h; j++ {
|
|
for i := 0; i < w; i++ {
|
|
d.SendData(image[i+j*w])
|
|
}
|
|
}
|
|
|
|
d.SendCommand(0x26)
|
|
for j := 0; j < h; j++ {
|
|
for i := 0; i < w; i++ {
|
|
d.SendData(image[i+j*w])
|
|
}
|
|
}
|
|
|
|
d.displayFrame()
|
|
}
|
|
|
|
func (d *Device) Display() error {
|
|
var w, h int
|
|
if Width%8 == 0 {
|
|
w = int(Width / 8)
|
|
} else {
|
|
w = int(Width/8 + 1)
|
|
}
|
|
h = int(Height)
|
|
|
|
d.SendCommand(0x24)
|
|
for j := 0; j < h; j++ {
|
|
for i := 0; i < w; i++ {
|
|
x := i + j*w
|
|
d.SendData(d.buffer[x])
|
|
}
|
|
}
|
|
|
|
d.SendCommand(0x26)
|
|
for j := 0; j < h; j++ {
|
|
for i := 0; i < w; i++ {
|
|
x := i + j*w
|
|
d.SendData(d.buffer[x])
|
|
}
|
|
}
|
|
|
|
d.displayFrame()
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d *Device) displayFrame() {
|
|
d.SendCommand(0x22)
|
|
d.SendData(0xC7)
|
|
d.SendCommand(0x20)
|
|
d.WaitUntilIdle()
|
|
}
|
|
|
|
func (d *Device) Clear() {
|
|
var w, h int
|
|
if Width%8 == 0 {
|
|
w = int(Width / 8)
|
|
} else {
|
|
w = int(Width/8 + 1)
|
|
}
|
|
h = int(Height)
|
|
|
|
d.SendCommand(0x24)
|
|
for j := 0; j < h; j++ {
|
|
for i := 0; i < w; i++ {
|
|
d.SendData(0xff)
|
|
}
|
|
}
|
|
|
|
d.SendCommand(0x26)
|
|
for j := 0; j < h; j++ {
|
|
for i := 0; i < w; i++ {
|
|
d.SendData(0xff)
|
|
}
|
|
}
|
|
|
|
d.displayFrame()
|
|
}
|
|
|
|
// WaitUntilIdle waits until the display is ready
|
|
func (d *Device) WaitUntilIdle() {
|
|
for d.isBusy() {
|
|
time.Sleep(100 * time.Millisecond)
|
|
}
|
|
time.Sleep(200 * time.Millisecond)
|
|
}
|
|
|
|
// IsBusy returns the busy status of the display
|
|
func (d *Device) IsBusy() bool {
|
|
return d.isBusy()
|
|
}
|
|
|
|
// ClearBuffer sets the buffer to 0xFF (white)
|
|
func (d *Device) ClearBuffer() {
|
|
for i := 0; i < len(d.buffer); i++ {
|
|
d.buffer[i] = 0xFF
|
|
}
|
|
}
|
|
|
|
// Size returns the current size of the display.
|
|
func (d *Device) Size() (w, h int16) {
|
|
if d.rotation == ROTATION_90 || d.rotation == ROTATION_270 {
|
|
return Height, Width
|
|
}
|
|
return Width, Height
|
|
}
|
|
|
|
// SetRotation changes the rotation (clock-wise) of the device
|
|
func (d *Device) SetRotation(rotation Rotation) {
|
|
d.rotation = rotation
|
|
}
|
|
|
|
// xy chages the coordinates according to the rotation
|
|
func (d *Device) xy(x, y int16) (int16, int16) {
|
|
switch d.rotation {
|
|
case NO_ROTATION:
|
|
return x, y
|
|
case ROTATION_90:
|
|
return Width - y - 1, x
|
|
case ROTATION_180:
|
|
return Width - x - 1, Height - y - 1
|
|
case ROTATION_270:
|
|
return y, Height - x - 1
|
|
}
|
|
return x, y
|
|
}
|
|
|
|
func (d *Device) Sleep() {
|
|
d.SendCommand(0x10)
|
|
d.SendData(0x01)
|
|
time.Sleep(200 * time.Millisecond)
|
|
|
|
d.rst.Low()
|
|
}
|