root / arduino-1.0 / hardware / arduino / bootloaders / atmega8 / ATmegaBOOT.c @ 58d82c77
History | View | Annotate | Download (15.1 KB)
1 | 58d82c77 | Tom Mullins | /**********************************************************/
|
---|---|---|---|
2 | /* Serial Bootloader for Atmel mega8 AVR Controller */
|
||
3 | /* */
|
||
4 | /* ATmegaBOOT.c */
|
||
5 | /* */
|
||
6 | /* Copyright (c) 2003, Jason P. Kyle */
|
||
7 | /* */
|
||
8 | /* Hacked by DojoCorp - ZGZ - MMX - IVR */
|
||
9 | /* Hacked by David A. Mellis */
|
||
10 | /* */
|
||
11 | /* This program is free software; you can redistribute it */
|
||
12 | /* and/or modify it under the terms of the GNU General */
|
||
13 | /* Public License as published by the Free Software */
|
||
14 | /* Foundation; either version 2 of the License, or */
|
||
15 | /* (at your option) any later version. */
|
||
16 | /* */
|
||
17 | /* This program is distributed in the hope that it will */
|
||
18 | /* be useful, but WITHOUT ANY WARRANTY; without even the */
|
||
19 | /* implied warranty of MERCHANTABILITY or FITNESS FOR A */
|
||
20 | /* PARTICULAR PURPOSE. See the GNU General Public */
|
||
21 | /* License for more details. */
|
||
22 | /* */
|
||
23 | /* You should have received a copy of the GNU General */
|
||
24 | /* Public License along with this program; if not, write */
|
||
25 | /* to the Free Software Foundation, Inc., */
|
||
26 | /* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||
27 | /* */
|
||
28 | /* Licence can be viewed at */
|
||
29 | /* http://www.fsf.org/licenses/gpl.txt */
|
||
30 | /* */
|
||
31 | /* Target = Atmel AVR m8 */
|
||
32 | /**********************************************************/
|
||
33 | |||
34 | #include <inttypes.h> |
||
35 | #include <avr/io.h> |
||
36 | #include <avr/pgmspace.h> |
||
37 | #include <avr/eeprom.h> |
||
38 | #include <avr/interrupt.h> |
||
39 | #include <avr/delay.h> |
||
40 | |||
41 | //#define F_CPU 16000000
|
||
42 | |||
43 | /* We, Malmoitians, like slow interaction
|
||
44 | * therefore the slow baud rate ;-)
|
||
45 | */
|
||
46 | //#define BAUD_RATE 9600
|
||
47 | |||
48 | /* 6.000.000 is more or less 8 seconds at the
|
||
49 | * speed configured here
|
||
50 | */
|
||
51 | //#define MAX_TIME_COUNT 6000000
|
||
52 | #define MAX_TIME_COUNT (F_CPU>>1) |
||
53 | ///#define MAX_TIME_COUNT_MORATORY 1600000
|
||
54 | |||
55 | /* SW_MAJOR and MINOR needs to be updated from time to time to avoid warning message from AVR Studio */
|
||
56 | #define HW_VER 0x02 |
||
57 | #define SW_MAJOR 0x01 |
||
58 | #define SW_MINOR 0x12 |
||
59 | |||
60 | // AVR-GCC compiler compatibility
|
||
61 | // avr-gcc compiler v3.1.x and older doesn't support outb() and inb()
|
||
62 | // if necessary, convert outb and inb to outp and inp
|
||
63 | #ifndef outb
|
||
64 | #define outb(sfr,val) (_SFR_BYTE(sfr) = (val))
|
||
65 | #endif
|
||
66 | #ifndef inb
|
||
67 | #define inb(sfr) _SFR_BYTE(sfr)
|
||
68 | #endif
|
||
69 | |||
70 | /* defines for future compatibility */
|
||
71 | #ifndef cbi
|
||
72 | #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
|
||
73 | #endif
|
||
74 | #ifndef sbi
|
||
75 | #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
|
||
76 | #endif
|
||
77 | |||
78 | /* Adjust to suit whatever pin your hardware uses to enter the bootloader */
|
||
79 | #define eeprom_rb(addr) eeprom_read_byte ((uint8_t *)(addr))
|
||
80 | #define eeprom_rw(addr) eeprom_read_word ((uint16_t *)(addr))
|
||
81 | #define eeprom_wb(addr, val) eeprom_write_byte ((uint8_t *)(addr), (uint8_t)(val))
|
||
82 | |||
83 | /* Onboard LED is connected to pin PB5 */
|
||
84 | #define LED_DDR DDRB
|
||
85 | #define LED_PORT PORTB
|
||
86 | #define LED_PIN PINB
|
||
87 | #define LED PINB5
|
||
88 | |||
89 | |||
90 | #define SIG1 0x1E // Yep, Atmel is the only manufacturer of AVR micros. Single source :( |
||
91 | #define SIG2 0x93 |
||
92 | #define SIG3 0x07 |
||
93 | #define PAGE_SIZE 0x20U //32 words |
||
94 | |||
95 | |||
96 | void putch(char); |
||
97 | char getch(void); |
||
98 | void getNch(uint8_t);
|
||
99 | void byte_response(uint8_t);
|
||
100 | void nothing_response(void); |
||
101 | |||
102 | union address_union {
|
||
103 | uint16_t word; |
||
104 | uint8_t byte[2];
|
||
105 | } address; |
||
106 | |||
107 | union length_union {
|
||
108 | uint16_t word; |
||
109 | uint8_t byte[2];
|
||
110 | } length; |
||
111 | |||
112 | struct flags_struct {
|
||
113 | unsigned eeprom : 1; |
||
114 | unsigned rampz : 1; |
||
115 | } flags; |
||
116 | |||
117 | uint8_t buff[256];
|
||
118 | //uint8_t address_high;
|
||
119 | |||
120 | uint8_t pagesz=0x80;
|
||
121 | |||
122 | uint8_t i; |
||
123 | //uint8_t bootuart0=0,bootuart1=0;
|
||
124 | |||
125 | |||
126 | void (*app_start)(void) = 0x0000; |
||
127 | |||
128 | int main(void) |
||
129 | { |
||
130 | uint8_t ch,ch2; |
||
131 | uint16_t w; |
||
132 | |||
133 | //cbi(BL_DDR,BL);
|
||
134 | //sbi(BL_PORT,BL);
|
||
135 | |||
136 | asm volatile("nop\n\t"); |
||
137 | |||
138 | /* check if flash is programmed already, if not start bootloader anyway */
|
||
139 | //if(pgm_read_byte_near(0x0000) != 0xFF) {
|
||
140 | |||
141 | /* check if bootloader pin is set low */
|
||
142 | //if(bit_is_set(BL_PIN,BL)) app_start();
|
||
143 | //}
|
||
144 | |||
145 | /* initialize UART(s) depending on CPU defined */
|
||
146 | /* m8 */
|
||
147 | UBRRH = (((F_CPU/BAUD_RATE)/16)-1)>>8; // set baud rate |
||
148 | UBRRL = (((F_CPU/BAUD_RATE)/16)-1); |
||
149 | UCSRB = (1<<RXEN)|(1<<TXEN); // enable Rx & Tx |
||
150 | UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0); // config USART; 8N1 |
||
151 | |||
152 | //UBRRL = (uint8_t)(F_CPU/(BAUD_RATE*16L)-1);
|
||
153 | //UBRRH = (F_CPU/(BAUD_RATE*16L)-1) >> 8;
|
||
154 | //UCSRA = 0x00;
|
||
155 | //UCSRC = 0x86;
|
||
156 | //UCSRB = _BV(TXEN)|_BV(RXEN);
|
||
157 | |||
158 | |||
159 | /* this was giving uisp problems, so I removed it; without it, the boot
|
||
160 | works on with uisp and avrdude on the mac (at least). */
|
||
161 | //putch('\0');
|
||
162 | |||
163 | //uint32_t l;
|
||
164 | //uint32_t time_count;
|
||
165 | //time_count=0;
|
||
166 | |||
167 | /* set LED pin as output */
|
||
168 | sbi(LED_DDR,LED); |
||
169 | for (i = 0; i < 16; i++) { |
||
170 | outb(LED_PORT, inb(LED_PORT) ^ _BV(LED)); |
||
171 | _delay_loop_2(0);
|
||
172 | } |
||
173 | |||
174 | //for (l=0; l<40000000; l++)
|
||
175 | //outb(LED_PORT, inb(LED_PORT) ^= _BV(LED));
|
||
176 | |||
177 | /* flash onboard LED three times to signal entering of bootloader */
|
||
178 | //for(i=0; i<3; ++i) {
|
||
179 | //for(l=0; l<40000000; ++l);
|
||
180 | //sbi(LED_PORT,LED);
|
||
181 | //for(l=0; l<40000000; ++l);
|
||
182 | //cbi(LED_PORT,LED);
|
||
183 | //}
|
||
184 | |||
185 | /* see comment at previous call to putch() */
|
||
186 | //putch('\0'); // this line is needed for the synchronization of the programmer
|
||
187 | |||
188 | /* forever */
|
||
189 | for (;;) {
|
||
190 | //if((inb(UCSRA) & _BV(RXC))){
|
||
191 | /* get character from UART */
|
||
192 | ch = getch(); |
||
193 | |||
194 | /* A bunch of if...else if... gives smaller code than switch...case ! */
|
||
195 | |||
196 | /* Hello is anyone home ? */
|
||
197 | if(ch=='0') { |
||
198 | nothing_response(); |
||
199 | } |
||
200 | |||
201 | /* Request programmer ID */
|
||
202 | /* Not using PROGMEM string due to boot block in m128 being beyond 64kB boundry */
|
||
203 | /* Would need to selectively manipulate RAMPZ, and it's only 9 characters anyway so who cares. */
|
||
204 | else if(ch=='1') { |
||
205 | if (getch() == ' ') { |
||
206 | putch(0x14);
|
||
207 | putch('A');
|
||
208 | putch('V');
|
||
209 | putch('R');
|
||
210 | putch(' ');
|
||
211 | putch('I');
|
||
212 | putch('S');
|
||
213 | putch('P');
|
||
214 | putch(0x10);
|
||
215 | } |
||
216 | } |
||
217 | |||
218 | /* AVR ISP/STK500 board commands DON'T CARE so default nothing_response */
|
||
219 | else if(ch=='@') { |
||
220 | ch2 = getch(); |
||
221 | if (ch2>0x85) getch(); |
||
222 | nothing_response(); |
||
223 | } |
||
224 | |||
225 | /* AVR ISP/STK500 board requests */
|
||
226 | else if(ch=='A') { |
||
227 | ch2 = getch(); |
||
228 | if(ch2==0x80) byte_response(HW_VER); // Hardware version |
||
229 | else if(ch2==0x81) byte_response(SW_MAJOR); // Software major version |
||
230 | else if(ch2==0x82) byte_response(SW_MINOR); // Software minor version |
||
231 | //else if(ch2==0x98) byte_response(0x03); // Unknown but seems to be required by avr studio 3.56
|
||
232 | else byte_response(0x00); // Covers various unnecessary responses we don't care about |
||
233 | } |
||
234 | |||
235 | /* Device Parameters DON'T CARE, DEVICE IS FIXED */
|
||
236 | else if(ch=='B') { |
||
237 | getNch(20);
|
||
238 | nothing_response(); |
||
239 | } |
||
240 | |||
241 | /* Parallel programming stuff DON'T CARE */
|
||
242 | else if(ch=='E') { |
||
243 | getNch(5);
|
||
244 | nothing_response(); |
||
245 | } |
||
246 | |||
247 | /* Enter programming mode */
|
||
248 | else if(ch=='P') { |
||
249 | nothing_response(); |
||
250 | // FIXME: modified only here by DojoCorp, Mumbai, India, 20050626
|
||
251 | //time_count=0; // exted the delay once entered prog.mode
|
||
252 | } |
||
253 | |||
254 | /* Leave programming mode */
|
||
255 | else if(ch=='Q') { |
||
256 | nothing_response(); |
||
257 | //time_count=MAX_TIME_COUNT_MORATORY; // once the programming is done,
|
||
258 | // we should start the application
|
||
259 | // but uisp has problems with this,
|
||
260 | // therefore we just change the times
|
||
261 | // and give the programmer 1 sec to react
|
||
262 | } |
||
263 | |||
264 | /* Erase device, don't care as we will erase one page at a time anyway. */
|
||
265 | else if(ch=='R') { |
||
266 | nothing_response(); |
||
267 | } |
||
268 | |||
269 | /* Set address, little endian. EEPROM in bytes, FLASH in words */
|
||
270 | /* Perhaps extra address bytes may be added in future to support > 128kB FLASH. */
|
||
271 | /* This might explain why little endian was used here, big endian used everywhere else. */
|
||
272 | else if(ch=='U') { |
||
273 | address.byte[0] = getch();
|
||
274 | address.byte[1] = getch();
|
||
275 | nothing_response(); |
||
276 | } |
||
277 | |||
278 | /* Universal SPI programming command, disabled. Would be used for fuses and lock bits. */
|
||
279 | else if(ch=='V') { |
||
280 | getNch(4);
|
||
281 | byte_response(0x00);
|
||
282 | } |
||
283 | |||
284 | /* Write memory, length is big endian and is in bytes */
|
||
285 | else if(ch=='d') { |
||
286 | length.byte[1] = getch();
|
||
287 | length.byte[0] = getch();
|
||
288 | flags.eeprom = 0;
|
||
289 | if (getch() == 'E') flags.eeprom = 1; |
||
290 | for (w=0;w<length.word;w++) { |
||
291 | buff[w] = getch(); // Store data in buffer, can't keep up with serial data stream whilst programming pages
|
||
292 | } |
||
293 | if (getch() == ' ') { |
||
294 | if (flags.eeprom) { //Write to EEPROM one byte at a time |
||
295 | for(w=0;w<length.word;w++) { |
||
296 | eeprom_wb(address.word,buff[w]); |
||
297 | address.word++; |
||
298 | } |
||
299 | } else { //Write to FLASH one page at a time |
||
300 | //if (address.byte[1]>127) address_high = 0x01; //Only possible with m128, m256 will need 3rd address byte. FIXME
|
||
301 | //else address_high = 0x00;
|
||
302 | |||
303 | //address.word = address.word << 1; //address * 2 -> byte location
|
||
304 | //if ((length.byte[0] & 0x01)) length.word++; //Even up an odd number of bytes
|
||
305 | cli(); //Disable interrupts, just to be sure
|
||
306 | while(bit_is_set(EECR,EEWE)); //Wait for previous EEPROM writes to complete |
||
307 | asm volatile( |
||
308 | "clr r17 \n\t" //page_word_count |
||
309 | "lds r30,address \n\t" //Address of FLASH location (in words) |
||
310 | "lds r31,address+1 \n\t"
|
||
311 | "lsl r30 \n\t" //address * 2 -> byte location |
||
312 | "rol r31 \n\t"
|
||
313 | "ldi r28,lo8(buff) \n\t" //Start of buffer array in RAM |
||
314 | "ldi r29,hi8(buff) \n\t"
|
||
315 | "lds r24,length \n\t" //Length of data to be written (in bytes) |
||
316 | "lds r25,length+1 \n\t"
|
||
317 | "sbrs r24,0 \n\t" //Even up an odd number of bytes |
||
318 | "rjmp length_loop \n\t"
|
||
319 | "adiw r24,1 \n\t"
|
||
320 | "length_loop: \n\t" //Main loop, repeat for number of words in block |
||
321 | "cpi r17,0x00 \n\t" //If page_word_count=0 then erase page |
||
322 | "brne no_page_erase \n\t"
|
||
323 | "rcall wait_spm \n\t"
|
||
324 | // "wait_spm1: \n\t"
|
||
325 | // "lds r16,%0 \n\t" //Wait for previous spm to complete
|
||
326 | // "andi r16,1 \n\t"
|
||
327 | // "cpi r16,1 \n\t"
|
||
328 | // "breq wait_spm1 \n\t"
|
||
329 | "ldi r16,0x03 \n\t" //Erase page pointed to by Z |
||
330 | "sts %0,r16 \n\t"
|
||
331 | "spm \n\t"
|
||
332 | "rcall wait_spm \n\t"
|
||
333 | // "wait_spm2: \n\t"
|
||
334 | // "lds r16,%0 \n\t" //Wait for previous spm to complete
|
||
335 | // "andi r16,1 \n\t"
|
||
336 | // "cpi r16,1 \n\t"
|
||
337 | // "breq wait_spm2 \n\t"
|
||
338 | "ldi r16,0x11 \n\t" //Re-enable RWW section |
||
339 | "sts %0,r16 \n\t"
|
||
340 | "spm \n\t"
|
||
341 | "no_page_erase: \n\t"
|
||
342 | "ld r0,Y+ \n\t" //Write 2 bytes into page buffer |
||
343 | "ld r1,Y+ \n\t"
|
||
344 | |||
345 | "rcall wait_spm \n\t"
|
||
346 | // "wait_spm3: \n\t"
|
||
347 | // "lds r16,%0 \n\t" //Wait for previous spm to complete
|
||
348 | // "andi r16,1 \n\t"
|
||
349 | // "cpi r16,1 \n\t"
|
||
350 | // "breq wait_spm3 \n\t"
|
||
351 | "ldi r16,0x01 \n\t" //Load r0,r1 into FLASH page buffer |
||
352 | "sts %0,r16 \n\t"
|
||
353 | "spm \n\t"
|
||
354 | |||
355 | "inc r17 \n\t" //page_word_count++ |
||
356 | "cpi r17,%1 \n\t"
|
||
357 | "brlo same_page \n\t" //Still same page in FLASH |
||
358 | "write_page: \n\t"
|
||
359 | "clr r17 \n\t" //New page, write current one first |
||
360 | "rcall wait_spm \n\t"
|
||
361 | // "wait_spm4: \n\t"
|
||
362 | // "lds r16,%0 \n\t" //Wait for previous spm to complete
|
||
363 | // "andi r16,1 \n\t"
|
||
364 | // "cpi r16,1 \n\t"
|
||
365 | // "breq wait_spm4 \n\t"
|
||
366 | "ldi r16,0x05 \n\t" //Write page pointed to by Z |
||
367 | "sts %0,r16 \n\t"
|
||
368 | "spm \n\t"
|
||
369 | "rcall wait_spm \n\t"
|
||
370 | // "wait_spm5: \n\t"
|
||
371 | // "lds r16,%0 \n\t" //Wait for previous spm to complete
|
||
372 | // "andi r16,1 \n\t"
|
||
373 | // "cpi r16,1 \n\t"
|
||
374 | // "breq wait_spm5 \n\t"
|
||
375 | "ldi r16,0x11 \n\t" //Re-enable RWW section |
||
376 | "sts %0,r16 \n\t"
|
||
377 | "spm \n\t"
|
||
378 | "same_page: \n\t"
|
||
379 | "adiw r30,2 \n\t" //Next word in FLASH |
||
380 | "sbiw r24,2 \n\t" //length-2 |
||
381 | "breq final_write \n\t" //Finished |
||
382 | "rjmp length_loop \n\t"
|
||
383 | |||
384 | "wait_spm: \n\t"
|
||
385 | "lds r16,%0 \n\t" //Wait for previous spm to complete |
||
386 | "andi r16,1 \n\t"
|
||
387 | "cpi r16,1 \n\t"
|
||
388 | "breq wait_spm \n\t"
|
||
389 | "ret \n\t"
|
||
390 | |||
391 | "final_write: \n\t"
|
||
392 | "cpi r17,0 \n\t"
|
||
393 | "breq block_done \n\t"
|
||
394 | "adiw r24,2 \n\t" //length+2, fool above check on length after short page write |
||
395 | "rjmp write_page \n\t"
|
||
396 | "block_done: \n\t"
|
||
397 | "clr __zero_reg__ \n\t" //restore zero register |
||
398 | : "=m" (SPMCR) : "M" (PAGE_SIZE) : "r0","r16","r17","r24","r25","r28","r29","r30","r31"); |
||
399 | |||
400 | /* Should really add a wait for RWW section to be enabled, don't actually need it since we never */
|
||
401 | /* exit the bootloader without a power cycle anyhow */
|
||
402 | } |
||
403 | putch(0x14);
|
||
404 | putch(0x10);
|
||
405 | } |
||
406 | } |
||
407 | |||
408 | /* Read memory block mode, length is big endian. */
|
||
409 | else if(ch=='t') { |
||
410 | length.byte[1] = getch();
|
||
411 | length.byte[0] = getch();
|
||
412 | if (getch() == 'E') flags.eeprom = 1; |
||
413 | else {
|
||
414 | flags.eeprom = 0;
|
||
415 | address.word = address.word << 1; // address * 2 -> byte location |
||
416 | } |
||
417 | if (getch() == ' ') { // Command terminator |
||
418 | putch(0x14);
|
||
419 | for (w=0;w < length.word;w++) { // Can handle odd and even lengths okay |
||
420 | if (flags.eeprom) { // Byte access EEPROM read |
||
421 | putch(eeprom_rb(address.word)); |
||
422 | address.word++; |
||
423 | } else {
|
||
424 | if (!flags.rampz) putch(pgm_read_byte_near(address.word));
|
||
425 | address.word++; |
||
426 | } |
||
427 | } |
||
428 | putch(0x10);
|
||
429 | } |
||
430 | } |
||
431 | |||
432 | /* Get device signature bytes */
|
||
433 | else if(ch=='u') { |
||
434 | if (getch() == ' ') { |
||
435 | putch(0x14);
|
||
436 | putch(SIG1); |
||
437 | putch(SIG2); |
||
438 | putch(SIG3); |
||
439 | putch(0x10);
|
||
440 | } |
||
441 | } |
||
442 | |||
443 | /* Read oscillator calibration byte */
|
||
444 | else if(ch=='v') { |
||
445 | byte_response(0x00);
|
||
446 | } |
||
447 | // } else {
|
||
448 | // time_count++;
|
||
449 | // if (time_count>=MAX_TIME_COUNT) {
|
||
450 | // app_start();
|
||
451 | // }
|
||
452 | // }
|
||
453 | } /* end of forever loop */
|
||
454 | } |
||
455 | |||
456 | void putch(char ch) |
||
457 | { |
||
458 | /* m8 */
|
||
459 | while (!(inb(UCSRA) & _BV(UDRE)));
|
||
460 | outb(UDR,ch); |
||
461 | } |
||
462 | |||
463 | char getch(void) |
||
464 | { |
||
465 | /* m8 */
|
||
466 | uint32_t count = 0;
|
||
467 | while(!(inb(UCSRA) & _BV(RXC))) {
|
||
468 | /* HACKME:: here is a good place to count times*/
|
||
469 | count++; |
||
470 | if (count > MAX_TIME_COUNT)
|
||
471 | app_start(); |
||
472 | } |
||
473 | return (inb(UDR));
|
||
474 | } |
||
475 | |||
476 | void getNch(uint8_t count)
|
||
477 | { |
||
478 | uint8_t i; |
||
479 | for(i=0;i<count;i++) { |
||
480 | /* m8 */
|
||
481 | //while(!(inb(UCSRA) & _BV(RXC)));
|
||
482 | //inb(UDR);
|
||
483 | getch(); // need to handle time out
|
||
484 | } |
||
485 | } |
||
486 | |||
487 | void byte_response(uint8_t val)
|
||
488 | { |
||
489 | if (getch() == ' ') { |
||
490 | putch(0x14);
|
||
491 | putch(val); |
||
492 | putch(0x10);
|
||
493 | } |
||
494 | } |
||
495 | |||
496 | void nothing_response(void) |
||
497 | { |
||
498 | if (getch() == ' ') { |
||
499 | putch(0x14);
|
||
500 | putch(0x10);
|
||
501 | } |
||
502 | } |
||
503 | |||
504 | /* end of file ATmegaBOOT.c */
|
||
505 | |||
506 |