4
4
import os
5
5
import logging
6
6
import itertools
7
+ import sysfs
7
8
import platform
8
9
9
10
(major , minor , patch ) = platform .release ().split ("-" )[0 ].split ("." )
13
14
'Please upgrade your kernel to use this module.\n '
14
15
'Your Linux kernel version is {}.' .format (platform .release ()))
15
16
17
+
18
+ # eQEP module channel identifiers
19
+ # eQEP 2 and 2b are the same channel, exposed on two different sets of pins,
20
+ # which are mutually exclusive
16
21
eQEP0 = 0
17
22
eQEP1 = 1
18
23
eQEP2 = 2
19
24
eQEP2b = 3
20
25
26
+ # Definitions to initialize the eQEP modules
21
27
_OCP_PATH = "/sys/devices/platform/ocp"
22
28
_eQEP_DEFS = [
23
29
{'channel' : 'eQEP0' , 'pin_A' : 'P9_92' , 'pin_B' : 'P9_27' ,
@@ -58,21 +64,43 @@ def __init__(self, channel, pin_A, pin_B, sys_path):
58
64
rotary encoder
59
65
sys_path (str): sys filesystem path to access the attributes
60
66
of this eQEP module
67
+ node (str): sys filesystem device node that contains the
68
+ readable or writable attributes to control the QEP channel
61
69
62
70
'''
63
71
self .channel = channel
64
72
self .pin_A = pin_A
65
73
self .pin_B = pin_B
66
74
self .sys_path = sys_path
75
+ self .node = sysfs .Node (sys_path )
67
76
68
77
69
78
class RotaryEncoder (object ):
79
+ '''
80
+ Rotary encoder class abstraction to control a given QEP channel.
81
+
82
+ Constructor:
83
+ eqep_num: QEP object that determines which channel to control
84
+
85
+ Properties:
86
+ position: current position of the encoder
87
+ frequency: frequency at which the encoder reports new positions
88
+ enabled: (read only) true if the module is enabled, false otherwise
89
+ mode: current mode of the encoder (absolute: 0, relative: 1)
90
+
91
+ Methods:
92
+ enable: enable the QEP channel
93
+ disable: disable the QEP channel
94
+ setAbsolute: shortcut for setting the mode to absolute
95
+ setRelative: shortcut for setting the mode to relative
96
+ zero: shortcut for setting the position to 0
97
+ '''
70
98
71
99
def _run_cmd (self , cmd ):
72
100
'''Runs a command. If not successful (i.e. error code different than
73
101
zero), print the stderr output as a warning.
74
- '''
75
102
103
+ '''
76
104
try :
77
105
output = check_output (cmd , stderr = STDOUT )
78
106
self ._logger .info (
@@ -83,164 +111,189 @@ def _run_cmd(self, cmd):
83
111
"_run_cmd(): cmd='{}' return code={} output={}" .format (
84
112
" " .join (cmd ), e .returncode , e .output ))
85
113
86
- def config_pin (self , pin ):
87
- '''
88
- config_pin()
89
- Config pin for QEP
90
- '''
114
+ def _config_pin (self , pin ):
115
+ '''Configures a pin in QEP mode using the `config-pin` binary'''
91
116
92
117
self ._run_cmd (["config-pin" , pin , "qep" ])
93
118
94
- def cat_file (self , path ):
95
- '''
96
- cat_file()
97
- Print contents of file
98
- '''
119
+ def __init__ (self , eqep_num ):
120
+ '''Creates an instance of the class RotaryEncoder.
99
121
100
- self ._run_cmd (["cat" , path ])
122
+ Arguments:
123
+ eqep_num: determines which eQEP pins are set up.
124
+ Allowed values: EQEP0, EQEP1, EQEP2 or EQEP2b,
125
+ based on which pins the physical rotary encoder
126
+ is connected to.
101
127
102
- def __init__ (self , eqep_num ):
103
- '''
104
- RotaryEncoder(eqep_num)
105
- Creates an instance of the class RotaryEncoder.
106
- eqep_num determines which eQEP pins are set up.
107
- eqep_num can be: EQEP0, EQEP1, EQEP2 or EQEP2b based on which pins \
108
- the rotary encoder is connected to.
109
128
'''
129
+ # nanoseconds factor to convert period to frequency and back
130
+ self ._NS_FACTOR = 1000000000
110
131
132
+ # Set up logging at the module level
111
133
self ._logger = logging .getLogger (__name__ )
112
134
self ._logger .addHandler (logging .NullHandler ())
113
135
114
- # Configure eqep module
136
+ # Initialize the eQEP channel structures
115
137
self ._eqep = eQEP .fromdict (_eQEP_DEFS [eqep_num ])
116
138
self ._logger .info (
117
139
"Configuring: {}, pin A: {}, pin B: {}, sys path: {}" .format (
118
140
self ._eqep .channel , self ._eqep .pin_A , self ._eqep .pin_B ,
119
141
self ._eqep .sys_path ))
120
142
121
- self .config_pin (self ._eqep .pin_A )
122
- self .config_pin (self ._eqep .pin_B )
143
+ # Configure the pins for the given channel
144
+ self ._config_pin (self ._eqep .pin_A )
145
+ self ._config_pin (self ._eqep .pin_B )
123
146
124
- self .base_dir = self ._eqep .sys_path
125
147
self ._logger .debug (
126
- "RotaryEncoder(): self.base_dir : {0}" .format (self .base_dir ))
148
+ "RotaryEncoder(): sys node : {0}" .format (self ._eqep . sys_path ))
127
149
150
+ # Enable the channel upon initialization
128
151
self .enable ()
129
152
130
- def enable (self ):
153
+ @property
154
+ def enabled (self ):
155
+ '''Returns the enabled status of the module:
156
+
157
+ true: module is enabled
158
+ false: module is disabled
131
159
'''
132
- enable()
133
- Turns the eQEP hardware ON
160
+ isEnabled = bool (int (self ._eqep .node .enabled ))
161
+
162
+ return isEnabled
163
+
164
+ def _setEnable (self , enabled ):
165
+ '''Turns the eQEP hardware ON or OFF
166
+
167
+ value (int): 1 represents enabled, 0 is disabled
168
+
134
169
'''
135
- enable_file = "%s/enabled" % self .base_dir
136
- self ._logger .debug ("enable(): enable_file: {0}" .format (enable_file ))
137
- self ._logger .warning (
138
- "enable(): TODO: not implemented, write 1 to {}" .format (enable_file ))
139
- # return sysfs.kernelFileIO(enable_file, '1')
170
+ enabled = int (enabled )
171
+ if enabled < 0 or enabled > 1 :
172
+ raise ValueError (
173
+ 'The "enabled" attribute can only be set to 0 or 1. '
174
+ 'You attempted to set it to {}.' .format (enabled ))
175
+
176
+ self ._eqep .node .enabled = str (enabled )
177
+ self ._logger .info ("Channel: {}, enabled: {}" .format (
178
+ self ._eqep .channel , self ._eqep .node .enabled ))
179
+
180
+ def enable (self ):
181
+ '''Turns the eQEP hardware ON'''
182
+
183
+ self ._setEnable (1 )
140
184
141
185
def disable (self ):
186
+ '''Turns the eQEP hardware OFF'''
187
+
188
+ self ._setEnable (0 )
189
+
190
+ @property
191
+ def mode (self ):
192
+ '''Returns the mode the eQEP hardware is in (absolute or relative).
193
+
142
194
'''
143
- disable()
144
- Turns the eQEP hardware OFF
195
+ mode = int (self ._eqep .node .mode )
196
+
197
+ if mode == 0 :
198
+ mode_name = "absolute"
199
+ elif mode == 1 :
200
+ mode_name = "relative"
201
+ else :
202
+ mode_name = "invalid"
203
+
204
+ self ._logger .debug ("getMode(): Channel {}, mode: {} ({})" .format (
205
+ self ._eqep .channel , mode , mode_name ))
206
+
207
+ return mode
208
+
209
+ @mode .setter
210
+ def mode (self , mode ):
211
+ '''Sets the eQEP mode as absolute (0) or relative (1).
212
+ See the setAbsolute() and setRelative() methods for
213
+ more information.
214
+
145
215
'''
146
- enable_file = "%s/enabled" % self .base_dir
147
- self ._logger .debug ("disable(): enable_file: {0}" .format (enable_file ))
148
- self ._logger .warning (
149
- "disable(): TODO: not implemented, write 0 to {}" .format (
150
- enable_file ))
151
- # return sysfs.kernelFileIO(enable_file, '0')
216
+ mode = int (mode )
217
+ if mode < 0 or mode > 1 :
218
+ raise ValueError (
219
+ 'The "mode" attribute can only be set to 0 or 1. '
220
+ 'You attempted to set it to {}.' .format (mode ))
221
+
222
+ self ._eqep .node .mode = str (mode )
223
+ self ._logger .debug ("Mode set to: {}" .format (
224
+ self ._eqep .node .mode ))
152
225
153
226
def setAbsolute (self ):
154
- '''
155
- setAbsolute()
156
- Set mode as Absolute
227
+ '''Sets the eQEP mode as Absolute:
157
228
The position starts at zero and is incremented or
158
229
decremented by the encoder's movement
230
+
159
231
'''
160
- mode_file = "%s/mode" % self .base_dir
161
- self ._logger .debug ("setAbsolute(): mode_file: {0}" .format (mode_file ))
162
- self ._logger .warning (
163
- "setAbsolute(): TODO: not implemented, write 0 to {}" .format (
164
- mode_file ))
165
- # return sysfs.kernelFileIO(mode_file, '0')
232
+ self .mode = 0
166
233
167
234
def setRelative (self ):
168
- '''
169
- setRelative()
170
- Set mode as Relative
235
+ '''Sets the eQEP mode as Relative:
171
236
The position is reset when the unit timer overflows.
172
- '''
173
- mode_file = "%s/mode" % self .base_dir
174
- self ._logger .debug ("setRelative(): mode_file: {0}" .format (mode_file ))
175
- self ._logger .warning (
176
- "setRelative(): TODO: not implemented, write 1 to {}" .format (
177
- mode_file ))
178
- # return sysfs.kernelFileIO(mode_file, '1')
179
-
180
- def getMode (self ):
181
- '''
182
- getMode()
183
- Returns the mode the eQEP hardware is in.
184
- '''
185
- mode_file = "%s/mode" % self .base_dir
186
- self ._logger .debug ("getMode(): mode_file: {0}" .format (mode_file ))
187
- self ._logger .warning ("getMode(): TODO: read mode_file" )
188
- # return sysfs.kernelFileIO(mode_file)
189
237
190
- def getPosition (self ):
191
238
'''
192
- getPosition()
193
- Get the current position of the encoder.
239
+ self .mode = 1
240
+
241
+ @property
242
+ def position (self ):
243
+ '''Returns the current position of the encoder.
194
244
In absolute mode, this attribute represents the current position
195
245
of the encoder.
196
246
In relative mode, this attribute represents the position of the
197
247
encoder at the last unit timer overflow.
248
+
198
249
'''
199
- self ._logger .debug ("Channel: {}" .format (self ._eqep .channel ))
200
- position_file = "%s/position" % self .base_dir
201
- self ._logger .debug (
202
- "getPosition(): position_file: {0}" .format (position_file ))
203
- position_handle = open (position_file , 'r' )
204
- self ._logger .debug (
205
- "getPosition(): position_handle: {0}" .format (position_handle ))
206
- position = position_handle .read ()
207
- self ._logger .debug ("getPosition(): position: {0}" .format (position ))
208
- # return sysfs.kernelFileIO(position_file)
250
+ position = self ._eqep .node .position
209
251
210
- return position
252
+ self ._logger .debug ("Get position: Channel {}, position: {}" .format (
253
+ self ._eqep .channel , position ))
254
+
255
+ return int (position )
256
+
257
+ @position .setter
258
+ def position (self , position ):
259
+ '''Sets the current position to a new value'''
260
+
261
+ position = int (position )
262
+ self ._eqep .node .position = str (position )
263
+
264
+ self ._logger .debug ("Set position: Channel {}, position: {}" .format (
265
+ self ._eqep .channel , position ))
266
+
267
+
268
+ @property
269
+ def frequency (self ):
270
+ '''Sets the frequency in Hz at which the driver reports
271
+ new positions.
211
272
212
- def setFrequency (self , freq ):
213
- '''
214
- setFrequency(freq)
215
- Set the frequency in Hz at which the driver reports new positions.
216
273
'''
217
- period_file = "%s/period" % self .base_dir
218
- self ._logger .debug (
219
- "setFrequency(): period_file: {0}" .format (period_file ))
220
- self ._logger .debug ("setFrequency(): freq: {0}" .format (freq ))
274
+ frequency = self ._eqep .node .period / self ._NS_FACTOR
275
+
221
276
self ._logger .debug (
222
- "setFrequency(): 1000000000/freq: {0}" .format (1000000000 / freq ))
223
- self ._logger .debug ("setFrequency(): str(1000000000/freq)): {0}" .format (
224
- str (1000000000 / freq )))
225
- self ._logger .warning (
226
- "setFrequency(): TODO: not implemented, set {} to {}" .format (
227
- period_file , str (1000000000 / freq )))
228
- # return sysfs.kernelFileIO(period_file, str(1000000000/freq))
229
-
230
- def setPosition (self , val ):
231
- '''
232
- setPosition(value)
233
- Give a new value to the current position
277
+ "Set frequency(): Channel {}, frequency: {} Hz, "
278
+ "period: {} ns" .format (
279
+ self ._eqep .channel , frequency , period ))
280
+
281
+ return frequency
282
+
283
+ @frequency .setter
284
+ def frequency (self , frequency ):
285
+ '''Sets the frequency in Hz at which the driver reports
286
+ new positions.
287
+
234
288
'''
235
- position_file = "%s/position" % self .base_dir
236
- self ._logger .warning (
237
- "setPosition(): TODO: not implemented, write position to {}" .format (
238
- position_file ))
239
- # return sysfs.kernelFileIO(position_file, str(val))
289
+ period = self ._NS_FACTOR / frequency # Period in nanoseconds
290
+ self ._eqep .node .period = str (period )
291
+ self ._logger .debug (
292
+ "Set frequency(): Channel {}, frequency: {} Hz, "
293
+ "period: {} ns" .format (
294
+ self ._eqep .channel , frequency , period ))
240
295
241
296
def zero (self ):
242
- '''
243
- zero()s
244
- Set the current position to 0
245
- '''
246
- return self .setPosition (0 )
297
+ '''Resets the current position to 0'''
298
+
299
+ self .position = 0
0 commit comments