Skip to content

Commit aa7ccf1

Browse files
authored
Merge pull request #149 from adafruit/eqep
WIP for rotary encoder support with eQEP
2 parents 4f007af + ed51778 commit aa7ccf1

File tree

3 files changed

+444
-0
lines changed

3 files changed

+444
-0
lines changed

Adafruit_BBIO/Encoder.py

Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
#!/usr/bin/python
2+
3+
# ===========================================================================
4+
# Adafruit_BBIO.Encoder Class
5+
# ===========================================================================
6+
# refers to graycatlabs/PyBBIO/bbio/libraries/RotaryEncoder/rotary_encoder.py
7+
8+
# BeagleBone must boot with cape-universal enabled
9+
# and load the cape-universala overlay in order to
10+
# use all the eQEP pins
11+
#
12+
# Install the latest Device Tree overlays:
13+
# ========================================
14+
# sudo apt-get upgrade bb-cape-overlays
15+
#
16+
# File: /boot/uEnv.txt
17+
# ====================
18+
# uname_r=4.4.62-ti-r99
19+
# cmdline=coherent_pool=1M quiet cape_universal=enable
20+
# cape_enable=bone_capemgr.enable_partno=cape-universala
21+
#
22+
# File: /sys/devices/platform/bone_capemgr/slots
23+
# ==============================================
24+
# 0: PF---- -1
25+
# 1: PF---- -1
26+
# 2: PF---- -1
27+
# 3: PF---- -1
28+
# 4: P-O-L- 0 Override Board Name,00A0,Override Manuf,cape-universala
29+
#
30+
# eqep0: P9_27, P9_92
31+
# ===================
32+
# config-pin P9_27 qep
33+
# config-pin P9_92 qep # alias for P9_42.1
34+
# cat /sys/devices/platform/ocp/48300000.epwmss/48300180.eqep/position
35+
#
36+
# eqep1: P8.33, P8.35
37+
# ===================
38+
# config-pin P8.33 qep
39+
# config-pin P8.35 qep
40+
# cat /sys/devices/platform/ocp/48302000.epwmss/48302180.eqep/position
41+
#
42+
# eqep2: P8.11, P8.12
43+
# ===================
44+
# config-pin P8.11 qep
45+
# config-pin P8.12 qep
46+
# cat /sys/devices/platform/ocp/48304000.epwmss/48304180.eqep/position
47+
#
48+
# alternate pins for eqep2 (mutually exclusive)
49+
# eqep2b: P8.41, P8.42
50+
# ====================
51+
# config-pin P8.41 qep
52+
# config-pin P8.42 qep
53+
# cat /sys/devices/platform/ocp/48304000.epwmss/48304180.eqep/position
54+
55+
from subprocess import call
56+
import os
57+
58+
class QEP :
59+
60+
def __init__(self, channel=1, debug=False):
61+
self.channel = channel
62+
self.debug = debug
63+
64+
def errMsg(self):
65+
print("Error accessing 0x%02X: Check your QEP channel" % self.address)
66+
return -1
67+
68+
# example method from Adafruit_I2C
69+
# TODO: delete this
70+
# def write8(self, reg, value):
71+
# "Writes an 8-bit value to the specified register/address"
72+
# try:
73+
# self.bus.write_byte_data(self.address, reg, value)
74+
# if self.debug:
75+
# print("Rotary: Wrote 0x%02X to register 0x%02X" % (value, reg))
76+
# except IOError as err:
77+
# return self.errMsg()
78+
#
79+
#
80+
#if __name__ == '__main__':
81+
# try:
82+
# qep = Adafruit_BBIO.Encoder.QEP()
83+
# print("Default QEP channel is accessible")
84+
# except:
85+
# print("Error accessing default Rotary bus")
86+
87+
88+
89+
class RotaryEncoder(object):
90+
# TODO: check that kernel 4.1+
91+
# TODO: use config-pin to set qep mode
92+
OCP_PATH = "/sys/devices/platform/ocp"
93+
_eqep_dirs = [
94+
'%s/48300000.epwmss/48300180.eqep' % OCP_PATH,
95+
'%s/48302000.epwmss/48302180.eqep' % OCP_PATH,
96+
'%s/48304000.epwmss/48304180.eqep' % OCP_PATH
97+
]
98+
99+
EQEP0 = 0
100+
EQEP1 = 1
101+
EQEP2 = 2
102+
EQEP2b = 3
103+
104+
def config_pin(self, pin):
105+
'''
106+
config_pin()
107+
Config pin for QEP
108+
'''
109+
result = call(["config-pin", pin, "qep"])
110+
print "config_pin> pin={0} result={1}".format(pin, result)
111+
return result
112+
113+
def cat_file(self, path):
114+
'''
115+
cat_file()
116+
Print contents of file
117+
'''
118+
result = call(["cat", path])
119+
print "cat_file> path={0} result={1}".format(path, result)
120+
return result
121+
122+
def __init__(self, eqep_num):
123+
'''
124+
RotaryEncoder(eqep_num)
125+
Creates an instance of the class RotaryEncoder.
126+
eqep_num determines which eQEP pins are set up.
127+
eqep_num can be: EQEP0, EQEP1, EQEP2 or EQEP2b based on which pins \
128+
the rotary encoder is connected to.
129+
'''
130+
print ">>>>>>>> TEST CALL BEGIN"
131+
132+
###################################
133+
print ">>>>>> eqep0: P9_27, P9_92"
134+
pin = "P9_27"
135+
self.config_pin(pin)
136+
137+
pin = "P9_92"
138+
self.config_pin(pin)
139+
140+
path = "/sys/devices/platform/ocp/48300000.epwmss/48300180.eqep/position"
141+
self.cat_file(path);
142+
143+
###################################
144+
print ">>>>>>> eqep1: P8.33, P8.35"
145+
146+
pin = "P8.33"
147+
self.config_pin(pin)
148+
149+
pin = "P8.35"
150+
self.config_pin(pin)
151+
152+
path = "/sys/devices/platform/ocp/48302000.epwmss/48302180.eqep/position"
153+
self.cat_file(path);
154+
155+
###################################
156+
print ">>>>>>> eqep2: P8.11, P8.12"
157+
158+
pin = "P8.11"
159+
self.config_pin(pin)
160+
161+
pin = "P8.12"
162+
self.config_pin(pin)
163+
164+
path = "/sys/devices/platform/ocp/48304000.epwmss/48304180.eqep/position"
165+
self.cat_file(path);
166+
167+
###################################
168+
print ">>>>>>> eqep2b: P8.41, P8.42"
169+
170+
pin = "P8.41"
171+
self.config_pin(pin)
172+
173+
pin = "P8.42"
174+
self.config_pin(pin)
175+
176+
path = "/sys/devices/platform/ocp/48304000.epwmss/48304180.eqep/position"
177+
self.cat_file(path);
178+
179+
###################################
180+
print ">>>>>>>> TEST CALL END"
181+
182+
print("RotaryEncoder(): eqep_num: {0}".format(eqep_num))
183+
print("RotaryEncoder(): self._eqep_dirs[0]: {0}".format(self._eqep_dirs[0]))
184+
print("RotaryEncoder(): self._eqep_dirs[1]: {0}".format(self._eqep_dirs[1]))
185+
print("RotaryEncoder(): self._eqep_dirs[2]: {0}".format(self._eqep_dirs[2]))
186+
print("RotaryEncoder(): self._eqep_dirs[eqep_num: {0}]: {1}".format(eqep_num, self._eqep_dirs[eqep_num]))
187+
assert 0 <= eqep_num <= 3 , "eqep_num must be between 0 and 3"
188+
self.base_dir = self._eqep_dirs[eqep_num]
189+
print("RotaryEncoder(): self.base_dir: {0}".format(self.base_dir))
190+
self.enable()
191+
192+
def enable(self):
193+
'''
194+
enable()
195+
Turns the eQEP hardware ON
196+
'''
197+
enable_file = "%s/enabled" % self.base_dir
198+
print("enable(): enable_file: {0}".format(enable_file))
199+
print("enable(): TODO: write 1 to enable_file")
200+
#return sysfs.kernelFileIO(enable_file, '1')
201+
202+
def disable(self):
203+
'''
204+
disable()
205+
Turns the eQEP hardware OFF
206+
'''
207+
enable_file = "%s/enabled" % self.base_dir
208+
print("disable(): enable_file: {0}".format(enable_file))
209+
print("disable(): TODO: write 0 to enable_file")
210+
#return sysfs.kernelFileIO(enable_file, '0')
211+
212+
def setAbsolute(self):
213+
'''
214+
setAbsolute()
215+
Set mode as Absolute
216+
The position starts at zero and is incremented or
217+
decremented by the encoder's movement
218+
'''
219+
mode_file = "%s/mode" % self.base_dir
220+
print("setAbsolute(): mode_file: {0}".format(mode_file))
221+
print("setAbsolute(): TODO: write 0 to mode_file")
222+
#return sysfs.kernelFileIO(mode_file, '0')
223+
224+
def setRelative(self):
225+
'''
226+
setRelative()
227+
Set mode as Relative
228+
The position is reset when the unit timer overflows.
229+
'''
230+
mode_file = "%s/mode" % self.base_dir
231+
print("setRelative(): mode_file: {0}".format(mode_file))
232+
print("setRelative(): TODO: write 1 to mode_file")
233+
#return sysfs.kernelFileIO(mode_file, '1')
234+
235+
def getMode(self):
236+
'''
237+
getMode()
238+
Returns the mode the eQEP hardware is in.
239+
'''
240+
mode_file = "%s/mode" % self.base_dir
241+
print("getMode(): mode_file: {0}".format(mode_file))
242+
print("getMode(): TODO: read mode_file")
243+
#return sysfs.kernelFileIO(mode_file)
244+
245+
def getPosition(self):
246+
'''
247+
getPosition()
248+
Get the current position of the encoder.
249+
In absolute mode, this attribute represents the current position
250+
of the encoder.
251+
In relative mode, this attribute represents the position of the
252+
encoder at the last unit timer overflow.
253+
'''
254+
position_file = "%s/position" % self.base_dir
255+
print("getPosition(): position_file: {0}".format(position_file))
256+
position_handle = open(position_file, 'r')
257+
print("getPosition(): position_handle: {0}".format(position_handle))
258+
position = position_handle.read()
259+
print("getPosition(): position: {0}".format(position))
260+
#return sysfs.kernelFileIO(position_file)
261+
return position
262+
263+
def setFrequency(self,freq):
264+
'''
265+
setFrequency(freq)
266+
Set the frequency in Hz at which the driver reports new positions.
267+
'''
268+
period_file = "%s/period" % self.base_dir
269+
print("setFrequency(): period_file: {0}".format(period_file))
270+
print("setFrequency(): freq: {0}".format(period_file))
271+
print("setFrequency(): freq: {0}".format(freq))
272+
print("setFrequency(): 1000000000/freq: {0}".format(1000000000/freq))
273+
print("setFrequency(): str(1000000000/freq)): {0}".format(str(1000000000/freq)))
274+
print("setFrequency(): TODO: set period_file: {0}".format(str(1000000000/freq)))
275+
#return sysfs.kernelFileIO(period_file, str(1000000000/freq))
276+
277+
def setPosition(self,val):
278+
'''
279+
setPosition(value)
280+
Give a new value to the current position
281+
'''
282+
position_file = "%s/position" % self.base_dir
283+
#return sysfs.kernelFileIO(position_file, str(val))
284+
285+
def zero(self):
286+
'''
287+
zero()s
288+
Set the current position to 0
289+
'''
290+
return self.setPosition(0)
291+
292+
293+
#"""
294+
# encoder_test.py
295+
# Rekha Seethamraju
296+
# An example to demonstrate the use of the eQEP library
297+
# for PyBBIO.
298+
# This example program is in the public domain.
299+
#"""
300+
#from bbio import *
301+
#from bbio.libraries.RotaryEncoder import RotaryEncoder
302+
#
303+
#encoder = RotaryEncoder(RotaryEncoder.EQEP2b)
304+
#
305+
#def setup():
306+
# encoder.setAbsolute()
307+
# encoder.zero()
308+
#
309+
#def loop():
310+
# print "encoder position : "+encoder.getPosition()
311+
# delay(1000)
312+
#
313+
#run(setup, loop)

doc/rotary-encoder-eqep-test.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
I have documented how-to to enable all the eqep pins:
2+
https://github.com/adafruit/adafruit-beaglebone-io-python/commit/c418cdae9a2a2c0d52412561c0125b0d227af4eb
3+
4+
BeagleBone must boot with cape-universal enabled and load the `cape-universala` overlay in order to
5+
use all the eQEP pins:
6+
7+
### Install the latest Device Tree overlays:
8+
```
9+
sudo apt-get upgrade bb-cape-overlays
10+
```
11+
12+
### File: /boot/uEnv.txt
13+
```
14+
uname_r=4.4.62-ti-r99
15+
cmdline=coherent_pool=1M quiet cape_universal=enable
16+
cape_enable=bone_capemgr.enable_partno=cape-universala
17+
```
18+
19+
### File: /sys/devices/platform/bone_capemgr/slots
20+
```
21+
0: PF---- -1
22+
1: PF---- -1
23+
2: PF---- -1
24+
3: PF---- -1
25+
4: P-O-L- 0 Override Board Name,00A0,Override Manuf,cape-universala
26+
```
27+
28+
### eqep0: P9_27, P9_92
29+
```
30+
config-pin P9_27 qep
31+
config-pin P9_92 qep # alias for P9_42.1
32+
cat /sys/devices/platform/ocp/48300000.epwmss/48300180.eqep/position
33+
```
34+
35+
### eqep1: P8.33, P8.35
36+
```
37+
config-pin P8.33 qep
38+
config-pin P8.35 qep
39+
cat /sys/devices/platform/ocp/48302000.epwmss/48302180.eqep/position
40+
```
41+
42+
### eqep2: P8.11, P8.12
43+
```
44+
config-pin P8.11 qep
45+
config-pin P8.12 qep
46+
cat /sys/devices/platform/ocp/48304000.epwmss/48304180.eqep/position
47+
```
48+
49+
### eqep2b: P8.41, P8.42
50+
_alternate pins for eqep2 (mutually exclusive)_
51+
```
52+
config-pin P8.41 qep
53+
config-pin P8.42 qep
54+
cat /sys/devices/platform/ocp/48304000.epwmss/48304180.eqep/position
55+
```
56+
57+
### TODO: implement in corresponding methods in `Encoder.py`

0 commit comments

Comments
 (0)