diff options
Diffstat (limited to 'bootloaders/optiboot/optiboot.c')
-rw-r--r-- | bootloaders/optiboot/optiboot.c | 288 |
1 files changed, 176 insertions, 112 deletions
diff --git a/bootloaders/optiboot/optiboot.c b/bootloaders/optiboot/optiboot.c index c7d817a..4b4a10a 100644 --- a/bootloaders/optiboot/optiboot.c +++ b/bootloaders/optiboot/optiboot.c @@ -1,6 +1,11 @@ /**********************************************************/ /* Optiboot bootloader for Arduino */ /* */ +/* http://optiboot.googlecode.com */ +/* */ +/* Arduino-maintained version : See README.TXT */ +/* http://code.google.com/p/arduino/ */ +/* */ /* Heavily optimised bootloader that is faster and */ /* smaller than the Arduino standard bootloader */ /* */ @@ -10,6 +15,8 @@ /* Higher baud rate speeds up programming */ /* Written almost entirely in C */ /* Customisable timeout with accurate timeconstant */ +/* Optional virtual UART. No hardware UART required. */ +/* Optional virtual boot partition for devices without. */ /* */ /* What you lose: */ /* Implements a skeleton STK500 protocol which is */ @@ -18,12 +25,19 @@ /* High baud rate breaks compatibility with standard */ /* Arduino flash settings */ /* */ -/* Currently supports: */ -/* ATmega168 based devices (Diecimila etc) */ +/* Fully supported: */ +/* ATmega168 based devices (Diecimila etc) */ /* ATmega328P based devices (Duemilanove etc) */ /* */ +/* Alpha test */ +/* ATmega1280 based devices (Arduino Mega) */ +/* */ +/* Work in progress: */ +/* ATmega644P based devices (Sanguino) */ +/* ATtiny84 based devices (Luminet) */ +/* */ /* Does not support: */ -/* ATmega1280 based devices (eg. Mega) */ +/* USB based devices (eg. Teensy) */ /* */ /* Assumptions: */ /* The code makes several assumptions that reduce the */ @@ -64,102 +78,85 @@ /* */ /**********************************************************/ + +/**********************************************************/ +/* */ +/* Optional defines: */ +/* */ +/**********************************************************/ +/* */ +/* BIG_BOOT: */ +/* Build a 1k bootloader, not 512 bytes. This turns on */ +/* extra functionality. */ +/* */ +/* BAUD_RATE: */ +/* Set bootloader baud rate. */ +/* */ +/* LUDICROUS_SPEED: */ +/* 230400 baud :-) */ +/* */ +/* SOFT_UART: */ +/* Use AVR305 soft-UART instead of hardware UART. */ +/* */ +/* LED_START_FLASHES: */ +/* Number of LED flashes on bootup. */ +/* */ +/* LED_DATA_FLASH: */ +/* Flash LED when transferring data. For boards without */ +/* TX or RX LEDs, or for people who like blinky lights. */ +/* */ +/* SUPPORT_EEPROM: */ +/* Support reading and writing from EEPROM. This is not */ +/* used by Arduino, so off by default. */ +/* */ +/* TIMEOUT_MS: */ +/* Bootloader timeout period, in milliseconds. */ +/* 500,1000,2000,4000,8000 supported. */ +/* */ +/**********************************************************/ + #include <inttypes.h> #include <avr/io.h> #include <avr/pgmspace.h> -#include <avr/boot.h> -//#define LED_DATA_FLASH +// <avr/boot.h> uses sts instructions, but this version uses out instructions +// This saves cycles and program memory. +#include "boot.h" + + +// We don't use <avr/wdt.h> as those routines have interrupt overhead we don't need. + +#include "pin_defs.h" +#include "stk500.h" #ifndef LED_START_FLASHES #define LED_START_FLASHES 0 #endif -/* Build-time variables */ -/* BAUD_RATE Programming baud rate */ -/* LED_NO_FLASHES Number of LED flashes on boot */ -/* FLASH_TIME_MS Duration of each LED flash */ -/* BOOT_TIMEOUT_MS Serial port wait time before exiting bootloader */ +#ifdef LUDICROUS_SPEED +#define BAUD_RATE 230400L +#endif -/* set the UART baud rate */ +/* set the UART baud rate defaults */ #ifndef BAUD_RATE -#define BAUD_RATE 19200 +#if F_CPU >= 8000000L +#define BAUD_RATE 115200L // Highest rate Avrdude win32 will support +#elsif F_CPU >= 1000000L +#define BAUD_RATE 9600L // 19200 also supported, but with significant error +#elsif F_CPU >= 128000L +#define BAUD_RATE 4800L // Good for 128kHz internal RC +#else +#define BAUD_RATE 1200L // Good even at 32768Hz +#endif #endif -#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) -/* Onboard LED is connected to pin PB5 in Arduino NG, Diecimila, and Duemilanove */ -#define LED_DDR DDRB -#define LED_PORT PORTB -#define LED_PIN PINB -#define LED PINB5 - -/* Ports for soft UART */ -#ifdef SOFT_UART -#define UART_PORT PORTD -#define UART_PIN PIND -#define UART_DDR DDRD -#define UART_TX_BIT 1 -#define UART_RX_BIT 0 +/* Switch in soft UART for hard baud rates */ +#if (F_CPU/BAUD_RATE) > 280 // > 57600 for 16MHz +#ifndef SOFT_UART +#define SOFT_UART #endif #endif -#if defined(__AVR_ATtiny84__) -/* Onboard LED is connected to pin PB5 in Arduino NG, Diecimila, and Duemilanove */ -#define LED_DDR DDRA -#define LED_PORT PORTA -#define LED_PIN PINA -#define LED PINA4 - -/* Ports for soft UART - left port only for now*/ -#ifdef SOFT_UART -#define UART_PORT PORTA -#define UART_PIN PINA -#define UART_DDR DDRA -#define UART_TX_BIT 2 -#define UART_RX_BIT 3 -#endif -#endif - -/* STK500 constants list, from AVRDUDE */ -#define STK_OK 0x10 -#define STK_FAILED 0x11 // Not used -#define STK_UNKNOWN 0x12 // Not used -#define STK_NODEVICE 0x13 // Not used -#define STK_INSYNC 0x14 // ' ' -#define STK_NOSYNC 0x15 // Not used -#define ADC_CHANNEL_ERROR 0x16 // Not used -#define ADC_MEASURE_OK 0x17 // Not used -#define PWM_CHANNEL_ERROR 0x18 // Not used -#define PWM_ADJUST_OK 0x19 // Not used -#define CRC_EOP 0x20 // 'SPACE' -#define STK_GET_SYNC 0x30 // '0' -#define STK_GET_SIGN_ON 0x31 // '1' -#define STK_SET_PARAMETER 0x40 // '@' -#define STK_GET_PARAMETER 0x41 // 'A' -#define STK_SET_DEVICE 0x42 // 'B' -#define STK_SET_DEVICE_EXT 0x45 // 'E' -#define STK_ENTER_PROGMODE 0x50 // 'P' -#define STK_LEAVE_PROGMODE 0x51 // 'Q' -#define STK_CHIP_ERASE 0x52 // 'R' -#define STK_CHECK_AUTOINC 0x53 // 'S' -#define STK_LOAD_ADDRESS 0x55 // 'U' -#define STK_UNIVERSAL 0x56 // 'V' -#define STK_PROG_FLASH 0x60 // '`' -#define STK_PROG_DATA 0x61 // 'a' -#define STK_PROG_FUSE 0x62 // 'b' -#define STK_PROG_LOCK 0x63 // 'c' -#define STK_PROG_PAGE 0x64 // 'd' -#define STK_PROG_FUSE_EXT 0x65 // 'e' -#define STK_READ_FLASH 0x70 // 'p' -#define STK_READ_DATA 0x71 // 'q' -#define STK_READ_FUSE 0x72 // 'r' -#define STK_READ_LOCK 0x73 // 's' -#define STK_READ_PAGE 0x74 // 't' -#define STK_READ_SIGN 0x75 // 'u' -#define STK_READ_OSCCAL 0x76 // 'v' -#define STK_READ_FUSE_EXT 0x77 // 'w' -#define STK_READ_OSCCAL_EXT 0x78 // 'x' - /* Watchdog settings */ #define WATCHDOG_OFF (0) #define WATCHDOG_16MS (_BV(WDE)) @@ -170,8 +167,10 @@ #define WATCHDOG_500MS (_BV(WDP2) | _BV(WDP0) | _BV(WDE)) #define WATCHDOG_1S (_BV(WDP2) | _BV(WDP1) | _BV(WDE)) #define WATCHDOG_2S (_BV(WDP2) | _BV(WDP1) | _BV(WDP0) | _BV(WDE)) +#ifndef __AVR_ATmega8__ #define WATCHDOG_4S (_BV(WDE3) | _BV(WDE)) #define WATCHDOG_8S (_BV(WDE3) | _BV(WDE0) | _BV(WDE)) +#endif /* Function Prototypes */ /* The main function is in init9, which removes the interrupt vector table */ @@ -191,18 +190,41 @@ void uartDelay() __attribute__ ((naked)); #endif void appStart() __attribute__ ((naked)); +#if defined(__AVR_ATmega168__) +#define RAMSTART (0x100) +#define NRWWSTART (0x3800) +#elif defined(__AVR_ATmega328P__) +#define RAMSTART (0x100) +#define NRWWSTART (0x7000) +#elif defined (__AVR_ATmega644P__) +#define RAMSTART (0x100) +#define NRWWSTART (0xE000) +#elif defined(__AVR_ATtiny84__) +#define RAMSTART (0x100) +#define NRWWSTART (0x0000) +#elif defined(__AVR_ATmega1280__) +#define RAMSTART (0x200) +#define NRWWSTART (0xE000) +#elif defined(__AVR_ATmega8__) || defined(__AVR_ATmega88__) +#define RAMSTART (0x100) +#define NRWWSTART (0x1800) +#endif + /* C zero initialises all global variables. However, that requires */ /* These definitions are NOT zero initialised, but that doesn't matter */ /* This allows us to drop the zero init code, saving us memory */ -#define buff ((uint8_t*)(0x100)) -#define address (*(uint16_t*)(0x200)) -#define length (*(uint8_t*)(0x202)) +#define buff ((uint8_t*)(RAMSTART)) +#define address (*(uint16_t*)(RAMSTART+SPM_PAGESIZE*2)) +#define length (*(uint8_t*)(RAMSTART+SPM_PAGESIZE*2+2)) #ifdef VIRTUAL_BOOT_PARTITION -#define rstVect (*(uint16_t*)(0x204)) -#define wdtVect (*(uint16_t*)(0x206)) +#define rstVect (*(uint16_t*)(RAMSTART+SPM_PAGESIZE*2+4)) +#define wdtVect (*(uint16_t*)(RAMSTART+SPM_PAGESIZE*2+6)) #endif + /* main program starts here */ int main(void) { + uint8_t ch; + // After the zero init loop, this is the first code to run. // // This code makes the following assumptions: @@ -212,29 +234,36 @@ int main(void) { // // If not, uncomment the following instructions: // cli(); - // SP=RAMEND; // This is done by hardware reset asm volatile ("clr __zero_reg__"); +#ifdef __AVR_ATmega8__ + SP=RAMEND; // This is done by hardware reset +#endif - uint8_t ch; + // Adaboot no-wait mod + ch = MCUSR; + MCUSR = 0; + if (!(ch & _BV(EXTRF))) appStart(); #if LED_START_FLASHES > 0 // Set up Timer 1 for timeout counter TCCR1B = _BV(CS12) | _BV(CS10); // div 1024 #endif #ifndef SOFT_UART +#ifdef __AVR_ATmega8__ + UCSRA = _BV(U2X); //Double speed mode USART + UCSRB = _BV(RXEN) | _BV(TXEN); // enable Rx & Tx + UCSRC = _BV(URSEL) | _BV(UCSZ1) | _BV(UCSZ0); // config USART; 8N1 + UBRRL = (uint8_t)( (F_CPU + BAUD_RATE * 4L) / (BAUD_RATE * 8L) - 1 ); +#else UCSR0A = _BV(U2X0); //Double speed mode USART0 UCSR0B = _BV(RXEN0) | _BV(TXEN0); UCSR0C = _BV(UCSZ00) | _BV(UCSZ01); UBRR0L = (uint8_t)( (F_CPU + BAUD_RATE * 4L) / (BAUD_RATE * 8L) - 1 ); #endif - - // Adaboot no-wait mod - ch = MCUSR; - MCUSR = 0; - if (!(ch & _BV(EXTRF))) appStart(); +#endif // Set up watchdog to trigger after 500ms - watchdogConfig(WATCHDOG_500MS); + watchdogConfig(WATCHDOG_1S); /* Set LED pin as output */ LED_DDR |= _BV(LED); @@ -269,9 +298,15 @@ int main(void) { } else if(ch == STK_LOAD_ADDRESS) { // LOAD ADDRESS - address = getch(); - address = (address & 0xff) | (getch() << 8); - address += address; // Convert from word address to byte address + uint16_t newAddress; + newAddress = getch(); + newAddress = (newAddress & 0xff) | (getch() << 8); +#ifdef RAMPZ + // Transfer top bit to RAMPZ + RAMPZ = (newAddress & 0x8000) ? 1 : 0; +#endif + newAddress += newAddress; // Convert from word address to byte address + address = newAddress; verifySpace(); } else if(ch == STK_UNIVERSAL) { @@ -279,7 +314,7 @@ int main(void) { getNch(4); putch(0x00); } - /* Write memory, length is big endian and is in bytes */ + /* Write memory, length is big endian and is in bytes */ else if(ch == STK_PROG_PAGE) { // PROGRAM PAGE - we support flash programming only, not EEPROM uint8_t *bufPtr; @@ -287,14 +322,18 @@ int main(void) { getLen(); - // Immediately start page erase - this will 4.5ms - boot_page_erase((uint16_t)(void*)address); - + // If we are in RWW section, immediately start page erase + if (address < NRWWSTART) __boot_page_erase_short((uint16_t)(void*)address); + // While that is going on, read in page contents bufPtr = buff; do *bufPtr++ = getch(); while (--length); + // If we are in NRWW section, page erase has to be delayed until now. + // Todo: Take RAMPZ into account + if (address >= NRWWSTART) __boot_page_erase_short((uint16_t)(void*)address); + // Read command terminator, start reply verifySpace(); @@ -310,10 +349,10 @@ int main(void) { // Move RESET vector to WDT vector uint16_t vect = buff[0] | (buff[1]<<8); rstVect = vect; - wdtVect = buff[10] | (buff[11]<<8); + wdtVect = buff[8] | (buff[9]<<8); vect -= 4; // Instruction is a relative jump (rjmp), so recalculate. - buff[10] = vect & 0xff; - buff[11] = vect >> 8; + buff[8] = vect & 0xff; + buff[9] = vect >> 8; // Add jump to bootloader at RESET vector buff[0] = 0x7f; @@ -329,12 +368,12 @@ int main(void) { uint16_t a; a = *bufPtr++; a |= (*bufPtr++) << 8; - boot_page_fill((uint16_t)(void*)addrPtr,a); + __boot_page_fill_short((uint16_t)(void*)addrPtr,a); addrPtr += 2; } while (--ch); // Write from programming buffer - boot_page_write((uint16_t)(void*)address); + __boot_page_write_short((uint16_t)(void*)address); boot_spm_busy_wait(); #if defined(RWWSRE) @@ -353,16 +392,28 @@ int main(void) { // Undo vector patch in bottom page so verify passes if (address == 0) ch=rstVect & 0xff; else if (address == 1) ch=rstVect >> 8; - else if (address == 10) ch=wdtVect & 0xff; - else if (address == 11) ch=wdtVect >> 8; + else if (address == 8) ch=wdtVect & 0xff; + else if (address == 9) ch=wdtVect >> 8; else ch = pgm_read_byte_near(address); address++; putch(ch); } while (--length); #else +#ifdef __AVR_ATmega1280__ +// do putch(pgm_read_byte_near(address++)); +// while (--length); + do { + uint8_t result; + __asm__ ("elpm %0,Z\n":"=r"(result):"z"(address)); + putch(result); + address++; + } + while (--length); +#else do putch(pgm_read_byte_near(address++)); while (--length); #endif +#endif } /* Get device signature bytes */ @@ -422,8 +473,12 @@ uint8_t getch(void) { watchdogReset(); #ifdef LED_DATA_FLASH +#ifdef __AVR_ATmega8__ + LED_PORT ^= _BV(LED); +#else LED_PIN |= _BV(LED); #endif +#endif #ifdef SOFT_UART __asm__ __volatile__ ( @@ -455,14 +510,19 @@ uint8_t getch(void) { #endif #ifdef LED_DATA_FLASH +#ifdef __AVR_ATmega8__ + LED_PORT ^= _BV(LED); +#else LED_PIN |= _BV(LED); #endif +#endif return ch; } #ifdef SOFT_UART -//#define UART_B_VALUE (((F_CPU/BAUD_RATE)-23)/6) +// AVR350 equation: #define UART_B_VALUE (((F_CPU/BAUD_RATE)-23)/6) +// Adding 3 to numerator simulates nearest rounding for more accurate baud rates #define UART_B_VALUE (((F_CPU/BAUD_RATE)-20)/6) #if UART_B_VALUE > 255 #error Baud rate too slow for soft UART @@ -495,7 +555,11 @@ void flash_led(uint8_t count) { TCNT1 = -(F_CPU/(1024*16)); TIFR1 = _BV(TOV1); while(!(TIFR1 & _BV(TOV1))); +#ifdef __AVR_ATmega8__ + LED_PORT ^= _BV(LED); +#else LED_PIN |= _BV(LED); +#endif watchdogReset(); } while (--count); } @@ -524,7 +588,7 @@ void appStart() { __asm__ __volatile__ ( #ifdef VIRTUAL_BOOT_PARTITION // Jump to WDT vector - "ldi r30,5\n" + "ldi r30,4\n" "clr r31\n" #else // Jump to RST vector |