From 34b5f2bfcbf5ee4be6a2c4084a08d5ddef979c04 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Ruel Date: Tue, 20 Nov 2018 21:04:58 -0500 Subject: [PATCH] nrzled: merge SPIDev into Dev - Remove Strip, now unnecessary. - Rename New() to NewStream(). - Merge SPIDev into Dev. - Rename all unit tests. - Unexport ToRGB(), this was in a unit test. - Increase test coverage to 93.8%. --- experimental/devices/nrzled/nrz_spi.go | 192 ------------ experimental/devices/nrzled/nrzled.go | 217 ++++++++++--- .../{nrz_spi_test.go => nrzled_spi_test.go} | 294 ++++++++++++------ .../{nrzled_test.go => nrzled_stream_test.go} | 73 +++-- 4 files changed, 435 insertions(+), 341 deletions(-) delete mode 100644 experimental/devices/nrzled/nrz_spi.go rename experimental/devices/nrzled/{nrz_spi_test.go => nrzled_spi_test.go} (63%) rename experimental/devices/nrzled/{nrzled_test.go => nrzled_stream_test.go} (87%) diff --git a/experimental/devices/nrzled/nrz_spi.go b/experimental/devices/nrzled/nrz_spi.go deleted file mode 100644 index 0b98170..0000000 --- a/experimental/devices/nrzled/nrz_spi.go +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright 2016 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 nrzled - -import ( - "errors" - "fmt" - "image" - "image/color" - "image/draw" - - "periph.io/x/periph/conn/display" - "periph.io/x/periph/conn/physic" - "periph.io/x/periph/conn/spi" -) - -// SPIDev represents a strip of WS2812b LEDs as a strip connected over a SPI port. -// It accepts a stream of raw RGB pixels and converts it to a bit pattern consistent -// with the WS812b protocol. -// Includes intensity and temperature correction. -type SPIDev struct { - s spi.Conn // - numPixels int // - rawBuf []byte // Raw buffer sent over SPI. Cached to reduce heap fragmentation. - pixels []byte // Double buffer of pixels, to enable partial painting via Draw(). Effectively points inside rawBuf. - rect image.Rectangle // Device bounds - pixelMap map[image.Point]int -} - -// NewSPI returns a strip that communicates over SPI to NRZ encoded LEDs. -// -// Due to the tight timing demands of these LEDs, -// the SPI port speed must be a reliable 2.5MHz -// -// Note that your SPI buffer should be at least 12*num_pixels+2 bytes long -func NewSPI(p spi.Port, o *Opts) (*SPIDev, error) { - c, err := p.Connect(2500*physic.KiloHertz, spi.Mode3|spi.NoCS, 8) - if err != nil { - return nil, err - } - rawBCt := 4 * (3 * o.NumPixels) //3 bytes per pixel, 4 symbol bytes per byte - buf := make([]byte, rawBCt+3) //3 bytes for latch. 24*400ns = 9600ns. - tail := buf[rawBCt:] - for i := range tail { - tail[i] = 0x00 - } - - return &SPIDev{ - s: c, - numPixels: o.NumPixels, - rawBuf: buf, - pixels: buf[:rawBCt], - rect: image.Rect(0, 0, o.NumPixels, 1), - }, nil -} - -func (d *SPIDev) String() string { - return fmt.Sprintf("nrzled: {%d, %s}", d.numPixels, d.s) -} - -// ColorModel implements display.Drawer. There's no surprise, it is -// color.NRGBAModel. -func (d *SPIDev) ColorModel() color.Model { - return color.NRGBAModel -} - -// Bounds implements display.Drawer. Min is guaranteed to be {0, 0}. -func (d *SPIDev) Bounds() image.Rectangle { - return d.rect -} - -// Draw implements display.Drawer. -// -// Using something else than image.NRGBA is 10x slower. When using image.NRGBA, -// the alpha channel is ignored. -func (d *SPIDev) Draw(r image.Rectangle, src image.Image, sp image.Point) error { - if r = r.Intersect(d.rect); r.Empty() { - return nil - } - srcR := src.Bounds() - srcR.Min = srcR.Min.Add(sp) - if dX := r.Dx(); dX < srcR.Dx() { - srcR.Max.X = srcR.Min.X + dX - } - if dY := r.Dy(); dY < srcR.Dy() { - srcR.Max.Y = srcR.Min.Y + dY - } - if srcR.Empty() { - return nil - } - d.rasterImg(d.pixels, r, src, srcR) - return d.s.Tx(d.rawBuf, nil) -} - -// Write accepts a stream of raw RGB pixels and sends it as WS2812b encoded -// stream. -func (d *SPIDev) Write(pixels []byte) (int, error) { - if len(pixels)%3 != 0 || len(pixels)/3 > d.numPixels { - return 0, errors.New("ws2812b: invalid RGB stream length") - } - // Do not touch footer. - d.raster(d.pixels, pixels, false) - err := d.s.Tx(d.rawBuf, nil) - return len(pixels), err -} - -// Halt turns off all the lights. -func (d *SPIDev) Halt() error { - // Zap out the buffer. - for i := range d.pixels { - d.pixels[i] = 0x88 - } - return d.s.Tx(d.rawBuf, nil) -} - -// raster serializes a buffer of RGB bytes to the WS2812b SPI format. -// -// It is expected to be given the part where pixels are, not the header nor -// footer. -// -// dst is in WS2812b SPI 32 bits word format. src is in RGB 24 bits, or 32 bits -// word format when srcHasAlpha is true. The src alpha channel is ignored in -// this case. -// -// src cannot be longer in pixel count than dst. -func (d *SPIDev) raster(dst []byte, src []byte, srcHasAlpha bool) { - pBytes := 3 - if srcHasAlpha { - pBytes = 4 - } - length := len(src) / pBytes - if l := len(dst) / 4; l < length { - length = l - } - if length == 0 { - // Save ourself some unneeded processing. - return - } - stride := 4 //number of spi-bytes in color-byte - for i := 0; i < length; i++ { - sOff := pBytes * i - dOff := 3 * stride * i //3 channels * stride - r, g, b := src[sOff], src[sOff+1], src[sOff+2] - //grb color order, msb first - copy(dst[dOff+stride*0:dOff+stride*1], nrzMSB4[r][:]) - copy(dst[dOff+stride*1:dOff+stride*2], nrzMSB4[g][:]) - copy(dst[dOff+stride*2:dOff+stride*3], nrzMSB4[b][:]) - } -} - -// rasterImg is the generic version of raster that converts an image instead of raw RGB values. -// -// It has 'fast paths' for image.RGBA and image.NRGBA that extract and convert the RGB values -// directly. For other image types, it converts to image.RGBA and then does the same. In all -// cases, alpha values are ignored. -// -// rect specifies where into the output buffer to draw. -// -// srcR specifies what portion of the source image to use. -func (d *SPIDev) rasterImg(dst []byte, rect image.Rectangle, src image.Image, srcR image.Rectangle) { - // Render directly into the buffer for maximum performance and to keep - // untouched sections intact. - switch im := src.(type) { - case *image.RGBA: - start := im.PixOffset(srcR.Min.X, srcR.Min.Y) - // srcR.Min.Y since the output display has only a single column - end := im.PixOffset(srcR.Max.X, srcR.Min.Y) - // Offset into the output buffer using rect - d.raster(dst[4*rect.Min.X:], im.Pix[start:end], true) - case *image.NRGBA: - // Ignores alpha - start := im.PixOffset(srcR.Min.X, srcR.Min.Y) - // srcR.Min.Y since the output display has only a single column - end := im.PixOffset(srcR.Max.X, srcR.Min.Y) - // Offset into the output buffer using rect - d.raster(dst[4*rect.Min.X:], im.Pix[start:end], true) - default: - // Slow path. Convert to RGBA - b := im.Bounds() - m := image.NewRGBA(image.Rect(0, 0, b.Dx(), b.Dy())) - draw.Draw(m, m.Bounds(), src, b.Min, draw.Src) - start := m.PixOffset(srcR.Min.X, srcR.Min.Y) - // srcR.Min.Y since the output display has only a single column - end := m.PixOffset(srcR.Max.X, srcR.Min.Y) - // Offset into the output buffer using rect - d.raster(dst[4*rect.Min.X:], m.Pix[start:end], true) - } -} - -var _ display.Drawer = &Dev{} diff --git a/experimental/devices/nrzled/nrzled.go b/experimental/devices/nrzled/nrzled.go index b215240..bca9903 100644 --- a/experimental/devices/nrzled/nrzled.go +++ b/experimental/devices/nrzled/nrzled.go @@ -9,18 +9,15 @@ import ( "fmt" "image" "image/color" + "image/draw" + "periph.io/x/periph/conn" "periph.io/x/periph/conn/display" "periph.io/x/periph/conn/gpio/gpiostream" "periph.io/x/periph/conn/physic" + "periph.io/x/periph/conn/spi" ) -// Strip is deprecated and will soon be removed. -type Strip interface { - display.Drawer - Write(pixels []byte) (int, error) -} - // DefaultOpts is the recommended default options. var DefaultOpts = Opts{ NumPixels: 150, // 150 LEDs is a common strip length. @@ -43,7 +40,7 @@ type Opts struct { } // New opens a handle to a compatible LED strip. -func New(p gpiostream.PinOut, opts *Opts) (*Dev, error) { +func NewStream(p gpiostream.PinOut, opts *Opts) (*Dev, error) { // Allow a wider range in case there's new devices with higher supported // frequency. if opts.Freq < 10*physic.KiloHertz || opts.Freq > 100*physic.MegaHertz { @@ -52,45 +49,108 @@ func New(p gpiostream.PinOut, opts *Opts) (*Dev, error) { if opts.Channels != 3 && opts.Channels != 4 { return nil, errors.New("nrzled: specify valid number of channels (3 or 4)") } + // 3 symbol bytes per byte, 3/4 bytes per pixel. + streamLen := 3 * (opts.Channels * opts.NumPixels) + // 3 bytes for latch. TODO: duration. + bufSize := streamLen + 3 + buf := make([]byte, bufSize) return &Dev{ + name: "nrzled{" + p.Name() + "}", p: p, numPixels: opts.NumPixels, channels: opts.Channels, - b: gpiostream.BitStream{ - Freq: opts.Freq, - // Each bit is encoded on 3 bits. - Bits: make([]byte, opts.NumPixels*3*opts.Channels), - LSBF: false, - }, - rect: image.Rect(0, 0, opts.NumPixels, 1), + b: gpiostream.BitStream{Freq: opts.Freq, Bits: buf, LSBF: false}, + rawBuf: buf[:streamLen], + rect: image.Rect(0, 0, opts.NumPixels, 1), + }, nil +} + +// NewSPI returns a strip that communicates over SPI to NRZ encoded LEDs. +// +// Due to the tight timing demands of these LEDs, the SPI port speed must be a +// reliable 2.4~2.5MHz; this is 3x 800kHz. +// +// The driver's SPI buffer must be at least 12*num_pixels+3 bytes long. +func NewSPI(p spi.Port, opts *Opts) (*Dev, error) { + const spiFreq = 2500 * physic.KiloHertz + if opts.Freq != spiFreq { + return nil, errors.New("nrzled: expected Freq " + spiFreq.String()) + } + if opts.Channels != 3 && opts.Channels != 4 { + return nil, errors.New("nrzled: specify valid number of channels (3 or 4)") + } + // 4 symbol bytes per byte, 3/4 bytes per pixel. + streamLen := 4 * (opts.Channels * opts.NumPixels) + // 3 bytes for latch. 24*400ns = 9600ns. In practice this could be skipped, + // as the overhead for SPI Tx() tear down and the next one is likely at least + // 10µs. + bufSize := streamLen + 3 + if l, ok := p.(conn.Limits); ok { + if s := l.MaxTxSize(); s < bufSize { + return nil, errors.New("spi port buffer is too short for the specified number of pixels") + } + } + c, err := p.Connect(spiFreq, spi.Mode3|spi.NoCS, 8) + if err != nil { + return nil, err + } + buf := make([]byte, bufSize) + return &Dev{ + name: "nrzled{" + c.String() + "}", + s: c, + numPixels: opts.NumPixels, + channels: opts.Channels, + b: gpiostream.BitStream{Freq: opts.Freq, Bits: buf, LSBF: false}, + rawBuf: buf[:streamLen], + rect: image.Rect(0, 0, opts.NumPixels, 1), }, nil } // Dev is a handle to the LED strip. type Dev struct { + // Immutable. + name string + s spi.Conn p gpiostream.PinOut numPixels int - channels int // Number of channels per pixel - b gpiostream.BitStream // NRZ encoded bits; cached to reduce heap fragmentation - buf []byte // Double buffer of RGB/RGBW pixels; enables partial Draw() - rect image.Rectangle // Device bounds + channels int // Number of channels per pixel + rect image.Rectangle // Device bounds + + // Mutable. + b gpiostream.BitStream // NRZ encoded bits; cached to reduce heap fragmentation + rawBuf []byte // NRZ encoded bits; excluding the padding + pixels []byte // Double buffer of RGB/RGBW pixels; enables partial Draw() } func (d *Dev) String() string { - return fmt.Sprintf("nrzled{%s}", d.p) + return d.name } // Halt turns the lights off. // // It doesn't affect the back buffer. func (d *Dev) Halt() error { - zero := nrzMSB3[0] - for i := 0; i < d.channels*d.numPixels; i++ { - d.b.Bits[3*i+0] = zero[0] - d.b.Bits[3*i+1] = zero[1] - d.b.Bits[3*i+2] = zero[2] + if d.s == nil { + // zero := nrzMSB3[0] + const a = 0x92 + const b = 0x49 + const c = 0x24 + for i := 0; i < len(d.rawBuf); i += 3 { + d.rawBuf[i+0] = a + d.rawBuf[i+1] = b + d.rawBuf[i+2] = c + } + if err := d.p.StreamOut(&d.b); err != nil { + return fmt.Errorf("nrzled: %v", err) + } + return nil } - if err := d.p.StreamOut(&d.b); err != nil { + + // Zap out the buffer. 0x88 is '0'. + for i := range d.rawBuf { + d.rawBuf[i] = 0x88 + } + if err := d.s.Tx(d.b.Bits, nil); err != nil { return fmt.Errorf("nrzled: %v", err) } return nil @@ -128,15 +188,22 @@ func (d *Dev) Draw(r image.Rectangle, src image.Image, sp image.Point) error { if dY := r.Dy(); dY < srcR.Dy() { srcR.Max.Y = srcR.Min.Y + dY } - if d.buf == nil { - // Allocate d.buf on first Draw() call, in case the user only wants to use - // .Write(). - d.buf = make([]byte, d.numPixels*d.channels) + if srcR.Empty() { + return nil + } + if d.s != nil { + d.rasterSPIImg(d.rawBuf, r, src, srcR) + return d.s.Tx(d.b.Bits, nil) + } + if d.pixels == nil { + // Allocate d.pixels on first Draw() call, in case the user only wants to + // use .Write(). + d.pixels = make([]byte, d.numPixels*d.channels) } if img, ok := src.(*image.NRGBA); ok { // Fast path for image.NRGBA. base := srcR.Min.Y * img.Stride - raster(d.b.Bits, img.Pix[base+4*srcR.Min.X:base+4*srcR.Max.X], d.channels, 4) + rasterBits(d.b.Bits, img.Pix[base+4*srcR.Min.X:base+4*srcR.Max.X], d.channels, 4) } else { // Generic version. m := srcR.Max.X - srcR.Min.X @@ -170,23 +237,27 @@ func (d *Dev) Write(pixels []byte) (int, error) { if len(pixels)%d.channels != 0 || len(pixels) > d.numPixels*d.channels { return 0, errors.New("nrzled: invalid RGB stream length") } - raster(d.b.Bits, pixels, d.channels, d.channels) - if err := d.p.StreamOut(&d.b); err != nil { - return 0, fmt.Errorf("nrzled: %v", err) + if d.s == nil { + rasterBits(d.b.Bits, pixels, d.channels, d.channels) + if err := d.p.StreamOut(&d.b); err != nil { + return 0, fmt.Errorf("nrzled: %v", err) + } + return len(pixels), nil } - return len(pixels), nil + d.rasterSPI(d.rawBuf, pixels, false) + return len(pixels), d.s.Tx(d.b.Bits, nil) } -// +// Bits -// raster converts a RGB/RGBW input stream into a MSB binary output stream as it -// must be sent over the GPIO pin. +// rasterBits converts a RGB/RGBW input stream into a MSB binary output stream +// as it must be sent over the GPIO pin. // // `in` is RGB 24 bits or RGBW 32 bits. Each bit is encoded over 3 bits so the // length of `out` must be 3x as large as `in`. // // Encoded output format is GRB as 72 bits (24 * 3) or 96 bits (32 * 3). -func raster(out, in []byte, outChannels, inChannels int) { +func rasterBits(out, in []byte, outChannels, inChannels int) { pixels := len(in) / inChannels if outChannels == 3 { for i := 0; i < pixels; i++ { @@ -214,4 +285,74 @@ func putNRZMSB3(out []byte, v byte) { copy(out, nrzMSB3[v][:]) } +// SPI + +// rasterSPI serializes a buffer of RGB bytes to the WS2812b SPI format. +// +// It is expected to be given the part where pixels are, not the header nor +// footer. +// +// dst is in WS2812b SPI 32 bits word format. src is in RGB 24 bits, or 32 bits +// word format when srcHasAlpha is true. The src alpha channel is ignored in +// this case. +// +// src cannot be longer in pixel count than dst. +func (d *Dev) rasterSPI(dst []byte, src []byte, srcHasAlpha bool) { + pBytes := 3 + if srcHasAlpha { + pBytes = 4 + } + length := len(src) / pBytes + stride := 4 //number of spi-bytes in color-byte + for i := 0; i < length; i++ { + sOff := pBytes * i + dOff := 3 * stride * i //3 channels * stride + r, g, b := src[sOff], src[sOff+1], src[sOff+2] + //grb color order, msb first + copy(dst[dOff+stride*0:dOff+stride*1], nrzMSB4[r][:]) + copy(dst[dOff+stride*1:dOff+stride*2], nrzMSB4[g][:]) + copy(dst[dOff+stride*2:dOff+stride*3], nrzMSB4[b][:]) + } +} + +// rasterSPIImg is the generic version of raster that converts an image instead +// of raw RGB values. +// +// It has 'fast paths' for image.RGBA and image.NRGBA that extract and convert +// the RGB values directly. For other image types, it converts to image.RGBA +// and then does the same. In all cases, alpha values are ignored. +// +// rect specifies where into the output buffer to draw. +// +// srcR specifies what portion of the source image to use. +func (d *Dev) rasterSPIImg(dst []byte, rect image.Rectangle, src image.Image, srcR image.Rectangle) { + // Render directly into the buffer for maximum performance and to keep + // untouched sections intact. + switch im := src.(type) { + case *image.RGBA: + start := im.PixOffset(srcR.Min.X, srcR.Min.Y) + // srcR.Min.Y since the output display has only a single column + end := im.PixOffset(srcR.Max.X, srcR.Min.Y) + // Offset into the output buffer using rect + d.rasterSPI(dst[4*rect.Min.X:], im.Pix[start:end], true) + case *image.NRGBA: + // Ignores alpha + start := im.PixOffset(srcR.Min.X, srcR.Min.Y) + // srcR.Min.Y since the output display has only a single column + end := im.PixOffset(srcR.Max.X, srcR.Min.Y) + // Offset into the output buffer using rect + d.rasterSPI(dst[4*rect.Min.X:], im.Pix[start:end], true) + default: + // Slow path. Convert to RGBA + b := im.Bounds() + m := image.NewRGBA(image.Rect(0, 0, b.Dx(), b.Dy())) + draw.Draw(m, m.Bounds(), src, b.Min, draw.Src) + start := m.PixOffset(srcR.Min.X, srcR.Min.Y) + // srcR.Min.Y since the output display has only a single column + end := m.PixOffset(srcR.Max.X, srcR.Min.Y) + // Offset into the output buffer using rect + d.rasterSPI(dst[4*rect.Min.X:], m.Pix[start:end], true) + } +} + var _ display.Drawer = &Dev{} diff --git a/experimental/devices/nrzled/nrz_spi_test.go b/experimental/devices/nrzled/nrzled_spi_test.go similarity index 63% rename from experimental/devices/nrzled/nrz_spi_test.go rename to experimental/devices/nrzled/nrzled_spi_test.go index 2a23036..5065853 100644 --- a/experimental/devices/nrzled/nrz_spi_test.go +++ b/experimental/devices/nrzled/nrzled_spi_test.go @@ -19,10 +19,10 @@ import ( "periph.io/x/periph/conn/spi/spitest" ) -// ToRGB converts a slice of color.NRGBA to a byte stream of RGB pixels. +// toRGB converts a slice of color.NRGBA to a byte stream of RGB pixels. // // Ignores alpha. -func ToRGB(p []color.NRGBA) []byte { +func toRGB(p []color.NRGBA) []byte { b := make([]byte, 0, len(p)*3) for _, c := range p { b = append(b, c.R, c.G, c.B) @@ -30,33 +30,61 @@ func ToRGB(p []color.NRGBA) []byte { return b } -func TestDevEmpty(t *testing.T) { +func TestSPI_Empty(t *testing.T) { buf := bytes.Buffer{} - o := Opts{NumPixels: 150} - o.NumPixels = 0 - d, _ := NewSPI(spitest.NewRecordRaw(&buf), &o) - if n, err := d.Write([]byte{}); n != 0 || err != nil { - t.Fatalf("%d %v", n, err) + o := Opts{NumPixels: 0, Channels: 3, Freq: 2500 * physic.KiloHertz} + s := spitest.Playback{ + Playback: conntest.Playback{ + Count: 1, + Ops: []conntest.IO{{W: []byte{0x00, 0x00, 0x00}}}, + }, } - if expected := []byte{0x0, 0x0, 0x0}; !bytes.Equal(expected, buf.Bytes()) { - t.Fatalf("\nGot: %#02v\nWant: %#02v\n", buf.Bytes(), expected) + d, err := NewSPI(spitest.NewRecordRaw(&buf), &o) + if err != nil { + t.Fatal(err) } - if got, expected := d.String(), "nrzled: {0, recordraw}"; got != expected { + if got, expected := d.String(), "nrzled{recordraw}"; got != expected { t.Fatalf("\nGot: %s\nWant: %s\n", got, expected) } + + if n, err := d.Write([]byte{}); n != 0 || err != nil { + t.Fatalf("%d %v", n, err) + } + if err := s.Close(); err != nil { + t.Fatal(err) + } } -func TestConnectFail(t *testing.T) { - if d, err := NewSPI(&configFail{}, &Opts{NumPixels: 150}); d != nil || err == nil { +func TestSPI_fail(t *testing.T) { + buf := bytes.Buffer{} + o := Opts{NumPixels: 1, Channels: 3, Freq: 1 * physic.KiloHertz} + if _, err := NewSPI(spitest.NewRecordRaw(&buf), &o); err == nil { + t.Fatal("invalid Freq") + } + + o = Opts{NumPixels: 1, Channels: 0, Freq: 2500 * physic.KiloHertz} + if _, err := NewSPI(spitest.NewRecordRaw(&buf), &o); err == nil { + t.Fatal("invalid Channels") + } + + o = Opts{NumPixels: 150, Channels: 3, Freq: 2500 * physic.KiloHertz} + if d, err := NewSPI(&configFail{}, &o); d != nil || err == nil { t.Fatal("Connect() call have failed") } + + o = Opts{NumPixels: 150, Channels: 3, Freq: 2500 * physic.KiloHertz} + if d, err := NewSPI(&limitLow{}, &o); d != nil || err == nil { + t.Fatal("MaxTxSize() is too small") + } } -func TestDevLen(t *testing.T) { +func TestSPI_Len(t *testing.T) { buf := bytes.Buffer{} - o := Opts{NumPixels: 150} - o.NumPixels = 1 - d, _ := NewSPI(spitest.NewRecordRaw(&buf), &o) + o := Opts{NumPixels: 1, Channels: 3, Freq: 2500 * physic.KiloHertz} + d, err := NewSPI(spitest.NewRecordRaw(&buf), &o) + if err != nil { + t.Fatal(err) + } if n, err := d.Write([]byte{0}); n != 0 || err == nil { t.Fatalf("%d %v", n, err) } @@ -73,7 +101,7 @@ var writeTests = []struct { }{ { name: "1 pixel to #FFFFFF", - pixels: ToRGB([]color.NRGBA{ + pixels: toRGB([]color.NRGBA{ {0xFF, 0xFF, 0xFF, 0x00}, }), want: []byte{ @@ -82,11 +110,13 @@ var writeTests = []struct { }, opts: Opts{ NumPixels: 1, + Channels: 3, + Freq: 2500 * physic.KiloHertz, }, }, { name: "1 pixel to #FEFEFE", - pixels: ToRGB([]color.NRGBA{ + pixels: toRGB([]color.NRGBA{ {0xFE, 0xFE, 0xFE, 0x00}, }), want: []byte{ @@ -95,11 +125,13 @@ var writeTests = []struct { }, opts: Opts{ NumPixels: 1, + Channels: 3, + Freq: 2500 * physic.KiloHertz, }, }, { name: "1 pixel to #F0F0F0", - pixels: ToRGB([]color.NRGBA{ + pixels: toRGB([]color.NRGBA{ {0xF0, 0xF0, 0xF0, 0x00}, }), want: []byte{ @@ -108,11 +140,13 @@ var writeTests = []struct { }, opts: Opts{ NumPixels: 1, + Channels: 3, + Freq: 2500 * physic.KiloHertz, }, }, { name: "1 pixel to #808080", - pixels: ToRGB([]color.NRGBA{ + pixels: toRGB([]color.NRGBA{ {0x80, 0x80, 0x80, 0x00}, }), want: []byte{ @@ -121,11 +155,13 @@ var writeTests = []struct { }, opts: Opts{ NumPixels: 1, + Channels: 3, + Freq: 2500 * physic.KiloHertz, }, }, { name: "1 pixel to #80FF00", - pixels: ToRGB([]color.NRGBA{ + pixels: toRGB([]color.NRGBA{ {0x80, 0xFF, 0x00, 0x00}, }), want: []byte{ @@ -134,11 +170,13 @@ var writeTests = []struct { }, opts: Opts{ NumPixels: 1, + Channels: 3, + Freq: 2500 * physic.KiloHertz, }, }, { name: "1 pixel to #800000", - pixels: ToRGB([]color.NRGBA{ + pixels: toRGB([]color.NRGBA{ {0x80, 0x00, 0x00, 0x00}, }), want: []byte{ @@ -147,11 +185,13 @@ var writeTests = []struct { }, opts: Opts{ NumPixels: 1, + Channels: 3, + Freq: 2500 * physic.KiloHertz, }, }, { name: "1 pixel to #008000", - pixels: ToRGB([]color.NRGBA{ + pixels: toRGB([]color.NRGBA{ {0x00, 0x80, 0x00, 0x00}, }), want: []byte{ @@ -160,11 +200,13 @@ var writeTests = []struct { }, opts: Opts{ NumPixels: 1, + Channels: 3, + Freq: 2500 * physic.KiloHertz, }, }, { name: "1 pixel to #000080", - pixels: ToRGB([]color.NRGBA{ + pixels: toRGB([]color.NRGBA{ {0x00, 0x00, 0x80, 0x00}, }), want: []byte{ @@ -173,11 +215,13 @@ var writeTests = []struct { }, opts: Opts{ NumPixels: 1, + Channels: 3, + Freq: 2500 * physic.KiloHertz, }, }, { name: "All at once", - pixels: ToRGB([]color.NRGBA{ + pixels: toRGB([]color.NRGBA{ {0xFF, 0xFF, 0xFF, 0x00}, {0xFE, 0xFE, 0xFE, 0x00}, {0xF0, 0xF0, 0xF0, 0x00}, @@ -208,15 +252,20 @@ var writeTests = []struct { }, opts: Opts{ NumPixels: 10, + Channels: 3, + Freq: 2500 * physic.KiloHertz, }, }, } -func TestWrites(t *testing.T) { +func TestSPI_Writes(t *testing.T) { for _, tt := range writeTests { buf := bytes.Buffer{} tt.opts.NumPixels = len(tt.pixels) / 3 - d, _ := NewSPI(spitest.NewRecordRaw(&buf), &tt.opts) + d, err := NewSPI(spitest.NewRecordRaw(&buf), &tt.opts) + if err != nil { + t.Fatal(err) + } n, err := d.Write(tt.pixels) if err != nil { t.Fatal(err) @@ -236,19 +285,21 @@ func TestWrites(t *testing.T) { } } -func TestDevColor(t *testing.T) { +func TestSPI_Color(t *testing.T) { if c := (&Dev{}).ColorModel(); c != color.NRGBAModel { t.Fatal(c) } } -func TestDevLong(t *testing.T) { +func TestSPI_Long(t *testing.T) { buf := bytes.Buffer{} colors := make([]color.NRGBA, 256) - o := Opts{NumPixels: 150} - o.NumPixels = len(colors) - d, _ := NewSPI(spitest.NewRecordRaw(&buf), &o) - if n, err := d.Write(ToRGB(colors)); n != len(colors)*3 || err != nil { + o := Opts{NumPixels: len(colors), Channels: 3, Freq: 2500 * physic.KiloHertz} + d, err := NewSPI(spitest.NewRecordRaw(&buf), &o) + if err != nil { + t.Fatal(err) + } + if n, err := d.Write(toRGB(colors)); n != len(colors)*3 || err != nil { t.Fatalf("%d %v", n, err) } expected := make([]byte, 12*o.NumPixels+3) @@ -267,11 +318,13 @@ func TestDevLong(t *testing.T) { } } -func TestDevWrite_Long(t *testing.T) { +func TestSPI_Write_Long(t *testing.T) { buf := bytes.Buffer{} - o := Opts{NumPixels: 150} - o.NumPixels = 1 - d, _ := NewSPI(spitest.NewRecordRaw(&buf), &o) + o := Opts{NumPixels: 1, Channels: 3, Freq: 2500 * physic.KiloHertz} + d, err := NewSPI(spitest.NewRecordRaw(&buf), &o) + if err != nil { + t.Fatal(err) + } if n, err := d.Write([]byte{0, 0, 0, 1, 1, 1}); n != 0 || err == nil { t.Fatal(n, err) } @@ -309,6 +362,8 @@ var drawTests = []struct { }(), opts: Opts{ NumPixels: 4, + Channels: 3, + Freq: 2500 * physic.KiloHertz, }, }, { @@ -323,14 +378,19 @@ var drawTests = []struct { }(), opts: Opts{ NumPixels: 4, + Channels: 3, + Freq: 2500 * physic.KiloHertz, }, }, } -func TestDraws(t *testing.T) { +func TestSPI_Draws(t *testing.T) { for _, tt := range drawTests { buf := bytes.Buffer{} - d, _ := NewSPI(spitest.NewRecordRaw(&buf), &tt.opts) + d, err := NewSPI(spitest.NewRecordRaw(&buf), &tt.opts) + if err != nil { + t.Fatal(err) + } if err := d.Draw(d.Bounds(), tt.img, image.Point{}); err != nil { t.Fatalf("%s: %v", tt.name, err) } @@ -347,6 +407,19 @@ func TestDraws(t *testing.T) { } } +func TestSPI_Draw_DstEmpty(t *testing.T) { + buf := bytes.Buffer{} + o := Opts{NumPixels: 4, Channels: 3, Freq: 2500 * physic.KiloHertz} + d, err := NewSPI(spitest.NewRecordRaw(&buf), &o) + if err != nil { + t.Fatal(err) + } + img := image.NewNRGBA(image.Rect(0, 0, 1, 1)) + if err := d.Draw(image.Rect(0, 0, 0, 0), img, image.Point{}); err != nil { + t.Fatal(err) + } +} + var offsetDrawWant = []byte{ 0x00, 0x00, 0x00, 0x00, 0xE1, 0x89, 0x79, 0x6B, @@ -392,6 +465,8 @@ var offsetDrawTests = []struct { want: offsetDrawWant, opts: Opts{ NumPixels: 15, + Channels: 3, + Freq: 2500 * physic.KiloHertz, }, }, { @@ -431,15 +506,16 @@ var offsetDrawTests = []struct { }, opts: Opts{ NumPixels: 17, + Channels: 3, + Freq: 2500 * physic.KiloHertz, }, }, } -func TestHalt(t *testing.T) { +func TestSPI_Halt(t *testing.T) { s := spitest.Playback{ Playback: conntest.Playback{ - DontPanic: false, - Count: 1, + Count: 1, Ops: []conntest.IO{ {}, {W: []byte{ @@ -453,9 +529,11 @@ func TestHalt(t *testing.T) { }, }, } - o := Opts{NumPixels: 150} - o.NumPixels = 4 - d, _ := NewSPI(&s, &o) + o := Opts{NumPixels: 4, Channels: 3, Freq: 2500 * physic.KiloHertz} + d, err := NewSPI(&s, &o) + if err != nil { + t.Fatal(err) + } if err := d.Halt(); err != nil { t.Fatal(err) } @@ -464,9 +542,24 @@ func TestHalt(t *testing.T) { } } +func TestSPI_Halt_fail(t *testing.T) { + s := spitest.Playback{Playback: conntest.Playback{DontPanic: true}} + o := Opts{NumPixels: 4, Channels: 3, Freq: 2500 * physic.KiloHertz} + d, err := NewSPI(&s, &o) + if err != nil { + t.Fatal(err) + } + if d.Halt() == nil { + t.Fatal("expected failure") + } + if err := s.Close(); err != nil { + t.Fatal(err) + } +} + type genColor func(int) [3]byte -func benchmarkWrite(b *testing.B, o Opts, length int, f genColor) { +func benchmarkSPIWrite(b *testing.B, o Opts, length int, f genColor) { var pixels []byte for i := 0; i < length; i++ { c := f(i) @@ -474,27 +567,34 @@ func benchmarkWrite(b *testing.B, o Opts, length int, f genColor) { } o.NumPixels = length b.ReportAllocs() - d, _ := NewSPI(spitest.NewRecordRaw(ioutil.Discard), &o) - _, _ = d.Write(pixels[:]) + d, err := NewSPI(spitest.NewRecordRaw(ioutil.Discard), &o) + if err != nil { + b.Fatal(err) + } + if _, err := d.Write(pixels[:]); err != nil { + b.Fatal(err) + } b.ResetTimer() for i := 0; i < b.N; i++ { - _, _ = d.Write(pixels[:]) + if _, err = d.Write(pixels[:]); err != nil { + b.Fatal(err) + } } } -func BenchmarkWriteWhite(b *testing.B) { - o := Opts{NumPixels: 150} - benchmarkWrite(b, o, 150, func(i int) [3]byte { return [3]byte{0xFF, 0xFF, 0xFF} }) +func BenchmarkSPI_WriteWhite(b *testing.B) { + o := Opts{NumPixels: 150, Channels: 3, Freq: 2500 * physic.KiloHertz} + benchmarkSPIWrite(b, o, 150, func(i int) [3]byte { return [3]byte{0xFF, 0xFF, 0xFF} }) } -func BenchmarkWriteDim(b *testing.B) { - o := Opts{NumPixels: 150} - benchmarkWrite(b, o, 150, func(i int) [3]byte { return [3]byte{0x01, 0x01, 0x01} }) +func BenchmarkSPI_WriteDim(b *testing.B) { + o := Opts{NumPixels: 150, Channels: 3, Freq: 2500 * physic.KiloHertz} + benchmarkSPIWrite(b, o, 150, func(i int) [3]byte { return [3]byte{0x01, 0x01, 0x01} }) } -func BenchmarkWriteBlack(b *testing.B) { - o := Opts{NumPixels: 150} - benchmarkWrite(b, o, 150, func(i int) [3]byte { return [3]byte{0x0, 0x0, 0x0} }) +func BenchmarkSPI_WriteBlack(b *testing.B) { + o := Opts{NumPixels: 150, Channels: 3, Freq: 2500 * physic.KiloHertz} + benchmarkSPIWrite(b, o, 150, func(i int) [3]byte { return [3]byte{0x0, 0x0, 0x0} }) } func genColorfulPixel(x int) [3]byte { @@ -505,36 +605,40 @@ func genColorfulPixel(x int) [3]byte { } } -func BenchmarkWriteColorful(b *testing.B) { - o := Opts{NumPixels: 150} - benchmarkWrite(b, o, 150, genColorfulPixel) +func BenchmarkSPI_WriteColorful(b *testing.B) { + o := Opts{NumPixels: 150, Channels: 3, Freq: 2500 * physic.KiloHertz} + benchmarkSPIWrite(b, o, 150, genColorfulPixel) } -func BenchmarkWriteColorfulPassThru(b *testing.B) { - o := Opts{ - NumPixels: 150, - } - benchmarkWrite(b, o, 150, genColorfulPixel) +func BenchmarkSPI_WriteColorfulPassThru(b *testing.B) { + o := Opts{NumPixels: 150, Channels: 3, Freq: 2500 * physic.KiloHertz} + benchmarkSPIWrite(b, o, 150, genColorfulPixel) } -func BenchmarkWriteColorfulVariation(b *testing.B) { +func BenchmarkSPI_WriteColorfulVariation(b *testing.B) { // Continuously vary the lookup tables. b.ReportAllocs() pixels := [256 * 3]byte{} for i := range pixels { pixels[i] = uint8(i) + uint8(i>>8) } - o := Opts{NumPixels: 150} - o.NumPixels = len(pixels) / 3 - d, _ := NewSPI(spitest.NewRecordRaw(ioutil.Discard), &o) - _, _ = d.Write(pixels[:]) + o := Opts{NumPixels: len(pixels) / 3, Channels: 3, Freq: 2500 * physic.KiloHertz} + d, err := NewSPI(spitest.NewRecordRaw(ioutil.Discard), &o) + if err != nil { + b.Fatal(err) + } + if _, err = d.Write(pixels[:]); err != nil { + b.Fatal(err) + } b.ResetTimer() for i := 0; i < b.N; i++ { - _, _ = d.Write(pixels[:]) + if _, err = d.Write(pixels[:]); err != nil { + b.Fatal(err) + } } } -func benchmarkDraw(b *testing.B, o Opts, img draw.Image, f genColor) { +func benchmarkSPIDraw(b *testing.B, o Opts, img draw.Image, f genColor) { for x := 0; x < img.Bounds().Dx(); x++ { for y := 0; y < img.Bounds().Dy(); y++ { pix := f(x) @@ -558,22 +662,22 @@ func benchmarkDraw(b *testing.B, o Opts, img draw.Image, f genColor) { } } -func BenchmarkDrawNRGBAColorful(b *testing.B) { - o := Opts{NumPixels: 150} - benchmarkDraw(b, o, image.NewNRGBA(image.Rect(0, 0, 150, 1)), genColorfulPixel) +func BenchmarkSPI_DrawNRGBAColorful(b *testing.B) { + o := Opts{NumPixels: 150, Channels: 3, Freq: 2500 * physic.KiloHertz} + benchmarkSPIDraw(b, o, image.NewNRGBA(image.Rect(0, 0, 150, 1)), genColorfulPixel) } -func BenchmarkDrawNRGBAWhite(b *testing.B) { - o := Opts{NumPixels: 150} - benchmarkDraw(b, o, image.NewNRGBA(image.Rect(0, 0, 150, 1)), func(i int) [3]byte { return [3]byte{0xFF, 0xFF, 0xFF} }) +func BenchmarkSPI_DrawNRGBAWhite(b *testing.B) { + o := Opts{NumPixels: 150, Channels: 3, Freq: 2500 * physic.KiloHertz} + benchmarkSPIDraw(b, o, image.NewNRGBA(image.Rect(0, 0, 150, 1)), func(i int) [3]byte { return [3]byte{0xFF, 0xFF, 0xFF} }) } func BenchmarkDrawRGBAColorful(b *testing.B) { - o := Opts{NumPixels: 150} - benchmarkDraw(b, o, image.NewRGBA(image.Rect(0, 0, 256, 1)), genColorfulPixel) + o := Opts{NumPixels: 150, Channels: 3, Freq: 2500 * physic.KiloHertz} + benchmarkSPIDraw(b, o, image.NewRGBA(image.Rect(0, 0, 256, 1)), genColorfulPixel) } -func BenchmarkDrawSlowpath(b *testing.B) { +func BenchmarkSPI_DrawSlowpath(b *testing.B) { // Should be an image type that doesn't have a fast path img := image.NewGray(image.Rect(0, 0, 150, 1)) for x := 0; x < img.Bounds().Dx(); x++ { @@ -582,10 +686,12 @@ func BenchmarkDrawSlowpath(b *testing.B) { img.Set(x, y, color.Gray{pix[0]}) } } - o := Opts{NumPixels: 150} - o.NumPixels = img.Bounds().Max.X + o := Opts{NumPixels: img.Bounds().Max.X, Channels: 3, Freq: 2500 * physic.KiloHertz} b.ReportAllocs() - d, _ := NewSPI(spitest.NewRecordRaw(ioutil.Discard), &o) + d, err := NewSPI(spitest.NewRecordRaw(ioutil.Discard), &o) + if err != nil { + b.Fatal(err) + } r := d.Bounds() p := image.Point{} if err := d.Draw(r, img, p); err != nil { @@ -599,9 +705,13 @@ func BenchmarkDrawSlowpath(b *testing.B) { } } -func BenchmarkHalt(b *testing.B) { +func BenchmarkSPI_Halt(b *testing.B) { b.ReportAllocs() - d, _ := NewSPI(spitest.NewRecordRaw(ioutil.Discard), &Opts{NumPixels: 150}) + o := &Opts{NumPixels: 150, Channels: 3, Freq: 2500 * physic.KiloHertz} + d, err := NewSPI(spitest.NewRecordRaw(ioutil.Discard), o) + if err != nil { + b.Fatal(err) + } b.ResetTimer() for i := 0; i < b.N; i++ { if err := d.Halt(); err != nil { @@ -620,6 +730,14 @@ func (c *configFail) Connect(f physic.Frequency, mode spi.Mode, bits int) (spi.C return nil, errors.New("injected error") } +type limitLow struct { + spitest.Record +} + +func (c *limitLow) MaxTxSize() int { + return 1 +} + func equalUint16(a, b []uint16) bool { if len(a) != len(b) { return false diff --git a/experimental/devices/nrzled/nrzled_test.go b/experimental/devices/nrzled/nrzled_stream_test.go similarity index 87% rename from experimental/devices/nrzled/nrzled_test.go rename to experimental/devices/nrzled/nrzled_stream_test.go index b312da6..6355bdd 100644 --- a/experimental/devices/nrzled/nrzled_test.go +++ b/experimental/devices/nrzled/nrzled_stream_test.go @@ -15,7 +15,7 @@ import ( "periph.io/x/periph/conn/physic" ) -func TestNew_3(t *testing.T) { +func TestStream_NewBits_3(t *testing.T) { g := gpiostreamtest.PinOutPlayback{ N: "Yo", Ops: []gpiostream.Stream{ @@ -27,6 +27,7 @@ func TestNew_3(t *testing.T) { 0x92, 0x49, 0x24, 0x92, 0x49, 0x24, 0x92, 0x49, 0x24, 0x92, 0x49, 0x24, 0x92, 0x49, 0x24, 0x92, 0x49, 0x24, 0x92, 0x49, 0x24, 0x92, 0x49, 0x24, 0x92, 0x49, 0x24, 0x92, 0x49, 0x24, 0x92, 0x49, 0x24, 0x92, 0x49, 0x24, 0x92, 0x49, 0x24, 0x92, 0x49, 0x24, + 0x00, 0x00, 0x00, }, Freq: 800 * physic.KiloHertz, LSBF: false, @@ -35,7 +36,7 @@ func TestNew_3(t *testing.T) { } opts := DefaultOpts opts.NumPixels = 10 - d, err := New(&g, &opts) + d, err := NewStream(&g, &opts) if err != nil { t.Fatal(err) } @@ -56,21 +57,21 @@ func TestNew_3(t *testing.T) { } } -func TestNew_fail(t *testing.T) { +func TestStream_New_fail(t *testing.T) { g := gpiostreamtest.PinOutPlayback{} opts := DefaultOpts opts.Freq = 0 - if _, err := New(&g, &opts); err == nil { + if _, err := NewStream(&g, &opts); err == nil { t.Fatal("hz == 0") } opts = DefaultOpts opts.Channels = 2 - if _, err := New(&g, &opts); err == nil { + if _, err := NewStream(&g, &opts); err == nil { t.Fatal("channels == 2") } } -func TestDraw_NRGBA_3(t *testing.T) { +func TestStream_Draw_NRGBA_3(t *testing.T) { g := gpiostreamtest.PinOutPlayback{ Ops: []gpiostream.Stream{ &gpiostream.BitStream{ @@ -81,6 +82,7 @@ func TestDraw_NRGBA_3(t *testing.T) { 0xdb, 0x6d, 0xb6, 0xdb, 0x6d, 0xb6, 0xdb, 0x6d, 0xb6, 0xdb, 0x6d, 0xb6, 0x92, 0x49, 0x24, 0xdb, 0x6d, 0xb6, 0xdb, 0x6d, 0xb6, 0xdb, 0x6d, 0xb6, 0xdb, 0x6d, 0xb6, 0xdb, 0x6d, 0xb6, 0xdb, 0x6d, 0xb6, 0x92, 0x49, 0xb6, 0x92, 0x49, 0xb4, 0x92, 0x4d, 0x24, + 0x00, 0x00, 0x00, }, Freq: 800 * physic.KiloHertz, LSBF: false, @@ -89,7 +91,10 @@ func TestDraw_NRGBA_3(t *testing.T) { } opts := DefaultOpts opts.NumPixels = 10 - d, _ := New(&g, &opts) + d, err := NewStream(&g, &opts) + if err != nil { + t.Fatal(err) + } img := image.NewNRGBA(d.Bounds()) copy(img.Pix, getRGBW()) if err := d.Draw(d.Bounds(), img, image.Point{}); err != nil { @@ -100,7 +105,7 @@ func TestDraw_NRGBA_3(t *testing.T) { } } -func TestDraw_RGBA_3(t *testing.T) { +func TestStream_Draw_RGBA_3(t *testing.T) { g := gpiostreamtest.PinOutPlayback{ Ops: []gpiostream.Stream{ &gpiostream.BitStream{ @@ -111,6 +116,7 @@ func TestDraw_RGBA_3(t *testing.T) { 0xdb, 0x6d, 0xb6, 0xdb, 0x6d, 0xb6, 0xdb, 0x6d, 0xb6, 0xdb, 0x6d, 0xb6, 0x92, 0x49, 0x24, 0xdb, 0x6d, 0xb6, 0xdb, 0x6d, 0xb6, 0xdb, 0x6d, 0xb6, 0xdb, 0x6d, 0xa6, 0xdb, 0x6d, 0xa6, 0xdb, 0x6d, 0xa6, 0xda, 0x49, 0xb6, 0xd3, 0x4d, 0x34, 0xdb, 0x49, 0x36, + 0x00, 0x00, 0x00, }, Freq: 800 * physic.KiloHertz, LSBF: false, @@ -119,7 +125,10 @@ func TestDraw_RGBA_3(t *testing.T) { } opts := DefaultOpts opts.NumPixels = 10 - d, _ := New(&g, &opts) + d, err := NewStream(&g, &opts) + if err != nil { + t.Fatal(err) + } img := image.NewRGBA(d.Bounds()) copy(img.Pix, getRGBW()) if err := d.Draw(d.Bounds(), img, image.Point{}); err != nil { @@ -130,7 +139,7 @@ func TestDraw_RGBA_3(t *testing.T) { } } -func TestDraw_RGBA_4(t *testing.T) { +func TestStream_Draw_RGBA_4(t *testing.T) { g := gpiostreamtest.PinOutPlayback{ Ops: []gpiostream.Stream{ &gpiostream.BitStream{ @@ -143,6 +152,7 @@ func TestDraw_RGBA_4(t *testing.T) { 0x24, 0xdb, 0x6d, 0xb6, 0xdb, 0x6d, 0xb6, 0xdb, 0x6d, 0xb6, 0xdb, 0x6d, 0xb6, 0xdb, 0x6d, 0xb6, 0xdb, 0x6d, 0xa6, 0xdb, 0x6d, 0xa6, 0xdb, 0x6d, 0xa6, 0xd2, 0x49, 0x24, 0xda, 0x49, 0xb6, 0xd3, 0x4d, 0x34, 0xdb, 0x49, 0x36, 0x92, 0x4d, 0x26, + 0x00, 0x00, 0x00, }, Freq: 800 * physic.KiloHertz, LSBF: false, @@ -152,7 +162,10 @@ func TestDraw_RGBA_4(t *testing.T) { opts := DefaultOpts opts.NumPixels = 10 opts.Channels = 4 - d, _ := New(&g, &opts) + d, err := NewStream(&g, &opts) + if err != nil { + t.Fatal(err) + } img := image.NewRGBA(d.Bounds()) copy(img.Pix, getRGBW()) if err := d.Draw(d.Bounds(), img, image.Point{}); err != nil { @@ -163,7 +176,7 @@ func TestDraw_RGBA_4(t *testing.T) { } } -func TestDraw_Limits(t *testing.T) { +func TestStream_Draw_Limits(t *testing.T) { g := gpiostreamtest.PinOutPlayback{ Ops: []gpiostream.Stream{ &gpiostream.BitStream{ @@ -174,6 +187,7 @@ func TestDraw_Limits(t *testing.T) { 0xdb, 0x6d, 0xb6, 0xdb, 0x6d, 0xb6, 0xdb, 0x6d, 0xb6, 0xdb, 0x6d, 0xb6, 0x92, 0x49, 0x24, 0xdb, 0x6d, 0xb6, 0xdb, 0x6d, 0xb6, 0xdb, 0x6d, 0xb6, 0xdb, 0x6d, 0xa6, 0xdb, 0x6d, 0xa6, 0xdb, 0x6d, 0xa6, 0xda, 0x49, 0xb6, 0xd3, 0x4d, 0x34, 0xdb, 0x49, 0x36, + 0x00, 0x00, 0x00, }, Freq: 800 * physic.KiloHertz, LSBF: false, @@ -182,7 +196,10 @@ func TestDraw_Limits(t *testing.T) { } opts := DefaultOpts opts.NumPixels = 10 - d, _ := New(&g, &opts) + d, err := NewStream(&g, &opts) + if err != nil { + t.Fatal(err) + } img := image.NewRGBA(image.Rect(-1, -1, 20, 20)) copy(img.Pix, getRGBW()) if err := d.Draw(d.Bounds(), img, image.Point{}); err != nil { @@ -193,7 +210,7 @@ func TestDraw_Limits(t *testing.T) { } } -func TestWrite_3(t *testing.T) { +func TestStream_Write_3(t *testing.T) { g := gpiostreamtest.PinOutPlayback{ Ops: []gpiostream.Stream{ &gpiostream.BitStream{ @@ -204,6 +221,7 @@ func TestWrite_3(t *testing.T) { 0xdb, 0x6d, 0xb6, 0xdb, 0x6d, 0xb6, 0xdb, 0x6d, 0xb6, 0xdb, 0x6d, 0xb6, 0x92, 0x49, 0x24, 0xdb, 0x6d, 0xb6, 0xdb, 0x6d, 0xb6, 0xdb, 0x6d, 0xb6, 0x92, 0x49, 0xa4, 0x92, 0x49, 0x36, 0x92, 0x49, 0xa6, 0x92, 0x49, 0xb6, 0x92, 0x49, 0xb4, 0x92, 0x4d, 0x24, + 0x00, 0x00, 0x00, }, Freq: 800 * physic.KiloHertz, LSBF: false, @@ -212,7 +230,10 @@ func TestWrite_3(t *testing.T) { } opts := DefaultOpts opts.NumPixels = 10 - d, _ := New(&g, &opts) + d, err := NewStream(&g, &opts) + if err != nil { + t.Fatal(err) + } if n, err := d.Write(getRGB()); n != 30 || err != nil { t.Fatal(n, err) } @@ -221,11 +242,14 @@ func TestWrite_3(t *testing.T) { } } -func TestWrite_fail(t *testing.T) { +func TestStream_Write_fail(t *testing.T) { g := gpiostreamtest.PinOutPlayback{DontPanic: true} opts := DefaultOpts opts.NumPixels = 10 - d, _ := New(&g, &opts) + d, err := NewStream(&g, &opts) + if err != nil { + t.Fatal(err) + } if n, err := d.Write([]byte{1}); n != 0 || err == nil { t.Fatal(n, err) } @@ -237,11 +261,14 @@ func TestWrite_fail(t *testing.T) { } } -func TestHalt_fail(t *testing.T) { +func TestStream_Halt_fail(t *testing.T) { g := gpiostreamtest.PinOutPlayback{DontPanic: true} opts := DefaultOpts opts.NumPixels = 10 - d, _ := New(&g, &opts) + d, err := NewStream(&g, &opts) + if err != nil { + t.Fatal(err) + } if d.Halt() == nil { t.Fatal("expected failure") } @@ -250,7 +277,7 @@ func TestHalt_fail(t *testing.T) { } } -func TestRaster_3_3(t *testing.T) { +func TestStream_Raster_3_3(t *testing.T) { data := []byte{ // 24 bits per pixel in RGB 0, 1, 2, @@ -262,13 +289,13 @@ func TestRaster_3_3(t *testing.T) { 0xdb, 0x6d, 0xb4, 0xdb, 0x6d, 0xa6, 0xdb, 0x6d, 0xb6, } actual := make([]byte, len(expected)) - raster(actual, data, 3, 3) + rasterBits(actual, data, 3, 3) if !bytes.Equal(expected, actual) { t.Fatalf("\nexpected %#v\n actual %#v", expected, actual) } } -func TestRaster_4_4(t *testing.T) { +func TestStream_Raster_4_4(t *testing.T) { data := []byte{ // 32 bits per pixel in RGBW 0, 1, 2, 3, @@ -280,7 +307,7 @@ func TestRaster_4_4(t *testing.T) { 0xdb, 0x6d, 0xa6, 0xdb, 0x6d, 0xa4, 0xdb, 0x6d, 0xb4, 0xdb, 0x6d, 0xb6, } actual := make([]byte, len(expected)) - raster(actual, data, 4, 4) + rasterBits(actual, data, 4, 4) if !bytes.Equal(expected, actual) { t.Fatalf("\nexpected %#v\n actual %#v", expected, actual) }