From 308763f5007acbf05ba7dd147082ad962ba006bb Mon Sep 17 00:00:00 2001 From: BCG Date: Fri, 26 Jan 2024 07:49:11 -0500 Subject: [PATCH] Adding driver for rotary encoder support --- README.md | 2 +- encoders/quadrature_device.go | 34 +++++++++ encoders/quadrature_interrupt.go | 69 +++++++++++++++++++ .../encoders/quadrature-interrupt/main.go | 28 ++++++++ smoketest.sh | 1 + 5 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 encoders/quadrature_device.go create mode 100644 encoders/quadrature_interrupt.go create mode 100644 examples/encoders/quadrature-interrupt/main.go diff --git a/README.md b/README.md index b77dcb4..bd30b16 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![PkgGoDev](https://pkg.go.dev/badge/tinygo.org/x/drivers)](https://pkg.go.dev/tinygo.org/x/drivers) [![Build](https://github.com/tinygo-org/drivers/actions/workflows/build.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/drivers/actions/workflows/build.yml) -This package provides a collection of 101 different hardware drivers for devices such as sensors and displays that can be used together with [TinyGo](https://tinygo.org). +This package provides a collection of 102 different hardware drivers for devices such as sensors and displays that can be used together with [TinyGo](https://tinygo.org). For the complete list, please see: https://tinygo.org/docs/reference/devices/ diff --git a/encoders/quadrature_device.go b/encoders/quadrature_device.go new file mode 100644 index 0000000..0482a92 --- /dev/null +++ b/encoders/quadrature_device.go @@ -0,0 +1,34 @@ +package encoders + +type QuadratureDevice struct { + cfg QuadratureConfig + impl quadratureImpl +} + +type QuadratureConfig struct { + Precision int +} + +type quadratureImpl interface { + configure(cfg QuadratureConfig) error + readValue() int + writeValue(int) +} + +func (enc *QuadratureDevice) Configure(cfg QuadratureConfig) error { + if cfg.Precision < 1 { + cfg.Precision = 4 + } + enc.cfg = cfg + return enc.impl.configure(cfg) +} + +// Position returns the stored int value for the encoder +func (enc *QuadratureDevice) Position() int { + return enc.impl.readValue() / enc.cfg.Precision +} + +// SetPosition overwrites the currently stored value with the specified int value +func (enc *QuadratureDevice) SetPosition(v int) { + enc.impl.writeValue(v * enc.cfg.Precision) +} diff --git a/encoders/quadrature_interrupt.go b/encoders/quadrature_interrupt.go new file mode 100644 index 0000000..98a7927 --- /dev/null +++ b/encoders/quadrature_interrupt.go @@ -0,0 +1,69 @@ +//go:build tinygo && (rp2040 || stm32 || k210 || esp32c3 || nrf || (avr && (atmega328p || atmega328pb))) + +// Implementation based on: +// https://gist.github.com/aykevl/3fc1683ed77bb0a9c07559dfe857304a + +// Note: build constraints in this file list targets that define machine.PinToggle. +// If this is supported for additional targets in the future, they can be added above. + +package encoders + +import ( + "machine" + "runtime/volatile" +) + +var ( + states = []int8{0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0} +) + +// NewQuadratureViaInterrupt returns a rotary encoder device that uses GPIO +// interrupts and a lookup table to keep track of quadrature state changes. +// +// This constructur is only available for TinyGo targets for which machine.PinToggle +// is defined as a valid interrupt type. +func NewQuadratureViaInterrupt(pinA, pinB machine.Pin) *QuadratureDevice { + return &QuadratureDevice{impl: &quadInterruptImpl{pinA: pinA, pinB: pinB, oldAB: 0b00000011}} +} + +type quadInterruptImpl struct { + pinA machine.Pin + pinB machine.Pin + + // precision int + + oldAB int + value volatile.Register32 +} + +func (enc *quadInterruptImpl) configure(cfg QuadratureConfig) error { + enc.pinA.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + enc.pinA.SetInterrupt(machine.PinToggle, enc.interrupt) + + enc.pinB.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + enc.pinB.SetInterrupt(machine.PinToggle, enc.interrupt) + + return nil +} + +func (enc *quadInterruptImpl) interrupt(pin machine.Pin) { + aHigh, bHigh := enc.pinA.Get(), enc.pinB.Get() + enc.oldAB <<= 2 + if aHigh { + enc.oldAB |= 1 << 1 + } + if bHigh { + enc.oldAB |= 1 + } + enc.writeValue(enc.readValue() + int(states[enc.oldAB&0x0f])) +} + +// readValue gets the value using volatile operations and returns it as an int +func (enc *quadInterruptImpl) readValue() int { + return int(enc.value.Get()) +} + +// writeValue set the value to the specified int using volatile operations +func (enc *quadInterruptImpl) writeValue(v int) { + enc.value.Set(uint32(v)) +} diff --git a/examples/encoders/quadrature-interrupt/main.go b/examples/encoders/quadrature-interrupt/main.go new file mode 100644 index 0000000..8ded393 --- /dev/null +++ b/examples/encoders/quadrature-interrupt/main.go @@ -0,0 +1,28 @@ +//go:build macropad_rp2040 + +package main + +import ( + "machine" + + "tinygo.org/x/drivers/encoders" +) + +var ( + enc = encoders.NewQuadratureViaInterrupt(machine.ROT_A, machine.ROT_B) +) + +func main() { + + enc.Configure(encoders.QuadratureConfig{ + Precision: 4, + }) + + for oldValue := 0; ; { + if newValue := enc.Position(); newValue != oldValue { + println("value: ", newValue) + oldValue = newValue + } + } + +} diff --git a/smoketest.sh b/smoketest.sh index ae45239..b7530b2 100755 --- a/smoketest.sh +++ b/smoketest.sh @@ -129,6 +129,7 @@ tinygo build -size short -o ./build/test.hex -target=microbit ./examples/ndir/ma tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/ndir/main_ndir.go tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/mpu9150/main.go tinygo build -size short -o ./build/test.hex -target=macropad-rp2040 ./examples/sh1106/macropad_spi +tinygo build -size short -o ./build/test.hex -target=macropad-rp2040 ./examples/encoders/quadrature-interrupt # network examples (espat) tinygo build -size short -o ./build/test.hex -target=challenger-rp2040 ./examples/net/ntpclient/ # network examples (wifinina)