|
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