Skip to content

Commit c58e3a0

Browse files
author
Thomas Preston
committed
Merge branch 'dev_interrupts' into testing
2 parents 29d1f98 + 05ea6fe commit c58e3a0

File tree

3 files changed

+71
-14
lines changed

3 files changed

+71
-14
lines changed

CHANGELOG

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
Change Log
22
==========
33

4+
v3.1.0
5+
------
6+
- Added debouncing with adjustable `settle time`.
7+
48
v3.0.0
59
------
610
- Added timeout class (fixing Issue #2) in interrupts.

pifacecommon/interrupts.py

Lines changed: 66 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,22 @@
3737

3838
READY_FOR_EVENTS = "ready for events"
3939

40+
# deboucing
41+
DEFAULT_SETTLE_TIME = 0.020 # 20ms
42+
4043

4144
class Timeout(Exception):
4245
pass
4346

4447

4548
class InterruptEvent(object):
4649
"""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):
4852
self.interrupt_flag = interrupt_flag
4953
self.interrupt_capture = interrupt_capture
5054
self.board_num = board_num
55+
self.timestamp = timestamp
5156

5257
@property
5358
def pin_num(self):
@@ -62,16 +67,52 @@ class FunctionMap(object):
6267
"""Maps something to a callback function.
6368
(This is an abstract class, you must implement a SomethingFunctionMap).
6469
"""
65-
def __init__(self, callback):
70+
def __init__(self, callback, settle_time=None):
6671
self.callback = callback
72+
self.settle_time = settle_time
6773

6874

6975
class PinFunctionMap(FunctionMap):
7076
"""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):
7278
self.pin_num = pin_num
7379
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()
75116

76117

77118
class PortEventListener(object):
@@ -92,18 +133,24 @@ def __init__(self, port, board_num=0):
92133
self.port = port
93134
self.board_num = board_num
94135
self.pin_function_maps = list()
95-
self.event_queue = multiprocessing.Queue()
136+
self.event_queue = EventQueue(self.pin_function_maps)
96137
self.detector = multiprocessing.Process(
97138
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))
99144
self.dispatcher = threading.Thread(
100-
target=handle_events, args=(
145+
target=handle_events,
146+
args=(
101147
self.pin_function_maps,
102148
self.event_queue,
103149
_event_matches_pin_function_map,
104150
PortEventListener.TERMINATE_SIGNAL))
105151

106-
def register(self, pin_num, direction, callback):
152+
def register(self, pin_num, direction, callback,
153+
settle_time=DEFAULT_SETTLE_TIME):
107154
"""Registers a pin number and direction to a callback function.
108155
109156
:param pin_num: The pin pin number.
@@ -113,9 +160,11 @@ def register(self, pin_num, direction, callback):
113160
:type direction: int
114161
:param callback: The function to run when event is detected.
115162
:type callback: function
163+
:param settle_time: Time within which subsequent events are ignored.
164+
:type settle_time: int
116165
"""
117166
self.pin_function_maps.append(
118-
PinFunctionMap(pin_num, direction, callback))
167+
PinFunctionMap(pin_num, direction, callback, settle_time))
119168

120169
def activate(self):
121170
"""When activated the :class:`PortEventListener` will run callbacks
@@ -140,14 +189,17 @@ def _event_matches_pin_function_map(event, pin_function_map):
140189
return pin_match and direction_match
141190

142191

143-
def watch_port_events(port, board_num, event_queue):
192+
def watch_port_events(port, board_num, pin_function_maps, event_queue):
144193
"""Waits for a port event. When a port event occurs it is placed onto the
145194
event queue.
146195
147196
:param port: The port we are waiting for interrupts on (GPIOA/GPIOB).
148197
:type port: int
149198
:param board_num: The board we are waiting for interrupts on.
150199
: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
151203
:param event_queue: A queue to put events on.
152204
:type event_queue: :py:class:`multiprocessing.Queue`
153205
"""
@@ -158,7 +210,7 @@ def watch_port_events(port, board_num, event_queue):
158210

159211
# a bit map showing what caused the interrupt
160212
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
162214
intcapture = INTCAPA if port == GPIOA else INTCAPB
163215

164216
while True:
@@ -171,8 +223,8 @@ def watch_port_events(port, board_num, event_queue):
171223
continue # The interrupt has not been flagged on this board
172224
else:
173225
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()))
176228

177229
epoll.close()
178230

@@ -203,6 +255,7 @@ def handle_events(
203255
if event_matches_function_map(event, fm) else None,
204256
function_maps)
205257
# reduce to just the callback functions (remove None)
258+
# TODO: I think this can just be filter(None, functions)
206259
functions = filter(lambda f: f is not None, functions)
207260

208261
for function in functions:

pifacecommon/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '3.0.0'
1+
__version__ = '3.1.0'

0 commit comments

Comments
 (0)