447 lines
14 KiB
C
447 lines
14 KiB
C
/*****************************************************************************
|
|
*
|
|
* AVRPROG compatible boot-loader
|
|
* Version : 0.2 (24. March 2004)
|
|
* Compiler : avr-gcc 3.3.1 / avr-libc 1.0
|
|
* size : ca. 610 word ( larger than 512 words :-( )
|
|
* by : Martin Thomas, Kaiserslautern, Germany
|
|
* eversmith@heizung-thomas.de
|
|
*
|
|
* based on the Butterfly Bootloader-Code
|
|
* Copyright (C) 1996-1998 Atmel Corporation
|
|
* Author(s) : BBrandal, PKastnes, ARodland, LHM
|
|
*
|
|
* The orignal code has been made available by ATMEL together with the
|
|
* Butterfly application code. Since ATMEL.NO had no problem with
|
|
* the application gcc-port they hopefully will not have any concerns about
|
|
* publishing this port. Make sure to keep the copyright notice in derived
|
|
* work to avoid trouble.
|
|
****************************************************************************
|
|
*
|
|
* Many functions used by "AVRPROG" (fuses) have been disabled by ATMEL in
|
|
* the original source code of the Butterfly Boot-loader not by me.
|
|
* Please 'diff' against the original source to see everything that has been
|
|
* changed for the gcc port.
|
|
*
|
|
* The boot interrupt vector is included (this bootloader is completly in
|
|
* ".text" section). If you need this space for further functions you have to
|
|
* add a separate section for the bootloader-functions and add an attribute
|
|
* for this section to _all_ function prototypes of functions in the loader.
|
|
* With this the interrupt vector will be placed at .0000 and the bootloader
|
|
* code (without interrupt vector) at the adress you define in the linker
|
|
* options for the newly created section. See the avr-libc FAQ and the avr-
|
|
* libc's avr/boot.h documentation for further details.
|
|
*
|
|
* For this bootloader a Boot-Size of at least 0x4C4 (1220) bytes =
|
|
* 610 words is needed. Sorry, so far efforts to shrink to 512 words failed.
|
|
* See the makefile for information how to adopt the linker-settings to
|
|
* the selected Boot Size (_Bxxx below)
|
|
*
|
|
****************************************************************************/
|
|
|
|
|
|
#include <avr/io.h>
|
|
#include <avr/interrupt.h>
|
|
#include <avr/signal.h>
|
|
#include <avr/wdt.h>
|
|
|
|
|
|
/* READFUSELOCK is untested, will not work for Mega169 since not
|
|
supported by AVRPROG 1.37 */
|
|
// #define ENABLEREADFUSELOCK
|
|
|
|
#define BAUDRATE 19200
|
|
#define XTAL 3686400
|
|
|
|
/* Select Boot Size (select one, comment out the others)
|
|
select at least _B1024 */
|
|
// NO! #define _B128
|
|
// NO! #define _B256
|
|
// NO! #define _B512
|
|
#define _B1024
|
|
//#define _B2048
|
|
|
|
#include "chipdef.h"
|
|
|
|
#define UART_RX_BUFFER_SIZE PAGESIZE
|
|
|
|
#include "lowlevel.h"
|
|
#include "uart.h"
|
|
|
|
unsigned char BufferLoad(unsigned int , unsigned char ) ;
|
|
void BlockRead(unsigned int , unsigned char ) ;
|
|
|
|
unsigned int address;
|
|
unsigned char device;
|
|
|
|
void send_boot(void)
|
|
{
|
|
sendchar('A');
|
|
sendchar('V');
|
|
sendchar('R');
|
|
sendchar('B');
|
|
sendchar('O');
|
|
sendchar('O');
|
|
sendchar('T');
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
void (*funcptr)( void ) = 0x0000; // Set up function pointer
|
|
|
|
unsigned int tempi;
|
|
char val;
|
|
char OK = 1;
|
|
|
|
PORTA = 0xFF; // Enable pullups on Port A
|
|
|
|
USART_Init(UART_BAUD_SELECT(BAUDRATE,XTAL),UARTSINGLE);
|
|
// USART_Init(UART_BAUD_SELECT(BAUDRATE/2,XTAL),UARTDOUBLE);
|
|
|
|
MCUCR = (1<<IVCE);
|
|
MCUCR = (1<<IVSEL); //move interruptvectors to the Boot sector
|
|
|
|
/* 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((PINA & (1<<PINA7)))
|
|
{
|
|
// jump to main app if PIN A0 is not grounded
|
|
MCUCR = (1<<IVCE);
|
|
MCUCR = (0<<IVSEL); //move interruptvectors to the Application sector
|
|
funcptr(); // Jump to application sector
|
|
}
|
|
else
|
|
{
|
|
val = recchar();
|
|
|
|
if( val == 0x1B)
|
|
{ // AVRPROG connection
|
|
while (val != 'S') // Wait for signon
|
|
{
|
|
val = recchar();
|
|
}
|
|
send_boot(); // Report signon
|
|
OK = 0;
|
|
}
|
|
else
|
|
sendchar('?');
|
|
}
|
|
}
|
|
|
|
for(;;)
|
|
{
|
|
val=recchar();
|
|
|
|
if(val=='a') //Autoincrement?
|
|
{
|
|
sendchar('Y'); //Autoincrement is quicker
|
|
}
|
|
|
|
else if(val=='A') //write address
|
|
{
|
|
address=recchar(); //read address 8 MSB
|
|
address=(address<<8)|recchar();
|
|
|
|
address=address<<1; //convert from word address to byte address
|
|
sendchar('\r');
|
|
}
|
|
|
|
else if(val=='b')
|
|
{ // Buffer load support
|
|
sendchar('Y'); // Report buffer load supported
|
|
sendchar((UART_RX_BUFFER_SIZE >> 8) & 0xFF);
|
|
// Report buffer size in bytes
|
|
sendchar(UART_RX_BUFFER_SIZE & 0xFF);
|
|
}
|
|
|
|
else if(val=='B') // Start buffer load
|
|
{
|
|
tempi = recchar() << 8; // Load high byte of buffersize
|
|
tempi |= recchar(); // Load low byte of buffersize
|
|
val = recchar(); // Load memory type ('E' or 'F')
|
|
sendchar (BufferLoad(tempi,val));
|
|
// Start downloading of buffer
|
|
}
|
|
|
|
else if(val == 'g') // Block read
|
|
{
|
|
tempi = (recchar() << 8) | recchar();
|
|
|
|
val = recchar(); // Get memtype
|
|
BlockRead(tempi,val); // Perform the block read
|
|
}
|
|
/*
|
|
else if(val=='c') //Write program memory, low byte
|
|
{
|
|
ldata=recchar();
|
|
sendchar('\r');
|
|
}
|
|
|
|
else if(val== 'C') //Write program memory, high byte
|
|
{
|
|
data=ldata|(recchar()<<8);
|
|
if (device == devtype)
|
|
{
|
|
fill_temp_buffer(data,(address)); //call asm routine.
|
|
}
|
|
address=address+2;
|
|
sendchar('\r');
|
|
}
|
|
*/
|
|
else if(val=='e') //Chip erase
|
|
{
|
|
if (device == devtype)
|
|
{
|
|
for(address=0;address < APP_END;address += PAGESIZE) //Application section = 60 pages
|
|
{
|
|
write_page(address,(1<<PGERS) + (1<<SPMEN)); //Perform page erase
|
|
write_page(address,(1<<RWWSRE) + (1<<SPMEN)); //Re-enable the RWW section
|
|
}
|
|
}
|
|
write_page(address,(1<<RWWSRE) + (1<<SPMEN)); //Re-enable the RWW section
|
|
sendchar('\r');
|
|
}
|
|
|
|
else if(val=='E') //Exit upgrade
|
|
{
|
|
// WDTCR = (1<<WDTCE) | (1<<WDE); //Enable Watchdog Timer to give reset
|
|
wdt_enable(WDTO_15MS);
|
|
sendchar('\r');
|
|
}
|
|
/*
|
|
else if(val=='l') // write lockbits
|
|
{
|
|
if (device == devtype)
|
|
{
|
|
write_lock_bits(recchar());
|
|
}
|
|
sendchar('\r');
|
|
}
|
|
|
|
else if(val== 'm') // write page
|
|
{
|
|
if (device == devtype)
|
|
{
|
|
write_page(address,(1<<PGERS) + (1<<SPMEN)); //Perform page erase
|
|
write_page((address),0x05);
|
|
write_page(address,(1<<RWWSRE) + (1<<SPMEN)); //Re-enable the RWW section
|
|
}
|
|
sendchar('\r');
|
|
}
|
|
*/
|
|
|
|
else if(val=='P') // Enter programming mode
|
|
{
|
|
sendchar('\r');
|
|
}
|
|
|
|
else if(val=='L') // Leave programming mode
|
|
{
|
|
sendchar('\r');
|
|
}
|
|
|
|
|
|
else if (val=='p') // mt: return programmer type
|
|
{
|
|
sendchar('S'); // serial programmer
|
|
}
|
|
|
|
/*
|
|
else if(val=='R') //Read program memory
|
|
{
|
|
write_page(0,(1<<RWWSRE) + (1<<SPMEN)); //Re-enable the RWW section
|
|
// SPMCSR = (1<<RWWSRE) | (1<<SPMEN);
|
|
// __store_program_memory();
|
|
// while((SPMCSR & 0x01));
|
|
|
|
intval=read_program_memory(address,0x00);
|
|
sendchar((char)(intval>>8)); //send MSB
|
|
sendchar((char)intval); //send LSB
|
|
address=address+2;
|
|
}
|
|
|
|
else if (val == 'D') // write EEPROM
|
|
{
|
|
if (device == devtype)
|
|
{
|
|
EEARL = address;
|
|
EEARH = (address >> 8);
|
|
address++;
|
|
EEDR = recchar();
|
|
EECR |= (1<<EEMWE);
|
|
EECR |= (1<<EEWE);
|
|
while (EECR & (1<<EEWE))
|
|
;
|
|
}
|
|
sendchar('\r');
|
|
}
|
|
|
|
else if (val == 'd') // read eeprom
|
|
{
|
|
EEARL = address;
|
|
EEARH = (address >> 8);
|
|
address++;
|
|
EECR |= (1<<EERE);
|
|
sendchar(EEDR);
|
|
}
|
|
*/
|
|
#ifdef ENABLEREADFUSELOCK
|
|
#warning "Extension 'ReadFuseLock' enabled"
|
|
// mt TODO: Read fuse bit seems to work for clock speed, other settings are not
|
|
// interpreted correctly. Locks and high fuse do not work at all (in AVRPROG 1.37)
|
|
// Reason for this should be the difference between ATmega16 and ATmega169.
|
|
// AVRPROG interprets the results as from an ATmega16 while they are from an ATmega169
|
|
else if(val=='F') // read fuse bits
|
|
{
|
|
sendchar(read_program_memory(0x0000,0x09)); // 0x09 for (1<<BLBSET)|(1<<SPMEN)
|
|
}
|
|
|
|
else if(val=='r') // read lock bits
|
|
{
|
|
sendchar(read_program_memory(0x0001,0x09));
|
|
}
|
|
|
|
else if(val=='N') // read high fuse bits
|
|
{
|
|
// mt sendchar(read_program_memory(0x0003));
|
|
sendchar(read_program_memory(0x0003,0x09));
|
|
}
|
|
|
|
else if(val=='Q') // read extended fuse bits
|
|
{
|
|
sendchar(read_program_memory(0x0002,0x09));
|
|
}
|
|
#endif
|
|
// end of ENABLEREADFUSELOCK section
|
|
|
|
else if(val=='t') // Return programmer type
|
|
{
|
|
sendchar(devtype);
|
|
sendchar(0);
|
|
}
|
|
|
|
else if ((val=='x')||(val=='y')) // clear and set LED ignored
|
|
{
|
|
recchar();
|
|
sendchar('\r');
|
|
}
|
|
|
|
else if (val=='T') // set device/programmer type in bootloader (?)
|
|
{
|
|
device = recchar();
|
|
sendchar('\r');
|
|
}
|
|
|
|
else if (val=='S') // Return software identifier
|
|
{
|
|
send_boot();
|
|
}
|
|
|
|
else if (val=='V') // Return Software Version
|
|
{
|
|
sendchar('0'); // mt: changed from 2;0 to 0;1
|
|
sendchar('2');
|
|
}
|
|
|
|
else if (val=='s') // Return Signature Byte
|
|
{
|
|
sendchar(sig_byte1);
|
|
sendchar(sig_byte2);
|
|
sendchar(sig_byte3);
|
|
}
|
|
|
|
else if(val!=0x1b) // if not esc
|
|
{
|
|
sendchar('?');
|
|
}
|
|
} // "parser" for-loop
|
|
return 0;
|
|
}
|
|
|
|
|
|
unsigned char BufferLoad(unsigned int size, unsigned char mem)
|
|
{
|
|
int data, tempaddress;
|
|
|
|
tempaddress = address; // Store address in page
|
|
|
|
if (device == devtype)
|
|
{
|
|
if (mem == 'F')
|
|
{
|
|
|
|
do {
|
|
data = recchar();
|
|
data |= (recchar() << 8);
|
|
fill_temp_buffer(data,(address));
|
|
//call asm routine.
|
|
address=address+2; // Select next word in memory
|
|
size -= 2; // Reduce number of bytes to write by two
|
|
|
|
} while(size); // Loop until all bytes written
|
|
|
|
tempaddress &= 0xFF80; // Ensure the address points to the first byte in the page
|
|
write_page((tempaddress),0x05); // Program page contents
|
|
|
|
write_page(tempaddress,(1<<RWWSRE) + (1<<SPMEN));
|
|
//Re-enable the RWW section
|
|
if (address != (address & 0xFF80))
|
|
{ // Ensure that the address points to the beginning of the next page
|
|
address &= 0xFF80;
|
|
address += PAGESIZE;
|
|
}
|
|
} // End FLASH
|
|
|
|
if (mem == 'E') // Start EEPROM
|
|
{
|
|
do {
|
|
EEARL = address; // Setup EEPROM address
|
|
EEARH = (address >> 8);
|
|
address++; // Select next byte
|
|
EEDR = recchar(); // Load data to write
|
|
EECR |= (1<<EEMWE); // Write data into EEPROM
|
|
EECR |= (1<<EEWE);
|
|
while (EECR & (1<<EEWE)) // Wait for EEPROM write to finish
|
|
;
|
|
size--; // Decreas number of bytes to write
|
|
} while(size); // Loop until all bytes written
|
|
}
|
|
return '\r'; // Report programming OK
|
|
}
|
|
return 0; // Report programming failed
|
|
}
|
|
|
|
void BlockRead(unsigned int size, unsigned char mem)
|
|
{
|
|
unsigned int data;
|
|
|
|
if (mem == 'E') // Read EEPROM
|
|
{
|
|
do {
|
|
EEARL = address; // Setup EEPROM address
|
|
EEARH = (address >> 8);
|
|
address++; // Select next EEPROM byte
|
|
EECR |= (1<<EERE); // Read EEPROM
|
|
sendchar(EEDR); // Transmit EEPROM data to PC
|
|
size--; // Decrease number of bytes to read
|
|
} while (size); // Repeat until all block has been read
|
|
}
|
|
else // Read Flash
|
|
{
|
|
do {
|
|
data = read_program_memory(address,0x00);
|
|
sendchar((char)data); //send LSB
|
|
sendchar((char)(data >> 8)); //send MSB
|
|
address += 2; // Select next word in memory
|
|
size -= 2; // Subtract two bytes from number of bytes to read
|
|
} while (size); // Repeat until all block has been read
|
|
}
|
|
}
|
|
|