tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/encoders/quadrature_interrupt.go (about)

     1  //go:build tinygo && (rp2040 || stm32 || k210 || esp32c3 || nrf || sam || (avr && (atmega328p || atmega328pb)))
     2  
     3  // Implementation based on:
     4  // https://gist.github.com/aykevl/3fc1683ed77bb0a9c07559dfe857304a
     5  
     6  // Note: build constraints in this file list targets that define machine.PinToggle.
     7  // If this is supported for additional targets in the future, they can be added above.
     8  
     9  package encoders
    10  
    11  import (
    12  	"machine"
    13  	"runtime/volatile"
    14  )
    15  
    16  var (
    17  	states = []int8{0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0}
    18  )
    19  
    20  // NewQuadratureViaInterrupt returns a rotary encoder device that uses GPIO
    21  // interrupts and a lookup table to keep track of quadrature state changes.
    22  //
    23  // This constructur is only available for TinyGo targets for which machine.PinToggle
    24  // is defined as a valid interrupt type.
    25  func NewQuadratureViaInterrupt(pinA, pinB machine.Pin) *QuadratureDevice {
    26  	return &QuadratureDevice{impl: &quadInterruptImpl{pinA: pinA, pinB: pinB, oldAB: 0b00000011}}
    27  }
    28  
    29  type quadInterruptImpl struct {
    30  	pinA machine.Pin
    31  	pinB machine.Pin
    32  
    33  	// precision int
    34  
    35  	oldAB int
    36  	value volatile.Register32
    37  }
    38  
    39  func (enc *quadInterruptImpl) configure(cfg QuadratureConfig) error {
    40  	enc.pinA.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
    41  	enc.pinA.SetInterrupt(machine.PinToggle, enc.interrupt)
    42  
    43  	enc.pinB.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
    44  	enc.pinB.SetInterrupt(machine.PinToggle, enc.interrupt)
    45  
    46  	return nil
    47  }
    48  
    49  func (enc *quadInterruptImpl) interrupt(pin machine.Pin) {
    50  	aHigh, bHigh := enc.pinA.Get(), enc.pinB.Get()
    51  	enc.oldAB <<= 2
    52  	if aHigh {
    53  		enc.oldAB |= 1 << 1
    54  	}
    55  	if bHigh {
    56  		enc.oldAB |= 1
    57  	}
    58  	enc.writeValue(enc.readValue() + int(states[enc.oldAB&0x0f]))
    59  }
    60  
    61  // readValue gets the value using volatile operations and returns it as an int
    62  func (enc *quadInterruptImpl) readValue() int {
    63  	return int(enc.value.Get())
    64  }
    65  
    66  // writeValue set the value to the specified int using volatile operations
    67  func (enc *quadInterruptImpl) writeValue(v int) {
    68  	enc.value.Set(uint32(v))
    69  }