#include "Platform.h" // This bootloader creates a composite Serial device // // The serial interface supports a STK500v1 protocol that is very similar to optiboot // // The bootloader will timeout and start the firmware after a few hundred milliseconds // if a usb connection is not detected. // // The tweakier code is to keep the bootloader below 2k (no interrupt table, for example) extern "C" void entrypoint(void) __attribute__ ((naked)) __attribute__ ((section (".vectors"))); void entrypoint(void) { asm volatile ( "eor r1, r1\n" // Zero register "out 0x3F, r1\n" // SREG "ldi r28, 0xFF\n" "ldi r29, 0x0A\n" "out 0x3E, r29\n" // SPH "out 0x3D, r28\n" // SPL "rjmp main" // Stack is all set up, start the main code ::); } uint8_t _flashbuf[128]; uint8_t _inSync; uint8_t _ok; extern volatile uint8_t _ejected; extern volatile uint16_t _timeout; void Program(uint8_t ep, uint16_t page, uint8_t count) { uint8_t write = page < 30*1024; // Don't write over firmware please if (write) boot_page_erase(page); Recv(ep,_flashbuf,count); // Read while page is erasing if (!write) return; boot_spm_busy_wait(); // Wait until the memory is erased. count >>= 1; uint16_t* p = (uint16_t*)page; uint16_t* b = (uint16_t*)_flashbuf; for (uint8_t i = 0; i < count; i++) boot_page_fill(p++, b[i]); boot_page_write(page); boot_spm_busy_wait(); boot_rww_enable (); } void StartSketch(); int USBGetChar(); #define getch USBGetChar #define HW_VER 0x02 #define SW_MAJOR 0x01 #define SW_MINOR 0x10 #define STK_OK 0x10 #define STK_INSYNC 0x14 // ' ' #define CRC_EOP 0x20 // 'SPACE' #define STK_GET_SYNC 0x30 // '0' #define STK_GET_PARAMETER 0x41 // 'A' #define STK_SET_DEVICE 0x42 // 'B' #define STK_SET_DEVICE_EXT 0x45 // 'E' #define STK_LOAD_ADDRESS 0x55 // 'U' #define STK_UNIVERSAL 0x56 // 'V' #define STK_PROG_PAGE 0x64 // 'd' #define STK_READ_PAGE 0x74 // 't' #define STK_READ_SIGN 0x75 // 'u' extern const uint8_t _readSize[] PROGMEM; const uint8_t _readSize[] = { STK_GET_PARAMETER, 1, STK_SET_DEVICE, 20, STK_SET_DEVICE_EXT, 5, STK_UNIVERSAL, 4, STK_LOAD_ADDRESS, 2, STK_PROG_PAGE, 3, STK_READ_PAGE, 3, 0,0 }; extern const uint8_t _consts[] PROGMEM; const uint8_t _consts[] = { SIGNATURE_0, SIGNATURE_1, SIGNATURE_2, HW_VER, // Hardware version SW_MAJOR, // Software major version SW_MINOR, // Software minor version 0x03, // Unknown but seems to be required by avr studio 3.56 0x00, // }; void USBInit(void); int main(void) __attribute__ ((naked)); // STK500v1 main loop, very similar to optiboot in protocol and implementation int main() { uint8_t MCUSR_state = MCUSR; // store the reason for the reset MCUSR &= ~(1 << WDRF); // must clear the watchdog reset flag before disabling and reenabling WDT wdt_disable(); TX_LED_OFF(); RX_LED_OFF(); L_LED_OFF(); if (MCUSR_state & (1< 2) i = (i == 0x18) ? 3 : 4; // 0x80:HW_VER,0x81:SW_MAJOR,0x82:SW_MINOR,0x18:3 or 0 pgm = _consts + i + 3; send = 1; } else if (STK_UNIVERSAL == cmd) { if (packet[0] == 0x30) pgm = _consts + packet[2]; // read signature send = 1; } // Read signature bytes else if (STK_READ_SIGN == cmd) { pgm = _consts; send = 3; } else if (STK_LOAD_ADDRESS == cmd) { address = *((uint16_t*)packet); // word addresses address += address; } else if (STK_PROG_PAGE == cmd) { Program(CDC_RX,address,packet[1]); } else if (STK_READ_PAGE == cmd) { send = packet[1]; pgm = (const uint8_t*)address; address += send; // not sure of this is required } // Check sync if (getch() != ' ') break; Transfer(CDC_TX,&_inSync,1); // Send result if (send) Transfer(CDC_TX|TRANSFER_PGM,pgm,send); // All from pgm memory // Send ok Transfer(CDC_TX|TRANSFER_RELEASE,&_ok,1); if (cmd == 'Q') break; } _timeout = 500; // wait a moment before exiting the bootloader - may need to finish responding to 'Q' for example _ejected = 1; } } // Nice breathing LED indicates we are in the firmware uint16_t _pulse; void LEDPulse() { _pulse += 4; uint8_t p = _pulse >> 9; if (p > 63) p = 127-p; p += p; if (((uint8_t)_pulse) > p) L_LED_OFF(); else L_LED_ON(); } void StartSketch() { TX_LED_OFF(); // switch off the RX and TX LEDs before starting the user sketch RX_LED_OFF(); UDCON = 1; // Detach USB UDIEN = 0; asm volatile ( // Reset vector to run firmware "clr r30\n" "clr r31\n" "ijmp\n" ::); } void Reset() { wdt_enable(WDTO_15MS); // reset the microcontroller to reinitialize all IO and other registers for (;;) ; }