1 '''PMD1608Wrap.py - A Python Interface to MCC PMD-1608FS Device Library.
2 =====================================================================
3
4 Access most functions of the Measurement Computing PMD-1608FS USB data
5 acquisition and digital control device.
6
7 Author: Nick Glazzard 2013'''
8
9 from ctypes import *
10 from numpy import *
11 from time import sleep
12
13 lib = cdll.LoadLibrary( './PMD1608lib.so' )
14
16 raise RuntimeError( 'Invalid arguments.' )
17
19 raise RuntimeError( 'PMD1608lib call failed.' )
20
22 '''Class that encapsulates PMD-1608FS functionality.'''
24 self.obj = lib.PMD1608FS_new( c_int(0) )
25
27 '''Set all 8 DIO lines to be inputs.'''
28 if( lib.PMD1608FS_set_byte_in(self.obj) != 1 ):
29 callerr()
30 return True
31
33 '''Set all 8 DIO lines to be outputs.'''
34 if( lib.PMD1608FS_set_byte_out(self.obj) != 1 ):
35 callerr()
36 return True
37
39 '''Set DIO line bitnum to be an input.'''
40 if( lib.PMD1608FS_set_bit_in(self.obj, c_int(bitnum) ) != 1 ):
41 callerr()
42 return True
43
45 '''Set DIO line bitnum to be an output.'''
46 if( lib.PMD1608FS_set_bit_out(self.obj, c_int(bitnum) ) != 1 ):
47 callerr()
48 return True
49
51 '''Read all 8 DIO lines as a byte value.'''
52 inbyte = c_ubyte(0)
53 if( lib.PMD1608FS_read_byte(self.obj, byref(inbyte) ) != 1 ):
54 callerr()
55 return( inbyte.value )
56
58 '''Set all 8 DIO lines from a byte value.'''
59 if( lib.PMD1608FS_write_byte(self.obj, c_ubyte(byte) ) != 1 ):
60 callerr()
61 return True
62
64 '''Read the state of DIO line bitnum.'''
65 inbit = c_int(0)
66 if( lib.PMD1608FS_read_bit( self.obj, c_int(bitnum), byref(inbit) ) != 1 ):
67 callerr()
68 return( inbit.value )
69
71 '''Set the state of DIO line bitnum to bitval (0 or 1).'''
72 if( lib.PMD1608FS_write_bit( self.obj, c_int(bitnum), c_int(bitval) ) != 1 ):
73 callerr()
74 return True
75
77 '''Set the state of DIO line bitnum to 1 for usecs microseconds then 0.
78 N.B. Minimum achievable pulse width is about 3ms.'''
79 if( lib.PMD1608FS_pulse_bit( self.obj, c_int(bitnum), c_int(usecs) ) != 1 ):
80 callerr()
81 return True
82
84 '''Read the voltage on analog input channel using full scale range grange.
85 grange may be: '10V', '5V', '2.5V', '2V', '1.25V', '1V', '625mV', '312mV' '''
86 gns = { '10V':0, '5V':1, '2.5V':2, '2V':3, '1.25V':4, '1V':5, '625mV':6, '312mV':7,
87 '10v':0, '5v':1, '2.5v':2, '2v':3, '1.25v':4, '1v':5, '625mv':6, '312mv':7 }
88 try:
89 igrange = gns[grange]
90 except Exception, err:
91 argerr()
92 lib.PMD1608FS_read_channel.restype = c_float
93 return lib.PMD1608FS_read_channel( self.obj, c_int(channel), c_int(igrange) )
94
96 '''Reset the counter.'''
97 if( lib.PMD1608FS_reset_counter( self.obj ) != 1 ):
98 callerr()
99 return True
100
102 '''Read the counter value.'''
103 incount = c_int(0)
104 if( lib.PMD1608FS_read_counter( self.obj, byref(incount) ) != 1 ):
105 callerr()
106 return( incount.value )
107
109 '''Wait some milliseconds.'''
110 sleep( 0.001*ms )
111
112
114 '''Control a 2-Y stepper motor via PMD1608.
115 This is for a Muirhead Vactric 23MR302 motor controlled via the ANGEL
116 stepper motor driver.'''
118 self.pmd = PMD1608()
119 self.pmd.set_byte_out()
120 self.pmd.write_byte( 0 )
121 self.step = 0
122 self.waitfor = 2
123 self.lastdirection = 1
124 self.deg_per_step= 1.8
125 self.angle = 0.0
126 self.mode = 1
127 self.full_traj = [12,9,3,6]
128 self.half_traj = [12,8,9,1,3,2,6,4]
129
131 '''Set current angle as zero angle.'''
132 self.angle = 0.0
133
135 '''Return the current angle achieved.'''
136 return self.angle
137
139 '''Turn off all current to motor.'''
140 self.pmd.write_byte( 0 )
141
143 '''Set full or half step mode.'''
144 if( mode == 'full'):
145 self.mode = 1
146 self.deg_per_step = 1.8
147 else:
148 self.mode = 2
149 self.deg_per_step = 0.9
150
152 '''Set milliseconds to wait after every step.
153 Limits acceleration (sort of).'''
154 if( wait < 2 ):
155 self.waitfor = 2
156 else:
157 self.waitfor = wait
158
160 '''Step forward (clockwise) one step.'''
161 if( self.mode == 1 ):
162 k = self.step % 4
163 self.pmd.write_byte( self.full_traj[k] )
164 else:
165 k = self.step % 8
166 self.pmd.write_byte( self.half_traj[k] )
167 self.step += 1
168 self.pmd.waitms( self.waitfor )
169 self.angle += self.deg_per_step
170
172 '''Step backwards (widdershins) one step.'''
173 self.step -= 1
174 if( self.mode == 1 ):
175 k = self.step % 4
176 self.pmd.write_byte( self.full_traj[k] )
177 else:
178 k = self.step % 8
179 self.pmd.write_byte( self.half_traj[k] )
180 self.pmd.waitms( self.waitfor )
181 self.angle -= self.deg_per_step
182
184 '''Move nsteps forward (nsteps>0) or backwards (nsteps<0).'''
185 if( nsteps == 0 ):
186 return
187 direction = cmp(nsteps,0)
188 if( direction < self.lastdirection ):
189 self.step -= 1
190 elif( direction > self.lastdirection ):
191 self.step += 1
192 self.lastdirection = direction
193 for i in range(0,nsteps,direction):
194 if( direction > 0 ):
195 self.step_forward()
196 else:
197 self.step_backward()
198
200 '''Move by angle (forwards or backwards).'''
201 nsteps = int(round(angle/self.deg_per_step))
202 self.steps(nsteps)
203
205 '''Go to a specified angle. Return the angular positioning error.
206 If hold True, maintain current in the coils. In that case, the motors
207 holding torque should be needed to turn the motor shaft with an external
208 torque. Otherwise, turn off current to the coils, in which case the (lower)
209 detent torque should suffice to turn the shaft.'''
210 delta_angle = angle - self.angle
211 self.step_angle(delta_angle)
212 if( not hold):
213 self.current_off()
214 return( self.angle - angle )
215
218
219
220 if __name__ == '__main__':
221 if( False ):
222 d = PMD1608()
223 for i in range(0,8):
224 print d.read_channel( i, '1V' )
225 d.set_bit_out( 0 )
226 d.write_bit( 0, 0 )
227 d.reset_counter()
228 for i in range(0,100):
229 d.write_bit( 0, 1 )
230 d.waitms( 100 )
231 d.write_bit( 0, 0 )
232 print d.read_counter()
233
234 if( True ):
235 s = Stepper()
236 s.set_mode( 'full' )
237
238 for j in range(0,1):
239 for i in range(0,10):
240 s.steps(10)
241 s.steps(-10)
242 s.step_angle(180)
243 s.step_angle(-180)
244 aerr = s.goto_angle(37.5)
245 print 'Go to 37.5, error=', aerr, ' angle=', s.get_angle()
246 s.goto_angle(90)
247 s.waitms(500)
248 s.goto_angle(180)
249 s.waitms(500)
250 s.goto_angle(270)
251 s.waitms(500)
252 s.goto_angle(360)
253 s.waitms(500)
254 s.goto_angle(270)
255 s.waitms(500)
256 s.goto_angle(180)
257 s.waitms(500)
258 s.goto_angle(90)
259 s.waitms(500)
260 s.goto_angle(0)
261
262