Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ require (
github.com/mattn/go-colorable v0.1.13
golang.org/x/image v0.23.0
periph.io/x/conn/v3 v3.7.2
periph.io/x/host/v3 v3.8.4
periph.io/x/host/v3 v3.8.5
)

require (
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
periph.io/x/conn/v3 v3.7.2 h1:qt9dE6XGP5ljbFnCKRJ9OOCoiOyBGlw7JZgoi72zZ1s=
periph.io/x/conn/v3 v3.7.2/go.mod h1:Ao0b4sFRo4QOx6c1tROJU1fLJN1hUIYggjOrkIVnpGg=
periph.io/x/host/v3 v3.8.4 h1:QNleTythDd0k6Chu0n+ISrJFlf3LFig9oNbtOIkxoCc=
periph.io/x/host/v3 v3.8.4/go.mod h1:hPq8dISZIc+UNfWoRj+bPH3XEBQqJPdFdx218W92mdc=
periph.io/x/host/v3 v3.8.5 h1:g4g5xE1XZtDiGl1UAJaUur1aT7uNiFLMkyMEiZ7IHII=
periph.io/x/host/v3 v3.8.5/go.mod h1:hPq8dISZIc+UNfWoRj+bPH3XEBQqJPdFdx218W92mdc=
61 changes: 44 additions & 17 deletions inky/impression.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"periph.io/x/conn/v3"
"periph.io/x/conn/v3/display"
"periph.io/x/conn/v3/gpio"
"periph.io/x/conn/v3/gpio/gpioreg"
"periph.io/x/conn/v3/physic"
"periph.io/x/conn/v3/spi"
)
Expand All @@ -28,36 +29,36 @@
var (
// For more: https://github.com/pimoroni/inky/issues/115#issuecomment-887453065
dsc = []color.NRGBA{
{0, 0, 0, 0}, // Black
{0, 0, 0, 255}, // Black
{255, 255, 255, 255}, // White
{0, 255, 0, 255}, // Green
{0, 0, 255, 255}, // Blue
{255, 0, 0, 255}, // Red
{255, 255, 0, 255}, // Yellow
{255, 140, 0, 255}, // Orange
{255, 255, 255, 255},
{255, 255, 255, 0}, // Clear
}

sc = []color.NRGBA{
{57, 48, 57, 0}, // Black
{57, 48, 57, 255}, // Black
{255, 255, 255, 255}, // White
{58, 91, 70, 255}, // Green
{61, 59, 94, 255}, // Blue
{156, 72, 75, 255}, // Red
{208, 190, 71, 255}, // Yellow
{177, 106, 73, 255}, // Orange
{255, 255, 255, 255},
{255, 255, 255, 0}, // Clear
}

sc7 = []color.NRGBA{
{0, 0, 0, 0}, // Black
{0, 0, 0, 255}, // Black
{217, 242, 255, 255}, // White
{3, 124, 76, 255}, // Green
{27, 46, 198, 255}, // Blue
{245, 80, 34, 255}, // Red
{255, 255, 68, 255}, // Yellow
{239, 121, 44, 255}, // Orange
{255, 255, 255, 255},
{255, 255, 255, 0}, // Clear
}
)

Expand Down Expand Up @@ -155,7 +156,7 @@
if o.Model == IMPRESSION73 {
cSpeed = acSpeed
}
c, err := p.Connect(cSpeed, spi.Mode0, cs0Pin)
c, err := p.Connect(cSpeed, spi.Mode0, 8)

Check warning on line 159 in inky/impression.go

View check run for this annotation

Codecov / codecov/patch

inky/impression.go#L159

Added line #L159 was not covered by tests
if err != nil {
return nil, fmt.Errorf("failed to connect to inky over spi: %v", err)
}
Expand All @@ -173,6 +174,11 @@
maxTxSize = 4096 // Use a conservative default.
}
}
// If possible, grab the CS pin.
cs := gpioreg.ByName(cs0Pin)
if cs != nil && cs.Out(csDisabled) != nil {
cs = nil
}

Check warning on line 181 in inky/impression.go

View check run for this annotation

Codecov / codecov/patch

inky/impression.go#L178-L181

Added lines #L178 - L181 were not covered by tests

d := &DevImpression{
Dev: Dev{
Expand All @@ -186,6 +192,7 @@
model: o.Model,
variant: o.DisplayVariant,
pcbVariant: o.PCBVariant,
cs: cs,

Check warning on line 195 in inky/impression.go

View check run for this annotation

Codecov / codecov/patch

inky/impression.go#L195

Added line #L195 was not covered by tests
},
saturation: 50, // Looks good enough for most of the images.
}
Expand Down Expand Up @@ -217,10 +224,10 @@
}

// blend recalculates the palette based on the saturation level.
func (d *DevImpression) blend() []color.Color {
sat := float64(d.saturation / 100)
func (d *DevImpression) blend() color.Palette {
sat := float64(d.saturation) / 100.0

Check warning on line 228 in inky/impression.go

View check run for this annotation

Codecov / codecov/patch

inky/impression.go#L227-L228

Added lines #L227 - L228 were not covered by tests

pr := []color.Color{}
pr := make([]color.Color, 0)

Check warning on line 230 in inky/impression.go

View check run for this annotation

Codecov / codecov/patch

inky/impression.go#L230

Added line #L230 was not covered by tests
for i := 0; i < 7; i++ {
var rs, gs, bs uint8
if d.Dev.model == IMPRESSION73 {
Expand Down Expand Up @@ -288,7 +295,7 @@

merged := make([]uint8, len(d.Pix)/2)
for i, offset := 0, 0; i < len(d.Pix)-1; i, offset = i+2, offset+1 {
merged[offset] = (d.Pix[i]<<4)&0xF0 | d.Pix[i+1]&0x0F
merged[offset] = ((d.Pix[i] << 4) & 0xF0) | (d.Pix[i+1] & 0x0F)

Check warning on line 298 in inky/impression.go

View check run for this annotation

Codecov / codecov/patch

inky/impression.go#L298

Added line #L298 was not covered by tests
}

return d.update(merged)
Expand Down Expand Up @@ -519,17 +526,16 @@
}

// TODO there has to be a better way to force the white colour to be used instead of clear...
buf := make([]byte, len(pix))
for i := range pix {
if pix[i]&0xF == 7 {
buf[i] = (pix[i] & 0xF0) + 1
pix[i] = (pix[i] & 0xF0) + 1

Check warning on line 531 in inky/impression.go

View check run for this annotation

Codecov / codecov/patch

inky/impression.go#L531

Added line #L531 was not covered by tests
}
if pix[i]&0xF0 == 0x70 {
buf[i] = (pix[i] & 0xF) + 0x10
pix[i] = (pix[i] & 0xF) + 0x10

Check warning on line 534 in inky/impression.go

View check run for this annotation

Codecov / codecov/patch

inky/impression.go#L534

Added line #L534 was not covered by tests
}
}

if err := d.sendCommand(ac073TC1DTM, buf); err != nil {
if err := d.sendCommand(ac073TC1DTM, pix); err != nil {

Check warning on line 538 in inky/impression.go

View check run for this annotation

Codecov / codecov/patch

inky/impression.go#L538

Added line #L538 was not covered by tests
return err
}

Expand Down Expand Up @@ -558,8 +564,28 @@
log.Printf("Err: %s", err)
return
}
if d.busy.Read() == gpio.High {
time.Sleep(dur)
return
}

Check warning on line 570 in inky/impression.go

View check run for this annotation

Codecov / codecov/patch

inky/impression.go#L567-L570

Added lines #L567 - L570 were not covered by tests
// Wait for rising edges (Low -> High) or the timeout.
d.busy.WaitForEdge(dur)
tEnd := time.Now().Add(dur)
edgeDur := dur
for tEnd.After(time.Now()) {
// Debounce the edge
edge := d.busy.WaitForEdge(edgeDur)
if edge {
// The python driver is using 10ms debounce period
time.Sleep(10 * time.Millisecond)
l := d.busy.Read()
if l {
// It's still high. Return
return
}

Check warning on line 584 in inky/impression.go

View check run for this annotation

Codecov / codecov/patch

inky/impression.go#L572-L584

Added lines #L572 - L584 were not covered by tests
// It was a bounce. Recalculate the duration to wait for the edge.
edgeDur = time.Until(tEnd)

Check warning on line 586 in inky/impression.go

View check run for this annotation

Codecov / codecov/patch

inky/impression.go#L586

Added line #L586 was not covered by tests
}
}
}

// ColorModel returns the device native color model.
Expand Down Expand Up @@ -589,7 +615,7 @@
// Draw updates the display with the image.
func (d *DevImpression) Draw(r image.Rectangle, src image.Image, sp image.Point) error {
if r != d.Bounds() {
return fmt.Errorf("partial updates are not supported")
return fmt.Errorf("partial updates are not supported r=%#v bounds=%#v", r, d.Bounds())

Check warning on line 618 in inky/impression.go

View check run for this annotation

Codecov / codecov/patch

inky/impression.go#L618

Added line #L618 was not covered by tests
}

if src.Bounds() != d.Bounds() {
Expand All @@ -598,6 +624,7 @@

// Dither the image using Floyd–Steinberg dithering algorithm otherwise it won't look as good on the screen.
draw.FloydSteinberg.Draw(d, r, src, image.Point{})

Check warning on line 627 in inky/impression.go

View check run for this annotation

Codecov / codecov/patch

inky/impression.go#L627

Added line #L627 was not covered by tests
return d.Render()
}

Expand Down
86 changes: 66 additions & 20 deletions inky/inky.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,27 @@
"periph.io/x/conn/v3"
"periph.io/x/conn/v3/display"
"periph.io/x/conn/v3/gpio"
"periph.io/x/conn/v3/gpio/gpioreg"
"periph.io/x/conn/v3/physic"
"periph.io/x/conn/v3/spi"
)

var _ display.Drawer = &Dev{}
var _ conn.Resource = &Dev{}

const (
cs0Pin = 8
)

var borderColor = map[Color]byte{
Black: 0x00,
Red: 0x73,
Yellow: 0x33,
White: 0x31,
}

const (
cs0Pin = "GPIO8"
csEnabled = gpio.Low
csDisabled = gpio.High
)

// Dev is a handle to an Inky.
type Dev struct {
c conn.Conn
Expand Down Expand Up @@ -65,6 +68,8 @@
variant uint
// PCB Variant of the panel. Represents a version string as a number (12 -> 1.2).
pcbVariant uint
// cs is the chip-select pin for SPI. Refer to setCSPin() for information.
cs gpio.PinOut
}

// New opens a handle to an Inky pHAT or wHAT.
Expand All @@ -73,7 +78,7 @@
return nil, fmt.Errorf("unsupported color: %v", o.ModelColor)
}

c, err := p.Connect(488*physic.KiloHertz, spi.Mode0, cs0Pin)
c, err := p.Connect(488*physic.KiloHertz, spi.Mode0, 8)

Check warning on line 81 in inky/inky.go

View check run for this annotation

Codecov / codecov/patch

inky/inky.go#L81

Added line #L81 was not covered by tests
if err != nil {
return nil, fmt.Errorf("failed to connect to inky over spi: %v", err)
}
Expand All @@ -87,7 +92,11 @@
if maxTxSize == 0 {
maxTxSize = 4096 // Use a conservative default.
}

// If possible, grab the CS pin.
cs := gpioreg.ByName(cs0Pin)
if cs != nil && cs.Out(csDisabled) != nil {
cs = nil
}

Check warning on line 99 in inky/inky.go

View check run for this annotation

Codecov / codecov/patch

inky/inky.go#L96-L99

Added lines #L96 - L99 were not covered by tests
d := &Dev{
c: c,
maxTxSize: maxTxSize,
Expand All @@ -99,6 +108,7 @@
model: o.Model,
variant: o.DisplayVariant,
pcbVariant: o.PCBVariant,
cs: cs,

Check warning on line 111 in inky/inky.go

View check run for this annotation

Codecov / codecov/patch

inky/inky.go#L111

Added line #L111 was not covered by tests
}

switch o.Model {
Expand Down Expand Up @@ -330,43 +340,79 @@
}
}()
if err := d.sendCommand(0x12, nil); err != nil { // Soft Reset
return fmt.Errorf("failed to reset inky: %v", err)
return fmt.Errorf("inky: failed to reset inky: %v", err)

Check warning on line 343 in inky/inky.go

View check run for this annotation

Codecov / codecov/patch

inky/inky.go#L343

Added line #L343 was not covered by tests
}
d.busy.WaitForEdge(-1)
return
}

func (d *Dev) sendCommand(command byte, data []byte) error {
if err := d.dc.Out(gpio.Low); err != nil {
return err
// setCSPin sets the ChipSelect pin to the desired mode. The Pimoroni driver
// uses manual control over the CS pin. To do this, they require the
// Raspberry Pi /boot/firmware/config.txt to have dtloverlay=spi0-0cs set.
//
// So, if we run with automatic CS handling, we won't be compatible with the
// pimoroni samples. If we run with manual control required, we then require
// the dtoverlay setting. We really don't want to be incompatible with the
// Pimoroni driver because that will confuse people. If the CS Pin is
// not in use, use manual control, and if it is used by the SPI driver, let
// it handle it.
func (d *Dev) setCSPin(mode gpio.Level) error {
if d.cs != nil {
return d.cs.Out(mode)
}
return nil

Check warning on line 363 in inky/inky.go

View check run for this annotation

Codecov / codecov/patch

inky/inky.go#L359-L363

Added lines #L359 - L363 were not covered by tests
}

func (d *Dev) sendCommand(command byte, data []byte) (err error) {
err = d.setCSPin(csEnabled)
if err != nil {
return
}

Check warning on line 370 in inky/inky.go

View check run for this annotation

Codecov / codecov/patch

inky/inky.go#L366-L370

Added lines #L366 - L370 were not covered by tests

if err = d.dc.Out(gpio.Low); err != nil {
return

Check warning on line 373 in inky/inky.go

View check run for this annotation

Codecov / codecov/patch

inky/inky.go#L372-L373

Added lines #L372 - L373 were not covered by tests
}
if err := d.c.Tx([]byte{command}, nil); err != nil {
return fmt.Errorf("failed to send command %x to inky: %v", command, err)
if err = d.c.Tx([]byte{command}, nil); err != nil {
err = fmt.Errorf("inky: failed to send command %x to inky: %v", command, err)
return

Check warning on line 377 in inky/inky.go

View check run for this annotation

Codecov / codecov/patch

inky/inky.go#L375-L377

Added lines #L375 - L377 were not covered by tests
}
err = d.setCSPin(csDisabled)
if err != nil {
return
}

Check warning on line 382 in inky/inky.go

View check run for this annotation

Codecov / codecov/patch

inky/inky.go#L379-L382

Added lines #L379 - L382 were not covered by tests

if data != nil {
if err := d.sendData(data); err != nil {
return fmt.Errorf("failed to send data for command %x to inky: %v", command, err)
if err = d.sendData(data); err != nil {
err = fmt.Errorf("inky: failed to send data for command %x to inky: %v", command, err)
return

Check warning on line 387 in inky/inky.go

View check run for this annotation

Codecov / codecov/patch

inky/inky.go#L385-L387

Added lines #L385 - L387 were not covered by tests
}
}
return nil
return

Check warning on line 390 in inky/inky.go

View check run for this annotation

Codecov / codecov/patch

inky/inky.go#L390

Added line #L390 was not covered by tests
}

func (d *Dev) sendData(data []byte) error {
if err := d.dc.Out(gpio.High); err != nil {
func (d *Dev) sendData(data []byte) (err error) {
err = d.setCSPin(csEnabled)
if err != nil {
return
}
if err = d.dc.Out(gpio.High); err != nil {

Check warning on line 398 in inky/inky.go

View check run for this annotation

Codecov / codecov/patch

inky/inky.go#L393-L398

Added lines #L393 - L398 were not covered by tests
return err
}

for len(data) != 0 {
var chunk []byte
if len(data) > d.maxTxSize {
chunk, data = data[:d.maxTxSize], data[d.maxTxSize:]
} else {
chunk, data = data, nil
}
if err := d.c.Tx(chunk, nil); err != nil {
return fmt.Errorf("failed to send data to inky: %v", err)
if err = d.c.Tx(chunk, nil); err != nil {
err = fmt.Errorf("inky: failed to send data to inky: %v", err)
return

Check warning on line 411 in inky/inky.go

View check run for this annotation

Codecov / codecov/patch

inky/inky.go#L409-L411

Added lines #L409 - L411 were not covered by tests
}
}
return nil
err = d.setCSPin(csDisabled)
return

Check warning on line 415 in inky/inky.go

View check run for this annotation

Codecov / codecov/patch

inky/inky.go#L414-L415

Added lines #L414 - L415 were not covered by tests
}

func pack(bits []bool) ([]byte, error) {
Expand Down
2 changes: 2 additions & 0 deletions inky/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@
*c = Yellow
case "white":
*c = White
case "multi":
*c = Multi

Check warning on line 73 in inky/types.go

View check run for this annotation

Codecov / codecov/patch

inky/types.go#L72-L73

Added lines #L72 - L73 were not covered by tests
default:
return fmt.Errorf("unknown color %q: expected either black, red, yellow or white", s)
}
Expand Down