mirror of
https://github.com/RoganDawes/P4wnP1_aloa.git
synced 2025-03-31 16:08:05 +02:00
missing dependency for GPIO manager
This commit is contained in:
parent
637b51e4e1
commit
49267489f5
279
service/pgpio/p4wnp1gpio.go
Normal file
279
service/pgpio/p4wnp1gpio.go
Normal file
@ -0,0 +1,279 @@
|
||||
// +build linux
|
||||
|
||||
package pgpio
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"periph.io/x/periph/conn/gpio"
|
||||
"periph.io/x/periph/conn/physic"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
EEdgeDetectNotRunning = errors.New("edge detection not running")
|
||||
EEdgeDetectAborted = errors.New("edge detection aborted")
|
||||
)
|
||||
|
||||
type steadyTimeLevel struct {
|
||||
SteadyTime time.Duration
|
||||
Level gpio.Level
|
||||
}
|
||||
|
||||
type P4wnp1PinIO struct {
|
||||
piPin gpio.PinIO
|
||||
|
||||
/*
|
||||
edgeDetectLoopContext *context.Context //nil if no internal edge detect loop is running
|
||||
edgeDetectLoopCancel context.CancelFunc //nil if no internal edge detect loop is running
|
||||
edgeDetectionMutex *sync.Mutex
|
||||
*/
|
||||
|
||||
edge gpio.Edge
|
||||
pull gpio.Pull
|
||||
edgeDetectionAbort bool
|
||||
edgeDetectionChannel chan steadyTimeLevel //Every time a valid edge is detected, this channel returns the duration since the last valid edge (==steadyTime. could be used for debounce) and the gpio.Level during edge event
|
||||
waitForEdgeStopped bool
|
||||
lastEdgeTime time.Time
|
||||
}
|
||||
|
||||
func (p *P4wnp1PinIO) startEdgeDetection(pull gpio.Pull, edge gpio.Edge, preserveUnconsumed bool) (err error) {
|
||||
fmt.Println("starting edge detection for", p.Name() , "...")
|
||||
|
||||
|
||||
p.lastEdgeTime = time.Now()
|
||||
if preserveUnconsumed {
|
||||
p.edgeDetectionChannel = make(chan steadyTimeLevel, 0) // don't allow buffering --> block read loop if detected edge isn't consumed
|
||||
} else {
|
||||
p.edgeDetectionChannel = make(chan steadyTimeLevel, 1) // allow buffering a single value
|
||||
}
|
||||
|
||||
p.edgeDetectionAbort = false
|
||||
p.waitForEdgeStopped = false
|
||||
|
||||
go func() {
|
||||
Loop:
|
||||
for !p.edgeDetectionAbort {
|
||||
// Note: it seems to be impossible to force WaitForEdge to produce a 'false' result if no timeout is given.
|
||||
// Providing a timeout isn't suitable for this use case (continuous edge detection without interruption till
|
||||
// stopEdgeDetection() is called). A possible solution would be to call WaitForEdge in a loop with a timeout
|
||||
// and checking for the abort condition in every iteration, but this involves a tradeoff between responsiveness
|
||||
// to the stop method (long timeouts) and higher system load (short timeout).
|
||||
//
|
||||
// To cope with that, the state of `p.edgeDetectionAbort` is checked after every return of `WaitForEdge(-1)`.
|
||||
// In case no edges are detected after stopEdgeDetection has been called, this would block forever. In order to
|
||||
// deal with that, the stopEdgeDetection method changes the pull state of the GPIO resistor between PullUp and
|
||||
// PullDown, till this loop ends and indicates termination by setting p.waitForEdgeStopped to true.
|
||||
//
|
||||
// This approach is only tested on Pi0w and likely behaves different on other drivers or doesn't work at all
|
||||
// if no internal pull up/pull down resistors are present. The PinIO.Halt() method unfortunately doesn't interrupt
|
||||
// WaitForEdge.
|
||||
waitSuccess := p.piPin.WaitForEdge(-1)
|
||||
fmt.Println("Edge event received")
|
||||
if !waitSuccess {
|
||||
// don't consume the edge, stopEdgeDetection has been called meanwhile --> abort the loop
|
||||
fmt.Println("WaitForEdge failed, aborting EdgeDetection")
|
||||
break Loop
|
||||
}
|
||||
if p.edgeDetectionAbort {
|
||||
// don't consume the edge, stopEdgeDetection has been called meanwhile --> abort the loop
|
||||
fmt.Println("Ignored edge and because stopEdgeDetection was called, aborting edgeDetection")
|
||||
break Loop
|
||||
}
|
||||
|
||||
//if here, we got a un-debounced and unvalidated edge and thus measure the duration since the last detected edge
|
||||
now := time.Now()
|
||||
timeSinceLastEdge := now.Sub(p.lastEdgeTime)
|
||||
p.lastEdgeTime = now
|
||||
|
||||
var level gpio.Level
|
||||
switch p.edge {
|
||||
case gpio.RisingEdge:
|
||||
level = gpio.High // don't read from pin, assume correct detection
|
||||
case gpio.FallingEdge:
|
||||
level = gpio.Low // don't read from pin, assume correct detection
|
||||
case gpio.BothEdges:
|
||||
level = p.piPin.Read()
|
||||
default:
|
||||
// this includes gpio.NoEdge and shouldn't be set if this loop is running, so we abort the whole loop
|
||||
break Loop
|
||||
}
|
||||
|
||||
// Send the steadyTimeLeve along the channel
|
||||
stl := steadyTimeLevel{
|
||||
Level: level,
|
||||
SteadyTime: timeSinceLastEdge,
|
||||
}
|
||||
|
||||
if preserveUnconsumed {
|
||||
p.edgeDetectionChannel <- stl //write steadyTimeLevel to channel and block loop till read by ExtWaitForEdge
|
||||
// ToDo: fix debounce agnostic decision
|
||||
// Note: this has influence on timeSince last edge. The debounce duration has to be known here, to avoid blocking
|
||||
// write to the channel (and possibly increasing timeSinceLastEdge), in case the event occurred during debounce duration.
|
||||
} else {
|
||||
//pop old stl from channel if needed
|
||||
for len(p.edgeDetectionChannel) > 0 {
|
||||
<- p.edgeDetectionChannel
|
||||
}
|
||||
p.edgeDetectionChannel <- stl //put latest stl to channel
|
||||
}
|
||||
}
|
||||
|
||||
p.waitForEdgeStopped = true
|
||||
|
||||
//indicate that edge detection has successfully terminated by closing the edgeDetectionChannel channel (and set to nil to use as indicator for isEdgeDetectionRunning, without reading from this channel)
|
||||
close(p.edgeDetectionChannel)
|
||||
p.edgeDetectionChannel = nil
|
||||
|
||||
//assure edgeDetection is stopped
|
||||
fmt.Println("... edge detection for", p.Name(), "stopped")
|
||||
}()
|
||||
|
||||
|
||||
fmt.Println("... edge detection for", p.Name(), "started")
|
||||
fmt.Printf("p: %+v\n",p)
|
||||
return
|
||||
}
|
||||
|
||||
func (p *P4wnp1PinIO) isEdgeDetectionRunning() bool {
|
||||
if p.edgeDetectionChannel == nil {
|
||||
fmt.Println("edge detection not running")
|
||||
fmt.Printf("p: %+v\n",p)
|
||||
return false
|
||||
}
|
||||
fmt.Println("edge detection running")
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *P4wnp1PinIO) stopEdgeDetection() error {
|
||||
fmt.Println("stopping edge detection for", p.Name(), "...")
|
||||
|
||||
if !p.isEdgeDetectionRunning() {
|
||||
return EEdgeDetectNotRunning
|
||||
}
|
||||
|
||||
p.edgeDetectionAbort = true
|
||||
|
||||
// hackish approach to end WaitForEdge, by toggling Pull resistors (see comments in startEdgeDetection for details)
|
||||
for !p.waitForEdgeStopped {
|
||||
p.piPin.In(gpio.PullDown, p.edge)
|
||||
if !p.waitForEdgeStopped {
|
||||
p.piPin.In(gpio.PullUp, p.edge)
|
||||
}
|
||||
}
|
||||
|
||||
// disable edge detection interrupt and restore pull resistor state
|
||||
p.piPin.In(p.pull, gpio.NoEdge)
|
||||
|
||||
// wait till stop success is indicated
|
||||
if p.edgeDetectionChannel != nil { // if channel still exists
|
||||
<-p.edgeDetectionChannel //wait for close
|
||||
}
|
||||
|
||||
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p P4wnp1PinIO) Edge() gpio.Edge {
|
||||
return p.edge
|
||||
}
|
||||
|
||||
|
||||
func (p *P4wnp1PinIO) ExtWaitForEdge(ctx context.Context, debounceDuration time.Duration) (level gpio.Level, err error) {
|
||||
useDebounce := debounceDuration > 0
|
||||
|
||||
for { // the select statement is wrapped into a for loop, to account for edges not fulfilling the debounceDuration criteria
|
||||
select {
|
||||
case steadyTimeLevel,channelOpen := <- p.edgeDetectionChannel:
|
||||
//channel closed, so we return error
|
||||
if !channelOpen {
|
||||
return level,EEdgeDetectNotRunning
|
||||
}
|
||||
if !useDebounce {
|
||||
// no debounce needed, ultimately return the edge change
|
||||
return steadyTimeLevel.Level,nil
|
||||
} else {
|
||||
// we have to assure that no other edge change event occurs in debounce duration (steady level) before returning
|
||||
if steadyTimeLevel.SteadyTime >= debounceDuration {
|
||||
//detected edge fulfills debounce criteria
|
||||
return steadyTimeLevel.Level,nil
|
||||
}
|
||||
// else ignore edge
|
||||
fmt.Printf("Ignored detected edge as invalidated by debounce: %v\n", steadyTimeLevel)
|
||||
}
|
||||
case <-ctx.Done():
|
||||
return level,EEdgeDetectAborted
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
func (p P4wnp1PinIO) String() string {
|
||||
return p.piPin.String()
|
||||
}
|
||||
*/
|
||||
func (p P4wnp1PinIO) Halt() error {
|
||||
return p.piPin.Halt()
|
||||
}
|
||||
|
||||
func (p P4wnp1PinIO) Name() string {
|
||||
return p.piPin.Name()
|
||||
}
|
||||
|
||||
func (p P4wnp1PinIO) Number() int {
|
||||
return p.piPin.Number()
|
||||
}
|
||||
|
||||
func (p P4wnp1PinIO) Function() string {
|
||||
return p.piPin.Function()
|
||||
}
|
||||
|
||||
func (p *P4wnp1PinIO) In(pull gpio.Pull, edge gpio.Edge) (err error) {
|
||||
fmt.Println("Called In() on ", p.Name())
|
||||
p.stopEdgeDetection()
|
||||
// bring up edgeDetection go routine (again) if needed
|
||||
err = p.piPin.In(pull, edge)
|
||||
if err != nil { return }
|
||||
p.pull = pull
|
||||
p.edge = edge
|
||||
if edge != gpio.NoEdge {
|
||||
p.startEdgeDetection(pull,edge,false) //don't preserve unconsumed events
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (p P4wnp1PinIO) Read() gpio.Level {
|
||||
return p.piPin.Read()
|
||||
}
|
||||
|
||||
func (p P4wnp1PinIO) WaitForEdge(timeout time.Duration) bool {
|
||||
return p.piPin.WaitForEdge(timeout)
|
||||
}
|
||||
|
||||
func (p P4wnp1PinIO) Pull() gpio.Pull {
|
||||
return p.piPin.Pull()
|
||||
}
|
||||
|
||||
func (p P4wnp1PinIO) DefaultPull() gpio.Pull {
|
||||
return p.piPin.DefaultPull()
|
||||
}
|
||||
|
||||
func (p *P4wnp1PinIO) Out(l gpio.Level) error {
|
||||
//stop edge detection, if needed
|
||||
p.stopEdgeDetection()
|
||||
return p.piPin.Out(l)
|
||||
}
|
||||
|
||||
func (p *P4wnp1PinIO) PWM(duty gpio.Duty, f physic.Frequency) error {
|
||||
//stop edge detection, if needed
|
||||
p.stopEdgeDetection()
|
||||
return p.piPin.PWM(duty,f)
|
||||
}
|
||||
|
||||
func NewP4wnp1PinIO(p gpio.PinIO) *P4wnp1PinIO {
|
||||
return &P4wnp1PinIO{piPin: p}
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user