Mercurial > ~darius > hgwebdir.cgi > pyinst
comparison usb488.py @ 0:a43a47dfc902
First stab at code that actually works!
Example:
import usb488
u = usb488.USB488Device()
u.write("*IDN?\n")
Sending 20 bytes of data: [1, 1, 254, 0, 6, 0, 0, 0, 1, 0, 0, 0, 42, 73, 68, 78, 63, 10, 0, 0]
a = u.read()
Read 60 bytes: (2, 2, 253, 0, 48, 0, 0, 0, 1, 0, 0, 0, 84, 69, 75, 84, 82, 79, 78, 73, 88, 44, 84, 68, 83, 32
, 50, 48, 50, 52, 66, 44, 67, 48, 52, 55, 50, 54, 52, 44, 67, 70, 58, 57, 49, 46, 49, 67, 84, 32, 70, 86, 58,
118, 50, 50, 46, 48, 49, 10)
s = reduce(lambda x, y: x+y, map(chr, a))
print s
xxxxTEKTRONIX,TDS 2024B,C047264,CF:91.1CT FV:v22.01
author | Daniel O'Connor <darius@dons.net.au> |
---|---|
date | Wed, 13 May 2009 14:44:40 +0930 |
parents | |
children | e2089824735a |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:a43a47dfc902 |
---|---|
1 #!/usr/bin/env python | |
2 | |
3 # | |
4 # Spec/info.. | |
5 # | |
6 # http://www.usb.org/developers/devclass_docs/USBTMC_1_006a.zip | |
7 # http://svn.openmoko.org/developers/werner/ahrt/host/tmc/README | |
8 # http://www.home.agilent.com/agilent/redirector.jspx?action=ref&cname=AGILENT_EDITORIAL&ckey=1189335&lc=eng&cc=US&nfr=-35560.0.00 | |
9 # linux-2.6.29.3/drivers/usb/class/usbtmc.c | |
10 # | |
11 | |
12 import usb | |
13 | |
14 # | |
15 # The usual SCPI commands are wrapped before being sent. | |
16 # | |
17 # Write: | |
18 # Offset Field Size Value Description | |
19 # 0 MsgID 1 0x01 DEV_DEP_MSG_OUT | |
20 # 1 bTag 1 0x01 Varies with each transfer | |
21 # 2 bTagInverse 1 0xfe Inverse of previous field | |
22 # 3 Reserved 1 0x00 | |
23 # 4 TransferSize 4 0x06 | |
24 # 5 .. 0x00 | |
25 # 6 .. 0x00 | |
26 # 7 .. 0x00 | |
27 # 8 bmTransferAttr 1 0x01 1 == end of msg | |
28 # 9 Reserved 1 0x00 | |
29 # 10 Reserved 1 0x00 | |
30 # 11 Reserved 1 0x00 | |
31 # 12 Msg itself 1 0x2a '*' | |
32 # 13 1 0x49 'I' | |
33 # 14 1 0x44 'D' | |
34 # 15 1 0x4e 'N' | |
35 # 16 1 0x3f '?' | |
36 # 17 1 0x0a '\n' | |
37 # 18-19 Alignment 2 0x0000 Bring into 4 byte alignment | |
38 # | |
39 # | |
40 # Send a read request: | |
41 # Offset Field Size Value Description | |
42 # 0 MsgID 1 0x02 REQUEST_DEV_DEP_MSG_IN | |
43 # 1 bTag 1 0x02 Varies with each transfer | |
44 # 2 bTagInverse 1 0xfd Inverse of previous field | |
45 # 3 Reserved 1 0x00 | |
46 # 4 TransferSize 4 0x64 | |
47 # 5 .. 0x00 | |
48 # 6 .. 0x00 | |
49 # 7 .. 0x00 | |
50 # 8 bmTransferAttr 1 0x00 | |
51 # 9 Term char 1 0x00 | |
52 # 10 Reserved 1 0x00 | |
53 # 11 Reserved 1 0x00 | |
54 | |
55 # Tektronix TDS2024B | |
56 USB_VEND_TEKTRONIX = 1689 | |
57 USB_PROD_TEKTORNIX = 874 | |
58 | |
59 # No libusb versions of these available | |
60 USB_CLASS_APP_SPECIFIC = 254 | |
61 USB_SUBCLASS_TMC = 3 | |
62 USB_PROTOCOL_488 = 1 | |
63 | |
64 # USB488 message IDs | |
65 DEV_DEP_MSG_OUT = 1 | |
66 REQUEST_DEV_DEP_MSG_IN = 2 | |
67 DEV_DEP_MSG_IN = 2 | |
68 | |
69 class USB488Device(object): | |
70 def __init__(self, vendor = None, product = None, serial = None, path = None): | |
71 """Search for a USB488 class device, if specified vendor, | |
72 product, serial and path will refine the search""" | |
73 | |
74 busses = usb.busses() | |
75 | |
76 # | |
77 # Search for the device we want | |
78 # | |
79 found = False | |
80 for bus in busses: | |
81 for dev in bus.devices: | |
82 # Skip ones that don't match | |
83 if vendor != None and dev.idVendor != vendor: | |
84 continue | |
85 if product != None and dev.idProduct != product: | |
86 continue | |
87 if serial != None and dev.idSerialNumber != serial: | |
88 continue | |
89 if path != None and dev.filename != path: | |
90 continue | |
91 | |
92 # The libusb examples say you can check for device | |
93 # class and then open, however in that case you can't | |
94 # find the endpoint number which seems pretty useless | |
95 # unless you want to hard code everything. | |
96 for confidx in xrange(len(dev.configurations)): | |
97 for iface in dev.configurations[confidx].interfaces: | |
98 for altif in iface: | |
99 # Check if this is a USB488 capable interface | |
100 if altif.interfaceClass == USB_CLASS_APP_SPECIFIC and \ | |
101 altif.interfaceSubClass == USB_SUBCLASS_TMC and \ | |
102 altif.interfaceProtocol == USB_PROTOCOL_488: | |
103 found = True | |
104 break | |
105 if found: | |
106 break | |
107 if found: | |
108 break | |
109 if found: | |
110 break | |
111 if found: | |
112 break | |
113 if not found: | |
114 raise "Could not find a suitable USB device" | |
115 | |
116 # Open the device and claim the USB interface that supports the spec | |
117 self.handle = dev.open() | |
118 self.handle.setConfiguration(dev.configurations[confidx].value) | |
119 self.handle.claimInterface(altif.interfaceNumber) | |
120 self.handle.setAltInterface(altif.alternateSetting) | |
121 | |
122 # Get some info for humans | |
123 self.vendname = self.handle.getString(dev.iManufacturer, 1024) | |
124 self.prodname = self.handle.getString(dev.iProduct, 1024) | |
125 self.serial = self.handle.getString(dev.iSerialNumber, 1024) | |
126 | |
127 # Determine the endpoints for each operation type | |
128 self.intrep = self.bulkinep = self.bulkoutep = None | |
129 | |
130 for ep in altif.endpoints: | |
131 if ep.type == usb.ENDPOINT_TYPE_INTERRUPT and \ | |
132 ep.address & usb.ENDPOINT_IN == usb.ENDPOINT_IN: | |
133 self.intrep = ep.address | |
134 | |
135 if ep.type == usb.ENDPOINT_TYPE_BULK: | |
136 if ep.address & usb.ENDPOINT_IN == usb.ENDPOINT_IN: | |
137 self.bulkinep = ep.address | |
138 else: | |
139 self.bulkoutep = ep.address | |
140 self.maxPacket = ep.maxPacketSize | |
141 | |
142 # Required for 488.2 devices, optional otherwise | |
143 if self.intrep == None: | |
144 print "Can't find interrupt endpoint" | |
145 | |
146 # Data from the scope (mandatory) | |
147 if self.bulkinep == None: | |
148 raise "Can't find bulk-in endpoint" | |
149 | |
150 # Data to the scope (mandatory) | |
151 if self.bulkoutep == None: | |
152 raise "Can't find bulk-out endpoint" | |
153 | |
154 self.tag = 1 | |
155 | |
156 def __str__(self): | |
157 rtn = "Mfg: %s Prod: %s" % (self.vendname, self.prodname) | |
158 if self.serial != "": | |
159 rtn += " S/N: " + self.serial | |
160 | |
161 return rtn | |
162 | |
163 def incrtag(self): | |
164 self.tag += 1 | |
165 if self.tag == 0: | |
166 self.tag += 1 | |
167 | |
168 def write(self, data): | |
169 """Send data (string) to the scope""" | |
170 orddata = map(ord, data) | |
171 datalen = len(orddata) | |
172 | |
173 # Build the packet | |
174 pkt = [ DEV_DEP_MSG_OUT, self.tag, ~self.tag & 0xff, 0x00, | |
175 datalen & 0xff, datalen >> 8 & 0xff, datalen >> 16 & 0xff, | |
176 datalen >> 24 & 0xff, 1, 0, 0, 0 ] | |
177 | |
178 # Add the data | |
179 pkt = pkt + orddata | |
180 | |
181 # Align to 4 bytes | |
182 alignlen = ((len(pkt) / 4) + 1) * 4 | |
183 pkt = pkt + [0] * (alignlen - len(pkt)) | |
184 | |
185 # Bump the tag | |
186 self.incrtag() | |
187 | |
188 # Split it up into maxPacket sized chunks and send.. | |
189 while len(pkt) > 0: | |
190 chunk = pkt[0:self.maxPacket] | |
191 pkt = pkt[self.maxPacket:] | |
192 | |
193 print "Sending %s bytes of data: %s" % (len(chunk), chunk) | |
194 wrote = self.handle.bulkWrite(self.bulkoutep, chunk) | |
195 if wrote != len(chunk): | |
196 raise "Short write, got %d, expected %d" % (wrote, len(chunk)) | |
197 | |
198 def read(self): | |
199 """Read data from the device""" | |
200 | |
201 | |
202 datalen = 1024 | |
203 # Ask the device to send us something | |
204 pkt = [ REQUEST_DEV_DEP_MSG_IN, self.tag, ~self.tag & 0xff, 0x00, | |
205 datalen & 0xff, datalen >> 8 & 0xff, datalen >> 16 & 0xff, | |
206 datalen >> 24 & 0xff, 0, 0, 0, 0] | |
207 | |
208 # Bump tag | |
209 self.incrtag() | |
210 | |
211 # Send it | |
212 wrote = self.handle.bulkWrite(self.bulkoutep, pkt) | |
213 if wrote != len(pkt): | |
214 print "Short write, got %d, expected %d" % (wrote, len(pkt)) | |
215 | |
216 read = self.handle.bulkRead(self.bulkinep, datalen) | |
217 print "Read %s bytes: %s" % (len(read), str(read)) | |
218 return read | |
219 | |
220 def find488(): | |
221 """Search for a USB488 device, returns a handle, iface, dev tuple for it""" | |
222 | |
223 busses = usb.busses() | |
224 | |
225 found = False | |
226 for bus in busses: | |
227 for dev in bus.devices: | |
228 for confidx in xrange(len(dev.configurations)): | |
229 # XXX: what do multi-interface devices look like? | |
230 iface = dev.configurations[confidx].interfaces[0][0] | |
231 # Check if this is a USB488 capable interface | |
232 if iface.interfaceClass == USB_CLASS_APP_SPECIFIC and \ | |
233 iface.interfaceSubClass == USB_SUBCLASS_TMC and \ | |
234 iface.interfaceProtocol == USB_PROTOCOL_488: | |
235 handle = dev.open() | |
236 handle.setConfiguration(1) | |
237 handle.claimInterface(0) | |
238 handle.setAltInterface(0) | |
239 #handle.setConfiguration(confidx) | |
240 #handle.claimInterface(0) | |
241 found = True | |
242 break | |
243 | |
244 if found: | |
245 break | |
246 | |
247 if not found: | |
248 raise "Could not find scope, check perms" | |
249 | |
250 return (handle, iface, dev) | |
251 | |
252 def geteps(iface): | |
253 """Returns a tuple of intr,input,output addresses of endpoints for the interface""" | |
254 intrep = bulkinep = bulkoutep = None | |
255 | |
256 for ep in iface.endpoints: | |
257 if ep.type == usb.ENDPOINT_TYPE_INTERRUPT and \ | |
258 ep.address & usb.ENDPOINT_IN == usb.ENDPOINT_IN: | |
259 intrep = ep.address | |
260 | |
261 if ep.type == usb.ENDPOINT_TYPE_BULK: | |
262 if ep.address & usb.ENDPOINT_IN == usb.ENDPOINT_IN: | |
263 bulkinep = ep.address | |
264 else: | |
265 bulkoutep = ep.address | |
266 | |
267 # Required for 488.2 devices, optional otherwise | |
268 if intrep == None: | |
269 print "Can't find interrup endpoint" | |
270 | |
271 # Data from the scope | |
272 if bulkinep == None: | |
273 raise "Can't find bulk-in endpoint" | |
274 | |
275 # Data to the scope | |
276 if bulkoutep == None: | |
277 raise "Can't find bulk-out endpoint" | |
278 | |
279 return intrep, bulkinep, bulkoutep | |
280 | |
281 def main(): | |
282 handle, iface, dev = find488() | |
283 print "Found device" | |
284 | |
285 intrep, bulkinep, bulkoutep = geteps(iface) | |
286 print "Found endpoints" | |
287 | |
288 if __name__ == "__main__": | |
289 main() | |
290 |