-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgpio.go
187 lines (170 loc) · 5.1 KB
/
gpio.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
// gpio is a wrapper package using warthog618's gpiod to watch specific GPIO devices for
// changes. this is the main functionality of gpio-timer
package gpio
import (
"github.com/warthog618/gpiod"
"log"
"sync/atomic"
"time"
)
// pin mapping, see https://hub.libre.computer/t/libre-computer-wiring-tool/40
// Pin Chip Line sysfs Name Pad Ref Desc
// 1 3.3V 3.3V 3.3V 3.3V 3.3V VCC_IO 3.3V
// 3 2 25 89 GPIO2_D1 R17
// 5 2 24 88 GPIO2_D0 P17
// 6 GND GND GND GND GND GND GND
// 7 1 28 60 GPIO1_D4 Y17 PULL DOWN, NOT UP!
// 8 3 4 100 GPIO3_A4 E2
// 9 GND GND GND GND GND GND GND
// 11 2 20 84 GPIO2_C4 V18
// 13 2 21 85 GPIO2_C5 V17
// 17 GND GND GND GND GND VCC_IO GND
// Constants to customize this timer.
var startChip = "gpiochip3"
var laneChip = "gpiochip2"
var startGpio = 4
var lane2Gpio = 24
var lane3Gpio = 25
var lane4Gpio = 20
var lane1Gpio = 21
// GpioTime is a structure that represents a single GPIO event
type GpioTime struct {
Chip string
Lane int
Time time.Duration
Pending atomic.Bool
Line *gpiod.Line
Channel chan int
}
// New initializes the structure to default values
func (this *GpioTime) New(chip string, offset int) {
this.Close()
this.Chip = chip
this.Lane = offset
this.Time = 0
this.Pending.Store(true)
this.Line = nil
this.Channel = make(chan int)
}
// Arm will register the GPIO for a falling edge event
func (this *GpioTime) Arm() (err error) {
this.Pending.Store(true)
this.Line, err = gpiod.RequestLine(this.Chip, this.Lane, gpiod.AsInput,
gpiod.WithEventHandler(this.gpioHandler), gpiod.LineEdgeFalling, gpiod.WithPullUp,
gpiod.WithDebounce(1*time.Millisecond))
return
}
// gpioHandler handles a GPIO event for a given GpioTime struct
func (this *GpioTime) gpioHandler(evt gpiod.LineEvent) {
if evt.Offset == this.Lane {
// if pending, swap and set time
if this.Pending.CompareAndSwap(true, false) {
log.Printf("Received GPIO event %d at %v\n", evt.Offset, evt.Timestamp)
this.Time = evt.Timestamp
// need to non-blocking send this
select {
case this.Channel <- 1:
// message sent
default:
// message dropped
}
} else {
log.Printf("Received GPIO event %d twice\n", evt.Offset)
}
} else {
log.Printf("Received unknown GPIO event %d\n", evt.Offset)
}
log.Println("..end handler..")
}
// WaitForever will wait until the handler is called
func (this *GpioTime) WaitForever() {
for this.Pending.Load() {
select {
case <-this.Channel:
log.Printf("GPIO %d complete.", this.Lane)
case <-time.After(1 * time.Second):
}
}
log.Println("..end wait..")
}
// WaitFor will wait until the handler is called or a set
// amount of time expires
func (this *GpioTime) WaitFor(timeout time.Duration) {
if timeout > 0 && this.Pending.Load() {
select {
case <-this.Channel:
log.Printf("GPIO %d complete.", this.Lane)
case t := <-time.After(timeout):
log.Printf("GPIO %d timeout at %v.\n", this.Lane, t)
}
}
}
// Close will close any open GPIO lanes for the GpioTime struct
// as well as the channel
func (this *GpioTime) Close() {
if this.Line != nil {
this.Line.Close()
this.Line = nil
}
// close(this.Channel) // not safe to do multiple times
log.Println("..end close..")
}
// createLanes initializes an array of GpioTime structures
// to represent the set of Gpio lanes
func createLanes() (lanes [4]*GpioTime) {
for i, _ := range lanes {
lanes[i] = new(GpioTime)
}
lanes[0].New(laneChip, lane1Gpio)
lanes[1].New(laneChip, lane2Gpio)
lanes[2].New(laneChip, lane3Gpio)
lanes[3].New(laneChip, lane4Gpio)
return
}
// ArmStart sets up the interrupt handler for the start GPIO line
func ArmStart() (start *GpioTime, err error) {
start = new(GpioTime)
start.New(startChip, startGpio)
err = start.Arm()
return
}
// ArmLanes sets up the interrupt handler for the all the lane GPIO lines
func ArmLanes() (lanes [4]*GpioTime, err error) {
lanes = createLanes()
for i, _ := range lanes {
err = lanes[i].Arm()
if err != nil {
log.Fatal(err)
}
}
return
}
// WaitForStart waits until the start GPIO triggers
func WaitForStart(start *GpioTime) {
start.WaitForever()
start.Close()
}
// deltaTimes calculates the difference betwene two timestamps
func deltaTimes(start *GpioTime, end *GpioTime) float64 {
if end.Pending.Load() || start.Pending.Load() {
return 0.0
}
return end.Time.Seconds() - start.Time.Seconds()
}
// WaitForLanes waits until all 4 lanes have triggered and returns
// the time difference for each lane
func WaitForLanes(lanes [4]*GpioTime) {
doneAt := time.Now().Add(20 * time.Second)
for i, _ := range lanes {
lanes[i].WaitFor(doneAt.Sub(time.Now()))
lanes[i].Close()
}
}
// GetTimes returns the difference between a set of lanes and start time
// GpioTime structures
func GetTimes(start *GpioTime, lanes [4]*GpioTime) (times [4]float64) {
for i, _ := range times {
times[i] = deltaTimes(start, lanes[i])
}
return
}