root / arduino-1.0 / hardware / arduino / bootloaders / optiboot / optiboot.c @ 58d82c77
History | View | Annotate | Download (21.8 KB)
1 |
/**********************************************************/
|
---|---|
2 |
/* Optiboot bootloader for Arduino */
|
3 |
/* */
|
4 |
/* http://optiboot.googlecode.com */
|
5 |
/* */
|
6 |
/* Arduino-maintained version : See README.TXT */
|
7 |
/* http://code.google.com/p/arduino/ */
|
8 |
/* */
|
9 |
/* Heavily optimised bootloader that is faster and */
|
10 |
/* smaller than the Arduino standard bootloader */
|
11 |
/* */
|
12 |
/* Enhancements: */
|
13 |
/* Fits in 512 bytes, saving 1.5K of code space */
|
14 |
/* Background page erasing speeds up programming */
|
15 |
/* Higher baud rate speeds up programming */
|
16 |
/* Written almost entirely in C */
|
17 |
/* Customisable timeout with accurate timeconstant */
|
18 |
/* Optional virtual UART. No hardware UART required. */
|
19 |
/* Optional virtual boot partition for devices without. */
|
20 |
/* */
|
21 |
/* What you lose: */
|
22 |
/* Implements a skeleton STK500 protocol which is */
|
23 |
/* missing several features including EEPROM */
|
24 |
/* programming and non-page-aligned writes */
|
25 |
/* High baud rate breaks compatibility with standard */
|
26 |
/* Arduino flash settings */
|
27 |
/* */
|
28 |
/* Fully supported: */
|
29 |
/* ATmega168 based devices (Diecimila etc) */
|
30 |
/* ATmega328P based devices (Duemilanove etc) */
|
31 |
/* */
|
32 |
/* Alpha test */
|
33 |
/* ATmega1280 based devices (Arduino Mega) */
|
34 |
/* */
|
35 |
/* Work in progress: */
|
36 |
/* ATmega644P based devices (Sanguino) */
|
37 |
/* ATtiny84 based devices (Luminet) */
|
38 |
/* */
|
39 |
/* Does not support: */
|
40 |
/* USB based devices (eg. Teensy) */
|
41 |
/* */
|
42 |
/* Assumptions: */
|
43 |
/* The code makes several assumptions that reduce the */
|
44 |
/* code size. They are all true after a hardware reset, */
|
45 |
/* but may not be true if the bootloader is called by */
|
46 |
/* other means or on other hardware. */
|
47 |
/* No interrupts can occur */
|
48 |
/* UART and Timer 1 are set to their reset state */
|
49 |
/* SP points to RAMEND */
|
50 |
/* */
|
51 |
/* Code builds on code, libraries and optimisations from: */
|
52 |
/* stk500boot.c by Jason P. Kyle */
|
53 |
/* Arduino bootloader http://arduino.cc */
|
54 |
/* Spiff's 1K bootloader http://spiffie.org/know/arduino_1k_bootloader/bootloader.shtml */
|
55 |
/* avr-libc project http://nongnu.org/avr-libc */
|
56 |
/* Adaboot http://www.ladyada.net/library/arduino/bootloader.html */
|
57 |
/* AVR305 Atmel Application Note */
|
58 |
/* */
|
59 |
/* This program is free software; you can redistribute it */
|
60 |
/* and/or modify it under the terms of the GNU General */
|
61 |
/* Public License as published by the Free Software */
|
62 |
/* Foundation; either version 2 of the License, or */
|
63 |
/* (at your option) any later version. */
|
64 |
/* */
|
65 |
/* This program is distributed in the hope that it will */
|
66 |
/* be useful, but WITHOUT ANY WARRANTY; without even the */
|
67 |
/* implied warranty of MERCHANTABILITY or FITNESS FOR A */
|
68 |
/* PARTICULAR PURPOSE. See the GNU General Public */
|
69 |
/* License for more details. */
|
70 |
/* */
|
71 |
/* You should have received a copy of the GNU General */
|
72 |
/* Public License along with this program; if not, write */
|
73 |
/* to the Free Software Foundation, Inc., */
|
74 |
/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
75 |
/* */
|
76 |
/* Licence can be viewed at */
|
77 |
/* http://www.fsf.org/licenses/gpl.txt */
|
78 |
/* */
|
79 |
/**********************************************************/
|
80 |
|
81 |
|
82 |
/**********************************************************/
|
83 |
/* */
|
84 |
/* Optional defines: */
|
85 |
/* */
|
86 |
/**********************************************************/
|
87 |
/* */
|
88 |
/* BIG_BOOT: */
|
89 |
/* Build a 1k bootloader, not 512 bytes. This turns on */
|
90 |
/* extra functionality. */
|
91 |
/* */
|
92 |
/* BAUD_RATE: */
|
93 |
/* Set bootloader baud rate. */
|
94 |
/* */
|
95 |
/* LUDICROUS_SPEED: */
|
96 |
/* 230400 baud :-) */
|
97 |
/* */
|
98 |
/* SOFT_UART: */
|
99 |
/* Use AVR305 soft-UART instead of hardware UART. */
|
100 |
/* */
|
101 |
/* LED_START_FLASHES: */
|
102 |
/* Number of LED flashes on bootup. */
|
103 |
/* */
|
104 |
/* LED_DATA_FLASH: */
|
105 |
/* Flash LED when transferring data. For boards without */
|
106 |
/* TX or RX LEDs, or for people who like blinky lights. */
|
107 |
/* */
|
108 |
/* SUPPORT_EEPROM: */
|
109 |
/* Support reading and writing from EEPROM. This is not */
|
110 |
/* used by Arduino, so off by default. */
|
111 |
/* */
|
112 |
/* TIMEOUT_MS: */
|
113 |
/* Bootloader timeout period, in milliseconds. */
|
114 |
/* 500,1000,2000,4000,8000 supported. */
|
115 |
/* */
|
116 |
/**********************************************************/
|
117 |
|
118 |
/**********************************************************/
|
119 |
/* Version Numbers! */
|
120 |
/* */
|
121 |
/* Arduino Optiboot now includes this Version number in */
|
122 |
/* the source and object code. */
|
123 |
/* */
|
124 |
/* Version 3 was released as zip from the optiboot */
|
125 |
/* repository and was distributed with Arduino 0022. */
|
126 |
/* Version 4 starts with the arduino repository commit */
|
127 |
/* that brought the arduino repository up-to-date with */
|
128 |
/* the optiboot source tree changes since v3. */
|
129 |
/* */
|
130 |
/**********************************************************/
|
131 |
|
132 |
/**********************************************************/
|
133 |
/* Edit History: */
|
134 |
/* */
|
135 |
/* 4.4 WestfW: add initialization of address to keep */
|
136 |
/* the compiler happy. Change SC'ed targets. */
|
137 |
/* Return the SW version via READ PARAM */
|
138 |
/* 4.3 WestfW: catch framing errors in getch(), so that */
|
139 |
/* AVRISP works without HW kludges. */
|
140 |
/* http://code.google.com/p/arduino/issues/detail?id=368n*/
|
141 |
/* 4.2 WestfW: reduce code size, fix timeouts, change */
|
142 |
/* verifySpace to use WDT instead of appstart */
|
143 |
/* 4.1 WestfW: put version number in binary. */
|
144 |
/**********************************************************/
|
145 |
|
146 |
#define OPTIBOOT_MAJVER 4 |
147 |
#define OPTIBOOT_MINVER 4 |
148 |
|
149 |
#define MAKESTR(a) #a |
150 |
#define MAKEVER(a, b) MAKESTR(a*256+b) |
151 |
|
152 |
asm(" .section .version\n" |
153 |
"optiboot_version: .word " MAKEVER(OPTIBOOT_MAJVER, OPTIBOOT_MINVER) "\n" |
154 |
" .section .text\n");
|
155 |
|
156 |
#include <inttypes.h> |
157 |
#include <avr/io.h> |
158 |
#include <avr/pgmspace.h> |
159 |
|
160 |
// <avr/boot.h> uses sts instructions, but this version uses out instructions
|
161 |
// This saves cycles and program memory.
|
162 |
#include "boot.h" |
163 |
|
164 |
|
165 |
// We don't use <avr/wdt.h> as those routines have interrupt overhead we don't need.
|
166 |
|
167 |
#include "pin_defs.h" |
168 |
#include "stk500.h" |
169 |
|
170 |
#ifndef LED_START_FLASHES
|
171 |
#define LED_START_FLASHES 0 |
172 |
#endif
|
173 |
|
174 |
#ifdef LUDICROUS_SPEED
|
175 |
#define BAUD_RATE 230400L |
176 |
#endif
|
177 |
|
178 |
/* set the UART baud rate defaults */
|
179 |
#ifndef BAUD_RATE
|
180 |
#if F_CPU >= 8000000L |
181 |
#define BAUD_RATE 115200L // Highest rate Avrdude win32 will support |
182 |
#elsif F_CPU >= 1000000L |
183 |
#define BAUD_RATE 9600L // 19200 also supported, but with significant error |
184 |
#elsif F_CPU >= 128000L |
185 |
#define BAUD_RATE 4800L // Good for 128kHz internal RC |
186 |
#else
|
187 |
#define BAUD_RATE 1200L // Good even at 32768Hz |
188 |
#endif
|
189 |
#endif
|
190 |
|
191 |
/* Switch in soft UART for hard baud rates */
|
192 |
#if (F_CPU/BAUD_RATE) > 280 // > 57600 for 16MHz |
193 |
#ifndef SOFT_UART
|
194 |
#define SOFT_UART
|
195 |
#endif
|
196 |
#endif
|
197 |
|
198 |
/* Watchdog settings */
|
199 |
#define WATCHDOG_OFF (0) |
200 |
#define WATCHDOG_16MS (_BV(WDE))
|
201 |
#define WATCHDOG_32MS (_BV(WDP0) | _BV(WDE))
|
202 |
#define WATCHDOG_64MS (_BV(WDP1) | _BV(WDE))
|
203 |
#define WATCHDOG_125MS (_BV(WDP1) | _BV(WDP0) | _BV(WDE))
|
204 |
#define WATCHDOG_250MS (_BV(WDP2) | _BV(WDE))
|
205 |
#define WATCHDOG_500MS (_BV(WDP2) | _BV(WDP0) | _BV(WDE))
|
206 |
#define WATCHDOG_1S (_BV(WDP2) | _BV(WDP1) | _BV(WDE))
|
207 |
#define WATCHDOG_2S (_BV(WDP2) | _BV(WDP1) | _BV(WDP0) | _BV(WDE))
|
208 |
#ifndef __AVR_ATmega8__
|
209 |
#define WATCHDOG_4S (_BV(WDP3) | _BV(WDE))
|
210 |
#define WATCHDOG_8S (_BV(WDP3) | _BV(WDP0) | _BV(WDE))
|
211 |
#endif
|
212 |
|
213 |
/* Function Prototypes */
|
214 |
/* The main function is in init9, which removes the interrupt vector table */
|
215 |
/* we don't need. It is also 'naked', which means the compiler does not */
|
216 |
/* generate any entry or exit code itself. */
|
217 |
int main(void) __attribute__ ((naked)) __attribute__ ((section (".init9"))); |
218 |
void putch(char); |
219 |
uint8_t getch(void);
|
220 |
static inline void getNch(uint8_t); /* "static inline" is a compiler hint to reduce code size */ |
221 |
void verifySpace();
|
222 |
static inline void flash_led(uint8_t); |
223 |
uint8_t getLen(); |
224 |
static inline void watchdogReset(); |
225 |
void watchdogConfig(uint8_t x);
|
226 |
#ifdef SOFT_UART
|
227 |
void uartDelay() __attribute__ ((naked));
|
228 |
#endif
|
229 |
void appStart() __attribute__ ((naked));
|
230 |
|
231 |
#if defined(__AVR_ATmega168__)
|
232 |
#define RAMSTART (0x100) |
233 |
#define NRWWSTART (0x3800) |
234 |
#elif defined(__AVR_ATmega328P__)
|
235 |
#define RAMSTART (0x100) |
236 |
#define NRWWSTART (0x7000) |
237 |
#elif defined (__AVR_ATmega644P__)
|
238 |
#define RAMSTART (0x100) |
239 |
#define NRWWSTART (0xE000) |
240 |
#elif defined(__AVR_ATtiny84__)
|
241 |
#define RAMSTART (0x100) |
242 |
#define NRWWSTART (0x0000) |
243 |
#elif defined(__AVR_ATmega1280__)
|
244 |
#define RAMSTART (0x200) |
245 |
#define NRWWSTART (0xE000) |
246 |
#elif defined(__AVR_ATmega8__) || defined(__AVR_ATmega88__)
|
247 |
#define RAMSTART (0x100) |
248 |
#define NRWWSTART (0x1800) |
249 |
#endif
|
250 |
|
251 |
/* C zero initialises all global variables. However, that requires */
|
252 |
/* These definitions are NOT zero initialised, but that doesn't matter */
|
253 |
/* This allows us to drop the zero init code, saving us memory */
|
254 |
#define buff ((uint8_t*)(RAMSTART))
|
255 |
#ifdef VIRTUAL_BOOT_PARTITION
|
256 |
#define rstVect (*(uint16_t*)(RAMSTART+SPM_PAGESIZE*2+4)) |
257 |
#define wdtVect (*(uint16_t*)(RAMSTART+SPM_PAGESIZE*2+6)) |
258 |
#endif
|
259 |
|
260 |
/* main program starts here */
|
261 |
int main(void) { |
262 |
uint8_t ch; |
263 |
|
264 |
/*
|
265 |
* Making these local and in registers prevents the need for initializing
|
266 |
* them, and also saves space because code no longer stores to memory.
|
267 |
* (initializing address keeps the compiler happy, but isn't really
|
268 |
* necessary, and uses 4 bytes of flash.)
|
269 |
*/
|
270 |
register uint16_t address = 0; |
271 |
register uint8_t length;
|
272 |
|
273 |
// After the zero init loop, this is the first code to run.
|
274 |
//
|
275 |
// This code makes the following assumptions:
|
276 |
// No interrupts will execute
|
277 |
// SP points to RAMEND
|
278 |
// r1 contains zero
|
279 |
//
|
280 |
// If not, uncomment the following instructions:
|
281 |
// cli();
|
282 |
asm volatile ("clr __zero_reg__"); |
283 |
#ifdef __AVR_ATmega8__
|
284 |
SP=RAMEND; // This is done by hardware reset
|
285 |
#endif
|
286 |
|
287 |
// Adaboot no-wait mod
|
288 |
ch = MCUSR; |
289 |
MCUSR = 0;
|
290 |
if (!(ch & _BV(EXTRF))) appStart();
|
291 |
|
292 |
#if LED_START_FLASHES > 0 |
293 |
// Set up Timer 1 for timeout counter
|
294 |
TCCR1B = _BV(CS12) | _BV(CS10); // div 1024
|
295 |
#endif
|
296 |
#ifndef SOFT_UART
|
297 |
#ifdef __AVR_ATmega8__
|
298 |
UCSRA = _BV(U2X); //Double speed mode USART
|
299 |
UCSRB = _BV(RXEN) | _BV(TXEN); // enable Rx & Tx
|
300 |
UCSRC = _BV(URSEL) | _BV(UCSZ1) | _BV(UCSZ0); // config USART; 8N1
|
301 |
UBRRL = (uint8_t)( (F_CPU + BAUD_RATE * 4L) / (BAUD_RATE * 8L) - 1 ); |
302 |
#else
|
303 |
UCSR0A = _BV(U2X0); //Double speed mode USART0
|
304 |
UCSR0B = _BV(RXEN0) | _BV(TXEN0); |
305 |
UCSR0C = _BV(UCSZ00) | _BV(UCSZ01); |
306 |
UBRR0L = (uint8_t)( (F_CPU + BAUD_RATE * 4L) / (BAUD_RATE * 8L) - 1 ); |
307 |
#endif
|
308 |
#endif
|
309 |
|
310 |
// Set up watchdog to trigger after 500ms
|
311 |
watchdogConfig(WATCHDOG_1S); |
312 |
|
313 |
/* Set LED pin as output */
|
314 |
LED_DDR |= _BV(LED); |
315 |
|
316 |
#ifdef SOFT_UART
|
317 |
/* Set TX pin as output */
|
318 |
UART_DDR |= _BV(UART_TX_BIT); |
319 |
#endif
|
320 |
|
321 |
#if LED_START_FLASHES > 0 |
322 |
/* Flash onboard LED to signal entering of bootloader */
|
323 |
flash_led(LED_START_FLASHES * 2);
|
324 |
#endif
|
325 |
|
326 |
/* Forever loop */
|
327 |
for (;;) {
|
328 |
/* get character from UART */
|
329 |
ch = getch(); |
330 |
|
331 |
if(ch == STK_GET_PARAMETER) {
|
332 |
unsigned char which = getch(); |
333 |
verifySpace(); |
334 |
if (which == 0x82) { |
335 |
/*
|
336 |
* Send optiboot version as "minor SW version"
|
337 |
*/
|
338 |
putch(OPTIBOOT_MINVER); |
339 |
} else if (which == 0x81) { |
340 |
putch(OPTIBOOT_MAJVER); |
341 |
} else {
|
342 |
/*
|
343 |
* GET PARAMETER returns a generic 0x03 reply for
|
344 |
* other parameters - enough to keep Avrdude happy
|
345 |
*/
|
346 |
putch(0x03);
|
347 |
} |
348 |
} |
349 |
else if(ch == STK_SET_DEVICE) { |
350 |
// SET DEVICE is ignored
|
351 |
getNch(20);
|
352 |
} |
353 |
else if(ch == STK_SET_DEVICE_EXT) { |
354 |
// SET DEVICE EXT is ignored
|
355 |
getNch(5);
|
356 |
} |
357 |
else if(ch == STK_LOAD_ADDRESS) { |
358 |
// LOAD ADDRESS
|
359 |
uint16_t newAddress; |
360 |
newAddress = getch(); |
361 |
newAddress = (newAddress & 0xff) | (getch() << 8); |
362 |
#ifdef RAMPZ
|
363 |
// Transfer top bit to RAMPZ
|
364 |
RAMPZ = (newAddress & 0x8000) ? 1 : 0; |
365 |
#endif
|
366 |
newAddress += newAddress; // Convert from word address to byte address
|
367 |
address = newAddress; |
368 |
verifySpace(); |
369 |
} |
370 |
else if(ch == STK_UNIVERSAL) { |
371 |
// UNIVERSAL command is ignored
|
372 |
getNch(4);
|
373 |
putch(0x00);
|
374 |
} |
375 |
/* Write memory, length is big endian and is in bytes */
|
376 |
else if(ch == STK_PROG_PAGE) { |
377 |
// PROGRAM PAGE - we support flash programming only, not EEPROM
|
378 |
uint8_t *bufPtr; |
379 |
uint16_t addrPtr; |
380 |
|
381 |
getch(); /* getlen() */
|
382 |
length = getch(); |
383 |
getch(); |
384 |
|
385 |
// If we are in RWW section, immediately start page erase
|
386 |
if (address < NRWWSTART) __boot_page_erase_short((uint16_t)(void*)address); |
387 |
|
388 |
// While that is going on, read in page contents
|
389 |
bufPtr = buff; |
390 |
do *bufPtr++ = getch();
|
391 |
while (--length);
|
392 |
|
393 |
// If we are in NRWW section, page erase has to be delayed until now.
|
394 |
// Todo: Take RAMPZ into account
|
395 |
if (address >= NRWWSTART) __boot_page_erase_short((uint16_t)(void*)address); |
396 |
|
397 |
// Read command terminator, start reply
|
398 |
verifySpace(); |
399 |
|
400 |
// If only a partial page is to be programmed, the erase might not be complete.
|
401 |
// So check that here
|
402 |
boot_spm_busy_wait(); |
403 |
|
404 |
#ifdef VIRTUAL_BOOT_PARTITION
|
405 |
if ((uint16_t)(void*)address == 0) { |
406 |
// This is the reset vector page. We need to live-patch the code so the
|
407 |
// bootloader runs.
|
408 |
//
|
409 |
// Move RESET vector to WDT vector
|
410 |
uint16_t vect = buff[0] | (buff[1]<<8); |
411 |
rstVect = vect; |
412 |
wdtVect = buff[8] | (buff[9]<<8); |
413 |
vect -= 4; // Instruction is a relative jump (rjmp), so recalculate. |
414 |
buff[8] = vect & 0xff; |
415 |
buff[9] = vect >> 8; |
416 |
|
417 |
// Add jump to bootloader at RESET vector
|
418 |
buff[0] = 0x7f; |
419 |
buff[1] = 0xce; // rjmp 0x1d00 instruction |
420 |
} |
421 |
#endif
|
422 |
|
423 |
// Copy buffer into programming buffer
|
424 |
bufPtr = buff; |
425 |
addrPtr = (uint16_t)(void*)address;
|
426 |
ch = SPM_PAGESIZE / 2;
|
427 |
do {
|
428 |
uint16_t a; |
429 |
a = *bufPtr++; |
430 |
a |= (*bufPtr++) << 8;
|
431 |
__boot_page_fill_short((uint16_t)(void*)addrPtr,a);
|
432 |
addrPtr += 2;
|
433 |
} while (--ch);
|
434 |
|
435 |
// Write from programming buffer
|
436 |
__boot_page_write_short((uint16_t)(void*)address);
|
437 |
boot_spm_busy_wait(); |
438 |
|
439 |
#if defined(RWWSRE)
|
440 |
// Reenable read access to flash
|
441 |
boot_rww_enable(); |
442 |
#endif
|
443 |
|
444 |
} |
445 |
/* Read memory block mode, length is big endian. */
|
446 |
else if(ch == STK_READ_PAGE) { |
447 |
// READ PAGE - we only read flash
|
448 |
getch(); /* getlen() */
|
449 |
length = getch(); |
450 |
getch(); |
451 |
|
452 |
verifySpace(); |
453 |
#ifdef VIRTUAL_BOOT_PARTITION
|
454 |
do {
|
455 |
// Undo vector patch in bottom page so verify passes
|
456 |
if (address == 0) ch=rstVect & 0xff; |
457 |
else if (address == 1) ch=rstVect >> 8; |
458 |
else if (address == 8) ch=wdtVect & 0xff; |
459 |
else if (address == 9) ch=wdtVect >> 8; |
460 |
else ch = pgm_read_byte_near(address);
|
461 |
address++; |
462 |
putch(ch); |
463 |
} while (--length);
|
464 |
#else
|
465 |
#ifdef __AVR_ATmega1280__
|
466 |
// do putch(pgm_read_byte_near(address++));
|
467 |
// while (--length);
|
468 |
do {
|
469 |
uint8_t result; |
470 |
__asm__ ("elpm %0,Z\n":"=r"(result):"z"(address)); |
471 |
putch(result); |
472 |
address++; |
473 |
} |
474 |
while (--length);
|
475 |
#else
|
476 |
do putch(pgm_read_byte_near(address++));
|
477 |
while (--length);
|
478 |
#endif
|
479 |
#endif
|
480 |
} |
481 |
|
482 |
/* Get device signature bytes */
|
483 |
else if(ch == STK_READ_SIGN) { |
484 |
// READ SIGN - return what Avrdude wants to hear
|
485 |
verifySpace(); |
486 |
putch(SIGNATURE_0); |
487 |
putch(SIGNATURE_1); |
488 |
putch(SIGNATURE_2); |
489 |
} |
490 |
else if (ch == 'Q') { |
491 |
// Adaboot no-wait mod
|
492 |
watchdogConfig(WATCHDOG_16MS); |
493 |
verifySpace(); |
494 |
} |
495 |
else {
|
496 |
// This covers the response to commands like STK_ENTER_PROGMODE
|
497 |
verifySpace(); |
498 |
} |
499 |
putch(STK_OK); |
500 |
} |
501 |
} |
502 |
|
503 |
void putch(char ch) { |
504 |
#ifndef SOFT_UART
|
505 |
while (!(UCSR0A & _BV(UDRE0)));
|
506 |
UDR0 = ch; |
507 |
#else
|
508 |
__asm__ __volatile__ ( |
509 |
" com %[ch]\n" // ones complement, carry set |
510 |
" sec\n"
|
511 |
"1: brcc 2f\n"
|
512 |
" cbi %[uartPort],%[uartBit]\n"
|
513 |
" rjmp 3f\n"
|
514 |
"2: sbi %[uartPort],%[uartBit]\n"
|
515 |
" nop\n"
|
516 |
"3: rcall uartDelay\n"
|
517 |
" rcall uartDelay\n"
|
518 |
" lsr %[ch]\n"
|
519 |
" dec %[bitcnt]\n"
|
520 |
" brne 1b\n"
|
521 |
: |
522 |
: |
523 |
[bitcnt] "d" (10), |
524 |
[ch] "r" (ch),
|
525 |
[uartPort] "I" (_SFR_IO_ADDR(UART_PORT)),
|
526 |
[uartBit] "I" (UART_TX_BIT)
|
527 |
: |
528 |
"r25"
|
529 |
); |
530 |
#endif
|
531 |
} |
532 |
|
533 |
uint8_t getch(void) {
|
534 |
uint8_t ch; |
535 |
|
536 |
#ifdef LED_DATA_FLASH
|
537 |
#ifdef __AVR_ATmega8__
|
538 |
LED_PORT ^= _BV(LED); |
539 |
#else
|
540 |
LED_PIN |= _BV(LED); |
541 |
#endif
|
542 |
#endif
|
543 |
|
544 |
#ifdef SOFT_UART
|
545 |
__asm__ __volatile__ ( |
546 |
"1: sbic %[uartPin],%[uartBit]\n" // Wait for start edge |
547 |
" rjmp 1b\n"
|
548 |
" rcall uartDelay\n" // Get to middle of start bit |
549 |
"2: rcall uartDelay\n" // Wait 1 bit period |
550 |
" rcall uartDelay\n" // Wait 1 bit period |
551 |
" clc\n"
|
552 |
" sbic %[uartPin],%[uartBit]\n"
|
553 |
" sec\n"
|
554 |
" dec %[bitCnt]\n"
|
555 |
" breq 3f\n"
|
556 |
" ror %[ch]\n"
|
557 |
" rjmp 2b\n"
|
558 |
"3:\n"
|
559 |
: |
560 |
[ch] "=r" (ch)
|
561 |
: |
562 |
[bitCnt] "d" (9), |
563 |
[uartPin] "I" (_SFR_IO_ADDR(UART_PIN)),
|
564 |
[uartBit] "I" (UART_RX_BIT)
|
565 |
: |
566 |
"r25"
|
567 |
); |
568 |
#else
|
569 |
while(!(UCSR0A & _BV(RXC0)))
|
570 |
; |
571 |
if (!(UCSR0A & _BV(FE0))) {
|
572 |
/*
|
573 |
* A Framing Error indicates (probably) that something is talking
|
574 |
* to us at the wrong bit rate. Assume that this is because it
|
575 |
* expects to be talking to the application, and DON'T reset the
|
576 |
* watchdog. This should cause the bootloader to abort and run
|
577 |
* the application "soon", if it keeps happening. (Note that we
|
578 |
* don't care that an invalid char is returned...)
|
579 |
*/
|
580 |
watchdogReset(); |
581 |
} |
582 |
|
583 |
ch = UDR0; |
584 |
#endif
|
585 |
|
586 |
#ifdef LED_DATA_FLASH
|
587 |
#ifdef __AVR_ATmega8__
|
588 |
LED_PORT ^= _BV(LED); |
589 |
#else
|
590 |
LED_PIN |= _BV(LED); |
591 |
#endif
|
592 |
#endif
|
593 |
|
594 |
return ch;
|
595 |
} |
596 |
|
597 |
#ifdef SOFT_UART
|
598 |
// AVR350 equation: #define UART_B_VALUE (((F_CPU/BAUD_RATE)-23)/6)
|
599 |
// Adding 3 to numerator simulates nearest rounding for more accurate baud rates
|
600 |
#define UART_B_VALUE (((F_CPU/BAUD_RATE)-20)/6) |
601 |
#if UART_B_VALUE > 255 |
602 |
#error Baud rate too slow for soft UART |
603 |
#endif
|
604 |
|
605 |
void uartDelay() {
|
606 |
__asm__ __volatile__ ( |
607 |
"ldi r25,%[count]\n"
|
608 |
"1:dec r25\n"
|
609 |
"brne 1b\n"
|
610 |
"ret\n"
|
611 |
::[count] "M" (UART_B_VALUE)
|
612 |
); |
613 |
} |
614 |
#endif
|
615 |
|
616 |
void getNch(uint8_t count) {
|
617 |
do getch(); while (--count); |
618 |
verifySpace(); |
619 |
} |
620 |
|
621 |
void verifySpace() {
|
622 |
if (getch() != CRC_EOP) {
|
623 |
watchdogConfig(WATCHDOG_16MS); // shorten WD timeout
|
624 |
while (1) // and busy-loop so that WD causes |
625 |
; // a reset and app start.
|
626 |
} |
627 |
putch(STK_INSYNC); |
628 |
} |
629 |
|
630 |
#if LED_START_FLASHES > 0 |
631 |
void flash_led(uint8_t count) {
|
632 |
do {
|
633 |
TCNT1 = -(F_CPU/(1024*16)); |
634 |
TIFR1 = _BV(TOV1); |
635 |
while(!(TIFR1 & _BV(TOV1)));
|
636 |
#ifdef __AVR_ATmega8__
|
637 |
LED_PORT ^= _BV(LED); |
638 |
#else
|
639 |
LED_PIN |= _BV(LED); |
640 |
#endif
|
641 |
watchdogReset(); |
642 |
} while (--count);
|
643 |
} |
644 |
#endif
|
645 |
|
646 |
// Watchdog functions. These are only safe with interrupts turned off.
|
647 |
void watchdogReset() {
|
648 |
__asm__ __volatile__ ( |
649 |
"wdr\n"
|
650 |
); |
651 |
} |
652 |
|
653 |
void watchdogConfig(uint8_t x) {
|
654 |
WDTCSR = _BV(WDCE) | _BV(WDE); |
655 |
WDTCSR = x; |
656 |
} |
657 |
|
658 |
void appStart() {
|
659 |
watchdogConfig(WATCHDOG_OFF); |
660 |
__asm__ __volatile__ ( |
661 |
#ifdef VIRTUAL_BOOT_PARTITION
|
662 |
// Jump to WDT vector
|
663 |
"ldi r30,4\n"
|
664 |
"clr r31\n"
|
665 |
#else
|
666 |
// Jump to RST vector
|
667 |
"clr r30\n"
|
668 |
"clr r31\n"
|
669 |
#endif
|
670 |
"ijmp\n"
|
671 |
); |
672 |
} |