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%.
pull/1/head
Marc-Antoine Ruel 8 years ago
parent b930b20a2d
commit 34b5f2bfcb

@ -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{}

@ -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,43 +49,96 @@ 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,
},
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
// 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)
@ -96,6 +146,16 @@ func (d *Dev) Halt() error {
return 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
}
// ColorModel implements display.Drawer.
//
// It is color.NRGBAModel.
@ -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 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
}
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{}

@ -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)
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}}},
},
}
d, err := NewSPI(spitest.NewRecordRaw(&buf), &o)
if err != nil {
t.Fatal(err)
}
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 expected := []byte{0x0, 0x0, 0x0}; !bytes.Equal(expected, buf.Bytes()) {
t.Fatalf("\nGot: %#02v\nWant: %#02v\n", buf.Bytes(), expected)
if err := s.Close(); err != nil {
t.Fatal(err)
}
if got, expected := d.String(), "nrzled: {0, recordraw}"; got != expected {
t.Fatalf("\nGot: %s\nWant: %s\n", got, expected)
}
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")
}
func TestConnectFail(t *testing.T) {
if d, err := NewSPI(&configFail{}, &Opts{NumPixels: 150}); d != nil || err == nil {
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,14 +506,15 @@ 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,
Ops: []conntest.IO{
{},
@ -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

@ -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)
}
Loading…
Cancel
Save