From 2ec057fd07cf5c077ebffbc8d24635da0a0a2006 Mon Sep 17 00:00:00 2001 From: Drahoslav Date: Mon, 4 Jun 2018 14:46:15 +0200 Subject: [PATCH 1/7] Impelement edge event detection --- rpio.go | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 66 insertions(+), 5 deletions(-) diff --git a/rpio.go b/rpio.go index 62109ce..59b9ae4 100644 --- a/rpio.go +++ b/rpio.go @@ -76,6 +76,7 @@ type Mode uint8 type Pin uint8 type State uint8 type Pull uint8 +type Edge uint8 // Memory offsets for gpio, see the spec for more details const ( @@ -121,6 +122,14 @@ const ( PullUp ) +// Edge events +const ( + NoEdge Edge = iota + RiseEdge + FallEdge + AnyEdge = RiseEdge | FallEdge +) + // Arrays for 8 / 32 bit access to memory and a semaphore for write locking var ( memlock sync.Mutex @@ -212,6 +221,16 @@ func (pin Pin) PullOff() { PullMode(pin, PullOff) } +// Enable edge event detection on pin +func (pin Pin) Detect(edge Edge) { + DetectEdge(pin, edge) +} + +// Check edge event on pin +func (pin Pin) EdgeDetected() bool { + return EdgeDetected(pin) +} + // PinMode sets the mode (direction) of a given pin (Input, Output, Clock or Pwm) // // Clock is possible only for pins 4, 5, 6, 20, 21. @@ -262,7 +281,6 @@ func PinMode(pin Pin, mode Mode) { // WritePin sets a given pin High or Low // by setting the clear or set registers respectively func WritePin(pin Pin, state State) { - p := uint8(pin) // Clear register, 10 / 11 depending on bank @@ -278,7 +296,6 @@ func WritePin(pin Pin, state State) { } else { gpioMem[setReg] = 1 << (p & 31) } - } // Read the state of a pin @@ -304,6 +321,50 @@ func TogglePin(pin Pin) { } } +// Enable edge event detection on pin +// +// Call with RiseEdge followed by call with FallEdge has the same effect as call with AnyEdge, +// to disable previously enabled event call it with NoEdge. +// +// Combine with pin.EdgeDetected() to check whether event occured. +// +// It also clears previously detected events of this pin if any. +func DetectEdge(pin Pin, edge Edge) { + p := uint8(pin) + + // Rising edge detect enable register (19/20 depending on bank) + // Falling edge detect enable register (22/23 depending on bank) + renReg := p/32 + 19 + fenReg := p/32 + 23 + + if edge == NoEdge { // clear bits + gpioMem[renReg] = gpioMem[renReg] &^ (1 << (p&31)) + gpioMem[fenReg] = gpioMem[fenReg] &^ (1 << (p&31)) + } + if edge & RiseEdge > 0 { // set bit + gpioMem[renReg] = gpioMem[renReg] | (1 << (p&31)) + } + if edge & FallEdge > 0 { // set bit + gpioMem[fenReg] = gpioMem[fenReg] | (1 << (p&31)) + } + + EdgeDetected(pin) // to clear outdated detection +} + +// Check whether edge event occured +// +// Event detection has to be enabled first, by pin.Detect(edge) +func EdgeDetected(pin Pin) bool { + p := uint8(pin) + + // Event detect status register (16/17) + edsReg := p/32 + 16 + + test := gpioMem[edsReg] & (1 << (p&31)) + gpioMem[edsReg] = test // set bit to clear it + return test > 0 +} + func PullMode(pin Pin, pull Pull) { // Pull up/down/off register has offset 38 / 39, pull is 37 pullClkReg := pin/32 + 38 @@ -315,7 +376,7 @@ func PullMode(pin Pin, pull Pull) { switch pull { case PullDown, PullUp: - gpioMem[pullReg] = gpioMem[pullReg]&^3 | uint32(pull) + gpioMem[pullReg] = gpioMem[pullReg] &^ 3 | uint32(pull) case PullOff: gpioMem[pullReg] = gpioMem[pullReg] &^ 3 } @@ -343,7 +404,7 @@ func PullMode(pin Pin, pull Pull) { // changing frequency for one pin will change it also for all pins within a group. // The groups are: // gp_clk0: pins 4, 20, 32, 34 -// gp_clk1: pins 5, 21, 42, 43 +// gp_clk1: pins 5, 21, 42, 44 // gp_clk2: pins 6 and 43 // pwm_clk: pins 12, 13, 18, 19, 40, 41, 45 func SetFreq(pin Pin, freq int) { @@ -452,7 +513,7 @@ func SetDutyCycle(pin Pin, dutyLen, cycleLen uint32) { const msen = 1 << 7 // use M/S transition instead of pwm algorithm // reset settings - pwmMem[pwmCtlReg] = pwmMem[pwmCtlReg]&^(ctlMask< Date: Mon, 4 Jun 2018 15:46:49 +0200 Subject: [PATCH 2/7] Test edge events --- rpio.go | 8 ++--- rpio_test.go | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 4 deletions(-) create mode 100644 rpio_test.go diff --git a/rpio.go b/rpio.go index 59b9ae4..6a56cbe 100644 --- a/rpio.go +++ b/rpio.go @@ -323,12 +323,12 @@ func TogglePin(pin Pin) { // Enable edge event detection on pin // -// Call with RiseEdge followed by call with FallEdge has the same effect as call with AnyEdge, -// to disable previously enabled event call it with NoEdge. -// // Combine with pin.EdgeDetected() to check whether event occured. // -// It also clears previously detected events of this pin if any. +// It also clears previously detected event of this pin if any. +// +// Note that call with RiseEdge followed by call with FallEdge has the same effect as call with AnyEdge, +// to disable previously enabled event call it with NoEdge. func DetectEdge(pin Pin, edge Edge) { p := uint8(pin) diff --git a/rpio_test.go b/rpio_test.go new file mode 100644 index 0000000..da18321 --- /dev/null +++ b/rpio_test.go @@ -0,0 +1,93 @@ +package rpio + +import ( + "testing" + "time" + "os" +) + +func TestMain(m *testing.M) { + if err := Open(); err != nil { + panic(err) + } + os.Exit(m.Run()) +} + +func TestRisingEdgeEvent(t *testing.T) { + // bcm pins 2 and 3 has to be directly connected + src := Pin(3) + src.Mode(Output) + src.Low() + + pin := Pin(2) + pin.Mode(Input) + pin.PullDown() + pin.Detect(RiseEdge) + + timeout := time.After(time.Second) + loop: for { + src.High() + + time.Sleep(time.Second/5) + if pin.EdgeDetected() { + t.Log("edge rised") + } else { + t.Errorf("Raise event should be detected") + } + select { + case <- timeout: + break loop + default: + } + + src.Low() + } + if pin.EdgeDetected() { + t.Error("Rise should not be detected, no change since last call") + } + pin.Detect(NoEdge) + src.High() + if pin.EdgeDetected() { + t.Error("Fall should not be detected, events disabled") + } +} + +func TestFallingEdgeEvent(t *testing.T) { + // bcm pins 2 and 3 has to be directly connected + src := Pin(3) + src.Mode(Output) + src.High() + + pin := Pin(2) + pin.Mode(Input) + pin.PullDown() + pin.Detect(FallEdge) + + timeout := time.After(time.Second) + loop: for { + src.Low() + + time.Sleep(time.Second/5) + if pin.EdgeDetected() { + t.Log("edge fallen") + } else { + t.Errorf("Fall event should be detected") + } + + select { + case <- timeout: + break loop + default: + } + + src.High() + } + if pin.EdgeDetected() { + t.Error("Fall should not be detected, no change since last call") + } + pin.Detect(NoEdge) + src.Low() + if pin.EdgeDetected() { + t.Error("Fall should not be detected, events disabled") + } +} \ No newline at end of file From 22c39c30df7c01548e330f60068533dacbbd5649 Mon Sep 17 00:00:00 2001 From: Drahoslav Date: Mon, 4 Jun 2018 16:32:46 +0200 Subject: [PATCH 3/7] Fix falling edge detection --- rpio.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rpio.go b/rpio.go index 6a56cbe..2425a8b 100644 --- a/rpio.go +++ b/rpio.go @@ -335,7 +335,7 @@ func DetectEdge(pin Pin, edge Edge) { // Rising edge detect enable register (19/20 depending on bank) // Falling edge detect enable register (22/23 depending on bank) renReg := p/32 + 19 - fenReg := p/32 + 23 + fenReg := p/32 + 22 if edge == NoEdge { // clear bits gpioMem[renReg] = gpioMem[renReg] &^ (1 << (p&31)) @@ -362,7 +362,7 @@ func EdgeDetected(pin Pin) bool { test := gpioMem[edsReg] & (1 << (p&31)) gpioMem[edsReg] = test // set bit to clear it - return test > 0 + return test != 0 } func PullMode(pin Pin, pull Pull) { From 9e67063fd517f35612b480a974c2b5150c1f4753 Mon Sep 17 00:00:00 2001 From: Drahoslav Date: Mon, 4 Jun 2018 16:59:19 +0200 Subject: [PATCH 4/7] Edge event example --- examples/event/event.go | 48 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 examples/event/event.go diff --git a/examples/event/event.go b/examples/event/event.go new file mode 100644 index 0000000..5d24403 --- /dev/null +++ b/examples/event/event.go @@ -0,0 +1,48 @@ +/* + +An example of edge event handling by @Drahoslav7, using the go-rpio library + +Waits for button to be pressed before exit. + +Connect a button between pin 22 and some GND pin. + +*/ + +package main + +import ( + "fmt" + "time" + "os" + "github.com/stianeikeland/go-rpio" +) + +var ( + // Use mcu pin 22, corresponds to GPIO 3 on the pi + pin = rpio.Pin(22) +) + +func main() { + // Open and map memory to access gpio, check for errors + if err := rpio.Open(); err != nil { + fmt.Println(err) + os.Exit(1) + } + // Unmap gpio memory when done + defer rpio.Close() + + pin.Input() + pin.PullUp() + pin.Detect(rpio.FallEdge) // enable falling edge event detection + + fmt.Println("press a button") + + for { + if pin.EdgeDetected() { // check if event occured + fmt.Println("button pressed less than a second ago") + break + } + time.Sleep(time.Second) + } + pin.Detect(rpio.NoEdge) // disable edge event detection +} From 4ae1cfd964390dfc9e041d9d3e4a747119aa89ed Mon Sep 17 00:00:00 2001 From: Drahoslav Date: Tue, 5 Jun 2018 21:51:36 +0200 Subject: [PATCH 5/7] Minor change in edge event detection api Also other minor changes and gofmt --- examples/event/event.go | 13 ++++++----- rpio.go | 37 ++++++++++++++++------------- rpio_test.go | 52 +++++++++++++++++++++-------------------- 3 files changed, 55 insertions(+), 47 deletions(-) diff --git a/examples/event/event.go b/examples/event/event.go index 5d24403..66288c8 100644 --- a/examples/event/event.go +++ b/examples/event/event.go @@ -2,7 +2,7 @@ An example of edge event handling by @Drahoslav7, using the go-rpio library -Waits for button to be pressed before exit. +Waits for button to be pressed twice before exit. Connect a button between pin 22 and some GND pin. @@ -12,8 +12,9 @@ package main import ( "fmt" - "time" "os" + "time" + "github.com/stianeikeland/go-rpio" ) @@ -37,12 +38,12 @@ func main() { fmt.Println("press a button") - for { + for i := 0; i < 2; { if pin.EdgeDetected() { // check if event occured - fmt.Println("button pressed less than a second ago") - break + fmt.Println("button pressed") + i++ } - time.Sleep(time.Second) + time.Sleep(time.Second / 2) } pin.Detect(rpio.NoEdge) // disable edge event detection } diff --git a/rpio.go b/rpio.go index 2425a8b..d28e220 100644 --- a/rpio.go +++ b/rpio.go @@ -123,7 +123,7 @@ const ( ) // Edge events -const ( +const ( NoEdge Edge = iota RiseEdge FallEdge @@ -229,7 +229,7 @@ func (pin Pin) Detect(edge Edge) { // Check edge event on pin func (pin Pin) EdgeDetected() bool { return EdgeDetected(pin) -} +} // PinMode sets the mode (direction) of a given pin (Input, Output, Clock or Pwm) // @@ -322,33 +322,38 @@ func TogglePin(pin Pin) { } // Enable edge event detection on pin -// +// // Combine with pin.EdgeDetected() to check whether event occured. // // It also clears previously detected event of this pin if any. // -// Note that call with RiseEdge followed by call with FallEdge has the same effect as call with AnyEdge, -// to disable previously enabled event call it with NoEdge. +// Note that call with RiseEdge will disable previously set FallEdge detection and vice versa +// You have to call with AnyEdge, to enable detection for both edges +// To disable previously enabled detection call it with NoEdge func DetectEdge(pin Pin, edge Edge) { p := uint8(pin) // Rising edge detect enable register (19/20 depending on bank) // Falling edge detect enable register (22/23 depending on bank) + // Event detect status register (16/17) renReg := p/32 + 19 fenReg := p/32 + 22 + edsReg := p/32 + 16 - if edge == NoEdge { // clear bits - gpioMem[renReg] = gpioMem[renReg] &^ (1 << (p&31)) - gpioMem[fenReg] = gpioMem[fenReg] &^ (1 << (p&31)) + bit := uint32(1 << (p & 31)) + + if edge&RiseEdge > 0 { // set bit + gpioMem[renReg] = gpioMem[renReg] | bit + } else { // clear bit + gpioMem[renReg] = gpioMem[renReg] &^ bit } - if edge & RiseEdge > 0 { // set bit - gpioMem[renReg] = gpioMem[renReg] | (1 << (p&31)) - } - if edge & FallEdge > 0 { // set bit - gpioMem[fenReg] = gpioMem[fenReg] | (1 << (p&31)) + if edge&FallEdge > 0 { // set bit + gpioMem[fenReg] = gpioMem[fenReg] | bit + } else { // clear bit + gpioMem[fenReg] = gpioMem[fenReg] &^ bit } - EdgeDetected(pin) // to clear outdated detection + gpioMem[edsReg] = bit // to clear outdated detection } // Check whether edge event occured @@ -360,7 +365,7 @@ func EdgeDetected(pin Pin) bool { // Event detect status register (16/17) edsReg := p/32 + 16 - test := gpioMem[edsReg] & (1 << (p&31)) + test := gpioMem[edsReg] & (1 << (p & 31)) gpioMem[edsReg] = test // set bit to clear it return test != 0 } @@ -376,7 +381,7 @@ func PullMode(pin Pin, pull Pull) { switch pull { case PullDown, PullUp: - gpioMem[pullReg] = gpioMem[pullReg] &^ 3 | uint32(pull) + gpioMem[pullReg] = gpioMem[pullReg]&^3 | uint32(pull) case PullOff: gpioMem[pullReg] = gpioMem[pullReg] &^ 3 } diff --git a/rpio_test.go b/rpio_test.go index da18321..aed0312 100644 --- a/rpio_test.go +++ b/rpio_test.go @@ -1,20 +1,21 @@ package rpio import ( + "os" "testing" "time" - "os" ) func TestMain(m *testing.M) { + println("Note: bcm pins 2 and 3 has to be directly connected") if err := Open(); err != nil { panic(err) } + defer Close() os.Exit(m.Run()) } func TestRisingEdgeEvent(t *testing.T) { - // bcm pins 2 and 3 has to be directly connected src := Pin(3) src.Mode(Output) src.Low() @@ -25,19 +26,20 @@ func TestRisingEdgeEvent(t *testing.T) { pin.Detect(RiseEdge) timeout := time.After(time.Second) - loop: for { +loop: + for { src.High() - time.Sleep(time.Second/5) + time.Sleep(time.Second / 5) if pin.EdgeDetected() { t.Log("edge rised") } else { - t.Errorf("Raise event should be detected") + t.Errorf("Rise event should be detected") } select { - case <- timeout: - break loop - default: + case <-timeout: + break loop + default: } src.Low() @@ -48,12 +50,11 @@ func TestRisingEdgeEvent(t *testing.T) { pin.Detect(NoEdge) src.High() if pin.EdgeDetected() { - t.Error("Fall should not be detected, events disabled") + t.Error("Rise should not be detected, events disabled") } } func TestFallingEdgeEvent(t *testing.T) { - // bcm pins 2 and 3 has to be directly connected src := Pin(3) src.Mode(Output) src.High() @@ -64,23 +65,24 @@ func TestFallingEdgeEvent(t *testing.T) { pin.Detect(FallEdge) timeout := time.After(time.Second) - loop: for { - src.Low() +loop: + for { + src.Low() - time.Sleep(time.Second/5) - if pin.EdgeDetected() { - t.Log("edge fallen") - } else { - t.Errorf("Fall event should be detected") - } + time.Sleep(time.Second / 5) + if pin.EdgeDetected() { + t.Log("edge fallen") + } else { + t.Errorf("Fall event should be detected") + } - select { - case <- timeout: - break loop - default: - } + select { + case <-timeout: + break loop + default: + } - src.High() + src.High() } if pin.EdgeDetected() { t.Error("Fall should not be detected, no change since last call") @@ -90,4 +92,4 @@ func TestFallingEdgeEvent(t *testing.T) { if pin.EdgeDetected() { t.Error("Fall should not be detected, events disabled") } -} \ No newline at end of file +} From 250b0836f85b21ed8bb5c1cf2b46d45f19c5976e Mon Sep 17 00:00:00 2001 From: Drahoslav Date: Wed, 6 Jun 2018 17:51:04 +0200 Subject: [PATCH 6/7] Improve event unit test --- rpio_test.go | 155 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 90 insertions(+), 65 deletions(-) diff --git a/rpio_test.go b/rpio_test.go index aed0312..23e40fa 100644 --- a/rpio_test.go +++ b/rpio_test.go @@ -15,81 +15,106 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } -func TestRisingEdgeEvent(t *testing.T) { +func TestEvent(t *testing.T) { src := Pin(3) src.Mode(Output) - src.Low() pin := Pin(2) pin.Mode(Input) pin.PullDown() - pin.Detect(RiseEdge) - timeout := time.After(time.Second) -loop: - for { - src.High() - - time.Sleep(time.Second / 5) - if pin.EdgeDetected() { - t.Log("edge rised") - } else { - t.Errorf("Rise event should be detected") - } - select { - case <-timeout: - break loop - default: - } - - src.Low() - } - if pin.EdgeDetected() { - t.Error("Rise should not be detected, no change since last call") - } - pin.Detect(NoEdge) - src.High() - if pin.EdgeDetected() { - t.Error("Rise should not be detected, events disabled") - } -} - -func TestFallingEdgeEvent(t *testing.T) { - src := Pin(3) - src.Mode(Output) - src.High() - - pin := Pin(2) - pin.Mode(Input) - pin.PullDown() - pin.Detect(FallEdge) - - timeout := time.After(time.Second) -loop: - for { + t.Run("rising edge", func(t *testing.T) { + pin.Detect(RiseEdge) src.Low() - time.Sleep(time.Second / 5) + for i := 0; ; i++ { + src.High() + + time.Sleep(time.Second / 10) + if pin.EdgeDetected() { + t.Log("edge rised") + } else { + t.Errorf("Rise event should be detected") + } + if i == 5 { + break + } + + src.Low() + } + + time.Sleep(time.Second / 10) if pin.EdgeDetected() { - t.Log("edge fallen") - } else { - t.Errorf("Fall event should be detected") + t.Error("Rise should not be detected, no change since last call") } - - select { - case <-timeout: - break loop - default: - } - + pin.Detect(NoEdge) src.High() - } - if pin.EdgeDetected() { - t.Error("Fall should not be detected, no change since last call") - } - pin.Detect(NoEdge) - src.Low() - if pin.EdgeDetected() { - t.Error("Fall should not be detected, events disabled") - } + if pin.EdgeDetected() { + t.Error("Rise should not be detected, events disabled") + } + + }) + + t.Run("falling edge", func(t *testing.T) { + pin.Detect(FallEdge) + src.High() + + for i := 0; ; i++ { + src.Low() + + time.Sleep(time.Second / 10) + if pin.EdgeDetected() { + t.Log("edge fallen") + } else { + t.Errorf("Fall event should be detected") + } + + if i == 5 { + break + } + + src.High() + } + time.Sleep(time.Second / 10) + if pin.EdgeDetected() { + t.Error("Fall should not be detected, no change since last call") + } + pin.Detect(NoEdge) + src.Low() + if pin.EdgeDetected() { + t.Error("Fall should not be detected, events disabled") + } + }) + + t.Run("both edges", func(t *testing.T) { + pin.Detect(AnyEdge) + src.Low() + + for i := 0; i < 5; i++ { + src.High() + + if pin.EdgeDetected() { + t.Log("edge detected") + } else { + t.Errorf("Rise event shoud be detected") + } + + src.Low() + + if pin.EdgeDetected() { + t.Log("edge detected") + } else { + t.Errorf("Fall edge should be detected") + } + } + + pin.Detect(NoEdge) + src.High() + src.Low() + + if pin.EdgeDetected() { + t.Errorf("No edge should be detected, events disabled") + } + + }) } From 3084a4df91dcf9fef37f0bb324db6ae7bfc75c59 Mon Sep 17 00:00:00 2001 From: Drahoslav Date: Wed, 6 Jun 2018 17:54:16 +0200 Subject: [PATCH 7/7] Improve doc of edge detection --- rpio.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/rpio.go b/rpio.go index d28e220..155766c 100644 --- a/rpio.go +++ b/rpio.go @@ -6,6 +6,7 @@ Supports simple operations such as: - Pin mode/direction (input/output/clock/pwm) - Pin write (high/low) - Pin read (high/low) + - Pin edge detection (no/rise/fall/any) - Pull up/down/off And clock/pwm related oparations: - Set Clock frequency @@ -321,15 +322,17 @@ func TogglePin(pin Pin) { } } -// Enable edge event detection on pin +// Enable edge event detection on pin. // // Combine with pin.EdgeDetected() to check whether event occured. // +// Note that using this function might conflict with the same functionality of other gpio library. +// // It also clears previously detected event of this pin if any. // -// Note that call with RiseEdge will disable previously set FallEdge detection and vice versa -// You have to call with AnyEdge, to enable detection for both edges -// To disable previously enabled detection call it with NoEdge +// Note that call with RiseEdge will disable previously set FallEdge detection and vice versa. +// You have to call with AnyEdge, to enable detection for both edges. +// To disable previously enabled detection call it with NoEdge. func DetectEdge(pin Pin, edge Edge) { p := uint8(pin) @@ -356,7 +359,10 @@ func DetectEdge(pin Pin, edge Edge) { gpioMem[edsReg] = bit // to clear outdated detection } -// Check whether edge event occured +// Check whether edge event occured since last call +// or since detection was enabled +// +// There is no way (yet) to handle interruption caused by edge event, you have to use polling. // // Event detection has to be enabled first, by pin.Detect(edge) func EdgeDetected(pin Pin) bool {