|
4 | 4 | import os
|
5 | 5 | import logging
|
6 | 6 | import itertools
|
| 7 | +import sysfs |
7 | 8 | import platform
|
8 | 9 |
|
9 | 10 | if not platform.release().startswith('4.4'):
|
@@ -57,12 +58,15 @@ def __init__(self, channel, pin_A, pin_B, sys_path):
|
57 | 58 | rotary encoder
|
58 | 59 | sys_path (str): sys filesystem path to access the attributes
|
59 | 60 | of this eQEP module
|
| 61 | + node (str): sys filesystem device node that contains the |
| 62 | + readable or writable attributes to control the QEP channel |
60 | 63 |
|
61 | 64 | '''
|
62 | 65 | self.channel = channel
|
63 | 66 | self.pin_A = pin_A
|
64 | 67 | self.pin_B = pin_B
|
65 | 68 | self.sys_path = sys_path
|
| 69 | + self.node = sysfs.Node(sys_path) |
66 | 70 |
|
67 | 71 |
|
68 | 72 | class RotaryEncoder(object):
|
@@ -90,156 +94,145 @@ def config_pin(self, pin):
|
90 | 94 |
|
91 | 95 | self._run_cmd(["config-pin", pin, "qep"])
|
92 | 96 |
|
93 |
| - def cat_file(self, path): |
94 |
| - ''' |
95 |
| - cat_file() |
96 |
| - Print contents of file |
97 |
| - ''' |
98 |
| - |
99 |
| - self._run_cmd(["cat", path]) |
100 |
| - |
101 | 97 | def __init__(self, eqep_num):
|
102 |
| - ''' |
103 |
| - RotaryEncoder(eqep_num) |
104 |
| - Creates an instance of the class RotaryEncoder. |
105 |
| - eqep_num determines which eQEP pins are set up. |
106 |
| - eqep_num can be: EQEP0, EQEP1, EQEP2 or EQEP2b based on which pins \ |
107 |
| - the rotary encoder is connected to. |
108 |
| - ''' |
| 98 | + '''Creates an instance of the class RotaryEncoder. |
109 | 99 |
|
| 100 | + Arguments: |
| 101 | + eqep_num: determines which eQEP pins are set up. |
| 102 | + Allowed values: EQEP0, EQEP1, EQEP2 or EQEP2b, |
| 103 | + based on which pins the physical rotary encoder |
| 104 | + is connected to. |
| 105 | + |
| 106 | + ''' |
| 107 | + # Set up logging at the module level |
110 | 108 | self._logger = logging.getLogger(__name__)
|
111 | 109 | self._logger.addHandler(logging.NullHandler())
|
112 | 110 |
|
113 |
| - # Configure eqep module |
| 111 | + # Initialize the eQEP channel structures |
114 | 112 | self._eqep = eQEP.fromdict(_eQEP_DEFS[eqep_num])
|
115 | 113 | self._logger.info(
|
116 | 114 | "Configuring: {}, pin A: {}, pin B: {}, sys path: {}".format(
|
117 | 115 | self._eqep.channel, self._eqep.pin_A, self._eqep.pin_B,
|
118 | 116 | self._eqep.sys_path))
|
119 | 117 |
|
| 118 | + # Configure the pins for the given channel |
120 | 119 | self.config_pin(self._eqep.pin_A)
|
121 | 120 | self.config_pin(self._eqep.pin_B)
|
122 | 121 |
|
123 |
| - self.base_dir = self._eqep.sys_path |
124 | 122 | self._logger.debug(
|
125 |
| - "RotaryEncoder(): self.base_dir: {0}".format(self.base_dir)) |
| 123 | + "RotaryEncoder(): sys node: {0}".format(self._eqep.sys_path)) |
126 | 124 |
|
| 125 | + # Enable the channel upon initialization |
127 | 126 | self.enable()
|
128 | 127 |
|
129 |
| - def enable(self): |
130 |
| - ''' |
131 |
| - enable() |
132 |
| - Turns the eQEP hardware ON |
| 128 | + def _setEnable(self, value): |
| 129 | + '''Turns the eQEP hardware ON or OFF |
| 130 | +
|
| 131 | + value (int): 1 represents enabled, 0 is disabled |
133 | 132 | '''
|
134 |
| - enable_file = "%s/enabled" % self.base_dir |
135 |
| - self._logger.debug("enable(): enable_file: {0}".format(enable_file)) |
136 |
| - self._logger.warning( |
137 |
| - "enable(): TODO: not implemented, write 1 to {}".format(enable_file)) |
138 |
| - # return sysfs.kernelFileIO(enable_file, '1') |
| 133 | + if value < 0 or value > 1: |
| 134 | + raise ValueError( |
| 135 | + 'The "enabled" attribute can only be set to 0 or 1. ' |
| 136 | + 'You attempted to set it to {}.'.format(value)) |
| 137 | + |
| 138 | + self._eqep.node.enabled = str(int(value)) |
| 139 | + self._logger.info("Channel: {}, enabled: {}".format( |
| 140 | + self._eqep.channel, self._eqep.node.enabled)) |
| 141 | + |
| 142 | + def enable(self): |
| 143 | + '''Turns the eQEP hardware ON''' |
| 144 | + |
| 145 | + self._setEnable(1) |
139 | 146 |
|
140 | 147 | def disable(self):
|
| 148 | + '''Turns the eQEP hardware OFF''' |
| 149 | + |
| 150 | + self._setEnable(0) |
| 151 | + |
| 152 | + def _setMode(self, value): |
| 153 | + '''Sets the eQEP mode as absolute (0) or relative (1). |
| 154 | + See the setAbsolute() and setRelative() methods for |
| 155 | + more information. |
| 156 | +
|
141 | 157 | '''
|
142 |
| - disable() |
143 |
| - Turns the eQEP hardware OFF |
144 |
| - ''' |
145 |
| - enable_file = "%s/enabled" % self.base_dir |
146 |
| - self._logger.debug("disable(): enable_file: {0}".format(enable_file)) |
147 |
| - self._logger.warning( |
148 |
| - "disable(): TODO: not implemented, write 0 to {}".format( |
149 |
| - enable_file)) |
150 |
| - # return sysfs.kernelFileIO(enable_file, '0') |
| 158 | + if value < 0 or value > 1: |
| 159 | + raise ValueError( |
| 160 | + 'The "mode" attribute can only be set to 0 or 1. ' |
| 161 | + 'You attempted to set it to {}.'.format(value)) |
| 162 | + |
| 163 | + self._eqep.node.mode = str(int(value)) |
| 164 | + self._logger.debug("Mode set to: {}".format( |
| 165 | + self._eqep.node.mode)) |
151 | 166 |
|
152 | 167 | def setAbsolute(self):
|
153 |
| - ''' |
154 |
| - setAbsolute() |
155 |
| - Set mode as Absolute |
| 168 | + '''Sets the eQEP mode as Absolute: |
156 | 169 | The position starts at zero and is incremented or
|
157 | 170 | decremented by the encoder's movement
|
| 171 | +
|
158 | 172 | '''
|
159 |
| - mode_file = "%s/mode" % self.base_dir |
160 |
| - self._logger.debug("setAbsolute(): mode_file: {0}".format(mode_file)) |
161 |
| - self._logger.warning( |
162 |
| - "setAbsolute(): TODO: not implemented, write 0 to {}".format( |
163 |
| - mode_file)) |
164 |
| - # return sysfs.kernelFileIO(mode_file, '0') |
| 173 | + self._setMode(0) |
165 | 174 |
|
166 | 175 | def setRelative(self):
|
167 |
| - ''' |
168 |
| - setRelative() |
169 |
| - Set mode as Relative |
| 176 | + '''Sets the eQEP mode as Relative: |
170 | 177 | The position is reset when the unit timer overflows.
|
| 178 | +
|
171 | 179 | '''
|
172 |
| - mode_file = "%s/mode" % self.base_dir |
173 |
| - self._logger.debug("setRelative(): mode_file: {0}".format(mode_file)) |
174 |
| - self._logger.warning( |
175 |
| - "setRelative(): TODO: not implemented, write 1 to {}".format( |
176 |
| - mode_file)) |
177 |
| - # return sysfs.kernelFileIO(mode_file, '1') |
| 180 | + self._setMode(1) |
178 | 181 |
|
179 | 182 | def getMode(self):
|
| 183 | + '''Returns the mode the eQEP hardware is in (absolute or relative). |
| 184 | +
|
180 | 185 | '''
|
181 |
| - getMode() |
182 |
| - Returns the mode the eQEP hardware is in. |
183 |
| - ''' |
184 |
| - mode_file = "%s/mode" % self.base_dir |
185 |
| - self._logger.debug("getMode(): mode_file: {0}".format(mode_file)) |
186 |
| - self._logger.warning("getMode(): TODO: read mode_file") |
187 |
| - # return sysfs.kernelFileIO(mode_file) |
| 186 | + |
| 187 | + mode = int(self._eqep.node.mode) |
| 188 | + |
| 189 | + if mode == 0: |
| 190 | + mode_name = "absolute" |
| 191 | + elif mode == 1: |
| 192 | + mode_name = "relative" |
| 193 | + else: |
| 194 | + mode_name = "invalid" |
| 195 | + |
| 196 | + self._logger.debug("getMode(): Channel {}, mode: {} ({})".format( |
| 197 | + self._eqep.channel, mode, mode_name)) |
| 198 | + |
| 199 | + return mode |
188 | 200 |
|
189 | 201 | def getPosition(self):
|
190 |
| - ''' |
191 |
| - getPosition() |
192 |
| - Get the current position of the encoder. |
| 202 | + '''Returns the current position of the encoder. |
193 | 203 | In absolute mode, this attribute represents the current position
|
194 | 204 | of the encoder.
|
195 | 205 | In relative mode, this attribute represents the position of the
|
196 | 206 | encoder at the last unit timer overflow.
|
| 207 | +
|
197 | 208 | '''
|
198 |
| - self._logger.debug("Channel: {}".format(self._eqep.channel)) |
199 |
| - position_file = "%s/position" % self.base_dir |
200 |
| - self._logger.debug( |
201 |
| - "getPosition(): position_file: {0}".format(position_file)) |
202 |
| - position_handle = open(position_file, 'r') |
203 |
| - self._logger.debug( |
204 |
| - "getPosition(): position_handle: {0}".format(position_handle)) |
205 |
| - position = position_handle.read() |
206 |
| - self._logger.debug("getPosition(): position: {0}".format(position)) |
207 |
| - # return sysfs.kernelFileIO(position_file) |
| 209 | + position = self._eqep.node.position |
| 210 | + |
| 211 | + self._logger.debug("getPosition(): Channel {}, position: {}".format( |
| 212 | + self._eqep.channel, position)) |
208 | 213 |
|
209 |
| - return position |
| 214 | + return int(position) |
210 | 215 |
|
211 | 216 | def setFrequency(self, freq):
|
| 217 | + '''Sets the frequency in Hz at which the driver reports |
| 218 | + new positions. |
| 219 | +
|
212 | 220 | '''
|
213 |
| - setFrequency(freq) |
214 |
| - Set the frequency in Hz at which the driver reports new positions. |
215 |
| - ''' |
216 |
| - period_file = "%s/period" % self.base_dir |
217 |
| - self._logger.debug( |
218 |
| - "setFrequency(): period_file: {0}".format(period_file)) |
219 |
| - self._logger.debug("setFrequency(): freq: {0}".format(freq)) |
| 221 | + ns_factor = 1000000000 |
| 222 | + period = ns_factor/freq # Period in nanoseconds |
| 223 | + self._eqep.node.period = str(period) |
220 | 224 | self._logger.debug(
|
221 |
| - "setFrequency(): 1000000000/freq: {0}".format(1000000000/freq)) |
222 |
| - self._logger.debug("setFrequency(): str(1000000000/freq)): {0}".format( |
223 |
| - str(1000000000/freq))) |
224 |
| - self._logger.warning( |
225 |
| - "setFrequency(): TODO: not implemented, set {} to {}".format( |
226 |
| - period_file, str(1000000000/freq))) |
227 |
| - # return sysfs.kernelFileIO(period_file, str(1000000000/freq)) |
228 |
| - |
229 |
| - def setPosition(self, val): |
230 |
| - ''' |
231 |
| - setPosition(value) |
232 |
| - Give a new value to the current position |
233 |
| - ''' |
234 |
| - position_file = "%s/position" % self.base_dir |
235 |
| - self._logger.warning( |
236 |
| - "setPosition(): TODO: not implemented, write position to {}".format( |
237 |
| - position_file)) |
238 |
| - # return sysfs.kernelFileIO(position_file, str(val)) |
| 225 | + "setFrequency(): Channel {}, frequency: {} Hz, " |
| 226 | + "period: {} ns".format( |
| 227 | + self._eqep.channel, freq, period)) |
| 228 | + |
| 229 | + def setPosition(self, position): |
| 230 | + '''Sets the current position to a new value''' |
| 231 | + |
| 232 | + position = int(position) |
| 233 | + self._eqep.node.position = str(position) |
239 | 234 |
|
240 | 235 | def zero(self):
|
241 |
| - ''' |
242 |
| - zero()s |
243 |
| - Set the current position to 0 |
244 |
| - ''' |
| 236 | + '''Resets the current position to 0''' |
| 237 | + |
245 | 238 | return self.setPosition(0)
|
0 commit comments