apa102: minor tweaks (#158)

- Disallow writing a buffer longer than the number used. Silently
  dropping data is always annoying to debug. Clear failure is better.
- Rename private variables and document them better.
- Replace 'lights' by 'pixels'. This is more precise.
pull/1/head
M-A 9 years ago committed by GitHub
parent e6e8b5e2b3
commit 3d1aec5647

@ -218,15 +218,15 @@ func ToRGB(p []color.NRGBA) []byte {
type Dev struct { type Dev struct {
Intensity uint8 // Set an intensity between 0 (off) and 255 (full brightness). Intensity uint8 // Set an intensity between 0 (off) and 255 (full brightness).
Temperature uint16 // In Kelvin. Temperature uint16 // In Kelvin.
s spi.Conn s spi.Conn //
l lut // Updated at each .Write() call. l lut // Updated at each .Write() call.
numLights int numPixels int //
rawBuf []byte rawBuf []byte // Raw buffer sent over SPI. Cached to reduce heap fragmentation.
pixels []byte pixels []byte // Double buffer of pixels, to enable partial painting via Draw(). Effectively points inside rawBuf.
} }
func (d *Dev) String() string { func (d *Dev) String() string {
return fmt.Sprintf("APA102{I:%d, T:%dK, %dLEDs, %s}", d.Intensity, d.Temperature, d.numLights, d.s) return fmt.Sprintf("APA102{I:%d, T:%dK, %dLEDs, %s}", d.Intensity, d.Temperature, d.numPixels, d.s)
} }
// ColorModel implements devices.Display. There's no surprise, it is // ColorModel implements devices.Display. There's no surprise, it is
@ -237,7 +237,7 @@ func (d *Dev) ColorModel() color.Model {
// Bounds implements devices.Display. Min is guaranteed to be {0, 0}. // Bounds implements devices.Display. Min is guaranteed to be {0, 0}.
func (d *Dev) Bounds() image.Rectangle { func (d *Dev) Bounds() image.Rectangle {
return image.Rectangle{Max: image.Point{X: d.numLights, Y: 1}} return image.Rectangle{Max: image.Point{X: d.numPixels, Y: 1}}
} }
// Draw implements devices.Display. // Draw implements devices.Display.
@ -262,14 +262,10 @@ func (d *Dev) Draw(r image.Rectangle, src image.Image, sp image.Point) {
// Write accepts a stream of raw RGB pixels and sends it as APA102 encoded // Write accepts a stream of raw RGB pixels and sends it as APA102 encoded
// stream. // stream.
func (d *Dev) Write(pixels []byte) (int, error) { func (d *Dev) Write(pixels []byte) (int, error) {
if len(pixels)%3 != 0 { if len(pixels)%3 != 0 || len(pixels) > len(d.pixels) {
return 0, errLength return 0, errors.New("apa102: invalid RGB stream length")
} }
d.l.init(d.Intensity, d.Temperature) d.l.init(d.Intensity, d.Temperature)
// Trying to write more pixels than defined?
if o := d.numLights; o < len(pixels)/3 {
pixels = pixels[:o*3]
}
// Do not touch header and footer. // Do not touch header and footer.
d.l.raster(d.pixels, pixels) d.l.raster(d.pixels, pixels)
err := d.s.Tx(d.rawBuf, nil) err := d.s.Tx(d.rawBuf, nil)
@ -278,7 +274,7 @@ func (d *Dev) Write(pixels []byte) (int, error) {
// Halt turns off all the lights. // Halt turns off all the lights.
func (d *Dev) Halt() error { func (d *Dev) Halt() error {
_, err := d.Write(make([]byte, d.numLights*3)) _, err := d.Write(make([]byte, d.numPixels*3))
return err return err
} }
@ -293,7 +289,7 @@ func (d *Dev) Halt() error {
// As per APA102-C spec, the chip's max refresh rate is 400hz. // As per APA102-C spec, the chip's max refresh rate is 400hz.
// https://en.wikipedia.org/wiki/Flicker_fusion_threshold is a recommended // https://en.wikipedia.org/wiki/Flicker_fusion_threshold is a recommended
// reading. // reading.
func New(p spi.Port, numLights int, intensity uint8, temperature uint16) (*Dev, error) { func New(p spi.Port, numPixels int, intensity uint8, temperature uint16) (*Dev, error) {
c, err := p.Connect(20000000, spi.Mode3, 8) c, err := p.Connect(20000000, spi.Mode3, 8)
if err != nil { if err != nil {
return nil, err return nil, err
@ -301,8 +297,8 @@ func New(p spi.Port, numLights int, intensity uint8, temperature uint16) (*Dev,
// End frames are needed to be able to push enough SPI clock signals due to // End frames are needed to be able to push enough SPI clock signals due to
// internal half-delay of data signal from each individual LED. See // internal half-delay of data signal from each individual LED. See
// https://cpldcpu.wordpress.com/2014/11/30/understanding-the-apa102-superled/ // https://cpldcpu.wordpress.com/2014/11/30/understanding-the-apa102-superled/
buf := make([]byte, 4*(numLights+1)+numLights/2/8+1) buf := make([]byte, 4*(numPixels+1)+numPixels/2/8+1)
tail := buf[4+4*numLights:] tail := buf[4+4*numPixels:]
for i := range tail { for i := range tail {
tail[i] = 0xFF tail[i] = 0xFF
} }
@ -310,16 +306,14 @@ func New(p spi.Port, numLights int, intensity uint8, temperature uint16) (*Dev,
Intensity: intensity, Intensity: intensity,
Temperature: temperature, Temperature: temperature,
s: c, s: c,
numLights: numLights, numPixels: numPixels,
rawBuf: buf, rawBuf: buf,
pixels: buf[4 : 4+4*numLights], pixels: buf[4 : 4+4*numPixels],
}, nil }, nil
} }
// //
var errLength = errors.New("apa102: invalid RGB stream length")
var _ devices.Display = &Dev{}
var _ devices.Device = &Dev{} var _ devices.Device = &Dev{}
var _ devices.Display = &Dev{}
var _ fmt.Stringer = &Dev{} var _ fmt.Stringer = &Dev{}

@ -349,7 +349,7 @@ func TestConnectFail(t *testing.T) {
func TestDevLen(t *testing.T) { func TestDevLen(t *testing.T) {
buf := bytes.Buffer{} buf := bytes.Buffer{}
d, _ := New(spitest.NewRecordRaw(&buf), 1, 255, 6500) d, _ := New(spitest.NewRecordRaw(&buf), 1, 255, 6500)
if n, err := d.Write([]byte{0}); n != 0 || err != errLength { if n, err := d.Write([]byte{0}); n != 0 || err == nil {
t.Fatalf("%d %v", n, err) t.Fatalf("%d %v", n, err)
} }
if expected := []byte{}; !bytes.Equal(expected, buf.Bytes()) { if expected := []byte{}; !bytes.Equal(expected, buf.Bytes()) {
@ -457,10 +457,10 @@ func TestDevLong(t *testing.T) {
} }
} }
func TestDevWriteShort(t *testing.T) { func TestDevWrite_Long(t *testing.T) {
buf := bytes.Buffer{} buf := bytes.Buffer{}
d, _ := New(spitest.NewRecordRaw(&buf), 1, 250, 6500) d, _ := New(spitest.NewRecordRaw(&buf), 1, 250, 6500)
if n, err := d.Write([]byte{0, 0, 0, 1, 1, 1}); n != 3 || err != nil { if n, err := d.Write([]byte{0, 0, 0, 1, 1, 1}); n != 0 || err == nil {
t.Fatal(n, err) t.Fatal(n, err)
} }
} }

Loading…
Cancel
Save