There are no available options for this view.

Parent Directory Parent Directory | Revision Log Revision Log

Revision 1.12 - (show annotations) (download) (as text)
Sat Nov 4 03:50:27 2006 UTC (10 years, 2 months ago) by astronouth7303
Branch: MAIN
Changes since 1.11: +6 -2 lines
File MIME type: text/x-python
Bunches and bunches of typos
1 """
2 Interfaces with the RC.
3
4 IFI's protocol is based on AN851 from Microchip.
5 """
6 from serial import *
7 from sys import stdin, stdout, stderr
8 from time import sleep, time
9 from pyc.utils import *
10
11 __all__ = "pic", 'cmdINIT', 'cmdREAD', 'cmdWRITE', 'cmdRESET', 'cmdERASE', 'PacketParseError', 'BadPacketTypeError', 'BadReadError'
12
13 cmdINIT = 0x00
14 cmdREAD = 0x01
15 cmdWRITE = 0x02
16 cmdRESET = 0x08
17 cmdERASE = 0x09
18
19 VersionMask = 0xFFE0
20 RevisionMask = 0x001F
21
22 ## These are using the full word dev ID, with revision bits masked off (val & 0xFFE0)
23 ## IFI Loader in IFI_Loader.INI uses the dev ID with the revision bits shifted off (val >> 5)
24 Processors = {
25 ## 18Fxx20
26 0x0B20 : '18F6520',
27 0x0660 : '18F6620',
28 0x0620 : '18F6720',
29 0x0B00 : '18F8520',
30 0x0640 : '18F8620',
31 0x0600 : '18F8720',
32
33 ## 18F8722 family
34 0x1340 : '18F6527',
35 0x1380 : '18F6622',
36 0x13C0 : '18F6627',
37 0x1400 : '18F6722',
38 0x1360 : '18F8527',
39 0x13A0 : '18F8622',
40 0x13E0 : '18F8627',
41 0x1420 : '18F8722',
42 }
43
44 def _checkCommand(buf):
45 try:
46 i = buf.index(0x04)
47 except ValueError:
48 i = len(buf)
49 except:
50 print `buf`
51 raise
52 return [n & 0xFF for n in buf[:i]]
53
54 def _opentty(port, **nargs):
55 options = { 'baudrate': 115200,
56 'bytesize': EIGHTBITS,
57 'parity': PARITY_NONE,
58 'stopbits': STOPBITS_ONE,
59 'xonxoff': False,
60 'rtscts': False,
61 'dsrdtr': False,
62 'timeout': 5, ## Seconds
63 }
64 options.update(nargs)
65 return Serial(port=port, **options)\
66
67 class PicException(Exception):
68 """
69 Base type for all exceptions raised by this module.
70 """
71 raiseMe = False
72 data = None
73 def __init__(self, data=None):
74 self.data = data
75 def __str__(self):
76 return self.__doc__+"\n\t"+repr(self.data)
77 @classmethod
78 def mayRaise(cls, data):
79 if cls.raiseMe:
80 raise cls(data)
81 return True
82
83 class PacketParseError(PicException):
84 """An invalid packet was found."""
85 raiseMe = True
86
87 class BadPacketTypeError(PicException):
88 """
89 An packet was recieved with an unexpected packet type.
90 """
91 raiseMe = True
92
93 class BadReadError(PicException):
94 """
95 The response data to a READ command was for the wrong location.
96 """
97 raiseMe = True
98
99 class TimeOutError(PicException):
100 """
101 Timed out waiting for a response
102 """
103 raiseMe = False
104
105 class pic(object):
106 """
107 Interfaces the pic and handles downloading.
108 """
109 __ser = None
110 readLog = None
111 sendLog = None
112 __version = None
113 __bootver = None
114
115 def __init__(self, port, flush = True,
116 log = 'debug.log',
117 sendLog = 'debug.log',
118 **nargs):
119 self.__ser = _opentty(port=port, **nargs)
120 self.readLog = self.sendLog = file(log, 'w+')
121 if sendLog != log:
122 self.sendLog = file(sendLog, 'w+')
123 if flush:
124 self.__ser.flushInput()
125 self.reset()
126
127 def _command(self, cmdNum, data, time=None):
128 """Formats and sends a command. If time is given, waits that time and returns what is in the read
129 buffer."""
130 cmd = self._formatCommand(cmdNum, data)
131 self._write(cmd)
132 print >> self.sendLog, "#>[" + list2hex(cmd) + "]"
133 self.sendLog.flush()
134
135 if time is not None:
136 usleep(time)
137 buf = self._readResponse()
138
139 return buf
140 else:
141 return True
142
143 def close(self):
144 self.__ser.close()
145 self.__version = None
146 def open(self):
147 self.__ser.open()
148 self.__version = None
149
150 def _read(self, size=None):
151 """Reads up to size data, returning the data read as a list."""
152 #if self.__ser.timeout != 0: print "..."
153 buf = self.__ser.read(size)
154 if buf is not None: buf = [ord(x) for x in buf]
155 return buf
156 read = _read
157
158 def _readline(self, size=None, eol='\n'):
159 """Read a line which is terminated with end-of-line (eol) character
160 ('\\n' by default) or until timeout."""
161 #if self.__ser.timeout != 0: print "..."
162 buf = self.__ser.readline(size, eol)
163 if buf is not None: buf = [ord(x) for x in buf]
164 return buf
165 readline = _readline
166
167 def reset(self):
168 self.__ser.close()
169 self.__ser.open()
170
171 def _write(self, buf):
172 data = "".join([chr(c) for c in buf])
173 self.__ser.write( data )
174 write = _write
175
176 @staticmethod
177 def _formatCommand(cmdNumber, data):
178 """PRIVATE
179 Returns the sequence needed to be sent to the PIC for the given command and data."""
180 rv = [0x0F, 0x0F]
181 rv += [cmdNumber & 0xFF]
182 isMeta = lambda n: n == 0x04 or n == 0x05 or n == 0x0F
183 for x in data:
184 x &= 0xFF
185 if isMeta(x): rv += [0x05] ## Escape special characters
186 rv += [x]
187 ## Hash is based on command and data, excluding escapes
188 hash = (~(sum(data) + cmdNumber) & 0xFF) + 1
189 hash &= 0xFF
190 if isMeta(hash): rv += [0x05]
191 rv += [hash]
192 rv += [0x04]
193 return rv
194
195 @staticmethod
196 def _formatRead(addr, length):
197 if not (0 <= length <= 255):
198 raise ValueError, "length must be a byte"
199 data = [length & 0xFF]
200 while addr > 0: ## Little endian
201 data += [addr & 0xFF]
202 addr >>= 8
203 return {'cmdNum':cmdREAD, 'data':data}
204
205 @staticmethod
206 def _formatWrite(addr, data):
207 rv = [0x02]
208 while addr > 0:
209 rv += [addr & 0xFF]
210 addr >>= 8
211 for d in data:
212 rv += [d & 0xFF]
213 return {'cmdNum':cmdWRITE, 'data':rv}
214
215 @staticmethod
216 def _parsePacket(buf):
217 """PRIVATE
218 Parses a packet. Returns a tuple containing the command and the data (or None if a parse error was supressed.)"""
219 if buf[:2] != [0x0F, 0x0F] or buf[-1] != 0x04:
220 if PacketParseError.mayRaise(buf):
221 print "Quietly killing parse"
222 return None
223 cmd = buf[2]
224 rdata = buf[3:-2]
225 hash = buf[-2]
226 data = []
227 escaped = False
228 for d in rdata:
229 if d == 0x05 and not escaped:
230 escaped = True
231 continue
232 else: ## a little redundent
233 data += [d]
234 escaped = False
235 ## TODO: Add hash check
236 return (cmd, data)
237
238 def _readResponse(self):
239 """PRIVATE
240 Reads a response from the serial port and returns the parsed packet."""
241 ## This frames data and passes it to parsePacket() above
242 data = []
243 done = False
244 while True: ## Not infinate, exits if 0x04 is found
245 sleep(0)
246 newStuff = len(data)
247 buf = self.readline(eol='\x04')
248 if buf is None or len(buf) == 0: ## Timed out
249 print "Time out"
250 return None
251 data += buf
252 try:
253 idx = data.index(0x04, newStuff)
254 except: pass
255 else:
256 if idx == 0:
257 print "idx == 0"
258 continue ## We should be more intelligent than this, but w/e
259 elif data[idx-1] == 0x05:
260 print "escaped 04h"
261 continue ## Escaped
262 else:
263 print >> self.readLog, "#<[" + list2hex(data) + "]"
264 self.readLog.flush()
265 return self._parsePacket(data[:idx+1]) ## And lost data?
266 assert False, "Logic should never go here!"
267
268 def initializePIC(self):
269 """Attempts to initialize PIC. Returns True if successful, False if not."""
270 r = self._command(cmdINIT, [0x02], 0)
271 self.__bootver = listToLong_LE(r[2:])
272 return r is not None
273
274 def readMem(self, location, length):
275 """Reads a location in memory, returning a sequence of bytes."""
276 cmd = self._formatRead(location, length)
277 response = self._command(time=0, **cmd)
278 if response[0] != cmdREAD:
279 if BadPacketTypeError.mayRaise(response):
280 return None
281 data = response[1]
282 rSize = data[0]
283 rLoc = listToLong_LE(data[1:4])
284 rData = data[4:]
285 if (rLoc != location or rSize != length or rSize != len(rData)):
286 print rLoc, location, rSize, length, len(rData)
287 BadReadError.mayRaise(response)
288 return rData
289
290 def writeMem(self, location, data):
291 """Writes data to a location in memory, returning True if successful"""
292 cmd = self._formatWrite(location, data)
293 response = self._command(time=0, **cmd)
294 if response is None:
295 return False
296 if response[0] != cmdWRITE:
297 if BadPacketTypeError.mayRaise(response):
298 return False
299 return True
300
301 def DeviceID(self):
302 """Returns the device ID."""
303 if self.__version is None:
304 data = self.readMem(0x3FFFFE, 2)
305 self.__version = listToLong_LE(data)
306 return self.__version
307 version = DeviceID
308
309 def UserID(self):
310 """Returns the configurable ID (addresses 200000h through 200007h)."""
311 return self.readMem(0x200000, 8)
312
313 def erase(self):
314 """Erases PIC."""
315 msgs = (
316 ## Note that the IFI/Intellitek Loader sends all of these packets
317 [0xE0, 0x00, 0x08, 0x00, 0x00], ## Vex, part 1
318 [0xFF, 0x00, 0x40, 0x00, 0x00], ## Vex, part 2
319 [0x01, 0xC0, 0x7F, 0x00, 0x00], ## Vex, part 3
320 [0x01, 0x00, 0x00, 0x20, 0x00], ## Vex, part 4
321 [0x01, 0x00, 0x00, 0x30, 0x00], ## Vex, part 5
322 [0x10, 0x00, 0x00, 0x00, 0x00], ## Vex, part 6
323
324 [0xE0, 0x00, 0x08, 0x00, 0x00], ## Original C, part 1
325 [0xE0, 0x00, 0x40, 0x00, 0x00], ## Original C, part 2
326
327 [0x8A, 0x00, 0x08, 0x00, 0x00], ##JEB: EDU; PIC18F8520
328
329 [0xA2, 0x00, 0x02, 0x00, 0x00], ## Another EDU
330 )
331 for msg in msgs:
332 print "erase():", "trying", list2hex(msg)
333 resp = self.command(cmdERASE, msg, 0)
334 if resp is None:
335 continue
336 print "erase():", `resp`
337 if resp[0] == cmdERASE:
338 return True
339 else:
340 return False
341
342 def resetPIC(self):
343 self.command(cmdRESET, [0x40]) ## No response