root / trunk / code / projects / wireless_bootloader / downloader.c @ 1439
History | View | Annotate | Download (9.69 KB)
1 |
#include <stdio.h> |
---|---|
2 |
#include <termios.h> |
3 |
#include <fcntl.h> |
4 |
#include <stdlib.h> |
5 |
#include <unistd.h> |
6 |
#include <sys/ioctl.h> |
7 |
|
8 |
int setupSerial(int fd, int baud) |
9 |
{ |
10 |
struct termios options;
|
11 |
|
12 |
/* set baud rate, etc. correctly */
|
13 |
|
14 |
tcgetattr(fd, &options); |
15 |
cfsetispeed(&options, baud); |
16 |
cfsetospeed(&options, baud); |
17 |
options.c_iflag &= ~ICRNL; |
18 |
options.c_oflag &= ~OCRNL; |
19 |
options.c_cflag |= (CLOCAL | CREAD); |
20 |
options.c_cflag &= ~PARENB; |
21 |
options.c_cflag &= ~CSTOPB; |
22 |
options.c_cflag &= ~CSIZE; |
23 |
options.c_cflag |= CS8; |
24 |
options.c_lflag &= ~ICANON; |
25 |
options.c_cc[VMIN] = 1;
|
26 |
options.c_cc[VTIME] = 50;
|
27 |
|
28 |
if (tcsetattr(fd, TCSANOW, &options))
|
29 |
{ |
30 |
printf("Error setting attributes.\n");
|
31 |
return -1; |
32 |
} |
33 |
|
34 |
return 0; |
35 |
} |
36 |
|
37 |
int initSerial(char* portName) |
38 |
{ |
39 |
/* open port for I/O */
|
40 |
int fd = open(portName, O_RDWR | O_NOCTTY );
|
41 |
if (fd <0) |
42 |
{ |
43 |
perror(portName); |
44 |
return -1; |
45 |
} |
46 |
|
47 |
return fd;
|
48 |
} |
49 |
|
50 |
int writeSerial(int h, const char buf[], int length) |
51 |
{ |
52 |
return write(h,buf,length);
|
53 |
} |
54 |
int writeByte(int h, char buf) |
55 |
{ |
56 |
return writeSerial(h, &buf, 1); |
57 |
} |
58 |
|
59 |
int getAvailBytes(int h) |
60 |
{ |
61 |
int bytes;
|
62 |
ioctl(h, FIONREAD, &bytes); |
63 |
return bytes;
|
64 |
} |
65 |
|
66 |
int readSerial(int h, char buf[], int length) |
67 |
{ |
68 |
return read(h,buf,length);
|
69 |
} |
70 |
int readByte(int h) |
71 |
{ |
72 |
char buf;
|
73 |
if (readSerial(h, &buf, 1)<1) |
74 |
return -1; |
75 |
return buf;
|
76 |
} |
77 |
|
78 |
void closeSerial(int h) |
79 |
{ |
80 |
close(h); |
81 |
} |
82 |
|
83 |
int wait_for_ok(int h) |
84 |
{ |
85 |
const char* s = "OK\r"; |
86 |
const char* curr = s; |
87 |
int count = 0; |
88 |
while (curr - s < 3) |
89 |
{ |
90 |
int c;
|
91 |
while(!getAvailBytes(h))
|
92 |
{ |
93 |
if (count++ > 150) return 0; |
94 |
usleep(10000);
|
95 |
} |
96 |
c = readByte(h); |
97 |
if (c>=0) |
98 |
{ |
99 |
if (c == *curr) {
|
100 |
curr++; |
101 |
} else {
|
102 |
curr = s; |
103 |
} |
104 |
} |
105 |
} |
106 |
|
107 |
return 1; |
108 |
} |
109 |
|
110 |
int xbee_setup(int h, int robotNum) |
111 |
{ |
112 |
int _init = 0; |
113 |
|
114 |
char idstr[9] = "ATIDFF"; |
115 |
setupSerial(h, B9600); |
116 |
|
117 |
writeSerial(h, "+++", 3); |
118 |
if (wait_for_ok(h))
|
119 |
{ |
120 |
printf("Initializing XBee\n");
|
121 |
|
122 |
writeSerial(h, "ATCH0C\r", 7); |
123 |
wait_for_ok(h); |
124 |
|
125 |
idstr[6] = (robotNum / 10) + 0x30; |
126 |
idstr[7] = robotNum % 10 + 0x30; |
127 |
idstr[8] = '\r'; |
128 |
writeSerial(h, idstr, 9);
|
129 |
wait_for_ok(h); |
130 |
|
131 |
writeSerial(h, "ATDH0\r", 6); |
132 |
wait_for_ok(h); |
133 |
writeSerial(h, "ATDLFFFF\r", 9); |
134 |
wait_for_ok(h); |
135 |
writeSerial(h, "ATAP0\r", 6); |
136 |
wait_for_ok(h); |
137 |
writeSerial(h, "ATBD6\r", 6); |
138 |
wait_for_ok(h); |
139 |
writeSerial(h, "ATCN\r", 5); |
140 |
wait_for_ok(h); |
141 |
|
142 |
_init = 1;
|
143 |
} |
144 |
|
145 |
setupSerial(h, B57600); |
146 |
|
147 |
if (!_init)
|
148 |
{ |
149 |
printf("Configuring XBee\n");
|
150 |
|
151 |
writeSerial(h, "+++", 3); |
152 |
wait_for_ok(h); |
153 |
|
154 |
idstr[6] = (robotNum / 10) + 0x30; |
155 |
idstr[7] = robotNum % 10 + 0x30; |
156 |
idstr[8] = '\r'; |
157 |
writeSerial(h, idstr, 9);
|
158 |
wait_for_ok(h); |
159 |
|
160 |
writeSerial(h, "ATCN\r", 5); |
161 |
wait_for_ok(h); |
162 |
} |
163 |
|
164 |
return 0; |
165 |
} |
166 |
|
167 |
/* Take in an intel hex file and load it into a large array
|
168 |
* This allows us to read sequential data from the large array without having to
|
169 |
* worry about weird character line-feed breaks in the text/HEX file.
|
170 |
*
|
171 |
* Takes: file name?
|
172 |
* Returns: Big array
|
173 |
*
|
174 |
* For ATmega168, we have to load 128 bytes at a time (program full page). Document: AVR095
|
175 |
*/
|
176 |
|
177 |
#define MEM_SIZE 131072 |
178 |
|
179 |
unsigned char intel_hex_array[MEM_SIZE]; |
180 |
|
181 |
unsigned int Hex_Convert(char str[3]) |
182 |
{ |
183 |
unsigned int hex_num; |
184 |
char hstr[5] = { '0', 'x', 0, 0, 0 }; |
185 |
hstr[2] = str[0]; |
186 |
hstr[3] = str[1]; |
187 |
sscanf(hstr, "%x", &hex_num);
|
188 |
return hex_num;
|
189 |
} |
190 |
|
191 |
int parse_file(char* file) |
192 |
{ |
193 |
int i;
|
194 |
char buffer[3]; |
195 |
|
196 |
/* Read in the HEX file - clean it up */
|
197 |
FILE* fnum = fopen(file, "r");
|
198 |
if (fnum == NULL) |
199 |
return 1; |
200 |
|
201 |
/* Pre-fill hex_array with 0xFF */
|
202 |
for (i=0; i < MEM_SIZE; i++) |
203 |
{ |
204 |
intel_hex_array[i] = 0xff;
|
205 |
} |
206 |
|
207 |
/* Step through cleaned file and load file contents into large array
|
208 |
* Also convert from ASCII to binary */
|
209 |
for(;;)
|
210 |
{ |
211 |
int Record_Length, Memory_Address_High, Memory_Address_Low, Memory_Address, End_Record;
|
212 |
|
213 |
/* Peel off first character */
|
214 |
int first = fgetc(fnum);
|
215 |
|
216 |
/* Remove empty lines + lines not starting with a ":" */
|
217 |
if (first == EOF) break; |
218 |
if (first != ':') continue; |
219 |
|
220 |
/* Peel off record length */
|
221 |
Record_Length = Hex_Convert(fgets(buffer, 3, fnum));
|
222 |
|
223 |
/* Get the memory address of this line */
|
224 |
Memory_Address_High = Hex_Convert(fgets(buffer, 3, fnum));
|
225 |
Memory_Address_Low = Hex_Convert(fgets(buffer, 3, fnum));
|
226 |
Memory_Address = (Memory_Address_High << 8) + Memory_Address_Low;
|
227 |
|
228 |
/*Divide Memory address by 2
|
229 |
If (Memory_Address_High Mod 2) <> 0 Then
|
230 |
Memory_Address_Low = Memory_Address_Low + 256
|
231 |
End If
|
232 |
Memory_Address_High = Memory_Address_High \ 2
|
233 |
Memory_Address_Low = Memory_Address_Low \ 2*/
|
234 |
|
235 |
/* Peel off and check for end of file tage */
|
236 |
End_Record = Hex_Convert(fgets(buffer, 3, fnum));
|
237 |
if (End_Record == 04 ) continue; |
238 |
if (End_Record == 01) break; |
239 |
|
240 |
for (i=0; i < Record_Length; i++) |
241 |
{ |
242 |
/* Pull out the byte and store it in the memory_address location of the intel_hex array */
|
243 |
intel_hex_array[Memory_Address + i] = Hex_Convert(fgets(buffer, 3, fnum));
|
244 |
} |
245 |
|
246 |
} |
247 |
|
248 |
fclose(fnum); |
249 |
|
250 |
return 0; |
251 |
} |
252 |
|
253 |
|
254 |
int download(int h, char *file) |
255 |
{ |
256 |
int j;
|
257 |
|
258 |
int Memory_Address_High;
|
259 |
int Memory_Address_Low;
|
260 |
|
261 |
int Check_Sum;
|
262 |
|
263 |
char buf;
|
264 |
|
265 |
int last_memory_address;
|
266 |
int current_memory_address;
|
267 |
|
268 |
int page_size = 256; /* For ATmega168, we have to load 128 bytes at a time (program full page). Document: AVR095 */ |
269 |
|
270 |
/* Take intel hex file and store it squentially into large array */
|
271 |
if (parse_file(file))
|
272 |
{ |
273 |
fprintf(stderr, "Whoa - problem parsing file!\n");
|
274 |
return -1; |
275 |
} |
276 |
|
277 |
printf("Waiting for target IC to broadcast\n");
|
278 |
|
279 |
/* "Send the reset signal"; */
|
280 |
|
281 |
while(!getAvailBytes(h))
|
282 |
{ |
283 |
usleep(1500000);
|
284 |
writeByte(h, 6);
|
285 |
} |
286 |
|
287 |
printf("Load Mode Notification Received\n");
|
288 |
|
289 |
/* Find the last spot in the large hex_array */
|
290 |
last_memory_address = MEM_SIZE - 1;
|
291 |
while(last_memory_address > 0) |
292 |
{ |
293 |
if (intel_hex_array[last_memory_address] != 255) break; |
294 |
last_memory_address--; |
295 |
} |
296 |
|
297 |
/* Start at beginning of large intel array */
|
298 |
current_memory_address = 0;
|
299 |
while (current_memory_address < last_memory_address)
|
300 |
{ |
301 |
while((buf = readByte(h))==-1) |
302 |
usleep(1000);
|
303 |
|
304 |
if (buf == 'T') /* All is well */ |
305 |
{ |
306 |
/* Total_Code_Words = Total_Code_Words + (Record_Length / 2) */
|
307 |
} |
308 |
else if (buf == 7) /* Re-send line */ |
309 |
{ |
310 |
static int resend_count = 0; |
311 |
current_memory_address = current_memory_address - page_size; |
312 |
if (current_memory_address<0) current_memory_address=0; |
313 |
printf("\nResending: %d\n", ++resend_count);
|
314 |
usleep(1000);
|
315 |
} |
316 |
else
|
317 |
{ |
318 |
fprintf(stderr, "\nError : Incorrect response from target IC: %d. Programming is incomplete and will now halt.\n", buf);
|
319 |
return -2; |
320 |
} |
321 |
|
322 |
/* Update progress */
|
323 |
printf("\rLoaded %d code words", current_memory_address);
|
324 |
fflush(stdout); |
325 |
|
326 |
/*=============================================*/
|
327 |
/* Convert 16-bit current_memory_address into two 8-bit characters */
|
328 |
Memory_Address_High = current_memory_address >> 8;
|
329 |
Memory_Address_Low = current_memory_address & 0xff;
|
330 |
|
331 |
/*=============================================*/
|
332 |
/* Calculate current check_sum */
|
333 |
Check_Sum = 0;
|
334 |
Check_Sum += page_size >> 8; /* page size high byte */ |
335 |
Check_Sum += page_size & 0xff; /* page size low byte */ |
336 |
Check_Sum += Memory_Address_High; /* Convert high byte */
|
337 |
Check_Sum += Memory_Address_Low; /* Convert low byte */
|
338 |
|
339 |
for(j=0; j < page_size; j++) |
340 |
{ |
341 |
Check_Sum += intel_hex_array[current_memory_address + j]; |
342 |
} |
343 |
|
344 |
/* Now reduce check_sum to 8 bits */
|
345 |
Check_Sum &= 0xff;
|
346 |
/* Now take 2's compliment */
|
347 |
Check_Sum = 256 - Check_Sum;
|
348 |
|
349 |
/*=============================================*/
|
350 |
/* Send the start character */
|
351 |
writeByte(h, ':');
|
352 |
|
353 |
/* Send the record length */
|
354 |
writeByte(h, page_size & 0xff); /* low byte */ |
355 |
writeByte(h, page_size >> 8); /* high byte */ |
356 |
|
357 |
/* Send this block's address */
|
358 |
writeByte(h, Memory_Address_Low); |
359 |
writeByte(h, Memory_Address_High); |
360 |
|
361 |
/* Send this block's check sum */
|
362 |
writeByte(h, Check_Sum); |
363 |
|
364 |
/* Send the block */
|
365 |
for (j=0; j < page_size; j++) |
366 |
{ |
367 |
writeByte(h, intel_hex_array[current_memory_address + j]); |
368 |
} |
369 |
/*=============================================*/
|
370 |
|
371 |
current_memory_address += page_size; |
372 |
} |
373 |
|
374 |
/* Tell the target IC we are done transmitting data */
|
375 |
writeByte(h, ':');
|
376 |
writeByte(h, 'S');
|
377 |
writeByte(h, 'S');
|
378 |
|
379 |
printf("\nDownload Complete!\n");
|
380 |
|
381 |
return 0; |
382 |
} |
383 |
|
384 |
int main(int argc, char* argv[]) |
385 |
{ |
386 |
int h;
|
387 |
|
388 |
printf("//=\\\\ _ __ \n");
|
389 |
printf("|| . |_) _ |_ _ _ | |_ / _ | _ __ \\/\n");
|
390 |
printf("|| -A! | \\(_)|_)(_)(_ | |_||_) \\__(_) | (_)| | / \n");
|
391 |
printf(" \n");
|
392 |
printf(" Universal Programmer \n");
|
393 |
printf(" \n");
|
394 |
|
395 |
switch(argc)
|
396 |
{ |
397 |
case 3: |
398 |
/* open port for I/O */
|
399 |
h = initSerial(argv[1]);
|
400 |
setupSerial(h, B57600); |
401 |
break;
|
402 |
case 4: |
403 |
h = initSerial(argv[1]);
|
404 |
xbee_setup(h, atoi(argv[3]));
|
405 |
break;
|
406 |
default:
|
407 |
printf("Usage: %s <port name> <hex file> [<robot ID>]\n", argv[0]); |
408 |
return -1; |
409 |
} |
410 |
|
411 |
int ret = download(h, argv[2]); |
412 |
|
413 |
closeSerial(h); |
414 |
|
415 |
return ret;
|
416 |
} |