From cc2c7ab4dcdea188b2d0f77a8aab8349e8e3786c Mon Sep 17 00:00:00 2001 From: David Voswinkel Date: Mon, 20 Jul 2009 19:08:05 +0200 Subject: [PATCH] add boot loader --- avr/bootloader/bootloader.c | 411 ++++++++++++++++++++++++++++++++++++ 1 file changed, 411 insertions(+) create mode 100644 avr/bootloader/bootloader.c diff --git a/avr/bootloader/bootloader.c b/avr/bootloader/bootloader.c new file mode 100644 index 0000000..7372b94 --- /dev/null +++ b/avr/bootloader/bootloader.c @@ -0,0 +1,411 @@ +/* simple USBasp compatible bootloader + * by Alexander Neumann + * + * inspired by USBasploader by Christian Starkjohann, + * see http://www.obdev.at/products/avrusb/usbasploader.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * For more information on the GPL, please go to: + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "usbdrv/usbdrv.c" + +/* USBasp requests, taken from the original USBasp sourcecode */ +#define USBASP_FUNC_CONNECT 1 +#define USBASP_FUNC_DISCONNECT 2 +#define USBASP_FUNC_TRANSMIT 3 +#define USBASP_FUNC_READFLASH 4 +#define USBASP_FUNC_ENABLEPROG 5 +#define USBASP_FUNC_WRITEFLASH 6 +#define USBASP_FUNC_READEEPROM 7 +#define USBASP_FUNC_WRITEEEPROM 8 +#define USBASP_FUNC_SETLONGADDRESS 9 + +/* additional functions */ +#define FUNC_ECHO 0x17 + +/* atmel isp commands */ +#define ISP_CHIP_ERASE1 0xAC +#define ISP_CHIP_ERASE2 0x80 +#define ISP_READ_SIGNATURE 0x30 +#define ISP_READ_EEPROM 0xa0 +#define ISP_WRITE_EEPROM 0xc0 + + + +#define LED_PORT PORTC +#define LED_DIR DDRC +#define LED_PIN PC7 + +#define DLED_ON {((LED_PORT &=~ (1 << LED_PIN)),\ + (LED_DIR &=~ (1 << LED_PIN))); } +#define DLED_OFF {((LED_PORT &=~ (1 << LED_PIN)),\ + (LED_DIR |= (1 << LED_PIN))); } +#define DLED_TGL {((LED_PORT ^= (1 << LED_PIN)),\ + (LED_DIR |= (1 << LED_PIN)));} + + + + +/* some predefined signatures, taken from the original USBasp sourcecode */ +static const uint8_t signature[4] = { +#ifdef SIGNATURE_BYTES + SIGNATURE_BYTES +#elif defined (__AVR_ATmega8__) || defined (__AVR_ATmega8HVA__) + 0x1e, 0x93, 0x07, 0 +#elif defined (__AVR_ATmega48__) || defined (__AVR_ATmega48P__) + 0x1e, 0x92, 0x05, 0 +#elif defined (__AVR_ATmega88__) || defined (__AVR_ATmega88P__) + 0x1e, 0x93, 0x0a, 0 +#elif defined (__AVR_ATmega168__) || defined (__AVR_ATmega168P__) + 0x1e, 0x94, 0x06, 0 +#elif defined (__AVR_ATmega328P__) + 0x1e, 0x95, 0x0f, 0 +#elif defined (__AVR_ATmega644__) + 0x1e, 0x96, 0x06, 0 +#else +# error "Device signature is not known, please edit config.h!" +#endif +}; + +#ifndef BOOT_SECTION_START +# error "BOOT_SECTION_START undefined!" +#endif + + +#ifdef DEBUG_UART +static __attribute__ (( __noinline__ )) void putc(uint8_t data) { + while(!(UCSR0A & _BV(UDRE0))); + UDR0 = data; +} +#else +#define putc(x) +#endif + +/* supply custom usbDeviceConnect() and usbDeviceDisconnect() macros + * which turn the interrupt on and off at the right times, + * and prevent the execution of an interrupt while the pullup resistor + * is switched off */ +/* +#ifdef USB_CFG_PULLUP_IOPORTNAME +#undef usbDeviceConnect +#define usbDeviceConnect() do { \ + USB_PULLUP_DDR |= (1< 0xffff +# error "usbload only supports up to 64kb of flash!" +#endif + +/* we are just checking the lower byte of flash_address, + * so make sure SPM_PAGESIZE is <= 256 + */ +#if SPM_PAGESIZE > 256 +# error "SPM_PAGESIZE is too big (just checking lower byte)" +#endif + +/* start flash (byte address) read/write at this address */ +usbWord_t flash_address; +uint8_t bytes_remaining; +uint8_t request; +uint8_t request_exit; + +uint8_t timeout; + +usbMsgLen_t usbFunctionSetup(uchar data[8]) +{ + usbRequest_t *req = (void *)data; + uint8_t len = 0; + static uint8_t buf[4]; + + /* set global data pointer to local buffer */ + usbMsgPtr = buf; + + /* on enableprog just return one zero, which means success */ + if (req->bRequest == USBASP_FUNC_ENABLEPROG) { + buf[0] = 0; + len = 1; + timeout = 255; + } else if (req->bRequest == USBASP_FUNC_CONNECT) { + /* turn on led */ + DLED_ON; + } else if (req->bRequest == USBASP_FUNC_DISCONNECT) { + /* turn off led */ + DLED_OFF; + request_exit = 1; + /* catch query for the devicecode, chip erase and eeprom byte requests */ + } else if (req->bRequest == USBASP_FUNC_TRANSMIT) { + + /* reset buffer with zeroes */ + memset(buf, '\0', sizeof(buf)); + + /* read the address for eeprom operations */ + usbWord_t address; + address.bytes[0] = data[4]; /* low byte is data[4] */ + address.bytes[1] = data[3]; /* high byte is data[3] */ + + /* if this is a request to read the device signature, answer with the + * appropiate signature byte */ + if (data[2] == ISP_READ_SIGNATURE) { + /* the complete isp data is reported back to avrdude, but we just need byte 4 + * bits 0 and 1 of byte 3 determine the signature byte address */ + buf[3] = signature[data[4] & 0x03]; + +#ifdef ENABLE_CATCH_EEPROM_ISP + /* catch eeprom read */ + } else if (data[2] == ISP_READ_EEPROM) { + + buf[3] = eeprom_read_byte((uint8_t *)address.word); + + /* catch eeprom write */ + } else if (data[2] == ISP_WRITE_EEPROM) { + + /* address is in data[4], data[3], and databyte is in data[5] */ + eeprom_write_byte((uint8_t *)address.word, data[5]); +#endif + + /* catch a chip erase */ + } else if (data[2] == ISP_CHIP_ERASE1 && data[3] == ISP_CHIP_ERASE2) { + for (flash_address.word = 0; + flash_address.word < BOOT_SECTION_START; + flash_address.word += SPM_PAGESIZE) { + + /* wait and erase page */ + boot_spm_busy_wait(); + cli(); + boot_page_erase(flash_address.word); + sei(); + } + } + + /* in case no data has been filled in by the if's above, just return zeroes */ + len = 4; + +#ifdef ENABLE_ECHO_FUNC + /* implement a simple echo function, for testing the usb connectivity */ + } else if (req->bRequest == FUNC_ECHO) { + buf[0] = req->wValue.bytes[0]; + buf[1] = req->wValue.bytes[1]; + len = 2; +#endif + } else if (req->bRequest >= USBASP_FUNC_READFLASH) { + /* && req->bRequest <= USBASP_FUNC_SETLONGADDRESS */ + + putc('R'); + putc(req->bRequest); + + /* extract address and length */ + flash_address.word = req->wValue.word; + bytes_remaining = req->wLength.bytes[0]; + request = req->bRequest; + /* hand control over to usbFunctionRead()/usbFunctionWrite() */ + len = 0xff; + } + + return len; +} + +uchar usbFunctionWrite(uchar *data, uchar len) +{ + if (len > bytes_remaining) + len = bytes_remaining; + bytes_remaining -= len; + + if (request == USBASP_FUNC_WRITEEEPROM) { + for (uint8_t i = 0; i < len; i++) + eeprom_write_byte((uint8_t *)flash_address.word++, *data++); + } else { + /* data is handled wordwise, adjust len */ + len /= 2; + len -= 1; + for (uint8_t i = 0; i <= len; i++) { + uint16_t *w = (uint16_t *)data; + cli(); + boot_page_fill(flash_address.word, *w); + sei(); + + usbWord_t next_address; + next_address.word = flash_address.word; + next_address.word += 2; + data += 2; + + /* write page if page boundary is crossed or this is the last page */ + if ( next_address.bytes[0] % SPM_PAGESIZE == 0 || + (bytes_remaining == 0 && i == len) ) { + cli(); + boot_page_write(flash_address.word); + sei(); + boot_spm_busy_wait(); + cli(); + boot_rww_enable(); + sei(); + } + + flash_address.word = next_address.word; + } + } + + /* flash led on activity */ + DLED_TGL; + + return (bytes_remaining == 0); +} + +uchar usbFunctionRead(uchar *data, uchar len) +{ + if(len > bytes_remaining) + len = bytes_remaining; + bytes_remaining -= len; + + for (uint8_t i = 0; i < len; i++) { + if(request == USBASP_FUNC_READEEPROM) + *data = eeprom_read_byte((void *)flash_address.word); + else + *data = pgm_read_byte_near((void *)flash_address.word); + data++; + flash_address.word++; + } + + /* flash led on activity */ + DLED_TGL; + + return len; +} + +void (*jump_to_app)(void) = 0x0000; +void leave_bootloader(void) +{ + + cli(); + + /* disconnect usb */ + usbDeviceDisconnect(); + for (uint8_t i = 0; i < 38; i++) + _delay_loop_2(0); /* 0 means 0x10000, 38*1/f*0x10000 =~ 498ms */ + + /* enable watchdog to soft-reset the uC for clean startup of new application */ + wdt_enable(WDTO_15MS); + + /* let watchdog kick in and reset uC */ + while(1); + +} + + +int __attribute__ ((noreturn,OS_main)) main(void) +{ + /* start bootloader */ + + /* init LED-Pin */ + PORTB = 0; + + +#ifdef DEBUG_UART + /* init uart (115200 baud, at 20mhz) */ + UBRR0L = 10; + UCSR0C = _BV(UCSZ00) | _BV(UCSZ01); + UCSR0B = _BV(TXEN0); + putc('b'); +#endif + + uint8_t reset = MCUSR; + + /* if power-on reset, quit bootloader via watchdog reset */ + if (reset & _BV(PORF)){ + MCUSR = 0; + leave_bootloader(); + } + /* if watchdog reset, disable watchdog and jump to app */ + else if(reset & _BV(WDRF)){ + MCUSR = 0; + wdt_disable(); + jump_to_app(); + } + + /* else: enter programming mode */ + + + + + /* clear external reset flags */ + MCUSR = 0; + + /* init exit request state */ + request_exit = 0; + + /* move interrupts to boot section */ + MCUCR = (1 << IVCE); + MCUCR = (1 << IVSEL); + + /* enable interrupts */ + sei(); + + /* initialize usb pins */ + usbInit(); + + /* disconnect for ~500ms, so that the host re-enumerates this device */ + usbDeviceDisconnect(); + for (uint8_t i = 0; i < 38; i++) + _delay_loop_2(0); /* 0 means 0x10000, 38*1/f*0x10000 =~ 498ms */ + usbDeviceConnect(); + + uint16_t delay; + timeout = TIMEOUT; + + while(1) { + usbPoll(); + delay++; + + /* do some led blinking, so that it is visible that the bootloader is still running */ + if (delay == 0) { + DLED_TGL; + if (timeout < 255) + timeout--; + } + + if (request_exit || timeout == 0) { + _delay_loop_2(0); + leave_bootloader(); + } + } + + leave_bootloader(); +}