@ -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.
num Lights int
num Pixels 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 . num Light s, d . s )
return fmt . Sprintf ( "APA102{I:%d, T:%dK, %dLEDs, %s}" , d . Intensity , d . Temperature , d . num Pixel s, 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 . num Light s, Y : 1 } }
return image . Rectangle { Max : image . Point { X : d . num Pixel s, 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 , err Length
return 0 , err ors. 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 . num Light s* 3 ) )
_ , err := d . Write ( make ( [ ] byte , d . num Pixel s* 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 , num Light s int , intensity uint8 , temperature uint16 ) ( * Dev , error ) {
func New ( p spi . Port , num Pixel s 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 * ( num Lights+ 1 ) + numLight s/ 2 / 8 + 1 )
buf := make ( [ ] byte , 4 * ( num Pixels+ 1 ) + numPixel s/ 2 / 8 + 1 )
tail := buf [ 4 + 4 * num Light s: ]
tail := buf [ 4 + 4 * num Pixel s: ]
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 ,
num Lights: numLight s,
num Pixels: numPixel s,
rawBuf : buf ,
rawBuf : buf ,
pixels : buf [ 4 : 4 + 4 * num Light s] ,
pixels : buf [ 4 : 4 + 4 * num Pixel s] ,
} , 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 { }