chiark / gitweb /
82fb12f4b9181887a4612a58ee4f67af9ca9718d
[cura.git] / Cura / avr_isp / stk500v2.py
1 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
2 import os, struct, sys, time
3
4 from serial import Serial
5 from serial import SerialException
6
7 import ispBase, intelHex
8
9 class Stk500v2(ispBase.IspBase):
10         def __init__(self):
11                 self.serial = None
12                 self.seq = 1
13                 self.lastAddr = -1
14                 self.progressCallback = None
15         
16         def connect(self, port = 'COM22', speed = 115200):
17                 if self.serial != None:
18                         self.close()
19                 try:
20                         self.serial = Serial(str(port), speed, timeout=1, writeTimeout=10000)
21                 except SerialException as e:
22                         raise ispBase.IspError("Failed to open serial port")
23                 except:
24                         raise ispBase.IspError("Unexpected error while connecting to serial port:" + port + ":" + str(sys.exc_info()[0]))
25                 self.seq = 1
26                 
27                 #Reset the controller
28                 self.serial.setDTR(1)
29                 time.sleep(0.1)
30                 self.serial.setDTR(0)
31                 time.sleep(0.2)
32                 
33                 self.sendMessage([1])
34                 if self.sendMessage([0x10, 0xc8, 0x64, 0x19, 0x20, 0x00, 0x53, 0x03, 0xac, 0x53, 0x00, 0x00]) != [0x10, 0x00]:
35                         self.close()
36                         raise ispBase.IspError("Failed to enter programming mode")
37                 self.serial.timeout = 5
38
39         def close(self):
40                 if self.serial != None:
41                         self.serial.close()
42                         self.serial = None
43
44         #Leave ISP does not reset the serial port, only resets the device, and returns the serial port after disconnecting it from the programming interface.
45         #       This allows you to use the serial port without opening it again.
46         def leaveISP(self):
47                 if self.serial is not None:
48                         if self.sendMessage([0x11]) != [0x11, 0x00]:
49                                 raise ispBase.IspError("Failed to leave programming mode")
50                         ret = self.serial
51                         self.serial = None
52                         return ret
53                 return None
54         
55         def isConnected(self):
56                 return self.serial is not None
57         
58         def sendISP(self, data):
59                 recv = self.sendMessage([0x1D, 4, 4, 0, data[0], data[1], data[2], data[3]])
60                 return recv[2:6]
61         
62         def writeFlash(self, flashData):
63                 #Set load addr to 0, in case we have more then 64k flash we need to enable the address extension
64                 pageSize = self.chip['pageSize'] * 2
65                 flashSize = pageSize * self.chip['pageCount']
66                 if flashSize > 0xFFFF:
67                         self.sendMessage([0x06, 0x80, 0x00, 0x00, 0x00])
68                 else:
69                         self.sendMessage([0x06, 0x00, 0x00, 0x00, 0x00])
70                 
71                 loadCount = (len(flashData) + pageSize - 1) / pageSize
72                 for i in xrange(0, loadCount):
73                         recv = self.sendMessage([0x13, pageSize >> 8, pageSize & 0xFF, 0xc1, 0x0a, 0x40, 0x4c, 0x20, 0x00, 0x00] + flashData[(i * pageSize):(i * pageSize + pageSize)])
74                         if self.progressCallback != None:
75                                 self.progressCallback(i + 1, loadCount*2)
76         
77         def verifyFlash(self, flashData):
78                 #Set load addr to 0, in case we have more then 64k flash we need to enable the address extension
79                 flashSize = self.chip['pageSize'] * 2 * self.chip['pageCount']
80                 if flashSize > 0xFFFF:
81                         self.sendMessage([0x06, 0x80, 0x00, 0x00, 0x00])
82                 else:
83                         self.sendMessage([0x06, 0x00, 0x00, 0x00, 0x00])
84                 
85                 loadCount = (len(flashData) + 0xFF) / 0x100
86                 for i in xrange(0, loadCount):
87                         recv = self.sendMessage([0x14, 0x01, 0x00, 0x20])[2:0x102]
88                         if self.progressCallback != None:
89                                 self.progressCallback(loadCount + i + 1, loadCount*2)
90                         for j in xrange(0, 0x100):
91                                 if i * 0x100 + j < len(flashData) and flashData[i * 0x100 + j] != recv[j]:
92                                         raise ispBase.IspError('Verify error at: 0x%x' % (i * 0x100 + j))
93
94         def sendMessage(self, data):
95                 message = struct.pack(">BBHB", 0x1B, self.seq, len(data), 0x0E)
96                 for c in data:
97                         message += struct.pack(">B", c)
98                 checksum = 0
99                 for c in message:
100                         checksum ^= ord(c)
101                 message += struct.pack(">B", checksum)
102                 try:
103                         self.serial.write(message)
104                         self.serial.flush()
105                 except Serial.SerialTimeoutException:
106                         raise ispBase.IspError('Serial send timeout')
107                 self.seq = (self.seq + 1) & 0xFF
108                 return self.recvMessage()
109         
110         def recvMessage(self):
111                 state = 'Start'
112                 checksum = 0
113                 while True:
114                         s = self.serial.read()
115                         if len(s) < 1:
116                                 raise ispBase.IspError("Timeout")
117                         b = struct.unpack(">B", s)[0]
118                         checksum ^= b
119                         #print(hex(b))
120                         if state == 'Start':
121                                 if b == 0x1B:
122                                         state = 'GetSeq'
123                                         checksum = 0x1B
124                         elif state == 'GetSeq':
125                                 state = 'MsgSize1'
126                         elif state == 'MsgSize1':
127                                 msgSize = b << 8
128                                 state = 'MsgSize2'
129                         elif state == 'MsgSize2':
130                                 msgSize |= b
131                                 state = 'Token'
132                         elif state == 'Token':
133                                 if b != 0x0E:
134                                         state = 'Start'
135                                 else:
136                                         state = 'Data'
137                                         data = []
138                         elif state == 'Data':
139                                 data.append(b)
140                                 if len(data) == msgSize:
141                                         state = 'Checksum'
142                         elif state == 'Checksum':
143                                 if checksum != 0:
144                                         state = 'Start'
145                                 else:
146                                         return data
147
148
149 def main():
150         programmer = Stk500v2()
151         if sys.argv[1] == 'AUTO':
152                 import _winreg
153                 key=_winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE,"HARDWARE\\DEVICEMAP\\SERIALCOMM")
154                 i=0
155                 while True:
156                         values = _winreg.EnumValue(key, i)
157                         if 'USBSER' in values[0]:
158                                 programmer.connect(port = values[1])
159                                 break
160                         i+=1
161         else:
162                 programmer.connect(port = sys.argv[1])
163         programmer.programChip(intelHex.readHex(sys.argv[2]))
164         sys.exit(1)
165
166 if __name__ == '__main__':
167         main()