/***************************************************************************** * * Atmel Corporation * * File : main.c * Compiler : IAR C 3.10C Kickstart, AVR-GCC/avr-libc(>= 1.2.5) * Revision : $Revision: 1.7 $ * Date : $Date: Tuesday, June 07, 200 $ * Updated by : $Author: raapeland $ * * Support mail : avr@atmel.com * * Target platform : All AVRs with bootloader support * * AppNote : AVR109 - Self-programming * * Description : This Program allows an AVR with bootloader capabilities to * Read/write its own Flash/EEprom. To enter Programming mode * an input pin is checked. If this pin is pulled low, programming mode * is entered. If not, normal execution is done from $0000 * "reset" vector in Application area. * * Preparations : Use the preprocessor.xls file for obtaining a customized * defines.h file and linker-file code-segment definition for * the device you are compiling for. ****************************************************************************/ #include "defines.h" #include "serial.h" #include "flash.h" /* Uncomment the following to save code space */ /*#define REMOVE_AVRPROG_SUPPORT */ /*#define REMOVE_FUSE_AND_LOCK_BIT_SUPPORT */ /*#define REMOVE_BLOCK_SUPPORT */ /*#define REMOVE_EEPROM_BYTE_SUPPORT */ /*#define REMOVE_FLASH_BYTE_SUPPORT */ /* * GCC doesn't optimize long int arithmetics very clever. As the * address only needs to be larger than 16 bits for the ATmega128 and * above (where flash consumptions isn't much of an issue as the * entire boot loader will still fit even into the smallest possible * boot loader section), save space by using a 16-bit variable for the * smaller devices. */ #ifdef LARGE_MEMORY #define ADDR_T unsigned long #else /* !LARGE_MEMORY */ #define ADDR_T unsigned int #endif /* LARGE_MEMORY */ #ifndef REMOVE_BLOCK_SUPPORT unsigned char BlockLoad( unsigned int size, unsigned char mem, ADDR_T * address); void BlockRead( unsigned int size, unsigned char mem, ADDR_T * address); /* BLOCKSIZE should be chosen so that the following holds: BLOCKSIZE*n = PAGESIZE, where n=1,2,3... */ #define BLOCKSIZE PAGESIZE #endif /* REMOVE_BLOCK_SUPPORT */ #ifdef __ICCAVR__ __C_task void main( void) #else /* ! __ICCAVR__ */ int main( void) #endif /* __ICCAVR__ */ { ADDR_T address; unsigned int temp_int; unsigned char val; /* Initialization */ void ( *funcptr) ( void) = 0x0000; /* Set up function pointer to RESET vector. */ PROGPORT |= (1 << PROG_NO); /* Enable pull-up on PROG_NO line on PROGPORT. */ initbootuart(); /* Initialize UART. */ /* Branch to bootloader or application code? */ if (!(PROGPIN & (1 << PROG_NO))) { /* If PROGPIN is pulled low, enter programmingmode. */ /* Main loop */ for (;;) { val = recchar(); /* Wait for command character. */ /* Check autoincrement status. */ if (val == 'a') { sendchar('Y'); /* Yes, we do autoincrement. */ } /* Set address. */ else if (val == 'A') { /* Set address... *//* NOTE: Flash addresses are given in words, not bytes. */ address = (recchar() << 8) | recchar(); /* Read address high and low byte. */ sendchar('\r'); /* Send OK back. */ } /* Chip erase. */ else if (val == 'e') { for (address = 0; address < APP_END; address += PAGESIZE) { /* NOTE: Here we use address as a byte-address, not word-address, for convenience. */ _WAIT_FOR_SPM(); #ifdef __ICCAVR__ #pragma diag_suppress=Pe1053 /* Suppress warning for conversion from long-type address to flash ptr. */ #endif _PAGE_ERASE(address); #ifdef __ICCAVR__ #pragma diag_default=Pe1053 /* Back to default. */ #endif } sendchar('\r'); /* Send OK back. */ } #ifndef REMOVE_BLOCK_SUPPORT /* Check block load support. */ else if (val == 'b') { sendchar('Y'); /* Report block load supported. */ sendchar((BLOCKSIZE >> 8) & 0xFF); /* MSB first. */ sendchar(BLOCKSIZE & 0xFF); /* Report BLOCKSIZE (bytes). */ } /* Start block load. */ else if (val == 'B') { temp_int = (recchar() << 8) | recchar(); /* Get block size. */ val = recchar(); /* Get memtype. */ sendchar(BlockLoad(temp_int, val, &address)); /* Block load. */ } /* Start block read. */ else if (val == 'g') { temp_int = (recchar() << 8) | recchar(); /* Get block size. */ val = recchar(); /* Get memtype */ BlockRead(temp_int, val, &address); /* Block read */ } #endif /* REMOVE_BLOCK_SUPPORT */ #ifndef REMOVE_FLASH_BYTE_SUPPORT /* Read program memory. */ else if (val == 'R') { /* Send high byte, then low byte of flash word. */ _WAIT_FOR_SPM(); _ENABLE_RWW_SECTION(); #ifdef __ICCAVR__ #pragma diag_suppress=Pe1053 /* Suppress warning for conversion from long-type address to flash ptr. */ #endif sendchar(_LOAD_PROGRAM_MEMORY((address << 1) + 1)); sendchar(_LOAD_PROGRAM_MEMORY((address << 1) + 0)); #ifdef __ICCAVR__ #pragma diag_default=Pe1053 /* Back to default. */ #endif address++; /* Auto-advance to next Flash word. */ } /* Write program memory, low byte. */ else if (val == 'c') { /* NOTE: Always use this command before sending high byte. */ temp_int = recchar(); /* Get low byte for later _FILL_TEMP_WORD. */ sendchar('\r'); /* Send OK back. */ } /* Write program memory, high byte. */ else if (val == 'C') { temp_int |= (recchar() << 8); /* Get and insert high byte. */ _WAIT_FOR_SPM(); #ifdef __ICCAVR__ #pragma diag_suppress=Pe1053 /* Suppress warning for conversion from long-type address to flash ptr. */ #endif _FILL_TEMP_WORD((address << 1), temp_int); /* Convert word-address to byte-address and fill. */ #ifdef __ICCAVR__ #pragma diag_default=Pe1053 /* Back to default. */ #endif address++; /* Auto-advance to next Flash word. */ sendchar('\r'); /* Send OK back. */ } /* Write page. */ else if (val == 'm') { if (address >= (APP_END >> 1)) { /* Protect bootloader area. */ sendchar('?'); } else { _WAIT_FOR_SPM(); #ifdef __ICCAVR__ #pragma diag_suppress=Pe1053 /* Suppress warning for conversion from long-type address to flash ptr. */ #endif _PAGE_WRITE(address << 1); /* Convert word-address to byte-address and write. */ #ifdef __ICCAVR__ #pragma diag_default=Pe1053 /* Back to default. */ #endif } sendchar('\r'); /* Send OK back. */ } #endif /* REMOVE_FLASH_BYTE_SUPPORT */ #ifndef REMOVE_EEPROM_BYTE_SUPPORT /* Write EEPROM memory. */ else if (val == 'D') { _WAIT_FOR_SPM(); EEARL = address; /* Setup EEPROM address. */ EEARH = (address >> 8); EEDR = recchar(); /* Get byte. */ EECR |= (1 << EEMWE); /* Write byte. */ EECR |= (1 << EEWE); while (EECR & (1 << EEWE)) /* Wait for write operation to finish. */ ; address++; /* Auto-advance to next EEPROM byte. */ sendchar('\r'); /* Send OK back. */ } /* Read EEPROM memory. */ else if (val == 'd') { EEARL = address; /* Setup EEPROM address. */ EEARH = (address >> 8); EECR |= (1 << EERE); /* Read byte... */ sendchar(EEDR); /* ...and send it back. */ address++; /* Auto-advance to next EEPROM byte. */ } #endif /* REMOVE_EEPROM_BYTE_SUPPORT */ #ifndef REMOVE_FUSE_AND_LOCK_BIT_SUPPORT /* Write lockbits. */ else if (val == 'l') { _WAIT_FOR_SPM(); _SET_LOCK_BITS(recchar()); /* Read and set lock bits. */ sendchar('\r'); /* Send OK back. */ } #if defined(_GET_LOCK_BITS) /* Read lock bits. */ else if (val == 'r') { _WAIT_FOR_SPM(); sendchar(_GET_LOCK_BITS()); } /* Read fuse bits. */ else if (val == 'F') { _WAIT_FOR_SPM(); sendchar(_GET_LOW_FUSES()); } /* Read high fuse bits. */ else if (val == 'N') { _WAIT_FOR_SPM(); sendchar(_GET_HIGH_FUSES()); } /* Read extended fuse bits. */ else if (val == 'Q') { _WAIT_FOR_SPM(); sendchar(_GET_EXTENDED_FUSES()); } #endif /* defined(_GET_LOCK_BITS) */ #endif /* REMOVE_FUSE_AND_LOCK_BIT_SUPPORT */ #ifndef REMOVE_AVRPROG_SUPPORT /* Enter and leave programming mode. */ else if ((val == 'P') || (val == 'L')) { sendchar('\r'); /* Nothing special to do, just answer OK. */ } /* Exit bootloader. */ else if (val == 'E') { _WAIT_FOR_SPM(); _ENABLE_RWW_SECTION(); sendchar('\r'); funcptr(); /* Jump to Reset vector 0x0000 in Application Section. */ } /* Get programmer type. */ else if (val == 'p') { sendchar('S'); /* Answer 'SERIAL'. */ } /* Return supported device codes. */ else if (val == 't') { #if PARTCODE+0 > 0 sendchar(PARTCODE); /* Supports only this device, of course. */ #endif /* PARTCODE */ sendchar(0); /* Send list terminator. */ } /* Set LED, clear LED and set device type. */ else if ((val == 'x') || (val == 'y') || (val == 'T')) { recchar(); /* Ignore the command and it's parameter. */ sendchar('\r'); /* Send OK back. */ } #endif /* REMOVE_AVRPROG_SUPPORT */ /* Return programmer identifier. */ else if (val == 'S') { sendchar('A'); /* Return 'AVRBOOT'. */ sendchar('V'); /* Software identifier (aka programmer signature) is always 7 characters. */ sendchar('R'); sendchar('B'); sendchar('O'); sendchar('O'); sendchar('T'); } /* Return software version. */ else if (val == 'V') { sendchar('1'); sendchar('5'); } /* Return signature bytes. */ else if (val == 's') { sendchar(SIGNATURE_BYTE_3); sendchar(SIGNATURE_BYTE_2); sendchar(SIGNATURE_BYTE_1); } /* The last command to accept is ESC (synchronization). */ else if (val != 0x1b) { /* If not ESC, then it is unrecognized... */ sendchar('?'); } } /* end: for(;;) */ } else { _WAIT_FOR_SPM(); _ENABLE_RWW_SECTION(); funcptr(); /* Jump to Reset vector 0x0000 in Application Section. */ } } /* end: main */ #ifndef REMOVE_BLOCK_SUPPORT unsigned char BlockLoad( unsigned int size, unsigned char mem, ADDR_T * address) { unsigned char buffer[BLOCKSIZE]; unsigned int data; ADDR_T tempaddress; /* EEPROM memory type. */ if (mem == 'E') { /* Fill buffer first, as EEPROM is too slow to copy with UART speed */ for (tempaddress = 0; tempaddress < size; tempaddress++) buffer[tempaddress] = recchar(); /* Then program the EEPROM */ _WAIT_FOR_SPM(); for (tempaddress = 0; tempaddress < size; tempaddress++) { EEARL = *address; /* Setup EEPROM address */ EEARH = ((*address) >> 8); EEDR = buffer[tempaddress]; /* Get byte. */ EECR |= (1 << EEMWE); /* Write byte. */ EECR |= (1 << EEWE); while (EECR & (1 << EEWE)) /* Wait for write operation to finish. */ ; (*address)++; /* Select next EEPROM byte */ } return '\r'; /* Report programming OK */ } /* Flash memory type. */ else if (mem == 'F') { /* NOTE: For flash programming, 'address' is given in words. */ (*address) <<= 1; /* Convert address to bytes temporarily. */ tempaddress = (*address); /* Store address in page. */ do { data = recchar(); data |= (recchar() << 8); #ifdef __ICCAVR__ #pragma diag_suppress=Pe1053 /* Suppress warning for conversion from long-type address to flash ptr. */ #endif _FILL_TEMP_WORD(*address, data); #ifdef __ICCAVR__ #pragma diag_default=Pe1053 /* Back to default. */ #endif (*address) += 2; /* Select next word in memory. */ size -= 2; /* Reduce number of bytes to write by two. */ } while (size); /* Loop until all bytes written. */ #ifdef __ICCAVR__ #pragma diag_suppress=Pe1053 /* Suppress warning for conversion from long-type address to flash ptr. */ #endif _PAGE_WRITE(tempaddress); #ifdef __ICCAVR__ #pragma diag_default=Pe1053 /* Back to default. */ #endif _WAIT_FOR_SPM(); _ENABLE_RWW_SECTION(); (*address) >>= 1; /* Convert address back to Flash words again. */ return '\r'; /* Report programming OK */ } /* Invalid memory type? */ else { return '?'; } } void BlockRead( unsigned int size, unsigned char mem, ADDR_T * address) { /* EEPROM memory type. */ 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 dat ato PC */ size--; /* Decrease number of bytes to read */ } while (size); /* Repeat until all block has been read */ } /* Flash memory type. */ else if (mem == 'F') { (*address) <<= 1; /* Convert address to bytes temporarily. */ do { #ifdef __ICCAVR__ #pragma diag_suppress=Pe1053 /* Suppress warning for conversion from long-type address to flash ptr. */ #endif sendchar(_LOAD_PROGRAM_MEMORY(*address)); sendchar(_LOAD_PROGRAM_MEMORY((*address) + 1)); #ifdef __ICCAVR__ #pragma diag_default=Pe1053 /* Back to default. */ #endif (*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 */ (*address) >>= 1; /* Convert address back to Flash words again. */ } } #endif /* REMOVE_BLOCK_SUPPORT */ /* end of file */