From fe8f84735118d1db731a09cab32c022060848618 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=87a=C4=9Flar=20Onur?= Date: Thu, 5 Jan 2023 02:04:46 -0800 Subject: [PATCH 1/8] Initial Inky Impression support It is a 7 colour ePaper/eInk HAT that comes with 5.7" (600 x 448 pixel) or 4" )640 x 400 pixel). Refactors the existing 3 color Inky and adds additional fields to auto-detect via EEPROM. Tested with 5.7" version. --- inky/example_test.go | 50 ++++++ inky/impression.go | 414 +++++++++++++++++++++++++++++++++++++++++++ inky/inky.go | 244 ++++++++----------------- inky/opts.go | 120 +++++++++++++ inky/types.go | 110 ++++++++++++ inky/types_string.go | 72 ++++++++ 6 files changed, 839 insertions(+), 171 deletions(-) create mode 100644 inky/impression.go create mode 100644 inky/opts.go create mode 100644 inky/types.go create mode 100644 inky/types_string.go diff --git a/inky/example_test.go b/inky/example_test.go index e3e4583..23177d7 100644 --- a/inky/example_test.go +++ b/inky/example_test.go @@ -12,6 +12,7 @@ import ( "os" "periph.io/x/conn/v3/gpio/gpioreg" + "periph.io/x/conn/v3/i2c/i2creg" "periph.io/x/conn/v3/spi/spireg" "periph.io/x/devices/v3/inky" "periph.io/x/host/v3" @@ -58,3 +59,52 @@ func Example() { log.Fatal(err) } } + +func ExampleImpression() { + path := flag.String("image", "", "Path to image file (600x448) to display") + flag.Parse() + + f, err := os.Open(*path) + if err != nil { + log.Fatal(err) + } + defer f.Close() + + m, _, err := image.Decode(f) + if err != nil { + log.Fatal(err) + } + + if _, err := host.Init(); err != nil { + log.Fatal(err) + } + + b, err := spireg.Open("SPI0.0") + if err != nil { + log.Fatal(err) + } + + dc := gpioreg.ByName("22") + reset := gpioreg.ByName("27") + busy := gpioreg.ByName("17") + + eeprom, err := i2creg.Open("") + if err != nil { + log.Fatal(err) + } + defer eeprom.Close() + + o, err := inky.DetectOpts(eeprom) + if err != nil { + log.Fatal(err) + } + + dev, err := inky.NewImpression(b, dc, reset, busy, o) + if err != nil { + log.Fatal(err) + } + + if err := dev.Draw(m.Bounds(), m, image.Point{}); err != nil { + log.Fatal(err) + } +} diff --git a/inky/impression.go b/inky/impression.go new file mode 100644 index 0000000..a237b55 --- /dev/null +++ b/inky/impression.go @@ -0,0 +1,414 @@ +// Copyright 2023 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 inky + +import ( + "encoding/binary" + "fmt" + "image" + "image/color" + "image/draw" + "log" + "time" + + "periph.io/x/conn/v3" + "periph.io/x/conn/v3/display" + "periph.io/x/conn/v3/gpio" + "periph.io/x/conn/v3/physic" + "periph.io/x/conn/v3/spi" +) + +var _ display.Drawer = &DevImpression{} +var _ conn.Resource = &DevImpression{} +var _ draw.Image = &DevImpression{} + +var ( + // For more: https://github.com/pimoroni/inky/issues/115#issuecomment-887453065 + dsc = []color.NRGBA{ + {0, 0, 0, 0}, // 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}, + } + + sc = []color.NRGBA{ + {57, 48, 57, 0}, // 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}, + } +) + +const ( + UC8159PSR = 0x00 + UC8159PWR = 0x01 + UC8159POF = 0x02 + UC8159PFS = 0x03 + UC8159PON = 0x04 + UC8159BTST = 0x06 + UC8159DSLP = 0x07 + UC8159DTM1 = 0x10 + UC8159DSP = 0x11 + UC8159DRF = 0x12 + UC8159IPC = 0x13 + UC8159PLL = 0x30 + UC8159TSC = 0x40 + UC8159TSE = 0x41 + UC8159TSW = 0x42 + UC8159TSR = 0x43 + UC8159CDI = 0x50 + UC8159LPD = 0x51 + UC8159TCON = 0x60 + UC8159TRES = 0x61 + UC8159DAM = 0x65 + UC8159REV = 0x70 + UC8159FLG = 0x71 + UC8159AMV = 0x80 + UC8159VV = 0x81 + UC8159VDCS = 0x82 + UC8159PWS = 0xE3 + UC8159TSSET = 0xE5 +) + +// DevImpression is a handle to an Inky Impression. +type DevImpression struct { + *Dev + + // Color Palette used to convert images to the 7 color. + Palette color.Palette + // Represantation of the pixels. + Pix []uint8 + + // Saturation level used by the color palette. + saturation float64 + // Resolution magic number used for resetting the panel. + res int +} + +// NewMulti opens a handle to an Inky Impression. +func NewImpression(p spi.Port, dc gpio.PinOut, reset gpio.PinOut, busy gpio.PinIn, o *Opts) (*DevImpression, error) { + if o.ModelColor != Multi { + return nil, fmt.Errorf("unsupported color: %v", o.ModelColor) + } + + c, err := p.Connect(3000*physic.KiloHertz, spi.Mode0, CS0Pin) + if err != nil { + return nil, fmt.Errorf("failed to connect to inky over spi: %v", err) + } + + // Get the maxTxSize from the conn if it implements the conn.Limits interface, + // otherwise use 4096 bytes. + maxTxSize := 0 + if limits, ok := c.(conn.Limits); ok { + maxTxSize = limits.MaxTxSize() + } + if maxTxSize == 0 { + maxTxSize = 4096 // Use a conservative default. + } + + d := &DevImpression{ + Dev: &Dev{ + c: c, + maxTxSize: maxTxSize, + dc: dc, + r: reset, + busy: busy, + color: o.ModelColor, + border: o.BorderColor, + model: o.Model, + variant: o.DisplayVariant, + }, + saturation: 0.5, // Looks good enough for most of the images. + } + + switch o.Model { + case IMPRESSION4: + d.width = 640 + d.height = 400 + d.res = 0b10 + case IMPRESSION57: + d.width = 600 + d.height = 448 + d.res = 0b11 + } + // Prefer the passed in values via Opts. + if o.Width == 0 && o.Height == 0 { + d.width = o.Width + d.height = o.Height + } + d.bounds = image.Rect(0, 0, d.width, d.height) + + d.Pix = make([]uint8, d.height*d.width) + + return d, nil +} + +// blend recalculates the palette based on the saturation level. +func (d *DevImpression) blend() []color.Color { + sat := d.saturation + + pr := []color.Color{} + for i := 0; i < 7; i++ { + rs, gs, bs := + uint8(float64(sc[i].R)*sat), + uint8(float64(sc[i].G)*sat), + uint8(float64(sc[i].B)*sat) + + rd, gd, bd := + uint8(float64(dsc[i].R)*(1.0-sat)), + uint8(float64(dsc[i].G)*(1.0-sat)), + uint8(float64(dsc[i].B)*(1.0-sat)) + + pr = append(pr, color.RGBA{rs + rd, gs + gd, bs + bd, dsc[i].A}) + } + // Add Transparent color and return the result. + return append(pr, color.RGBA{255, 255, 255, 0}) +} + +// Saturation returns the current saturation level. +func (d *DevImpression) Saturation() float64 { + return d.saturation +} + +// SetSaturaton changes the saturation level. This will not take effect until the next Draw(). +func (d *DevImpression) SetSaturation(level float64) error { + if level < 0 && level > 1 { + return fmt.Errorf("saturation level needs to be between 0 and 1") + } + d.saturation = level + // so that caller can recalculate next time they need it. + d.Palette = nil + + return nil +} + +// SetBorder changes the border color. This will not take effect until the next Draw(). +func (d *DevImpression) SetBorder(c ImpressionColor) { + d.border = Color(c) +} + +// SetPixel sets a pixel to the given color index. +func (d *DevImpression) SetPixel(x, y int, color uint8) { + d.Pix[y*d.width+x] = color & 0x07 +} + +// Render renders the content of the Pix to the screen. +func (d *DevImpression) Render() error { + if d.flipVertically { + for w := 0; w < len(d.Pix)/2-1; w = w + d.width { + for offset := 0; offset < d.width; offset++ { + d.Pix[w+offset], d.Pix[len(d.Pix)-d.width-w+offset] = d.Pix[len(d.Pix)-d.width-w+offset], d.Pix[w+offset] + } + } + } + + if d.flipHorizontally { + for offset := 0; offset < len(d.Pix)-1; offset = offset + d.width { + for i, j := 0, d.width-1; i < j; i, j = i+1, j-1 { + d.Pix[i+offset], d.Pix[j+offset] = d.Pix[j+offset], d.Pix[i+offset] + } + } + } + + 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 + } + + return d.update(merged) +} + +func (d *DevImpression) reset() error { + if err := d.r.Out(gpio.Low); err != nil { + return err + } + time.Sleep(100 * time.Millisecond) + if err := d.r.Out(gpio.High); err != nil { + return err + } + d.wait(1 * time.Second) + + // Resolution Setting + // 10bit horizontal followed by a 10bit vertical resolution + tres := make([]byte, 4) + binary.LittleEndian.PutUint16(tres[0:], uint16(d.width)) + binary.LittleEndian.PutUint16(tres[2:], uint16(d.height)) + + if err := d.sendCommand(UC8159TRES, tres); err != nil { + return err + } + + // Panel Setting + // 0b11000000 = Resolution select, 0b00 = 640x480, our panel is 0b11 = 600x448 + // 0b00100000 = LUT selection, 0 = ext flash, 1 = registers, we use ext flash + // 0b00010000 = Ignore + // 0b00001000 = Gate scan direction, 0 = down, 1 = up (default) + // 0b00000100 = Source shift direction, 0 = left, 1 = right (default) + // 0b00000010 = DC-DC converter, 0 = off, 1 = on + // 0b00000001 = Soft reset, 0 = Reset, 1 = Normal (Default) + // 0b11 = 600x448 + // 0b10 = 640x400 + if err := d.sendCommand( + UC8159PSR, + []byte{ + byte(d.res<<6) | 0b101111, // See above for more magic numbers + 0x08, // display_colours == UC81597C + }); err != nil { + return err + } + + // Power Settings + if err := d.sendCommand( + UC8159PWR, + []byte{ + (0x06 << 3) | // ??? - not documented in UC8159 datasheet + (0x01 << 2) | // SOURCE_INTERNAL_DC_DC + (0x01 << 1) | // GATE_INTERNAL_DC_DC + (0x01), // LV_SOURCE_INTERNAL_DC_DC + 0x00, // VGx_20V + 0x23, // UC81597C + 0x23, // UC81597C + }); err != nil { + return err + } + + // Set the PLL clock frequency to 50Hz + // 0b11000000 = Ignore + // 0b00111000 = M + // 0b00000111 = N + // PLL = 2MHz * (M / N) + // PLL = 2MHz * (7 / 4) + // PLL = 2,800,000 ??? + if err := d.sendCommand(UC8159PLL, []byte{0x3C}); err != nil { + return err + } + // 0b00111100 + // Send the TSE register to the display + if err := d.sendCommand(UC8159TSE, []byte{0x00}); err != nil { // Color + return err + } + // VCOM and Data Interval setting + // 0b11100000 = Vborder control (0b001 = LUTB voltage) + // 0b00010000 = Data polarity + // 0b00001111 = Vcom and data interval (0b0111 = 10, default) + + cdi := make([]byte, 2) + binary.LittleEndian.PutUint16(cdi[0:], uint16(d.border<<5)|0x17) // 0b00110111 + if err := d.sendCommand(UC8159CDI, cdi); err != nil { + return err + } + + // Gate/Source non-overlap period + // 0b11110000 = Source to Gate (0b0010 = 12nS, default) + // 0b00001111 = Gate to Source + if err := d.sendCommand(UC8159TCON, []byte{0x22}); err != nil { // 0b00100010 + return err + } + + // Disable external flash + if err := d.sendCommand(UC8159DAM, []byte{0x00}); err != nil { + return err + } + + // UC81597C + if err := d.sendCommand(UC8159PWS, []byte{0xAA}); err != nil { + return err + } + + // Power off sequence + // 0b00110000 = power off sequence of VDH and VDL, 0b00 = 1 frame (default) + // All other bits ignored? + if err := d.sendCommand(UC8159PFS, []byte{0x00}); err != nil { // PFS_1_FRAME + return err + } + + return nil +} + +func (d *DevImpression) update(pix []uint8) error { + if err := d.reset(); err != nil { + return err + } + + if err := d.sendCommand(UC8159DTM1, pix); err != nil { + return err + } + + if err := d.sendCommand(UC8159PON, nil); err != nil { + return err + } + d.wait(200 * time.Millisecond) + + if err := d.sendCommand(UC8159DRF, nil); err != nil { + return err + } + d.wait(32 * time.Second) + + if err := d.sendCommand(UC8159POF, nil); err != nil { + return err + } + d.wait(200 * time.Millisecond) + + return nil +} + +// Wait for busy/wait pin. +func (d *DevImpression) wait(dur time.Duration) { + // Set it as input, with a pull down and enable rising edge triggering. + if err := d.busy.In(gpio.PullDown, gpio.RisingEdge); err != nil { + log.Printf("Err: %s", err) + return + } + // Wait for rising edges (Low -> High) or the timeout. + d.busy.WaitForEdge(dur) +} + +func (d *DevImpression) ColorModel() color.Model { + if d.Palette == nil { + d.Palette = d.blend() + } + return d.Palette +} + +func (d *DevImpression) At(x, y int) color.Color { + if d.Palette == nil { + d.Palette = d.blend() + } + return d.Palette[d.Pix[y*d.width+x]] +} + +func (d *DevImpression) Set(x, y int, c color.Color) { + if d.Palette == nil { + d.Palette = d.blend() + } + d.Pix[y*d.width+x] = uint8(d.Palette.Index(c)) +} + +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") + } + + if src.Bounds() != d.Bounds() { + return fmt.Errorf("image must be the same size as bounds: %v", d.Bounds()) + } + + // 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{}) + return d.Render() +} + +// DrawAll redraws the whole display. +func (d *DevImpression) DrawAll(src image.Image) error { + return d.Draw(d.Bounds(), src, image.Point{}) +} diff --git a/inky/inky.go b/inky/inky.go index c746a6d..be54427 100644 --- a/inky/inky.go +++ b/inky/inky.go @@ -14,142 +14,17 @@ import ( "periph.io/x/conn/v3" "periph.io/x/conn/v3/display" "periph.io/x/conn/v3/gpio" - "periph.io/x/conn/v3/i2c" "periph.io/x/conn/v3/physic" "periph.io/x/conn/v3/spi" ) -// Color is used to define which model of inky is being used, and also for -// setting the border color. -type Color int - -// Valid Color. -const ( - Black Color = iota - Red - Yellow - White -) - -func (c *Color) String() string { - switch *c { - case Black: - return "black" - case Red: - return "red" - case Yellow: - return "yellow" - case White: - return "white" - default: - return "unknown" - } -} - -// Set sets the Color to a value represented by the string s. Set implements the flag.Value interface. -func (c *Color) Set(s string) error { - switch s { - case "black": - *c = Black - case "red": - *c = Red - case "yellow": - *c = Yellow - case "white": - *c = White - default: - return fmt.Errorf("unknown color %q: expected either black, red, yellow or white", s) - } - return nil -} - -// Model lists the supported e-ink display models. -type Model int +var _ display.Drawer = &Dev{} +var _ conn.Resource = &Dev{} -// Supported Model. const ( - PHAT Model = iota - WHAT - PHAT2 + CS0Pin = 8 ) -func (m *Model) String() string { - switch *m { - case PHAT: - return "PHAT" - case PHAT2: - return "PHAT2" - case WHAT: - return "WHAT" - default: - return "Unknown" - } -} - -// Set sets the Model to a value represented by the string s. Set implements the flag.Value interface. -func (m *Model) Set(s string) error { - switch s { - case "PHAT": - *m = PHAT - case "PHAT2": - *m = PHAT2 - case "WHAT": - *m = WHAT - default: - return fmt.Errorf("unknown model %q: expected either PHAT or WHAT", s) - } - return nil -} - -// Opts is the options to specify which device is being controlled and its -// default settings. -type Opts struct { - // Model being used. - Model Model - // Model color. - ModelColor Color - // Initial border color. Will be set on the first Draw(). - BorderColor Color -} - -// DetectOpts tries to read the device opts from EEPROM. -func DetectOpts(bus i2c.Bus) (*Opts, error) { - // Read data from EEPROM - data, err := readEep(bus) - if err != nil { - return nil, fmt.Errorf("failed to detect Inky board: %v", err) - } - - options := new(Opts) - - switch data[6] { - case 1, 4, 5: - options.Model = PHAT - case 10, 11, 12: - options.Model = PHAT2 - case 2, 3, 6, 7, 8: - options.Model = WHAT - default: - return nil, fmt.Errorf("failed to get ops: display type not supported") - } - - switch data[4] { - case 1: - options.ModelColor = Black - options.BorderColor = Black - case 2: - options.ModelColor = Red - options.BorderColor = Red - case 3: - options.ModelColor = Yellow - options.BorderColor = Yellow - default: - return nil, fmt.Errorf("failed to get ops: color not supported") - } - - return options, nil -} - var borderColor = map[Color]byte{ Black: 0x00, Red: 0x73, @@ -157,13 +32,46 @@ var borderColor = map[Color]byte{ White: 0x31, } +// Dev is a handle to an Inky. +type Dev struct { + c conn.Conn + // Maximum number of bytes allowed to be sent as a single I/O on c. + maxTxSize int + // Low when sending a command, high when sending data. + dc gpio.PinOut + // Reset pin, active low. + r gpio.PinOut + // High when device is busy. + busy gpio.PinIn + // Size of this model's display. + bounds image.Rectangle + // Whether this model needs the image flipped vertically. + flipVertically bool + // Whether this model needs the image flipped horizontally. + flipHorizontally bool + // Color of device screen (red, yellow or black). + color Color + // Modifiable color of border. + border Color + + // Width of the panel. + width int + // Height of the panel. + height int + + // Model being used. + model Model + // Variant of the panel. + variant int +} + // New opens a handle to an Inky pHAT or wHAT. func New(p spi.Port, dc gpio.PinOut, reset gpio.PinOut, busy gpio.PinIn, o *Opts) (*Dev, error) { if o.ModelColor != Black && o.ModelColor != Red && o.ModelColor != Yellow { return nil, fmt.Errorf("unsupported color: %v", o.ModelColor) } - c, err := p.Connect(488*physic.KiloHertz, spi.Mode0, 8) + c, err := p.Connect(488*physic.KiloHertz, spi.Mode0, CS0Pin) if err != nil { return nil, fmt.Errorf("failed to connect to inky over spi: %v", err) } @@ -186,44 +94,32 @@ func New(p spi.Port, dc gpio.PinOut, reset gpio.PinOut, busy gpio.PinIn, o *Opts busy: busy, color: o.ModelColor, border: o.BorderColor, + model: o.Model, + variant: o.DisplayVariant, } switch o.Model { case PHAT: - d.bounds = image.Rect(0, 0, 104, 212) + d.width = 104 + d.height = 212 d.flipVertically = true case PHAT2: - d.bounds = image.Rect(0, 0, 122, 250) + d.width = 122 + d.height = 250 d.flipVertically = true case WHAT: - d.bounds = image.Rect(0, 0, 400, 300) + d.width = 400 + d.height = 300 } - + // Prefer the passed in values via Opts. + if o.Width == 0 && o.Height == 0 { + d.width = o.Width + d.height = o.Height + } + d.bounds = image.Rect(0, 0, d.width, d.height) return d, nil } -// Dev is a handle to an Inky. -type Dev struct { - c conn.Conn - // Maximum number of bytes allowed to be sent as a single I/O on c. - maxTxSize int - // Low when sending a command, high when sending data. - dc gpio.PinOut - // Reset pin, active low. - r gpio.PinOut - // High when device is busy. - busy gpio.PinIn - // Size of this model's display. - bounds image.Rectangle - // Whether this model needs the image flipped vertically. - flipVertically bool - - // Color of device screen (red, yellow or black). - color Color - // Modifiable color of border. - border Color -} - // SetBorder changes the border color. This will not take effect until the next Draw(). func (d *Dev) SetBorder(c Color) { d.border = c @@ -241,9 +137,31 @@ func (d *Dev) SetModelColor(c Color) error { // String implements conn.Resource. func (d *Dev) String() string { + v, ok := displayVariantMap[d.variant] + if ok { + return v + } return "Inky pHAT" } +func (d *Dev) Height() int { + return d.height +} + +func (d *Dev) Width() int { + return d.width +} + +// SetBorder changes the border color. This will not take effect until the next Draw(). +func (d *Dev) SetFlipVertically(f bool) { + d.flipVertically = f +} + +// SetBorder changes the border color. This will not take effect until the next Draw(). +func (d *Dev) SetFlipHorizontally(f bool) { + d.flipHorizontally = f +} + // Halt implements conn.Resource func (d *Dev) Halt() error { return nil @@ -461,19 +379,3 @@ func pack(bits []bool) ([]byte, error) { } return ret, nil } - -func readEep(bus i2c.Bus) ([]byte, error) { - // Inky uses SMBus, specify read registry with data - write := []byte{0x00, 0x00} - - data := make([]byte, 29) - - if err := bus.Tx(0x50, write, data); err != nil { - return nil, err - } - - return data, nil -} - -var _ display.Drawer = &Dev{} -var _ conn.Resource = &Dev{} diff --git a/inky/opts.go b/inky/opts.go new file mode 100644 index 0000000..1a30b51 --- /dev/null +++ b/inky/opts.go @@ -0,0 +1,120 @@ +// Copyright 2023 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 inky + +import ( + "encoding/binary" + "fmt" + + "periph.io/x/conn/v3/i2c" +) + +var ( + displayVariantMap = map[int]string{ + 0: "", + 1: "Red pHAT (High-Temp)", + 2: "Yellow wHAT", + 3: "Black wHAT", + 4: "Black pHAT", + 5: "Yellow pHAT", + 6: "Red wHAT", + 7: "Red wHAT (High-Temp)", + 8: "Red wHAT", + 9: "", + 10: "Black pHAT (SSD1608)", + 11: "Red pHAT (SSD1608)", + 12: "Yellow pHAT (SSD1608)", + 13: "", + 14: "7-Colour (UC8159)", + 15: "7-Colour 640x400 (UC8159)", + 16: "7-Colour 640x400 (UC8159)", + 17: "Black wHAT (SSD1683)", + 18: "Red wHAT (SSD1683)", + 19: "Yellow wHAT (SSD1683)", + } +) + +// Opts is the options to specify which device is being controlled and its +// default settings. +type Opts struct { + // Boards's width and height. + Width int + Height int + + // Model being used. + Model Model + // Model color. + ModelColor Color + // Initial border color. Will be set on the first Draw(). + BorderColor Color + + // Board information. + PCBVariant float64 + DisplayVariant int +} + +// DetectOpts tries to read the device opts from EEPROM. +func DetectOpts(bus i2c.Bus) (*Opts, error) { + // Read data from EEPROM + data, err := readEep(bus) + if err != nil { + return nil, fmt.Errorf("failed to detect Inky board: %v", err) + } + options := new(Opts) + + options.Width = int(binary.LittleEndian.Uint16(data[0:])) + options.Height = int(binary.LittleEndian.Uint16(data[2:])) + + switch data[4] { + case 1: + options.ModelColor = Black + options.BorderColor = Black + case 2: + options.ModelColor = Red + options.BorderColor = Red + case 3: + options.ModelColor = Yellow + options.BorderColor = Yellow + case 4: + options.ModelColor = Multi + options.BorderColor = Color(WhiteImpression) + default: + return nil, fmt.Errorf("failed to get ops: color %v not supported", data[4]) + } + + options.PCBVariant = float64(data[5] / 10.0) + + switch data[6] { + case 1, 4, 5: + options.Model = PHAT + case 10, 11, 12: + options.Model = PHAT2 + case 2, 3, 6, 7, 8: + options.Model = WHAT + case 14: + options.Model = IMPRESSION57 + case 15, 16: + options.Model = IMPRESSION4 + default: + return nil, fmt.Errorf("failed to get ops: display type %v not supported", data[6]) + } + + options.DisplayVariant = int(data[6]) + + return options, nil +} + +func readEep(bus i2c.Bus) ([]byte, error) { + // Inky uses SMBus, specify read registry with data + write := []byte{0x00, 0x00} + + data := make([]byte, 29) + + if err := bus.Tx(0x50, write, data); err != nil { + return nil, err + } + + return data, nil +} diff --git a/inky/types.go b/inky/types.go new file mode 100644 index 0000000..2099f69 --- /dev/null +++ b/inky/types.go @@ -0,0 +1,110 @@ +// Copyright 2023 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. + +//go:generate stringer -type=Model,Color,ImpressionColor -output types_string.go +package inky + +import ( + "fmt" +) + +// Model lists the supported e-ink display models. +type Model int + +// Supported Model. +const ( + PHAT Model = iota + WHAT + PHAT2 + IMPRESSION4 + IMPRESSION57 +) + +// Set sets the Model to a value represented by the string s. Set implements the flag.Value interface. +func (m *Model) Set(s string) error { + switch s { + case "PHAT": + *m = PHAT + case "PHAT2": + *m = PHAT2 + case "WHAT": + *m = WHAT + case "IMPRESSION4": + *m = IMPRESSION4 + case "IMPRESSION57": + *m = IMPRESSION57 + default: + return fmt.Errorf("unknown model %q: expected either PHAT or WHAT", s) + } + return nil +} + +// Color is used to define which model of inky is being used, and also for +// setting the border color. +type Color int + +// Valid Color. +const ( + Black Color = iota + Red + Yellow + White + Multi +) + +// Set sets the Color to a value represented by the string s. Set implements the flag.Value interface. +func (c *Color) Set(s string) error { + switch s { + case "black": + *c = Black + case "red": + *c = Red + case "yellow": + *c = Yellow + case "white": + *c = White + default: + return fmt.Errorf("unknown color %q: expected either black, red, yellow, white or multi", s) + } + return nil +} + +// ImpressionColor is used to define colors used by Inky Impression models. +type ImpressionColor uint8 + +const ( + BlackImpression ImpressionColor = iota + WhiteImpression + GreenImpression + BlueImpression + RedImpression + YellowImpression + OrangeImpression + CleanImpression +) + +// Set sets the ImpressionColor to a value represented by the string s. Set implements the flag.Value interface. +func (c *ImpressionColor) Set(s string) error { + switch s { + case "black": + *c = BlackImpression + case "white": + *c = WhiteImpression + case "green": + *c = GreenImpression + case "blue": + *c = BlueImpression + case "red": + *c = RedImpression + case "yellow": + *c = YellowImpression + case "orange": + *c = OrangeImpression + case "clean": + *c = CleanImpression + default: + return fmt.Errorf("unknown color %q: expected either black, white. green, blue, red, yellow, orange or clean", s) + } + return nil +} diff --git a/inky/types_string.go b/inky/types_string.go new file mode 100644 index 0000000..a5f1ea2 --- /dev/null +++ b/inky/types_string.go @@ -0,0 +1,72 @@ +// Code generated by "stringer -type=Model,Color,ImpressionColor -output types_string.go"; DO NOT EDIT. + +package inky + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[PHAT-0] + _ = x[WHAT-1] + _ = x[PHAT2-2] + _ = x[IMPRESSION4-3] + _ = x[IMPRESSION57-4] +} + +const _Model_name = "PHATWHATPHAT2IMPRESSION4IMPRESSION57" + +var _Model_index = [...]uint8{0, 4, 8, 13, 24, 36} + +func (i Model) String() string { + if i < 0 || i >= Model(len(_Model_index)-1) { + return "Model(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _Model_name[_Model_index[i]:_Model_index[i+1]] +} +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[Black-0] + _ = x[Red-1] + _ = x[Yellow-2] + _ = x[White-3] + _ = x[Multi-4] +} + +const _Color_name = "BlackRedYellowWhiteMulti" + +var _Color_index = [...]uint8{0, 5, 8, 14, 19, 24} + +func (i Color) String() string { + if i < 0 || i >= Color(len(_Color_index)-1) { + return "Color(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _Color_name[_Color_index[i]:_Color_index[i+1]] +} +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[BlackImpression-0] + _ = x[WhiteImpression-1] + _ = x[GreenImpression-2] + _ = x[BlueImpression-3] + _ = x[RedImpression-4] + _ = x[YellowImpression-5] + _ = x[OrangeImpression-6] + _ = x[CleanImpression-7] +} + +const _ImpressionColor_name = "BlackImpressionWhiteImpressionGreenImpressionBlueImpressionRedImpressionYellowImpressionOrangeImpressionCleanImpression" + +var _ImpressionColor_index = [...]uint8{0, 15, 30, 45, 59, 72, 88, 104, 119} + +func (i ImpressionColor) String() string { + if i >= ImpressionColor(len(_ImpressionColor_index)-1) { + return "ImpressionColor(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _ImpressionColor_name[_ImpressionColor_index[i]:_ImpressionColor_index[i+1]] +} From 13e191a3ee67702e5df5e6d6a1e67362c5458679 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=87a=C4=9Flar=20Onur?= Date: Sun, 8 Jan 2023 14:41:41 -0800 Subject: [PATCH 2/8] Fix the typo --- inky/impression.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inky/impression.go b/inky/impression.go index a237b55..2890670 100644 --- a/inky/impression.go +++ b/inky/impression.go @@ -86,7 +86,7 @@ type DevImpression struct { // Color Palette used to convert images to the 7 color. Palette color.Palette - // Represantation of the pixels. + // Representation of the pixels. Pix []uint8 // Saturation level used by the color palette. From 8c02fb687ab2379988a4f9f2e19a2e24012b0654 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=87a=C4=9Flar=20Onur?= Date: Sun, 8 Jan 2023 22:04:14 -0800 Subject: [PATCH 3/8] Install stringer for the linter tests --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e926963..67528f6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -128,6 +128,7 @@ jobs: go install github.com/securego/gosec/v2/cmd/gosec@latest go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@latest go install honnef.co/go/tools/cmd/staticcheck@latest + go install golang.org/x/tools/cmd/stringer@latest - name: 'go install necessary tools (ubuntu)' if: always() && matrix.os == 'ubuntu-latest' run: | From f2d8384275d3d836f6218052eb3298f03a91dec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=87a=C4=9Flar=20Onur?= Date: Sat, 14 Jan 2023 15:06:48 -0800 Subject: [PATCH 4/8] Address the initial review comments --- inky/example_test.go | 2 +- inky/impression.go | 100 +++++++++++++++++++++---------------------- inky/inky.go | 39 +++++++++-------- inky/opts.go | 52 +++++++++++----------- inky/types.go | 6 +-- 5 files changed, 101 insertions(+), 98 deletions(-) diff --git a/inky/example_test.go b/inky/example_test.go index 23177d7..7d385d2 100644 --- a/inky/example_test.go +++ b/inky/example_test.go @@ -60,7 +60,7 @@ func Example() { } } -func ExampleImpression() { +func ExampleNewImpression() { path := flag.String("image", "", "Path to image file (600x448) to display") flag.Parse() diff --git a/inky/impression.go b/inky/impression.go index 2890670..d495669 100644 --- a/inky/impression.go +++ b/inky/impression.go @@ -50,34 +50,34 @@ var ( ) const ( - UC8159PSR = 0x00 - UC8159PWR = 0x01 - UC8159POF = 0x02 - UC8159PFS = 0x03 - UC8159PON = 0x04 - UC8159BTST = 0x06 - UC8159DSLP = 0x07 - UC8159DTM1 = 0x10 - UC8159DSP = 0x11 - UC8159DRF = 0x12 - UC8159IPC = 0x13 - UC8159PLL = 0x30 - UC8159TSC = 0x40 - UC8159TSE = 0x41 - UC8159TSW = 0x42 - UC8159TSR = 0x43 - UC8159CDI = 0x50 - UC8159LPD = 0x51 - UC8159TCON = 0x60 - UC8159TRES = 0x61 - UC8159DAM = 0x65 - UC8159REV = 0x70 - UC8159FLG = 0x71 - UC8159AMV = 0x80 - UC8159VV = 0x81 - UC8159VDCS = 0x82 - UC8159PWS = 0xE3 - UC8159TSSET = 0xE5 + uc8159PSR = 0x00 + uc8159PWR = 0x01 + uc8159POF = 0x02 + uc8159PFS = 0x03 + uc8159PON = 0x04 + uc8159BTST = 0x06 + uc8159DSLP = 0x07 + uc8159DTM1 = 0x10 + uc8159DSP = 0x11 + uc8159DRF = 0x12 + uc8159IPC = 0x13 + uc8159PLL = 0x30 + uc8159TSC = 0x40 + uc8159TSE = 0x41 + uc8159TSW = 0x42 + uc8159TSR = 0x43 + uc8159CDI = 0x50 + uc8159LPD = 0x51 + uc8159TCON = 0x60 + uc8159TRES = 0x61 + uc8159DAM = 0x65 + uc8159REV = 0x70 + uc8159FLG = 0x71 + uc8159AMV = 0x80 + uc8159VV = 0x81 + uc8159VDCS = 0x82 + uc8159PWS = 0xE3 + uc8159TSSET = 0xE5 ) // DevImpression is a handle to an Inky Impression. @@ -90,7 +90,7 @@ type DevImpression struct { Pix []uint8 // Saturation level used by the color palette. - saturation float64 + saturation uint // Resolution magic number used for resetting the panel. res int } @@ -101,7 +101,7 @@ func NewImpression(p spi.Port, dc gpio.PinOut, reset gpio.PinOut, busy gpio.PinI return nil, fmt.Errorf("unsupported color: %v", o.ModelColor) } - c, err := p.Connect(3000*physic.KiloHertz, spi.Mode0, CS0Pin) + c, err := p.Connect(3000*physic.KiloHertz, spi.Mode0, cs0Pin) if err != nil { return nil, fmt.Errorf("failed to connect to inky over spi: %v", err) } @@ -128,7 +128,7 @@ func NewImpression(p spi.Port, dc gpio.PinOut, reset gpio.PinOut, busy gpio.PinI model: o.Model, variant: o.DisplayVariant, }, - saturation: 0.5, // Looks good enough for most of the images. + saturation: 50, // Looks good enough for most of the images. } switch o.Model { @@ -155,7 +155,7 @@ func NewImpression(p spi.Port, dc gpio.PinOut, reset gpio.PinOut, busy gpio.PinI // blend recalculates the palette based on the saturation level. func (d *DevImpression) blend() []color.Color { - sat := d.saturation + sat := float64(d.saturation / 100) pr := []color.Color{} for i := 0; i < 7; i++ { @@ -176,14 +176,14 @@ func (d *DevImpression) blend() []color.Color { } // Saturation returns the current saturation level. -func (d *DevImpression) Saturation() float64 { +func (d *DevImpression) Saturation() uint { return d.saturation } // SetSaturaton changes the saturation level. This will not take effect until the next Draw(). -func (d *DevImpression) SetSaturation(level float64) error { - if level < 0 && level > 1 { - return fmt.Errorf("saturation level needs to be between 0 and 1") +func (d *DevImpression) SetSaturation(level uint) error { + if level > 100 { + return fmt.Errorf("saturation level needs to be between 0 and 100") } d.saturation = level // so that caller can recalculate next time they need it. @@ -244,7 +244,7 @@ func (d *DevImpression) reset() error { binary.LittleEndian.PutUint16(tres[0:], uint16(d.width)) binary.LittleEndian.PutUint16(tres[2:], uint16(d.height)) - if err := d.sendCommand(UC8159TRES, tres); err != nil { + if err := d.sendCommand(uc8159TRES, tres); err != nil { return err } @@ -259,7 +259,7 @@ func (d *DevImpression) reset() error { // 0b11 = 600x448 // 0b10 = 640x400 if err := d.sendCommand( - UC8159PSR, + uc8159PSR, []byte{ byte(d.res<<6) | 0b101111, // See above for more magic numbers 0x08, // display_colours == UC81597C @@ -269,7 +269,7 @@ func (d *DevImpression) reset() error { // Power Settings if err := d.sendCommand( - UC8159PWR, + uc8159PWR, []byte{ (0x06 << 3) | // ??? - not documented in UC8159 datasheet (0x01 << 2) | // SOURCE_INTERNAL_DC_DC @@ -289,12 +289,12 @@ func (d *DevImpression) reset() error { // PLL = 2MHz * (M / N) // PLL = 2MHz * (7 / 4) // PLL = 2,800,000 ??? - if err := d.sendCommand(UC8159PLL, []byte{0x3C}); err != nil { + if err := d.sendCommand(uc8159PLL, []byte{0x3C}); err != nil { return err } // 0b00111100 // Send the TSE register to the display - if err := d.sendCommand(UC8159TSE, []byte{0x00}); err != nil { // Color + if err := d.sendCommand(uc8159TSE, []byte{0x00}); err != nil { // Color return err } // VCOM and Data Interval setting @@ -304,31 +304,31 @@ func (d *DevImpression) reset() error { cdi := make([]byte, 2) binary.LittleEndian.PutUint16(cdi[0:], uint16(d.border<<5)|0x17) // 0b00110111 - if err := d.sendCommand(UC8159CDI, cdi); err != nil { + if err := d.sendCommand(uc8159CDI, cdi); err != nil { return err } // Gate/Source non-overlap period // 0b11110000 = Source to Gate (0b0010 = 12nS, default) // 0b00001111 = Gate to Source - if err := d.sendCommand(UC8159TCON, []byte{0x22}); err != nil { // 0b00100010 + if err := d.sendCommand(uc8159TCON, []byte{0x22}); err != nil { // 0b00100010 return err } // Disable external flash - if err := d.sendCommand(UC8159DAM, []byte{0x00}); err != nil { + if err := d.sendCommand(uc8159DAM, []byte{0x00}); err != nil { return err } // UC81597C - if err := d.sendCommand(UC8159PWS, []byte{0xAA}); err != nil { + if err := d.sendCommand(uc8159PWS, []byte{0xAA}); err != nil { return err } // Power off sequence // 0b00110000 = power off sequence of VDH and VDL, 0b00 = 1 frame (default) // All other bits ignored? - if err := d.sendCommand(UC8159PFS, []byte{0x00}); err != nil { // PFS_1_FRAME + if err := d.sendCommand(uc8159PFS, []byte{0x00}); err != nil { // PFS_1_FRAME return err } @@ -340,21 +340,21 @@ func (d *DevImpression) update(pix []uint8) error { return err } - if err := d.sendCommand(UC8159DTM1, pix); err != nil { + if err := d.sendCommand(uc8159DTM1, pix); err != nil { return err } - if err := d.sendCommand(UC8159PON, nil); err != nil { + if err := d.sendCommand(uc8159PON, nil); err != nil { return err } d.wait(200 * time.Millisecond) - if err := d.sendCommand(UC8159DRF, nil); err != nil { + if err := d.sendCommand(uc8159DRF, nil); err != nil { return err } d.wait(32 * time.Second) - if err := d.sendCommand(UC8159POF, nil); err != nil { + if err := d.sendCommand(uc8159POF, nil); err != nil { return err } d.wait(200 * time.Millisecond) diff --git a/inky/inky.go b/inky/inky.go index be54427..9fcd60c 100644 --- a/inky/inky.go +++ b/inky/inky.go @@ -22,7 +22,7 @@ var _ display.Drawer = &Dev{} var _ conn.Resource = &Dev{} const ( - CS0Pin = 8 + cs0Pin = 8 ) var borderColor = map[Color]byte{ @@ -62,7 +62,9 @@ type Dev struct { // Model being used. model Model // Variant of the panel. - variant int + variant uint + // PCB Variant of the panel. Represents a version string as a number (12 -> 1.2). + pcbVariant uint } // New opens a handle to an Inky pHAT or wHAT. @@ -71,7 +73,7 @@ func New(p spi.Port, dc gpio.PinOut, reset gpio.PinOut, busy gpio.PinIn, o *Opts 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, cs0Pin) if err != nil { return nil, fmt.Errorf("failed to connect to inky over spi: %v", err) } @@ -87,15 +89,16 @@ func New(p spi.Port, dc gpio.PinOut, reset gpio.PinOut, busy gpio.PinIn, o *Opts } d := &Dev{ - c: c, - maxTxSize: maxTxSize, - dc: dc, - r: reset, - busy: busy, - color: o.ModelColor, - border: o.BorderColor, - model: o.Model, - variant: o.DisplayVariant, + c: c, + maxTxSize: maxTxSize, + dc: dc, + r: reset, + busy: busy, + color: o.ModelColor, + border: o.BorderColor, + model: o.Model, + variant: o.DisplayVariant, + pcbVariant: o.PCBVariant, } switch o.Model { @@ -137,9 +140,9 @@ func (d *Dev) SetModelColor(c Color) error { // String implements conn.Resource. func (d *Dev) String() string { - v, ok := displayVariantMap[d.variant] - if ok { - return v + index := int(d.variant) + if index < len(displayVariantMap) { + return displayVariantMap[index] } return "Inky pHAT" } @@ -152,17 +155,17 @@ func (d *Dev) Width() int { return d.width } -// SetBorder changes the border color. This will not take effect until the next Draw(). +// SetFlipVertically flips the image horizontally. func (d *Dev) SetFlipVertically(f bool) { d.flipVertically = f } -// SetBorder changes the border color. This will not take effect until the next Draw(). +// SetFlipHorizontally flips the image horizontally. func (d *Dev) SetFlipHorizontally(f bool) { d.flipHorizontally = f } -// Halt implements conn.Resource +// Halt implements conn.Resource. func (d *Dev) Halt() error { return nil } diff --git a/inky/opts.go b/inky/opts.go index 1a30b51..74700f7 100644 --- a/inky/opts.go +++ b/inky/opts.go @@ -12,27 +12,27 @@ import ( ) var ( - displayVariantMap = map[int]string{ - 0: "", - 1: "Red pHAT (High-Temp)", - 2: "Yellow wHAT", - 3: "Black wHAT", - 4: "Black pHAT", - 5: "Yellow pHAT", - 6: "Red wHAT", - 7: "Red wHAT (High-Temp)", - 8: "Red wHAT", - 9: "", - 10: "Black pHAT (SSD1608)", - 11: "Red pHAT (SSD1608)", - 12: "Yellow pHAT (SSD1608)", - 13: "", - 14: "7-Colour (UC8159)", - 15: "7-Colour 640x400 (UC8159)", - 16: "7-Colour 640x400 (UC8159)", - 17: "Black wHAT (SSD1683)", - 18: "Red wHAT (SSD1683)", - 19: "Yellow wHAT (SSD1683)", + displayVariantMap = []string{ + "", + "Red pHAT (High-Temp)", + "Yellow wHAT", + "Black wHAT", + "Black pHAT", + "Yellow pHAT", + "Red wHAT", + "Red wHAT (High-Temp)", + "Red wHAT", + "", + "Black pHAT (SSD1608)", + "Red pHAT (SSD1608)", + "Yellow pHAT (SSD1608)", + "", + "7-Colour (UC8159)", + "7-Colour 640x400 (UC8159)", + "7-Colour 640x400 (UC8159)", + "Black wHAT (SSD1683)", + "Red wHAT (SSD1683)", + "Yellow wHAT (SSD1683)", } ) @@ -51,8 +51,8 @@ type Opts struct { BorderColor Color // Board information. - PCBVariant float64 - DisplayVariant int + PCBVariant uint + DisplayVariant uint } // DetectOpts tries to read the device opts from EEPROM. @@ -83,8 +83,8 @@ func DetectOpts(bus i2c.Bus) (*Opts, error) { default: return nil, fmt.Errorf("failed to get ops: color %v not supported", data[4]) } - - options.PCBVariant = float64(data[5] / 10.0) + // PCB Variant is stored as a number in the eeprom but is actually corresponds a version string (12 -> 1.2) + options.PCBVariant = uint(data[5]) switch data[6] { case 1, 4, 5: @@ -101,7 +101,7 @@ func DetectOpts(bus i2c.Bus) (*Opts, error) { return nil, fmt.Errorf("failed to get ops: display type %v not supported", data[6]) } - options.DisplayVariant = int(data[6]) + options.DisplayVariant = uint(data[6]) return options, nil } diff --git a/inky/types.go b/inky/types.go index 2099f69..b1496da 100644 --- a/inky/types.go +++ b/inky/types.go @@ -1,9 +1,9 @@ // Copyright 2023 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 inky //go:generate stringer -type=Model,Color,ImpressionColor -output types_string.go -package inky import ( "fmt" @@ -35,7 +35,7 @@ func (m *Model) Set(s string) error { case "IMPRESSION57": *m = IMPRESSION57 default: - return fmt.Errorf("unknown model %q: expected either PHAT or WHAT", s) + return fmt.Errorf("unknown model %q: expected PHAT, PHAT2, WHAT, IMPRESSION4 or IMPRESSION57", s) } return nil } @@ -65,7 +65,7 @@ func (c *Color) Set(s string) error { case "white": *c = White default: - return fmt.Errorf("unknown color %q: expected either black, red, yellow, white or multi", s) + return fmt.Errorf("unknown color %q: expected either black, red, yellow or white", s) } return nil } From dc2c0ac397206d197612365c9e3002e87ccbc638 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=87a=C4=9Flar=20Onur?= Date: Sat, 14 Jan 2023 15:10:45 -0800 Subject: [PATCH 5/8] Another outdated comment fix --- inky/impression.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inky/impression.go b/inky/impression.go index d495669..5fe996b 100644 --- a/inky/impression.go +++ b/inky/impression.go @@ -95,7 +95,7 @@ type DevImpression struct { res int } -// NewMulti opens a handle to an Inky Impression. +// NewImpression opens a handle to an Inky Impression. func NewImpression(p spi.Port, dc gpio.PinOut, reset gpio.PinOut, busy gpio.PinIn, o *Opts) (*DevImpression, error) { if o.ModelColor != Multi { return nil, fmt.Errorf("unsupported color: %v", o.ModelColor) From 053989c77c4e51df9cb4c4944a9b5a1d7d34264d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=87a=C4=9Flar=20Onur?= Date: Sat, 14 Jan 2023 15:22:23 -0800 Subject: [PATCH 6/8] Small fixes --- inky/impression.go | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/inky/impression.go b/inky/impression.go index 5fe996b..a24f918 100644 --- a/inky/impression.go +++ b/inky/impression.go @@ -118,15 +118,16 @@ func NewImpression(p spi.Port, dc gpio.PinOut, reset gpio.PinOut, busy gpio.PinI d := &DevImpression{ Dev: &Dev{ - c: c, - maxTxSize: maxTxSize, - dc: dc, - r: reset, - busy: busy, - color: o.ModelColor, - border: o.BorderColor, - model: o.Model, - variant: o.DisplayVariant, + c: c, + maxTxSize: maxTxSize, + dc: dc, + r: reset, + busy: busy, + color: o.ModelColor, + border: o.BorderColor, + model: o.Model, + variant: o.DisplayVariant, + pcbVariant: o.PCBVariant, }, saturation: 50, // Looks good enough for most of the images. } @@ -197,11 +198,6 @@ func (d *DevImpression) SetBorder(c ImpressionColor) { d.border = Color(c) } -// SetPixel sets a pixel to the given color index. -func (d *DevImpression) SetPixel(x, y int, color uint8) { - d.Pix[y*d.width+x] = color & 0x07 -} - // Render renders the content of the Pix to the screen. func (d *DevImpression) Render() error { if d.flipVertically { @@ -373,6 +369,7 @@ func (d *DevImpression) wait(dur time.Duration) { d.busy.WaitForEdge(dur) } +// ColorModel returns the device native color model. func (d *DevImpression) ColorModel() color.Model { if d.Palette == nil { d.Palette = d.blend() @@ -380,6 +377,7 @@ func (d *DevImpression) ColorModel() color.Model { return d.Palette } +// At returns the color of the pixel at (x, y). func (d *DevImpression) At(x, y int) color.Color { if d.Palette == nil { d.Palette = d.blend() @@ -387,6 +385,7 @@ func (d *DevImpression) At(x, y int) color.Color { return d.Palette[d.Pix[y*d.width+x]] } +// Set sets the pixel at (x, y) to the given color. This will not take effect until the next Draw(). func (d *DevImpression) Set(x, y int, c color.Color) { if d.Palette == nil { d.Palette = d.blend() @@ -394,6 +393,7 @@ func (d *DevImpression) Set(x, y int, c color.Color) { d.Pix[y*d.width+x] = uint8(d.Palette.Index(c)) } +// 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") From 0de3494d495d5e8f681e62da69da6fa3cdc5d0f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=87a=C4=9Flar=20Onur?= Date: Sun, 15 Jan 2023 14:15:20 -0800 Subject: [PATCH 7/8] Address the remaining review comments --- inky/opts.go | 2 +- inky/types.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/inky/opts.go b/inky/opts.go index 74700f7..d7ecc3b 100644 --- a/inky/opts.go +++ b/inky/opts.go @@ -12,7 +12,7 @@ import ( ) var ( - displayVariantMap = []string{ + displayVariantMap = [...]string{ "", "Red pHAT (High-Temp)", "Yellow wHAT", diff --git a/inky/types.go b/inky/types.go index b1496da..b5ac48b 100644 --- a/inky/types.go +++ b/inky/types.go @@ -1,6 +1,7 @@ // Copyright 2023 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 inky //go:generate stringer -type=Model,Color,ImpressionColor -output types_string.go From 3e8d181acba693183779004dfbd0ad31b33ef309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=87a=C4=9Flar=20Onur?= Date: Mon, 16 Jan 2023 13:17:12 -0800 Subject: [PATCH 8/8] Fix the err shadowing --- inky/example_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inky/example_test.go b/inky/example_test.go index 7d385d2..c9082c7 100644 --- a/inky/example_test.go +++ b/inky/example_test.go @@ -75,7 +75,7 @@ func ExampleNewImpression() { log.Fatal(err) } - if _, err := host.Init(); err != nil { + if _, err = host.Init(); err != nil { log.Fatal(err) }