0,0 → 1,613 |
/***************************************************************************** |
* |
* AVRPROG compatible boot-loader |
* Version : 0.85 (Dec. 2008) |
* Compiler : avr-gcc 4.1.2 / avr-libc 1.4.6 |
* size : depends on features and startup ( minmal features < 512 words) |
* by : Martin Thomas, Kaiserslautern, Germany |
* eversmith@heizung-thomas.de |
* Additional code and improvements contributed by: |
* - Uwe Bonnes |
* - Bjoern Riemer |
* - Olaf Rempel |
* |
* License : Copyright (c) 2006-2008 M. Thomas, U. Bonnes, O. Rempel |
* Free to use. You have to mention the copyright |
* owners in source-code and documentation of derived |
* work. No warranty! (Yes, you can insert the BSD |
* license here) |
* |
* Tested with ATmega8, ATmega16, ATmega162, ATmega32, ATmega324P, |
* ATmega644, ATmega644P, ATmega128, AT90CAN128 |
* |
* - Initial versions have been based on the Butterfly bootloader-code |
* by Atmel Corporation (Authors: BBrandal, PKastnes, ARodland, LHM) |
* |
**************************************************************************** |
* |
* See the makefile and readme.txt for information on how to adapt |
* the linker-settings to the selected Boot Size (BOOTSIZE=xxxx) and |
* the MCU-type. Other configurations futher down in this file. |
* |
* With BOOT_SIMPLE, minimal features and discarded int-vectors |
* this bootloader has should fit into a a 512 word (1024, 0x400 bytes) |
* bootloader-section. |
* |
****************************************************************************/ |
/* |
TODOs: |
- check lock-bits set |
- __bad_interrupt still linked even with modified |
linker-scripts which needs a default-handler, |
"wasted": 3 words for AVR5 (>8kB), 2 words for AVR4 |
- Check watchdog-disable-function in avr-libc. |
*/ |
// tabsize: 4 |
|
// Fuses ATmega 644P D7 DC FC |
|
|
/* MCU frequency */ |
#ifndef F_CPU |
// #define F_CPU 7372800 |
#define F_CPU (20000000) |
#endif |
|
#define BOOTSIZE 1024 |
|
#define set_LED1() (PORTC &= ~(1 << PC3)) |
#define clr_LED1() (PORTC |= (1 << PC3)) |
#define set_LED2() (PORTC &= ~(1 << PC2)) |
#define clr_LED2() (PORTC |= (1 << PC2)) |
#define set_LED3() (PORTB &= ~(1 << PB1)) |
#define clr_LED3() (PORTB |= (1 << PB1)) |
#define set_LED4() (PORTB &= ~(1 << PB0)) |
#define clr_LED4() (PORTB |= (1 << PB0)) |
|
/* UART Baudrate */ |
// #define BAUDRATE 9600 |
// #define BAUDRATE 19200 |
#define BAUDRATE 115200 |
|
/* use "Double Speed Operation" */ |
//#define UART_DOUBLESPEED |
|
/* use second UART on mega128 / can128 / mega162 / mega324p / mega644p */ |
#define UART_USE_SECOND |
|
/* Device-Type: |
For AVRProg the BOOT-option is prefered |
which is the "correct" value for a bootloader. |
avrdude may only detect the part-code for ISP */ |
#define DEVTYPE DEVTYPE_BOOT |
// #define DEVTYPE DEVTYPE_ISP |
|
/* |
* Pin "STARTPIN" on port "STARTPORT" in this port has to grounded |
* (active low) to start the bootloader |
*/ |
#define BLPORT PORTA |
#define BLDDR DDRA |
#define BLPIN PINA |
#define BLPNUM PINA4 |
|
/* |
* Define if Watchdog-Timer should be disable at startup |
*/ |
#define DISABLE_WDT_AT_STARTUP |
|
/* |
* Watchdog-reset is issued at exit |
* define the timeout-value here (see avr-libc manual) |
*/ |
#define EXIT_WDT_TIME WDTO_250MS |
|
/* |
* Select startup-mode |
* SIMPLE-Mode - Jump to bootloader main BL-loop if key is |
* pressed (Pin grounded) "during" reset or jump to the |
* application if the pin is not grounded. The internal |
* pull-up resistor is enabled during the startup and |
* gets disabled before the application is started. |
* POWERSAVE-Mode - Startup is separated in two loops |
* which makes power-saving a little easier if no firmware |
* is on the chip. Needs more memory |
* BOOTICE-Mode - to flash the JTAGICE upgrade.ebn file. |
* No startup-sequence in this mode. Jump directly to the |
* parser-loop on reset |
* F_CPU in BOOTICEMODE must be 7372800 Hz to be compatible |
* with the org. JTAGICE-Firmware |
* WAIT-mode waits 1 sec for the defined character if nothing |
* is recived then the user prog is started. |
*/ |
#define START_SIMPLE |
//#define START_WAIT |
//#define START_POWERSAVE |
//#define START_BOOTICE |
|
/* character to start the bootloader in mode START_WAIT */ |
#define START_WAIT_UARTCHAR 'S' |
|
/* wait-time for START_WAIT mode ( t = WAIT_TIME * 10ms ) */ |
#define WAIT_VALUE 100 /* here: 100*10ms = 1000ms = 1sec */ |
|
/* |
* enable/disable readout of fuse and lock-bits |
* (AVRPROG has to detect the AVR correctly by device-code |
* to show the correct information). |
*/ |
//#define ENABLEREADFUSELOCK |
|
/* enable/disable write of lock-bits |
* WARNING: lock-bits can not be reseted by bootloader (as far as I know) |
* Only protection no unprotection, "chip erase" from bootloader only |
* clears the flash but does no real "chip erase" (this is not possible |
* with a bootloader as far as I know) |
* Keep this undefined! |
*/ |
//#define WRITELOCKBITS |
|
/* |
* define the following if the bootloader should not output |
* itself at flash read (will fake an empty boot-section) |
*/ |
#define READ_PROTECT_BOOTLOADER |
|
|
#define VERSION_HIGH '0' |
#define VERSION_LOW '8' |
|
#define GET_LOCK_BITS 0x0001 |
#define GET_LOW_FUSE_BITS 0x0000 |
#define GET_HIGH_FUSE_BITS 0x0003 |
#define GET_EXTENDED_FUSE_BITS 0x0002 |
|
|
#ifdef UART_DOUBLESPEED |
// #define UART_CALC_BAUDRATE(baudRate) (((F_CPU*10UL) / ((baudRate) *8UL) +5)/10 -1) |
#define UART_CALC_BAUDRATE(baudRate) ((uint32_t)((F_CPU) + ((uint32_t)baudRate * 4UL)) / ((uint32_t)(baudRate) * 8UL) - 1) |
#else |
// #define UART_CALC_BAUDRATE(baudRate) (((F_CPU*10UL) / ((baudRate)*16UL) +5)/10 -1) |
#define UART_CALC_BAUDRATE(baudRate) ((uint32_t)((F_CPU) + ((uint32_t)baudRate * 8UL)) / ((uint32_t)(baudRate) * 16UL) - 1) |
#endif |
|
|
#include <stdint.h> |
#include <avr/io.h> |
#include <avr/wdt.h> |
#include <avr/boot.h> |
#include <avr/pgmspace.h> |
#include <avr/eeprom.h> |
#include <avr/interrupt.h> |
#include <util/delay.h> |
|
#include "chipdef.h" |
|
//#include "lcd.h" |
|
uint8_t gBuffer[SPM_PAGESIZE]; |
|
#if defined(BOOTLOADERHASNOVECTORS) |
#warning "This Bootloader does not link interrupt vectors - see makefile" |
/* make the linker happy - it wants to see __vector_default */ |
// void __vector_default(void) { ; } |
void __vector_default(void) { ; } |
#endif |
|
static void sendchar(uint8_t data) |
{ |
while (!(UART_STATUS & (1<<UART_TXREADY))); |
UART_DATA = data; |
} |
|
static uint8_t recvchar(void) |
{ |
while (!(UART_STATUS & (1<<UART_RXREADY))); |
clr_LED1(); |
return UART_DATA; |
} |
|
static inline void eraseFlash(void) |
{ |
// erase only main section (bootloader protection) |
uint32_t addr = 0; |
while (APP_END > addr) { |
boot_page_erase(addr); // Perform page erase |
boot_spm_busy_wait(); // Wait until the memory is erased. |
addr += SPM_PAGESIZE; |
} |
boot_rww_enable(); |
} |
|
static inline void recvBuffer(pagebuf_t size) |
{ |
pagebuf_t cnt; |
uint8_t *tmp = gBuffer; |
|
for (cnt = 0; cnt < sizeof(gBuffer); cnt++) { |
*tmp++ = (cnt < size) ? recvchar() : 0xFF; |
} |
} |
|
static inline uint16_t writeFlashPage(uint16_t waddr, pagebuf_t size) |
{ |
uint32_t pagestart = (uint32_t)waddr<<1; |
uint32_t baddr = pagestart; |
uint16_t data; |
uint8_t *tmp = gBuffer; |
|
do { |
data = *tmp++; |
data |= *tmp++ << 8; |
boot_page_fill(baddr, data); // call asm routine. |
|
baddr += 2; // Select next word in memory |
size -= 2; // Reduce number of bytes to write by two |
} while (size); // Loop until all bytes written |
|
boot_page_write(pagestart); |
boot_spm_busy_wait(); |
boot_rww_enable(); // Re-enable the RWW section |
|
return baddr>>1; |
} |
|
static inline uint16_t writeEEpromPage(uint16_t address, pagebuf_t size) |
{ |
uint8_t *tmp = gBuffer; |
|
do { |
eeprom_write_byte( (uint8_t*)address, *tmp++ ); |
address++; // Select next byte |
size--; // Decreas number of bytes to write |
} while (size); // Loop until all bytes written |
|
// eeprom_busy_wait(); |
|
return address; |
} |
|
static inline uint16_t readFlashPage(uint16_t waddr, pagebuf_t size) |
{ |
uint32_t baddr = (uint32_t)waddr<<1; |
uint16_t data; |
|
do { |
#ifndef READ_PROTECT_BOOTLOADER |
#warning "Bootloader not read-protected" |
#if defined(RAMPZ) |
data = pgm_read_word_far(baddr); |
#else |
data = pgm_read_word_near(baddr); |
#endif |
#else |
// don't read bootloader |
if ( baddr < APP_END ) { |
#if defined(RAMPZ) |
data = pgm_read_word_far(baddr); |
#else |
data = pgm_read_word_near(baddr); |
#endif |
} |
else { |
data = 0xFFFF; // fake empty |
} |
#endif |
sendchar(data); // send LSB |
sendchar((data >> 8)); // send MSB |
baddr += 2; // Select next word in memory |
size -= 2; // Subtract two bytes from number of bytes to read |
} while (size); // Repeat until block has been read |
|
return baddr>>1; |
} |
|
static inline uint16_t readEEpromPage(uint16_t address, pagebuf_t size) |
{ |
do { |
sendchar( eeprom_read_byte( (uint8_t*)address ) ); |
address++; |
size--; // Decrease number of bytes to read |
} while (size); // Repeat until block has been read |
|
return address; |
} |
|
#if defined(ENABLEREADFUSELOCK) |
static uint8_t read_fuse_lock(uint16_t addr) |
{ |
uint8_t mode = (1<<BLBSET) | (1<<SPMEN); |
uint8_t retval; |
|
asm volatile |
( |
"movw r30, %3\n\t" /* Z to addr */ \ |
"sts %0, %2\n\t" /* set mode in SPM_REG */ \ |
"lpm\n\t" /* load fuse/lock value into r0 */ \ |
"mov %1,r0\n\t" /* save return value */ \ |
: "=m" (SPM_REG), |
"=r" (retval) |
: "r" (mode), |
"r" (addr) |
: "r30", "r31", "r0" |
); |
return retval; |
} |
#endif |
|
static void send_boot(void) |
{ |
sendchar('A'); |
sendchar('V'); |
sendchar('R'); |
sendchar('B'); |
sendchar('O'); |
sendchar('O'); |
sendchar('T'); |
} |
|
|
|
static void (*jump_to_app)(void) = 0x0000; |
|
int main(void) |
{ |
uint16_t address = 0; |
uint8_t device = 0, val; |
|
DDRC = 0xFF; |
PORTC = PORTC | 0xff; |
|
|
|
#ifdef DISABLE_WDT_AT_STARTUP |
#ifdef WDT_OFF_SPECIAL |
#warning "using target specific watchdog_off" |
bootloader_wdt_off(); |
#else |
cli(); |
wdt_reset(); |
wdt_disable(); |
#endif |
#endif |
|
#ifdef START_POWERSAVE |
uint8_t OK = 1; |
#endif |
|
BLDDR &= ~(1<<BLPNUM); // set as Input |
BLPORT |= (1<<BLPNUM); // Enable pullup |
|
// Set baud rate |
UART_BAUD_HIGH = (UART_CALC_BAUDRATE(BAUDRATE)>>8) & 0xFF; |
UART_BAUD_LOW = (UART_CALC_BAUDRATE(BAUDRATE) & 0xFF); |
|
#ifdef UART_DOUBLESPEED |
UART_STATUS = ( 1<<UART_DOUBLE ); |
#endif |
|
UART_CTRL = UART_CTRL_DATA; |
UART_CTRL2 = UART_CTRL2_DATA; |
|
#if defined(START_POWERSAVE) |
/* |
This is an adoption of the Butterfly Bootloader startup-sequence. |
It may look a little strange but separating the login-loop from |
the main parser-loop gives a lot a possibilities (timeout, sleep-modes |
etc.). |
*/ |
for(;OK;) { |
if ((BLPIN & (1<<BLPNUM))) { |
// jump to main app if pin is not grounded |
BLPORT &= ~(1<<BLPNUM); // set to default |
#ifdef UART_DOUBLESPEED |
UART_STATUS &= ~( 1<<UART_DOUBLE ); |
#endif |
jump_to_app(); // Jump to application sector |
|
} else { |
val = recvchar(); |
/* ESC */ |
if (val == 0x1B) { |
// AVRPROG connection |
// Wait for signon |
while (val != 'S') |
val = recvchar(); |
|
send_boot(); // Report signon |
OK = 0; |
|
} else { |
sendchar('?'); |
} |
} |
// Power-Save code here |
} |
|
#elif defined(START_SIMPLE) |
|
if ((BLPIN & (1<<BLPNUM))) { |
// jump to main app if pin is not grounded |
BLPORT &= ~(1<<BLPNUM); // set to default |
#ifdef UART_DOUBLESPEED |
UART_STATUS &= ~( 1<<UART_DOUBLE ); |
#endif |
jump_to_app(); // Jump to application sector |
} |
|
#elif defined(START_WAIT) |
|
uint16_t cnt = 0; |
|
while (1) { |
if (UART_STATUS & (1<<UART_RXREADY)) |
if (UART_DATA == START_WAIT_UARTCHAR) |
break; |
|
if (cnt++ >= WAIT_VALUE) { |
BLPORT &= ~(1<<BLPNUM); // set to default |
jump_to_app(); // Jump to application sector |
} |
|
_delay_ms(10); |
} |
send_boot(); |
|
#elif defined(START_BOOTICE) |
#warning "BOOTICE mode - no startup-condition" |
|
#else |
#error "Select START_ condition for bootloader in main.c" |
#endif |
|
|
|
for(;;) { |
|
set_LED1(); |
|
val = recvchar(); |
|
// Autoincrement? |
if (val == 'a') { |
sendchar('Y'); // Autoincrement is quicker |
|
//write address |
} else if (val == 'A') { |
address = recvchar(); //read address 8 MSB |
address = (address<<8) | recvchar(); |
sendchar('\r'); |
|
// Buffer load support |
} else if (val == 'b') { |
sendchar('Y'); // Report buffer load supported |
sendchar((sizeof(gBuffer) >> 8) & 0xFF); // Report buffer size in bytes |
sendchar(sizeof(gBuffer) & 0xFF); |
|
// Start buffer load |
} else if (val == 'B') { |
pagebuf_t size; |
size = recvchar() << 8; // Load high byte of buffersize |
size |= recvchar(); // Load low byte of buffersize |
val = recvchar(); // Load memory type ('E' or 'F') |
recvBuffer(size); |
|
if (device == DEVTYPE) { |
if (val == 'F') { |
address = writeFlashPage(address, size); |
} else if (val == 'E') { |
address = writeEEpromPage(address, size); |
} |
sendchar('\r'); |
} else { |
sendchar(0); |
} |
|
// Block read |
} else if (val == 'g') { |
pagebuf_t size; |
size = recvchar() << 8; // Load high byte of buffersize |
size |= recvchar(); // Load low byte of buffersize |
val = recvchar(); // Get memtype |
|
if (val == 'F') { |
address = readFlashPage(address, size); |
} else if (val == 'E') { |
address = readEEpromPage(address, size); |
} |
|
// Chip erase |
} else if (val == 'e') { |
if (device == DEVTYPE) { |
eraseFlash(); |
} |
sendchar('\r'); |
|
// Exit upgrade |
} else if (val == 'E') { |
wdt_enable(EXIT_WDT_TIME); // Enable Watchdog Timer to give reset |
sendchar('\r'); |
|
#ifdef WRITELOCKBITS |
#warning "Extension 'WriteLockBits' enabled" |
// TODO: does not work reliably |
// write lockbits |
} else if (val == 'l') { |
if (device == DEVTYPE) { |
// write_lock_bits(recvchar()); |
boot_lock_bits_set(recvchar()); // boot.h takes care of mask |
boot_spm_busy_wait(); |
} |
sendchar('\r'); |
#endif |
// Enter programming mode |
} else if (val == 'P') { |
sendchar('\r'); |
|
// Leave programming mode |
} else if (val == 'L') { |
sendchar('\r'); |
|
// return programmer type |
} else if (val == 'p') { |
sendchar('S'); // always serial programmer |
|
#ifdef ENABLEREADFUSELOCK |
#warning "Extension 'ReadFuseLock' enabled" |
// read "low" fuse bits |
} else if (val == 'F') { |
sendchar(read_fuse_lock(GET_LOW_FUSE_BITS)); |
|
// read lock bits |
} else if (val == 'r') { |
sendchar(read_fuse_lock(GET_LOCK_BITS)); |
|
// read high fuse bits |
} else if (val == 'N') { |
sendchar(read_fuse_lock(GET_HIGH_FUSE_BITS)); |
|
// read extended fuse bits |
} else if (val == 'Q') { |
sendchar(read_fuse_lock(GET_EXTENDED_FUSE_BITS)); |
#endif |
|
// Return device type |
} else if (val == 't') { |
sendchar(DEVTYPE); |
sendchar(0); |
|
// clear and set LED ignored |
} else if ((val == 'x') || (val == 'y')) { |
recvchar(); |
sendchar('\r'); |
|
// set device |
} else if (val == 'T') { |
device = recvchar(); |
sendchar('\r'); |
|
// Return software identifier |
} else if (val == 'S') { |
send_boot(); |
|
// Return Software Version |
} else if (val == 'V') { |
sendchar(VERSION_HIGH); |
sendchar(VERSION_LOW); |
|
// Return Signature Bytes (it seems that |
// AVRProg expects the "Atmel-byte" 0x1E last |
// but shows it first in the dialog-window) |
} else if (val == 's') { |
sendchar(SIG_BYTE3); |
sendchar(SIG_BYTE2); |
sendchar(SIG_BYTE1); |
|
/* ESC */ |
} else if(val != 0x1b) { |
sendchar('?'); |
} |
|
} |
return 0; |
} |