37
37
38
38
READY_FOR_EVENTS = "ready for events"
39
39
40
+ # deboucing
41
+ DEFAULT_SETTLE_TIME = 0.020 # 20ms
42
+
40
43
41
44
class Timeout (Exception ):
42
45
pass
43
46
44
47
45
48
class InterruptEvent (object ):
46
49
"""An interrupt event."""
47
- def __init__ (self , interrupt_flag , interrupt_capture , board_num ):
50
+ def __init__ (
51
+ self , interrupt_flag , interrupt_capture , board_num , timestamp ):
48
52
self .interrupt_flag = interrupt_flag
49
53
self .interrupt_capture = interrupt_capture
50
54
self .board_num = board_num
55
+ self .timestamp = timestamp
51
56
52
57
@property
53
58
def pin_num (self ):
@@ -62,16 +67,52 @@ class FunctionMap(object):
62
67
"""Maps something to a callback function.
63
68
(This is an abstract class, you must implement a SomethingFunctionMap).
64
69
"""
65
- def __init__ (self , callback ):
70
+ def __init__ (self , callback , settle_time = None ):
66
71
self .callback = callback
72
+ self .settle_time = settle_time
67
73
68
74
69
75
class PinFunctionMap (FunctionMap ):
70
76
"""Maps an IO pin and a direction to callback function."""
71
- def __init__ (self , pin_num , direction , callback ):
77
+ def __init__ (self , pin_num , direction , callback , settle_time ):
72
78
self .pin_num = pin_num
73
79
self .direction = direction
74
- super (PinFunctionMap , self ).__init__ (callback )
80
+ super (PinFunctionMap , self ).__init__ (callback , settle_time )
81
+
82
+
83
+ class EventQueue (object ):
84
+ """Stores events in a queue."""
85
+ def __init__ (self , pin_function_maps ):
86
+ super (EventQueue , self ).__init__ ()
87
+ self .last_event_time = [0 ]* 8 # last event time on each pin
88
+ self .pin_function_maps = pin_function_maps
89
+ self .queue = multiprocessing .Queue ()
90
+
91
+ def add_event (self , event ):
92
+ """Adds events to the queue. Will ignore events that occur before the
93
+ settle time for that pin/direction. Such events are assumed to be
94
+ bouncing.
95
+ """
96
+ # find out the pin settle time
97
+ for pin_function_map in self .pin_function_maps :
98
+ if pin_function_map .pin_num == event .pin_num and \
99
+ pin_function_map .direction == event .direction :
100
+ pin_settle_time = pin_function_map .settle_time
101
+ break
102
+ else :
103
+ # Couldn't find event in map, don't bother adding it to the queue
104
+ return
105
+
106
+ threshold_time = self .last_event_time [event .pin_num ] + pin_settle_time
107
+ if event .timestamp > threshold_time :
108
+ self .put (event )
109
+ self .last_event_time [event .pin_num ] = event .timestamp
110
+
111
+ def put (self , thing ):
112
+ self .queue .put (thing )
113
+
114
+ def get (self ):
115
+ return self .queue .get ()
75
116
76
117
77
118
class PortEventListener (object ):
@@ -92,18 +133,24 @@ def __init__(self, port, board_num=0):
92
133
self .port = port
93
134
self .board_num = board_num
94
135
self .pin_function_maps = list ()
95
- self .event_queue = multiprocessing . Queue ( )
136
+ self .event_queue = EventQueue ( self . pin_function_maps )
96
137
self .detector = multiprocessing .Process (
97
138
target = watch_port_events ,
98
- args = (self .port , self .board_num , self .event_queue ))
139
+ args = (
140
+ self .port ,
141
+ self .board_num ,
142
+ self .pin_function_maps ,
143
+ self .event_queue ))
99
144
self .dispatcher = threading .Thread (
100
- target = handle_events , args = (
145
+ target = handle_events ,
146
+ args = (
101
147
self .pin_function_maps ,
102
148
self .event_queue ,
103
149
_event_matches_pin_function_map ,
104
150
PortEventListener .TERMINATE_SIGNAL ))
105
151
106
- def register (self , pin_num , direction , callback ):
152
+ def register (self , pin_num , direction , callback ,
153
+ settle_time = DEFAULT_SETTLE_TIME ):
107
154
"""Registers a pin number and direction to a callback function.
108
155
109
156
:param pin_num: The pin pin number.
@@ -113,9 +160,11 @@ def register(self, pin_num, direction, callback):
113
160
:type direction: int
114
161
:param callback: The function to run when event is detected.
115
162
:type callback: function
163
+ :param settle_time: Time within which subsequent events are ignored.
164
+ :type settle_time: int
116
165
"""
117
166
self .pin_function_maps .append (
118
- PinFunctionMap (pin_num , direction , callback ))
167
+ PinFunctionMap (pin_num , direction , callback , settle_time ))
119
168
120
169
def activate (self ):
121
170
"""When activated the :class:`PortEventListener` will run callbacks
@@ -140,14 +189,17 @@ def _event_matches_pin_function_map(event, pin_function_map):
140
189
return pin_match and direction_match
141
190
142
191
143
- def watch_port_events (port , board_num , event_queue ):
192
+ def watch_port_events (port , board_num , pin_function_maps , event_queue ):
144
193
"""Waits for a port event. When a port event occurs it is placed onto the
145
194
event queue.
146
195
147
196
:param port: The port we are waiting for interrupts on (GPIOA/GPIOB).
148
197
:type port: int
149
198
:param board_num: The board we are waiting for interrupts on.
150
199
:type board_num: int
200
+ :param pin_function_maps: A list of classes that have inheritted from
201
+ :class:`FunctionMap`\ s describing what to do with events.
202
+ :type pin_function_maps: list
151
203
:param event_queue: A queue to put events on.
152
204
:type event_queue: :py:class:`multiprocessing.Queue`
153
205
"""
@@ -158,7 +210,7 @@ def watch_port_events(port, board_num, event_queue):
158
210
159
211
# a bit map showing what caused the interrupt
160
212
intflag = INTFA if port == GPIOA else INTFB
161
- # a snapshot of the port when int occured
213
+ # a snapshot of the port when interrupt occured
162
214
intcapture = INTCAPA if port == GPIOA else INTCAPB
163
215
164
216
while True :
@@ -171,8 +223,8 @@ def watch_port_events(port, board_num, event_queue):
171
223
continue # The interrupt has not been flagged on this board
172
224
else :
173
225
interrupt_capture = read (intcapture , board_num )
174
- event_queue .put (
175
- InterruptEvent ( interrupt_flag , interrupt_capture , board_num ))
226
+ event_queue .add_event ( InterruptEvent (
227
+ interrupt_flag , interrupt_capture , board_num , time . time () ))
176
228
177
229
epoll .close ()
178
230
@@ -203,6 +255,7 @@ def handle_events(
203
255
if event_matches_function_map (event , fm ) else None ,
204
256
function_maps )
205
257
# reduce to just the callback functions (remove None)
258
+ # TODO: I think this can just be filter(None, functions)
206
259
functions = filter (lambda f : f is not None , functions )
207
260
208
261
for function in functions :
0 commit comments