3
3
# Variable baud rate bootloader for Artemis Apollo3 modules
4
4
5
5
# Immediately upon reset the Artemis module will search for the timing character
6
- # to auto-detect the baud rate. If a valid baud rate is found the Artemis will
6
+ # to auto-detect the baud rate. If a valid baud rate is found the Artemis will
7
7
# respond with the bootloader version packet
8
8
# If the computer receives a well-formatted version number packet at the desired
9
- # baud rate it will send a command to begin bootloading. The Artemis shall then
10
- # respond with the a command asking for the next frame.
11
- # The host will then send a frame packet. If the CRC is OK the Artemis will write
9
+ # baud rate it will send a command to begin bootloading. The Artemis shall then
10
+ # respond with the a command asking for the next frame.
11
+ # The host will then send a frame packet. If the CRC is OK the Artemis will write
12
12
# that to memory and request the next frame. If the CRC fails the Artemis will
13
13
# discard that data and send a request to re-send the previous frame.
14
14
# This cycle repeats until the Artemis receives a done command in place of the
15
15
# requested frame data command.
16
- # The initial baud rate determination must occur within some small timeout. Once
17
- # baud rate detection has completed all additional communication will have a
16
+ # The initial baud rate determination must occur within some small timeout. Once
17
+ # baud rate detection has completed all additional communication will have a
18
18
# universal timeout value. Once the Artemis has begun requesting data it may no
19
- # no longer exit the bootloader. If the host detects a timeout at any point it
20
- # will stop bootloading.
19
+ # no longer exit the bootloader. If the host detects a timeout at any point it
20
+ # will stop bootloading.
21
21
22
22
# Notes about PySerial timeout:
23
- # The timeout operates on whole functions - that is to say that a call to
24
- # ser.read(10) will return after ser.timeout, just as will ser.read(1) (assuming
23
+ # The timeout operates on whole functions - that is to say that a call to
24
+ # ser.read(10) will return after ser.timeout, just as will ser.read(1) (assuming
25
25
# that the necessary bytes were not found)
26
- # If there are no incoming bytes (on the line or in the buffer) then two calls to
26
+ # If there are no incoming bytes (on the line or in the buffer) then two calls to
27
27
# ser.read(n) will time out after 2*ser.timeout
28
28
# Incoming UART data is buffered behind the scenes, probably by the OS.
29
29
39
39
import sys
40
40
import time
41
41
import math
42
+ import os .path
42
43
from sys import exit
43
44
45
+ SCRIPT_VERSION_MAJOR = "1"
46
+ SCRIPT_VERSION_MINOR = "7"
47
+
44
48
# ***********************************************************************************
45
49
#
46
50
# Commands
47
51
#
48
52
# ***********************************************************************************
49
- SVL_CMD_VER = 0x01 # version
50
- SVL_CMD_BL = 0x02 # enter bootload mode
51
- SVL_CMD_NEXT = 0x03 # request next chunk
52
- SVL_CMD_FRAME = 0x04 # indicate app data frame
53
- SVL_CMD_RETRY = 0x05 # request re-send frame
54
- SVL_CMD_DONE = 0x06 # finished - all data sent
53
+ SVL_CMD_VER = 0x01 # version
54
+ SVL_CMD_BL = 0x02 # enter bootload mode
55
+ SVL_CMD_NEXT = 0x03 # request next chunk
56
+ SVL_CMD_FRAME = 0x04 # indicate app data frame
57
+ SVL_CMD_RETRY = 0x05 # request re-send frame
58
+ SVL_CMD_DONE = 0x06 # finished - all data sent
55
59
56
60
barWidthInCharacters = 50 # Width of progress bar, ie [###### % complete
57
61
97
101
98
102
def get_crc16 (data ):
99
103
100
- #Table and code ported from Artemis SVL bootloader
104
+ # Table and code ported from Artemis SVL bootloader
101
105
crc = 0x0000
102
106
data = bytearray (data )
103
107
for ch in data :
@@ -108,30 +112,33 @@ def get_crc16(data):
108
112
return crc
109
113
110
114
111
-
112
115
# ***********************************************************************************
113
116
#
114
- # Wait for a packet
117
+ # Wait for a packet
115
118
#
116
119
# ***********************************************************************************
117
120
def wait_for_packet (ser ):
118
121
119
- packet = {'len' :0 , 'cmd' :0 , 'data' :0 , 'crc' :1 , 'timeout' :1 }
122
+ packet = {'len' : 0 , 'cmd' : 0 , 'data' : 0 , 'crc' : 1 , 'timeout' : 1 }
120
123
121
- n = ser .read (2 ) # get the number of bytes
124
+ n = ser .read (2 ) # get the number of bytes
122
125
if (len (n ) < 2 ):
123
126
return packet
124
-
125
- packet ['len' ] = int .from_bytes (n , byteorder = 'big' , signed = False ) #
127
+
128
+ packet ['len' ] = int .from_bytes (n , byteorder = 'big' , signed = False ) #
126
129
payload = ser .read (packet ['len' ])
127
130
128
131
if (len (payload ) != packet ['len' ]):
129
132
return packet
130
-
131
- packet ['timeout' ] = 0 # all bytes received, so timeout is not true
132
- packet ['cmd' ] = payload [0 ] # cmd is the first byte of the payload
133
- packet ['data' ] = payload [1 :packet ['len' ]- 2 ] # the data is the part of the payload that is not cmd or crc
134
- packet ['crc' ] = get_crc16 (payload ) # performing the crc on the whole payload should return 0
133
+
134
+ # all bytes received, so timeout is not true
135
+ packet ['timeout' ] = 0
136
+ # cmd is the first byte of the payload
137
+ packet ['cmd' ] = payload [0 ]
138
+ # the data is the part of the payload that is not cmd or crc
139
+ packet ['data' ] = payload [1 :packet ['len' ]- 2 ]
140
+ # performing the crc on the whole payload should return 0
141
+ packet ['crc' ] = get_crc16 (payload )
135
142
136
143
return packet
137
144
@@ -140,22 +147,20 @@ def wait_for_packet(ser):
140
147
# Send a packet
141
148
#
142
149
# ***********************************************************************************
150
+
151
+
143
152
def send_packet (ser , cmd , data ):
144
153
data = bytearray (data )
145
154
num_bytes = 3 + len (data )
146
- payload = bytearray (cmd .to_bytes (1 ,'big' ))
155
+ payload = bytearray (cmd .to_bytes (1 , 'big' ))
147
156
payload .extend (data )
148
157
crc = get_crc16 (payload )
149
- payload .extend (bytearray (crc .to_bytes (2 ,'big' )))
158
+ payload .extend (bytearray (crc .to_bytes (2 , 'big' )))
150
159
151
- ser .write (num_bytes .to_bytes (2 ,'big' ))
160
+ ser .write (num_bytes .to_bytes (2 , 'big' ))
152
161
ser .write (bytes (payload ))
153
162
154
163
155
-
156
-
157
-
158
-
159
164
# ***********************************************************************************
160
165
#
161
166
# Setup: signal baud rate, get version, and command BL enter
@@ -165,29 +170,27 @@ def phase_setup(ser):
165
170
166
171
baud_detect_byte = b'U'
167
172
168
- verboseprint ('\n phase: \t setup ' )
169
-
170
- # Handle the serial startup blip
173
+ verboseprint ('\n Phase: \t Setup ' )
174
+
175
+ # Handle the serial startup blip
171
176
ser .reset_input_buffer ()
172
- verboseprint ('\t cleared startup blip' )
177
+ verboseprint ('\t Cleared startup blip' )
173
178
174
179
ser .write (baud_detect_byte ) # send the baud detection character
175
180
176
181
packet = wait_for_packet (ser )
177
182
if (packet ['timeout' ] or packet ['crc' ]):
178
- return 1
179
-
180
- twopartprint ('\t ' ,'Got SVL Bootloader Version: ' +
183
+ return False # failed to enter bootloader
184
+
185
+ twopartprint ('\t ' , 'Got SVL Bootloader Version: ' +
181
186
str (int .from_bytes (packet ['data' ], 'big' )))
182
187
verboseprint ('\t Sending \' enter bootloader\' command' )
183
188
184
189
send_packet (ser , SVL_CMD_BL , b'' )
185
190
186
- # Now enter the bootload phase
187
-
188
-
189
-
191
+ return True
190
192
193
+ # Now enter the bootload phase
191
194
192
195
193
196
# ***********************************************************************************
@@ -203,7 +206,7 @@ def phase_bootload(ser):
203
206
resend_max = 4
204
207
resend_count = 0
205
208
206
- verboseprint ('\n phase: \t bootload ' )
209
+ verboseprint ('\n Phase: \t Bootload ' )
207
210
208
211
with open (args .binfile , mode = 'rb' ) as binfile :
209
212
application = binfile .read ()
@@ -220,36 +223,38 @@ def phase_bootload(ser):
220
223
' bytes to send in ' + str (total_frames ) + ' frames' )
221
224
222
225
bl_done = False
223
- bl_failed = False
224
- while ((not bl_done ) and (not bl_failed )):
225
-
226
- packet = wait_for_packet (ser ) # wait for indication by Artemis
226
+ bl_succeeded = True
227
+ while ((bl_done == False ) and (bl_succeeded == True )):
228
+
229
+ # wait for indication by Artemis
230
+ packet = wait_for_packet (ser )
227
231
if (packet ['timeout' ] or packet ['crc' ]):
228
- print ('\n \t error receiving packet' )
229
- print (packet )
230
- print ('\n ' )
231
- bl_failed = True
232
+ verboseprint ('\n \t Error receiving packet' )
233
+ verboseprint (packet )
234
+ verboseprint ('\n ' )
235
+ bl_succeeded = False
232
236
bl_done = True
233
237
234
- if ( packet ['cmd' ] == SVL_CMD_NEXT ):
238
+ if (packet ['cmd' ] == SVL_CMD_NEXT ):
235
239
# verboseprint('\tgot frame request')
236
240
curr_frame += 1
237
241
resend_count = 0
238
- elif ( packet ['cmd' ] == SVL_CMD_RETRY ):
239
- verboseprint ('\t \t retrying ...' )
242
+ elif (packet ['cmd' ] == SVL_CMD_RETRY ):
243
+ verboseprint ('\t \t Retrying ...' )
240
244
resend_count += 1
241
- if ( resend_count >= resend_max ):
242
- bl_failed = True
245
+ if (resend_count >= resend_max ):
246
+ bl_succeeded = False
243
247
bl_done = True
244
248
else :
245
- print ('unknown error' )
246
- bl_failed = True
249
+ print ('Timeout or unknown error' )
250
+ bl_succeeded = False
247
251
bl_done = True
248
252
249
- if ( curr_frame <= total_frames ):
250
- frame_data = application [((curr_frame - 1 )* frame_size ):((curr_frame - 1 + 1 )* frame_size )]
253
+ if (curr_frame <= total_frames ):
254
+ frame_data = application [(
255
+ (curr_frame - 1 )* frame_size ):((curr_frame - 1 + 1 )* frame_size )]
251
256
if (args .verbose ):
252
- verboseprint ('\t sending frame #' + str (curr_frame ) +
257
+ verboseprint ('\t Sending frame #' + str (curr_frame ) +
253
258
', length: ' + str (len (frame_data )))
254
259
else :
255
260
percentComplete = curr_frame * 100 / total_frames
@@ -267,19 +272,15 @@ def phase_bootload(ser):
267
272
send_packet (ser , SVL_CMD_DONE , b'' )
268
273
bl_done = True
269
274
270
- if ( bl_failed == False ):
275
+ if (bl_succeeded == True ):
271
276
twopartprint ('\n \t ' , 'Upload complete' )
272
277
endTime = time .time ()
273
278
bps = total_len / (endTime - startTime )
274
279
verboseprint ('\n \t Nominal bootload bps: ' + str (round (bps , 2 )))
275
280
else :
276
281
twopartprint ('\n \t ' , 'Upload failed' )
277
282
278
- return bl_failed
279
-
280
-
281
-
282
-
283
+ return bl_succeeded
283
284
284
285
285
286
# ***********************************************************************************
@@ -294,12 +295,12 @@ def phase_serial_port_help():
294
295
for dev in devices :
295
296
if (dev .device .upper () == args .port .upper ()):
296
297
print (dev .device + " is currently open. Please close any other terminal programs that may be using " +
297
- dev .device + " and try again." )
298
+ dev .device + " and try again." )
298
299
exit ()
299
300
300
301
# otherwise, give user a list of possible com ports
301
302
print (args .port .upper () +
302
- " not found but we detected the following serial ports:" )
303
+ " not found but we detected the following serial ports:" )
303
304
for dev in devices :
304
305
if 'CH340' in dev .description :
305
306
print (
@@ -325,23 +326,46 @@ def main():
325
326
326
327
print ('\n \n Artemis SVL Bootloader' )
327
328
329
+ verboseprint ("Script version " + SCRIPT_VERSION_MAJOR +
330
+ "." + SCRIPT_VERSION_MINOR )
331
+
332
+ if not os .path .exists (args .binfile ):
333
+ print ("Bin file {} does not exist." .format (args .binfile ))
334
+ exit ()
335
+
336
+ bl_success = False
337
+ entered_bootloader = False
338
+
328
339
for _ in range (num_tries ):
329
340
330
341
with serial .Serial (args .port , args .baud , timeout = args .timeout ) as ser :
331
342
332
- t_su = 0.15 # startup time for Artemis bootloader (experimentally determined - 0.095 sec min delay)
343
+ # startup time for Artemis bootloader (experimentally determined - 0.095 sec min delay)
344
+ t_su = 0.15
333
345
334
346
time .sleep (t_su ) # Allow Artemis to come out of reset
335
- phase_setup (ser ) # Perform baud rate negotiation
336
347
337
- bl_failed = phase_bootload (ser ) # Bootload
348
+ # Perform baud rate negotiation
349
+ entered_bootloader = phase_setup (ser )
350
+
351
+ if (entered_bootloader == True ):
352
+ bl_success = phase_bootload (ser )
353
+ if (bl_success == True ): # Bootload
354
+ #print("Bootload complete!")
355
+ break
356
+ else :
357
+ verboseprint ("Failed to enter bootload phase" )
338
358
339
- if ( bl_failed == False ):
359
+ if (bl_success == True ):
340
360
break
341
361
342
- except :
362
+ if (entered_bootloader == False ):
363
+ print (
364
+ "Target failed to enter bootload mode. Verify the right COM port is selected and that your board has the SVL bootloader." )
365
+
366
+ except serial .SerialException :
343
367
phase_serial_port_help ()
344
-
368
+
345
369
exit ()
346
370
347
371
@@ -367,7 +391,7 @@ def main():
367
391
action = "store_true" )
368
392
369
393
parser .add_argument ("-t" , "--timeout" , default = 0.50 , help = "Communication timeout in seconds (default 0.5)" ,
370
- type = float )
394
+ type = float )
371
395
372
396
if len (sys .argv ) < 2 :
373
397
print ("No port selected. Detected Serial Ports:" )
@@ -390,7 +414,7 @@ def verboseprint(*args):
390
414
391
415
def twopartprint (verbosestr , printstr ):
392
416
if args .verbose :
393
- print (verbosestr , end = '' )
417
+ print (verbosestr , end = '' )
394
418
395
419
print (printstr )
396
420
0 commit comments