diff --git a/examples/rpio/main.go b/examples/rpio/main.go new file mode 100644 index 000000000..5ab403763 --- /dev/null +++ b/examples/rpio/main.go @@ -0,0 +1,91 @@ +package main + +// Example program for the ST7735 display (Waveshare 1.44" LCD HAT) using the rpio driver on a Raspberry Pi. +// Build command: +// GOOS=linux GOARCH=arm64 go build -o rpio-example ./examples/rpio/ + +import ( + "fmt" + "image/color" + "time" + + go_rpio "github.com/stianeikeland/go-rpio/v4" + "tinygo.org/x/drivers" + "tinygo.org/x/drivers/rpio" + "tinygo.org/x/drivers/st7735" +) + +var ( + black = color.RGBA{0, 0, 0, 255} + colors = [...]color.RGBA{ + {255, 0, 0, 255}, // red + {0, 255, 0, 255}, // green + {0, 0, 255, 255}, // blue + {255, 255, 0, 255}, // yellow + } +) + +func main() { + if err := go_rpio.Open(); err != nil { + fmt.Println("Error opening GPIO:", err) + return + } + defer go_rpio.Close() + + // Initialize SPI and pins + spi := rpio.NewSPI() + resetPin := rpio.NewPin(27) + dcPin := rpio.NewPin(25) + csPin := rpio.NewPin(8) + blPin := rpio.NewPin(24) + + rotatePin := rpio.NewPin(20) // middle button on the HAT + rotatePin.Pin.PullUp() // enable pull-up resistor, our wrapper sets only input mode automatically + + // Initialize display + device := st7735.New(spi, resetPin, dcPin, csPin, blPin) + device.Configure(st7735.Config{ + Width: 128, + Height: 128, + Model: st7735.GREENTAB, + RowOffset: 3, + ColumnOffset: 2, + }) + device.InvertColors(false) + device.EnableBacklight(true) + device.IsBGR(true) // no effect w/o rotation! + device.SetRotation(st7735.NO_ROTATION) + + width, height := device.Size() + + // Clear display + device.FillScreen(black) + + // Draw rectangles in a loop, clockwise rotation of colors + pos := 0 + for { + device.FillRectangle(0, 0, width/2, height/2, colors[(pos+0)%len(colors)]) // top left + device.FillRectangle(0, height/2, width/2, height/2, colors[(pos+1)%len(colors)]) // bottom left + device.FillRectangle(width/2, height/2, width/2, height/2, colors[(pos+2)%len(colors)]) // bottom right + device.FillRectangle(width/2, 0, width/2, height/2, colors[(pos+3)%len(colors)]) // top right + device.FillRectangle(0, 0, 10, 10, black) // rotation marker + pos++ + + timeout := time.Now().Add(1 * time.Second) + for time.Now().Before(timeout) { + // check button pressed + if !rotatePin.Get() { + currentRotation := device.Rotation() + newRotation := (currentRotation + 1) % 4 + device.SetRotation(drivers.Rotation(newRotation)) + // wait for button release + for !rotatePin.Get() { + time.Sleep(10 * time.Millisecond) + } + break + } + time.Sleep(50 * time.Millisecond) + } + + } +} diff --git a/go.mod b/go.mod index 10c3e98a7..e48ae9324 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/orsinium-labs/tinymath v1.1.0 github.com/soypat/natiu-mqtt v0.5.1 + github.com/stianeikeland/go-rpio/v4 v4.6.0 golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d golang.org/x/net v0.33.0 tinygo.org/x/tinyfont v0.3.0 diff --git a/go.sum b/go.sum index 6bb35574d..cf62ab3f5 100644 --- a/go.sum +++ b/go.sum @@ -17,6 +17,8 @@ github.com/orsinium-labs/tinymath v1.1.0 h1:KomdsyLHB7vE3f1nRAJF2dyf1m/gnM2HxfTe github.com/orsinium-labs/tinymath v1.1.0/go.mod h1:WPXX6ei3KSXG7JfA03a+ekCYaY9SWN4I+JRl2p6ck+A= github.com/soypat/natiu-mqtt v0.5.1 h1:rwaDmlvjzD2+3MCOjMZc4QEkDkNwDzbct2TJbpz+TPc= github.com/soypat/natiu-mqtt v0.5.1/go.mod h1:xEta+cwop9izVCW7xOx2W+ct9PRMqr0gNVkvBPnQTc4= +github.com/stianeikeland/go-rpio/v4 v4.6.0 h1:eAJgtw3jTtvn/CqwbC82ntcS+dtzUTgo5qlZKe677EY= +github.com/stianeikeland/go-rpio/v4 v4.6.0/go.mod h1:A3GvHxC1Om5zaId+HqB3HKqx4K/AqeckxB7qRjxMK7o= github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d h1:0olWaB5pg3+oychR51GUVCEsGkeCU/2JxjBgIo4f3M0= golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= diff --git a/rpio/pin.go b/rpio/pin.go new file mode 100644 index 000000000..3519785a0 --- /dev/null +++ b/rpio/pin.go @@ -0,0 +1,37 @@ +// Implements internal.pin.[Input,Output] interfaces for the Raspberry Pi GPIO pins. +// Depends on the go-rpio library. +// Configures pin modes automatically when Get or Set methods are called. + +package rpio // import "tinygo.org/x/drivers/rpio" + +import go_rpio "github.com/stianeikeland/go-rpio/v4" + +type Pin struct { + modeSet bool + Mode go_rpio.Mode + Pin go_rpio.Pin +} + +func NewPin(pinNumber int) *Pin { + return &Pin{Pin: go_rpio.Pin(pinNumber)} +} + +func (p *Pin) Get() bool { + if !p.modeSet || p.Mode != go_rpio.Input { + p.Pin.Input() + p.Mode = go_rpio.Input + } + return p.Pin.Read() == go_rpio.High +} + +func (p *Pin) Set(high bool) { + if !p.modeSet || p.Mode != go_rpio.Output { + p.Pin.Output() + p.Mode = go_rpio.Output + } + state := go_rpio.Low + if high { + state = go_rpio.High + } + p.Pin.Write(state) +} diff --git a/rpio/spi.go b/rpio/spi.go new file mode 100644 index 000000000..3384c86c2 --- /dev/null +++ b/rpio/spi.go @@ -0,0 +1,33 @@ +// Implemenmts drivers.SPI interface for the Raspberry Pi. +// Depends on the go-rpio library. + +package rpio // import "tinygo.org/x/drivers/rpio" + +import go_rpio "github.com/stianeikeland/go-rpio/v4" + +type SPI struct { +} + +func NewSPI() (s *SPI) { + if err := go_rpio.SpiBegin(go_rpio.Spi0); err != nil { + panic(err) + } + go_rpio.SpiSpeed(25_000_000) // 25 MHz + go_rpio.SpiChipSelect(0) + return &SPI{} +} + +func (s *SPI) Tx(w, r []byte) error { + data := make([]byte, len(w)) + copy(data, w) + go_rpio.SpiExchange(data) + copy(r, data) + return nil +} + +func (s *SPI) Transfer(b byte) (byte, error) { + w := []byte{b} + r := make([]byte, len(w)) + err := s.Tx(w, r) + return r[0], err +}