Skip to content

Commit ddf7d54

Browse files
committed
ws2812: add Strip type with PIO support for RP2040/RP2350
1 parent a514169 commit ddf7d54

8 files changed

Lines changed: 160 additions & 2 deletions

File tree

examples/ws2812-strip/main.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Demonstrates WS2812B RGB LED control using the Strip API.
2+
//
3+
// Tested on Waveshare RP2350-LCD-1.47 with onboard RGB LED on GP22.
4+
//
5+
// To flash:
6+
//
7+
// tinygo flash -target=pico2 ./examples/ws2812-strip/
8+
package main
9+
10+
import (
11+
"image/color"
12+
"machine"
13+
"time"
14+
15+
"tinygo.org/x/drivers/ws2812"
16+
)
17+
18+
func main() {
19+
strip, err := ws2812.NewStrip(machine.GP22, 1)
20+
if err != nil {
21+
panic(err)
22+
}
23+
strip.SetBrightness(50)
24+
25+
colors := []color.RGBA{
26+
{R: 255, G: 0, B: 0},
27+
{R: 0, G: 255, B: 0},
28+
{R: 0, G: 0, B: 255},
29+
}
30+
31+
for i := 0; ; i = (i + 1) % 3 {
32+
strip.SetPixel(0, colors[i])
33+
strip.Show()
34+
time.Sleep(time.Second)
35+
}
36+
}

go.mod

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
module tinygo.org/x/drivers
22

3-
43
go 1.22.1
54

65
toolchain go1.23.1
76

8-
97
require (
108
github.com/eclipse/paho.mqtt.golang v1.2.0
119
github.com/frankban/quicktest v1.10.2
1210
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
1311
github.com/orsinium-labs/tinymath v1.1.0
1412
github.com/soypat/natiu-mqtt v0.5.1
13+
github.com/tinygo-org/pio v0.3.0
1514
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d
1615
golang.org/x/net v0.33.0
1716
tinygo.org/x/tinyfont v0.3.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ github.com/orsinium-labs/tinymath v1.1.0 h1:KomdsyLHB7vE3f1nRAJF2dyf1m/gnM2HxfTe
1717
github.com/orsinium-labs/tinymath v1.1.0/go.mod h1:WPXX6ei3KSXG7JfA03a+ekCYaY9SWN4I+JRl2p6ck+A=
1818
github.com/soypat/natiu-mqtt v0.5.1 h1:rwaDmlvjzD2+3MCOjMZc4QEkDkNwDzbct2TJbpz+TPc=
1919
github.com/soypat/natiu-mqtt v0.5.1/go.mod h1:xEta+cwop9izVCW7xOx2W+ct9PRMqr0gNVkvBPnQTc4=
20+
github.com/tinygo-org/pio v0.3.0 h1:opEnOtw58KGB4RJD3/n/Rd0/djYGX3DeJiXLI6y/yDI=
21+
github.com/tinygo-org/pio v0.3.0/go.mod h1:wf6c6lKZp+pQOzKKcpzchmRuhiMc27ABRuo7KVnaMFU=
2022
github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
2123
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d h1:0olWaB5pg3+oychR51GUVCEsGkeCU/2JxjBgIo4f3M0=
2224
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=

smoketest.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ tinygo build -size short -o ./build/test.bin -target=m5stamp-c3 ./examp
9393
tinygo build -size short -o ./build/test.hex -target=feather-nrf52840 ./examples/is31fl3731/main.go
9494
tinygo build -size short -o ./build/test.hex -target=arduino ./examples/ws2812
9595
tinygo build -size short -o ./build/test.hex -target=digispark ./examples/ws2812
96+
tinygo build -size short -o ./build/test.bin -target=pico2 ./examples/ws2812-strip
9697
tinygo build -size short -o ./build/test.hex -target=trinket-m0 ./examples/bme280/main.go
9798
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/microphone/main.go
9899
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/buzzer/main.go

ws2812/strip.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package ws2812
2+
3+
import "image/color"
4+
5+
// Strip controls a strip of WS2812B LEDs with a pixel buffer and brightness control.
6+
// Use NewStrip to create a new instance.
7+
type Strip struct {
8+
pixels []color.RGBA
9+
brightness uint8
10+
writeFunc func(pixels []color.RGBA, brightness uint8) error
11+
}
12+
13+
// SetPixel sets the color of pixel at index i.
14+
func (s *Strip) SetPixel(i int, c color.RGBA) {
15+
if i >= 0 && i < len(s.pixels) {
16+
s.pixels[i] = c
17+
}
18+
}
19+
20+
// Show sends the current pixel buffer to the LED strip.
21+
func (s *Strip) Show() error {
22+
return s.writeFunc(s.pixels, s.brightness)
23+
}
24+
25+
// SetBrightness sets the global brightness (0-255).
26+
func (s *Strip) SetBrightness(b uint8) {
27+
s.brightness = b
28+
}
29+
30+
// Fill sets all pixels to the given color.
31+
func (s *Strip) Fill(c color.RGBA) {
32+
for i := range s.pixels {
33+
s.pixels[i] = c
34+
}
35+
}
36+
37+
// Clear turns off all pixels.
38+
func (s *Strip) Clear() {
39+
s.Fill(color.RGBA{})
40+
}
41+
42+
// NumPixels returns the number of pixels in the strip.
43+
func (s *Strip) NumPixels() int {
44+
return len(s.pixels)
45+
}
46+
47+
// applyBrightness scales a color by the brightness value.
48+
func applyBrightness(c color.RGBA, brightness uint8) (r, g, b uint8) {
49+
r = uint8((uint16(c.R) * uint16(brightness)) >> 8)
50+
g = uint8((uint16(c.G) * uint16(brightness)) >> 8)
51+
b = uint8((uint16(c.B) * uint16(brightness)) >> 8)
52+
return
53+
}

ws2812/strip_other.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//go:build !rp2040 && !rp2350
2+
3+
package ws2812
4+
5+
import (
6+
"image/color"
7+
"machine"
8+
)
9+
10+
// NewStrip creates a new WS2812B LED strip on the given pin with the specified
11+
// number of pixels. On platforms without PIO, it uses the bit-bang driver.
12+
func NewStrip(pin machine.Pin, numPixels int) (*Strip, error) {
13+
pin.Configure(machine.PinConfig{Mode: machine.PinOutput})
14+
dev := NewWS2812(pin)
15+
return &Strip{
16+
pixels: make([]color.RGBA, numPixels),
17+
brightness: 255,
18+
writeFunc: func(pixels []color.RGBA, brightness uint8) error {
19+
adjusted := make([]color.RGBA, len(pixels))
20+
for i, c := range pixels {
21+
r, g, bl := applyBrightness(c, brightness)
22+
adjusted[i] = color.RGBA{R: r, G: g, B: bl}
23+
}
24+
return dev.WriteColors(adjusted)
25+
},
26+
}, nil
27+
}

ws2812/strip_rp2.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//go:build rp2040 || rp2350
2+
3+
package ws2812
4+
5+
import (
6+
"image/color"
7+
"machine"
8+
9+
pio "github.com/tinygo-org/pio/rp2-pio"
10+
"github.com/tinygo-org/pio/rp2-pio/piolib"
11+
)
12+
13+
// NewStrip creates a new WS2812B LED strip on the given pin with the specified
14+
// number of pixels. On RP2040/RP2350, it uses PIO for hardware-timed control.
15+
func NewStrip(pin machine.Pin, numPixels int) (*Strip, error) {
16+
sm, err := pio.PIO0.ClaimStateMachine()
17+
if err != nil {
18+
return nil, err
19+
}
20+
ws, err := piolib.NewWS2812B(sm, pin)
21+
if err != nil {
22+
return nil, err
23+
}
24+
return &Strip{
25+
pixels: make([]color.RGBA, numPixels),
26+
brightness: 255,
27+
writeFunc: func(pixels []color.RGBA, brightness uint8) error {
28+
for _, c := range pixels {
29+
r, g, bl := applyBrightness(c, brightness)
30+
ws.PutRGB(r, g, bl)
31+
}
32+
return nil
33+
},
34+
}, nil
35+
}

ws2812/ws2812.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
// Package ws2812 implements a driver for WS2812 and SK6812 RGB LED strips.
2+
//
3+
// For low-level control, use Device with NewWS2812 or NewSK6812.
4+
// For a higher-level API with pixel buffering and brightness control,
5+
// use Strip with NewStrip. On RP2040/RP2350, Strip uses PIO for
6+
// hardware-timed control; on other platforms it falls back to bit-banging.
27
package ws2812 // import "tinygo.org/x/drivers/ws2812"
38

49
//go:generate go run gen-ws2812.go -arch=cortexm 16 48 64 120 125 150 168 200

0 commit comments

Comments
 (0)