-
Notifications
You must be signed in to change notification settings - Fork 1
/
myencoder.py
231 lines (206 loc) · 7.28 KB
/
myencoder.py
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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
#----------------------------------------------------------------------
# rotary_encoder.py from https://github.com/guyc/py-gaugette
# Guy Carpenter, Clearwater Software
# Updated by r0bin - using RPi GPIO lib & adding switch feature
#
# This is a class for reading quadrature rotary encoders
# like the PEC11 Series available from Adafruit:
# http://www.adafruit.com/products/377
# The datasheet for this encoder is here:
# http://www.adafruit.com/datasheets/pec11.pdf
#
# This library expects the common pin C to be connected
# to ground. Pins A and B will have their pull-up resistor
# pulled high.
#
# Usage:
#
# import gaugette.rotary_encoder
# A_PIN = 7 # use wiring pin numbers here
# B_PIN = 9
# gpio = gaugette.gpio.GPIO()
# encoder = gaugette.rotary_encoder.RotaryEncoder(gpio, A_PIN, B_PIN)
# while 1:
# delta = encoder.get_delta() # returns 0,1,or -1
# if delta!=0:
# print delta
import math
import threading
import time
from RPi import GPIO
class RotaryEncoder:
#----------------------------------------------------------------------
# Pass the wiring pin numbers here. See:
# https://projects.drogon.net/raspberry-pi/wiringpi2/pins/
#----------------------------------------------------------------------
def __init__(self, a_pin, b_pin, sw_pin, touch_pin):
self.a_pin = a_pin
self.b_pin = b_pin
self.sw_pin = sw_pin
self.t_pin = touch_pin
self.bPush = False
self.bTouch = False
self.lokE = threading.Lock()
self.lokP = threading.Lock()
self.lokT = threading.Lock()
GPIO.setmode(GPIO.BCM)
GPIO.setup(a_pin, GPIO.IN)
GPIO.setup(b_pin, GPIO.IN)
GPIO.setup(sw_pin, GPIO.IN)
#this one has no pullup
GPIO.setup(touch_pin, GPIO.IN,pull_up_down=GPIO.PUD_DOWN)
self.steps = 0
self.last_delta = 0
self.r_seq = self.rotation_sequence()
# steps_per_cycle and self.remainder are only used in get_cycles which
# returns a coarse-granularity step count. By default
# steps_per_cycle is 4 as there are 4 steps per
# detent on my encoder, and get_cycles() will return a signed
# count of full detent steps.
self.steps_per_cycle = 4
self.remainder = 0
# Gets the 2-bit rotation state of the current position
# This is deprecated - we now use rotation_sequence instead.
def rotation_state(self):
# a_state = self.gpio.input(self.a_pin)
# b_state = self.gpio.input(self.b_pin)
a_state = GPIO.input(self.a_pin)
b_state = GPIO.input(self.b_pin)
r_state = a_state | b_state << 1
return r_state
# Returns the quadrature encoder state converted into
# a numerical sequence 0,1,2,3,0,1,2,3...
#
# Turning the encoder clockwise generates these
# values for switches B and A:
# B A
# 0 0
# 0 1
# 1 1
# 1 0
# We convert these to an ordinal sequence number by returning
# seq = (A ^ B) | B << 2
#
def rotation_sequence(self):
# a_state = self.gpio.input(self.a_pin)
# b_state = self.gpio.input(self.b_pin)
a_state = GPIO.input(self.a_pin)
b_state = GPIO.input(self.b_pin)
r_seq = (a_state ^ b_state) | b_state << 1
return r_seq
def update(self):
delta = 0
r_seq = self.rotation_sequence()
if r_seq != self.r_seq:
delta = (r_seq - self.r_seq) % 4
if delta == 3:
delta = -1
elif delta == 2:
delta = int(math.copysign(delta, self.last_delta)) # same direction as previous, 2 steps
self.last_delta = delta
self.r_seq = r_seq
self.lokE.acquire()
self.steps += delta
self.lokE.release()
def get_steps(self):
self.lokE.acquire()
steps = self.steps
self.steps = 0
self.lokE.release()
return steps
def updatePush(self,args):
self.lokP.acquire()
#avoid false positives
if(GPIO.input(self.sw_pin) == 0):
self.bPush = True
# self.Ptimestamp = time.time()
# self.Pantibug = 1
# else:
# print "time diff=",(time.time()-self.Ptimestamp
#if we press enough time, treat as a key push
# if((time.time()-self.Ptimestamp)>1):
# if(self.Pantibug == 1):
# self.bPush = True
# self.Pantibug == 0
self.lokP.release()
def get_bPushed(self):
self.lokP.acquire()
if(self.bPush):
self.bPush = False
self.lokP.release()
return True
else:
self.lokP.release()
return False
def updateTouch(self):
self.lokT.acquire()
if(GPIO.input(self.t_pin)):
self.bTouch = True
self.lokT.release()
def get_bTouched(self):
self.lokT.acquire()
if(self.bTouch):
self.bTouch = False
self.lokT.release()
return True
else:
self.lokT.release()
return False
# get_cycles returns a scaled down step count to match (for example)
# the detents on an encoder switch. If you have 4 delta steps between
# each detent, and you want to count only full detent steps, use
# get_cycles() instead of get_delta(). It returns -1, 0 or 1. If
# you have 2 steps per detent, set encoder.steps_per_cycle to 2
# before you call this method.
def get_cycles(self):
# python negative integers do not behave like they do in C.
# -1 // 2 = -1 (not 0)
# -1 % 2 = 1 (not -1)
# // is integer division operator. Note the behaviour of the / operator
# when used on integers changed between python 2 and 3.
# See http://www.python.org/dev/peps/pep-0238/
self.remainder += self.get_steps()
cycles = self.remainder // self.steps_per_cycle
self.remainder %= self.steps_per_cycle # remainder always remains positive
return cycles
def start(self):
def isr(arg):
self.update()
def isr2(arg):
self.updatePush(arg)
def isr3(arg):
self.updateTouch()
#self.gpio.trigger(self.a_pin, self.gpio.EDGE_BOTH, isr)
#self.gpio.trigger(self.b_pin, self.gpio.EDGE_BOTH, isr)
# GPIO.add_event_detect(self.a_pin,GPIO.BOTH)
# GPIO.add_event_callback(self.a_pin,isr)
# GPIO.add_event_detect(self.b_pin,GPIO.BOTH)
# GPIO.add_event_callback(self.b_pin,isr)
# GPIO.add_event_detect(self.sw_pin,GPIO.FALLING)
# GPIO.add_event_callback(self.sw_pin,isr2)
# GPIO.add_event_detect(self.t_pin,GPIO.RISING)
# GPIO.add_event_callback(self.t_pin,isr3)
GPIO.add_event_detect(self.a_pin,GPIO.BOTH,callback=isr)
GPIO.add_event_detect(self.b_pin,GPIO.BOTH,callback=isr)
GPIO.add_event_detect(self.sw_pin,GPIO.BOTH,callback=isr2,bouncetime=300)
GPIO.add_event_detect(self.t_pin,GPIO.RISING,callback=isr3,bouncetime=150)
# GPIO.add_event_detect(self.t_pin,GPIO.RISING,callback=isr3)
class Worker(threading.Thread):
def __init__(self, gpio, a_pin, b_pin):
threading.Thread.__init__(self)
self.lock = threading.Lock()
self.stopping = False
self.encoder = RotaryEncoder(gpio, a_pin, b_pin)
self.daemon = True
self.delta = 0
self.delay = 0.001
def run(self):
while not self.stopping:
self.encoder.update()
time.sleep(self.delay)
#cleanup GPIOs after use
GPIO.cleanup()
def stop(self):
self.stopping = True
def get_steps(self):
return self.encoder.get_steps()