//---------------------------------------------------------------------------------------------------------// // __ __ ___ ___ _ _ // // / // / / |_ _______/ | __________(_|_) ___ ___ _ // // / // /_/ /| | | / / ___/ /| | / ___/ ___/ / / (__ \/ __) / ) // // /__ __/ ___ | |/ / / / ___ |(__ ) /__/ / / / __/\__ \/ , \ // // /_/ /_/ |_|___/_/ /_/ |_/____/\___/_/_/ \___)(___/\___/ // // ____ _ ____ _ _ _ // // | __ ) __ _| |__ _ _| __ ) ___ ___ | |_| | ___ __ _ __| | ___ _ __ // // | _ \ / _` | '_ \| | | | _ \ / _ \ / _ \| __| |/ _ \ / _` |/ _` |/ _ \ '__| // // | |_) | (_| | |_) | |_| | |_) | (_) | (_) | |_| | (_) | (_| | (_| | __/ | // // |____/ \__,_|_.__/ \__, |____/ \___/ \___/ \__|_|\___/ \__,_|\__,_|\___|_| // // |___/ // // __ __ \|/ // // / / __ __ ______ ______/ /__ ____ _ -{@}- // // / _ \/ // / / __/ // / __/ __/ |/ / ' \ /^\ // // /_.__/\_, / \__/\_,_/_/ \__/|___/_/_/_/ /.~,\ // // /___/ /,:.\ // // /,@'o~\ // // Curt's ATmega88/ATmega168/ATmega16 4AvrAscii 256 BabyBootloader /.,;-"\ // // Intel Hex Ascii Upload Bootloader in 256 Words /.^'.@.,\ // // /o!*:,~.\ // // Copyright 2007.2010 by Curt Van Maanen /,=.'@-.o.\ // // ^^^^^^^^^^^ // // Version 2010.12.09 [_] // //---------------------------------------------------------------------------------------------------------// /* modified to work with >64k avr RAMPZ/ELPM is used for >64k avr change BL_SIZE_BYTES as needed (probably 1024 for > 64k, as they do not have 512byte size for boot section) code to jump into bootloader not shown, as EIND will need to be used to 'jump' into bootloader (tested on a at90usb1287, which has 256 pae size, and 128kb flash, and seems to work ok) */ /*----------------------------------------------------------------------------------------------------------- 3 sections to this file, in this order- CONFIG - make your changes needed in this section PROGRAM - program located here, no changes normally needed HELP - help information section CHECKLIST- 1. .text section is set to bootloader start address (see help section) 2. linker options set to -nostartfiles and -nodefaultlibs (see help section) 3. -mshort-calls added to custom options for avr's >8k (see help section) 4. change whatever needed in the config section (see config section) 5. make sure boot reset fuse is programmed (via your avr programming method) 6. make sure boot size is correct- 256 words (via your avr programming method) 7. for 'newer' avr's, make sure your application clears watchdog reset flag (and WDE if not using watchdog) 8. terminal program settings- 8 data bits, no parity, 1 stop bit 20msec transmit line delay no flow control used, so this is needed incoming cr=cr+lf bootloader only sends cr, so terminal program needs to add lf outgoing cr only (lf's will be flushed after cr anyway, so it doesn't matter) no local echo bootloader will do the echo 9. goto 1 -----------------------------------------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------------------------------------- ___ _ _ ___ _ _ _ ___ ___ ___ |_ _| \| |/ __| | | | | | \| __/ __| | || .` | (__| |_| |_| | |) | _|\__ \ |___|_|\_|\___|____\___/|___/|___|___/ -----------------------------------------------------------------------------------------------------------*/ #include //---------------------------------------------------------------------------------------------------------// // ______ ______ .__ __. _______ __ _______ // // / | / __ \ | \ | | | ____|| | / _____| // // | ,----'| | | | | \| | | |__ | | | | __ // // | | | | | | | . ` | | __| | | | | |_ | // // | `----.| `--' | | |\ | | | | | | |__| | // // \______| \______/ |__| \__| |__| |__| \______| // // // //---------------------------------------------------------------------------------------------------------// // CONFIGURATION SECTION DEFINES (change defines as needed) // //---------------------------------------------------------------------------------------------------------// /*----------------------------------------------------------------------------------------------------------- baud rate defines page 197 of datasheet (mega88/168), 38,400 has little error, and works well with internal 8mhz clock set to whatever you need for your clock and desired baud rate high baud register is not used in bootloader, so no high cpu freq's with low baud rates ->its not that hard to look up the number needed in the datasheet, so no macros here<- -----------------------------------------------------------------------------------------------------------*/ #define BAUD_LO_VALUE 12 // BAUD 38400 .2% error @ 8Mhz /*----------------------------------------------------------------------------------------------------------- set the 'key' (character) you want for 'triggering' the bootloader (is set to 'escape' character here, use the hex number, not the slash thing like '\e') -----------------------------------------------------------------------------------------------------------*/ #define BL_TRIGGER_KEY 0x1B // character to trigger bootloader /*----------------------------------------------------------------------------------------------------------- set commands here for 'go' and 'erase', and 'dump flash' (only when decrypt is off) they have to be an odd number, so commands will not be confused with intel hex records (as they cannot have odd length data records since words are programmed) the command in hex will be the same as ascii-> for erase >:EF, for go >APP -----------------------------------------------------------------------------------------------------------*/ #define BL_GO_COMMAND 0x99 // :pp (app - 0x0A,0x09,0x09) #define BL_ERASE_COMMAND 0xEF // 'erase flash' #define BL_READ_PGM 0xDF // 'dump flash' /*----------------------------------------------------------------------------------------------------------- if 'pin trigger' wanted instead of a 'trigger character' on the usart, turn it on here and set the desired port and pin wanted, and what state will trigger the bootloader -----------------------------------------------------------------------------------------------------------*/ #define USE_PIN_TRIGGER 0 // 1 = use a pin to trigger bootloader #define PIN_TRIGGER PINE // which port you want to use #define PINn_TRIGGER 2 // which pin number you want to use #define PINn_TRIGGER_STATE 0 // 0 = LOW will trigger (HIGH = bypass) // 1 = HIGH will trigger (LOW = bypass) /*----------------------------------------------------------------------------------------------------------- ATmega16 has UDR, ATmega88/168 has UDR0, so use defines here to get them the same if you have an avr with udr1/udr2, change the number below to the desired usart number -----------------------------------------------------------------------------------------------------------*/ #ifdef UDR // for avr's with just a 'plain' udr, #define USART_NUM // leave this blank, nothing, empty #else // for newer avr's, set usart number #define USART_NUM 1 // usart number( here using usart1 ) #endif /*----------------------------------------------------------------------------------------------------------- ___ ___ ___ ___ _ _ ___ ___ | \| __| __|_ _| \| | __/ __| | |) | _|| _| | || .` | _|\__ \ |___/|___|_| |___|_|\_|___|___/ -----------------------------------------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------------------------------------- this defines the bootloader size in bytes, and the bootloader start address in bytes also an address is needed at the end of flash+1 so we can rjmp to 0x0000 (APP_ZERO) -----------------------------------------------------------------------------------------------------------*/ #define BL_SIZE_BYTES 1024 //512 #define BL_ADDRESS (FLASHEND - (BL_SIZE_BYTES-1)) #define APP_ZERO (main+BL_SIZE_BYTES) /*----------------------------------------------------------------------------------------------------------- concat macros, used in usart defines -----------------------------------------------------------------------------------------------------------*/ #define MY_CONCAT3b(a,b,c) a ## b ## c // needed to expand USART_NUM #define MY_CONCAT3(a,b,c) MY_CONCAT3b(a,b,c) // concat 3 'words' #define MY_CONCAT2b(a,b) a ## b #define MY_CONCAT2(a,b) MY_CONCAT2b(a,b) // concat 2 'words' /*----------------------------------------------------------------------------------------------------------- undefine older usart names for mega64, so usart macros below work correctly for the mega64 -----------------------------------------------------------------------------------------------------------*/ #if defined (__AVR_ATmega64__) #undef RXEN #undef TXEN #undef UDR #undef UDRE #undef RXC #undef FE #undef DOR #endif /*----------------------------------------------------------------------------------------------------------- usart definitions -----------------------------------------------------------------------------------------------------------*/ #define BAUD_RATE_REG_LO MY_CONCAT3(UBRR,USART_NUM,L) #define USART_CONTROL_B MY_CONCAT3(UCSR,USART_NUM,B) #define USART_STATUS_A MY_CONCAT3(UCSR,USART_NUM,A) #define RX_ENABLE MY_CONCAT2(RXEN,USART_NUM) #define TX_ENABLE MY_CONCAT2(TXEN,USART_NUM) #define RX_DATA MY_CONCAT2(UDR, USART_NUM) #define TX_DATA MY_CONCAT2(UDR, USART_NUM) #define TX_BUFFER_EMPTY MY_CONCAT2(UDRE,USART_NUM) #define RX_COMPLETED MY_CONCAT2(RXC, USART_NUM) #define FRAMING_ERROR MY_CONCAT2(FE, USART_NUM) #define OVERRUN_ERROR MY_CONCAT2(DOR, USART_NUM) #define MY_RX_FLAGS ((1< 128) // max data words for a record #define MAX_DD_BYTES 128 // limit to 128 bytes (64 words) #else #define MAX_DD_BYTES (SPM_PAGESIZE) // else limit to page size (words) #endif /*----------------------------------------------------------------------------------------------------------- from boot.h, for spm stuff -----------------------------------------------------------------------------------------------------------*/ #if defined (SPMCSR) #define SPM_REG SPMCSR #elif defined (SPMCR) #define SPM_REG SPMCR #else #error AVR processor does not provide bootloader support! #endif #ifdef ASRE #define COMMON_ASRE ASRE #else #define COMMON_ASRE RWWSRE #endif #define BOOT_PAGE_FILL (1< 64k -----------------------------------------------------------------------------------------------------------*/ //#if FLASHEND > 0xFFFF //#error This bootloader is not designed to support an avr >64K //#endif /*----------------------------------------------------------------------------------------------------------- get PIN_TRIGGER define into an asm compatable address -----------------------------------------------------------------------------------------------------------*/ #define PIN_TRIG _SFR_IO_ADDR(PIN_TRIGGER) /*----------------------------------------------------------------------------------------------------------- bootloader status defines ------------------------------------------------------------------------------------------------------------- bl_status bits1-0 (Y=1, N=0) ------------------------------ | >After | Go? | Program?| |------------------------------| | Start | Y | N | after bootloader starts, can go, but not program (erase first) |------------------------------| | Erase | N | Y | after erase, cannot go (nothing to go to), can program |------------------------------| | Error | N | N | after error, cannot go (unkown flash state), cannot program |------------------------------| | EOF ok | Y | N | after eof, can go, but cannot program (erase first) ------------------------------ */ #define BL_ERASE_OK 1 // erase ok state #define BL_EOF_OK 2 // file ok state /*----------------------------------------------------------------------------------------------------------- _ ___ __ __ __ __ _ ___ ___ ___ ___ /_\ / __| \/ | | \/ | /_\ / __| _ \/ _ \/ __| / _ \\__ \ |\/| | | |\/| |/ _ \ (__| / (_) \__ \ /_/ \_\___/_| |_| |_| |_/_/ \_\___|_|_\\___/|___/ -----------------------------------------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------------------------------------- asm macros to take care of using the correct instructions for i/o and sram, and for transmitting LOAD/STORE will use the instructions in/out/lds/sts as needed LOAD_S/STORE_S will always use lds/sts (force lds/sts even when in/out would work) STORE_S/LOAD_S is used in the bootloader anytime its a possibility the register could be in 'upper' i/o (to keep all builds the same size for various avr's) and LOAD/STORE is used only when it is known that all avr's use 'lower' i/o for the register the bootloader will only use the macros below, and not use in/out/lds/sts instructions directly -----------------------------------------------------------------------------------------------------------*/ .macro STORE addr,reg .if \addr < 0x60 out \addr - 0x20,\reg .else sts \addr,\reg .endif .endm .macro LOAD reg,addr .if \addr < 0x60 in \reg,\addr - 0x20 .else lds \reg,\addr .endif .endm .macro STORE_S addr,reg sts \addr,\reg .endm .macro LOAD_S reg,addr lds \reg,\addr .endm .macro XMIT char ldi r24,\char rcall sendchar .endm .macro ADDI reg,num subi \reg,-\num .endm /*----------------------------------------------------------------------------------------------------------- ___ _ ___ ___ _ _ __ ___ ___ ___ _ ___ _ ___ ___ / __| | / _ \| _ ) /_\ | | \ \ / /_\ | _ \_ _| /_\ | _ ) | | __/ __| | (_ | |_| (_) | _ \/ _ \| |__ \ V / _ \| /| | / _ \| _ \ |__| _|\__ \ \___|____\___/|___/_/ \_\____| \_/_/ \_\_|_\___/_/ \_\___/____|___|___/ -----------------------------------------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------------------------------------- receive buffer to hold intel hex record -----------------------------------------------------------------------------------------------------------*/ .struct 0 colon: // ':' (colon) .struct colon + 1 LL: // LL - record length .struct LL + 1 AAh: // AAxx - data byte address (byte) .struct AAh + 1 AAl: // xxAA - low byte of address .struct AAl + 1 TT: // TT - record type (0 or 1 used here) .struct TT + 1 DDDD: // DDDD - data bytes (words) .struct DDDD + MAX_DD_BYTES CC: // CC - used just for sizing of buffer .struct CC + 1 CR: // CR - used just for sizing of buffer .struct CR + 1 END_OF_BUFFER: // for checking if buffer overflow (size of buffer) .section .noinit // set RX_BUFFER to .noinit section (in data section) RX_BUFFER: // start of rx buffer in sram .ds.b END_OF_BUFFER // set size of buffer, will use struct above //---------------------------------------------------------------------------------------------------------// // .______ .______ ______ _______ .______ ___ .___ ___. // // | _ \ | _ \ / __ \ / _____|| _ \ / \ | \/ | // // | |_) | | |_) | | | | | | | __ | |_) | / ^ \ | \ / | // // | ___/ | / | | | | | | |_ | | / / /_\ \ | |\/| | // // | | | |\ \----.| `--' | | |__| | | |\ \----./ _____ \ | | | | // // | _| | _| `._____| \______/ \______| | _| `._____/__/ \__\ |__| |__| // // // //---------------------------------------------------------------------------------------------------------// // PROGRAM (normally no changes needed in this section) // //---------------------------------------------------------------------------------------------------------// /*=========================================================================================================== __ __ _ ___ _ _ | \/ | /_\ |_ _| \| | | |\/| |/ _ \ | || .` | |_| |_/_/ \_\___|_|\_| ===========================================================================================================*/ .section .text main: /* Reset Flags 'Logic' Table ---------------------------------------------------------------------- | MCU Status Register - MCUSR (1 = set 0 = clear x = don't care) | |----------------------------------------------------------------------| |x|x|x|x| Bit3 | Bit2 | Bit1 | Bit0 | Run Bootloader? | |----------------------------------------------------------------------| |x|x|x|x| WDRF | BORF | EXTRF | PORF | | |----------------------------------------------------------------------| |x|x|x|x| 0 | x | x | 1 | YES, this could be power up | |----------------------------------------------------------------------| |x|x|x|x| 1 | x | x | x | NO, watchdog reset | |----------------------------------------------------------------------| |x|x|x|x| 0 | x | x | 0 | NO, not a power up reset | ---------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------- first three instructions will cause an exit from the bootloader if the watchdog flag is set the fourth instruction sets up to enable ivsel change, the fifth sets ivce in MCUCR or GIFR any 'errant' jump into the bootloader to any of the first five instructions will still have to pass the PORF=1 test which takes place below the change enable instruction ---------------------------------------------------------------------------------------------------*/ /****************************/ #define tempF r0 #define tempI r31 /****************************/ LOAD tempF,RESET_FLAGS // get reset flags sbrc tempF,WDRF // skip if wdrf=0, rjmp APP_ZERO // else exit (wdrf=1) ldi tempI,(1< baud rate, tx/rx enable ---------------------------------------------------------------------------------------------------*/ start_usart: ldi temp,BAUD_LO_VALUE // load baud lo value STORE_S BAUD_RATE_REG_LO,temp // set it ldi temp,(1< can go, cannot program command prompt- = can program, cannot go command prompt- < can only erase ---------------------------------------------------------------------------------------------------*/ _prompt: STORE_S USART_CONTROL_B,r1 // flush any LF, also prevent overrun ldi temp,(1< '<' 1-> '=' 2-> '>' rcall sendchar // send prompt /*--------------------------------------------------------------------------------------------------- process intel hex record intel hex record >:LLAAAATTDD...DDCC Always starts with a ':' LL = Record length (in bytes, of DD bytes) AAAA = memory address (in bytes) TT = record type (00 = data,01 = end of file, others not used) DD = data byte , number of bytes = LL CC = checksum (add all bytes, byte sum only, invert, then add 1) CC = (!(LL + AA + AA + TT + DD...DD )) + 1, checksum total including checksum byte = 0 get the whole record here (input limited by size of rx_buffer) if rx_buffer overrun, last character will not be cr, and will ignore record (back to command prompt) cr has to be the last char (and breaks out of do loop) any 'control' character (0x00-0x1F) breaks out of loop ---------------------------------------------------------------------------------------------------*/ /****************************/ #define checksum r16 #define count r17 #define tempS r25 /****************************/ get_record: ldi count,1 // keep track of nibbles (1=store completed byte) ldi checksum,-0x0A // checksum (subtract 0x0A as ':' will also be added) ldi YL,lo8(RX_BUFFER) // Y pointer-> &rxbuffer ldi YH,hi8(RX_BUFFER) st Y,r1 // clear colon buffer position, prevent repeats of // previous record by a single cr /*................................................................................................... get rx status, wait until RXCn, FEn or DORn flag set (any one or more) also clear watchdog, since we could be here a while if any flag set, drop out of do loop, read udr to either get data or to flush rx & reset flags, then check if RXCn is the only flag set, if not, framing or overrun flag(s) set, so goto _error ...................................................................................................*/ rx_loop: // rx loop rcall safeT_check // safeT_check inside loops wdr // reset watchdog LOAD_S tempS,USART_STATUS_A // get usart status andi tempS,MY_RX_FLAGS // mask off unwanted bits breq rx_loop // if no flags, try again read_rx: LOAD_S temp,RX_DATA // read udr cpi tempS,(1<= ' ' brcs check_cr // if not, done here rcall sendchar // echo back character /*................................................................................................... is >= ' ' (0x20-0xFF), subtract 7 if >= 0x40, mask off high nibble to leave 0x00-0x0F '0' (0x30) converts to 0x00, '9' (0x39) to 0x09, 'A' (0x41) to 0x0A, 'F' (0x46) to 0x0F ANY characters above 0x1F are converted to 0x00-0x0F ...................................................................................................*/ convert_char: cpi temp,0x40 // check if 'A'-'F' (0x41-0x46) brcs 1f // if not, skip next subi temp,0x07 // yes, subtract 7 ('A'=0x41->0x3A,etc) 1: andi temp,0x0F // mask off bits3-7, make ALL 0x00-0x0F /*................................................................................................... first character (':') will be stored right away as a 'complete' byte, the second character will be shifted left into the high nibble position, third character will be added to second character, then saved, and so on (if buffer size is odd length, we save completed bytes on the odd count, if buffer size is even length, we save completed bytes on the even count- normally buffer size will be odd size if no changes were made to the buffer scruct) ...................................................................................................*/ put_in_buffer: sbrc count,0 // skip if count=0 rjmp 2f // else jump ahead to save 'completed' hex pair swap temp // low nibble to high nibble (like 0x0F->0xF0) st Y,temp // save it to buffer rjmp 3f // get next nibble 2: // odd 'position' in buffer ld tempS,Y // get previous high nibble add temp,tempS // merge high nibble + low nibble st Y+,temp // store it in buffer, inc Y pointer add checksum,temp // add to checksum 3: // now check if still in buffer and toggle count com count // count = ~count (1->0xFE->1) cpi YL,lo8(RX_BUFFER+END_OF_BUFFER) // buffer never > 255, so only need to check lo byte brne rx_loop // if not at end of buffer can, get another character /*--------------------------------------------------------------------------------------------------- if last rx character was not cr, go back to command prompt could be rx_buffer overrun or a 'control' character other than cr (like esc key) ---------------------------------------------------------------------------------------------------*/ check_cr: cpi temp,'\r' // check if last char was cr brne 2f // if not, jump ahead (will rjmp to _did_nothing) /*--------------------------------------------------------------------------------------------------- if first character was not ':', go back to command prompt ---------------------------------------------------------------------------------------------------*/ check_colon: LOAD_S temp,RX_BUFFER+colon // get byte in colon postition cpi temp,0x0A // if not ':' (0x3A & 0x0F = 0x0A) brne 2f // skip ahead if not ':' (will rjmp to _did_nothing) /*--------------------------------------------------------------------------------------------------- store record length - LL (data byte count) ---------------------------------------------------------------------------------------------------*/ /****************************/ #define reclength r21 /****************************/ get_reclength: LOAD_S reclength,RX_BUFFER+LL // get recordlength /*--------------------------------------------------------------------------------------------------- if LL is the 'go' command, loop and cause a wdt reset IF go bit set in bl_status ---------------------------------------------------------------------------------------------------*/ cpi reclength,BL_GO_COMMAND // check if LL= BL_GO_COMMAND brne check_erase // nope, jump ahead 1: // loop if can 'go' (cause wdt reset) rcall safeT_check // for any errant jumps into loop sbrc bl_status,1 // if go bit clear, skip next rjmp 1b // got bit set, so loop and cause watchdog timeout 2: rjmp _did_nothing // show that nothing was done /*--------------------------------------------------------------------------------------------------- Erase Flash loop if LL=0xEF, erase flash, set bl_status address will be a byte address, SPM_PAGESIZE is a byte size ---------------------------------------------------------------------------------------------------*/ check_erase: cpi reclength,BL_ERASE_COMMAND // check if erase command brne check_read // if not, jump ahead ldi ZL,lo8(BL_ADDRESS) // set to bootloader start address ldi ZH,hi8(BL_ADDRESS) #if defined RAMPZ // get RAMPZ set to ldi reclength,hlo8(BL_ADDRESS) // same as bootloader 'section' STORE RAMPZ,reclength #endif /*................................................................................................... loop through all pages (including page 0) ...................................................................................................*/ erase_loop: // loop start XMIT '.' // tx '.' every page wdr // reset watchdog in loop subi ZL,lo8(SPM_PAGESIZE) // back up a page sbci ZH,hi8(SPM_PAGESIZE) ldi r22,BOOT_PAGE_ERASE // pass command to do_spm rcall do_spm2 // erase page sbiw ZL,0 // check if was 0x0000 page brne erase_loop // if not, do again #if defined (RAMPZ) LOAD reclength,RAMPZ // keep erasing until tst reclength // RAMPZ = 0 breq erase_done dec reclength STORE RAMPZ,reclength rjmp erase_loop erase_done: #endif /*................................................................................................... set bl_status, then back to command prompt ...................................................................................................*/ ldi temp,BL_ERASE_OK // set bl_status mov bl_status,temp rjmp _prompt // back to command prompt /*--------------------------------------------------------------------------------------------------- Dump Flash if LL=BL_READ_PGM, 'dump' (read) program flash (ascii output) ---------------------------------------------------------------------------------------------------*/ check_read: /****************************/ #define bl_addrH r16 #define tempC r17 /****************************/ cpi reclength,BL_READ_PGM // check if LL=read command brne check_pgm_bit // if not, jump ahead ldi ZL,0 // Z=0, start reading at 0x0000 ldi ZH,0 #if defined RAMPZ STORE RAMPZ,r1 // clear RAMPZ #endif ldi bl_addrH,hi8(BL_ADDRESS) // store bootloader address high byte for compare read_loop: // loop start wdr // reset watchdog in this loop mov tempC,ZL // get ZL andi tempC,0x1F // and with 0x1F brne 1f // if not 0bxxx00000, skip next XMIT '\r' // else tx cr (every 32 bytes) 1: #if defined RAMPZ elpm r24,Z+ // use elpm #else lpm r24,Z+ // get pgm flash byte, inc Z #endif rcall hex2ascii // display byte as ascii hex pair cpi ZL,lo8(BL_ADDRESS) // check if Z < BL_ADDRESS cpc ZH,bl_addrH brcs read_loop // if so, do again #if defined RAMPZ // check RAMPZ, if not in same LOAD reclength,RAMPZ // section as bootloader, back cpi reclength,hlo8(BL_ADDRESS) // to read loop brne read_loop STORE RAMPZ,r1 // clear RAMPZ #endif rjmp _prompt // back to command prompt /*--------------------------------------------------------------------------------------------------- if program bit not set in bl_status, do not continue from here (erase flash first) ---------------------------------------------------------------------------------------------------*/ check_pgm_bit: sbrs bl_status,0 // if program bit is set, skip next 1: rjmp _did_nothing // can't go any farther, so show nothing was done /*--------------------------------------------------------------------------------------------------- only programming words, so if LL is an odd number, that is an error (already limited to MAX_DD_SIZE by record receive loop, so no need to check size) ---------------------------------------------------------------------------------------------------*/ check_LL: sbrc reclength,0 // if odd number DD bytes 2: rjmp _error // show error /*--------------------------------------------------------------------------------------------------- if checksum on all bytes received not 0, checksum error ---------------------------------------------------------------------------------------------------*/ check_CC: tst checksum // check if 0 brne 2b // if not, branch back (to rjmp _error) /*--------------------------------------------------------------------------------------------------- TT - Record type if TT is 1, that is an EOF record, set bl_status, goto command prompt ---------------------------------------------------------------------------------------------------*/ /****************************/ #define indexV r23 /****************************/ check_TT: LOAD_S indexV,RX_BUFFER+TT // get TT cpi indexV,0x01 // check if 0x01 brne check_type2 // if not, jump ahead eof_ok: ldi temp,BL_EOF_OK // is 0x01, so set bl_status mov bl_status,temp // to BL_EOF_OK rjmp _prompt // back to command prompt check_type2: // check for record type 2 #if defined RAMPZ cpi indexV,0x02 brne check_type0 LOAD_S indexV,RX_BUFFER+DDDD swap indexV STORE RAMPZ,indexV rjmp _prompt #endif /*--------------------------------------------------------------------------------------------------- now check if TT is NOT 0 (we only do data records which are type 0x00) ---------------------------------------------------------------------------------------------------*/ check_type0: tst indexV // check if 0 (use indexV, since we need 0 in it anyway) brne 1b // if not, jump back (will rjmp to _did_nothing) /*--------------------------------------------------------------------------------------------------- so far, so good. now write to program memory index-> used to store rx_buffer.DDDD (word) array position indexV-> store buffer position before page loop, used for verify loop address-> used to determine page address (byte address) addressV-> store address before page loop, used for verify loop recordlength-> number of bytes to program (byte length) ---------------------------------------------------------------------------------------------------*/ /****************************/ #define index r20 /****************************/ setup_addr_index: clr index // clear index (indexV will be 0 already from above) /*--------------------------------------------------------------------------------------------------- setup address and addressV to the address fron the hex file ---------------------------------------------------------------------------------------------------*/ /****************************/ #define address r28 #define addressH r29 #define addressV r16 #define addressVH r17 /****************************/ LOAD_S addressV,RX_BUFFER+AAl // get AAl into low byte of addressV LOAD_S addressVH,RX_BUFFER+AAh // get AAh into high byte of addressV movw address,addressV // copy it to address /*--------------------------------------------------------------------------------------------------- if address tries to get into our bootloader space-> error ---------------------------------------------------------------------------------------------------*/ pgm_loop: #if defined RAMPZ // check RAMPZ, if not in same LOAD temp,RAMPZ // section as bootloader, skip cpi temp,hlo8(BL_ADDRESS) // address check below brne addr_check_done #endif ldi temp,hi8(BL_ADDRESS-1) // check if address is into our bootloader cpi address,lo8(BL_ADDRESS-1) cpc addressH,temp brcc 2b // if so, jump back (will rjmp to _error) addr_check_done: /*................................................................................................... fill page buffer ...................................................................................................*/ ldi XH,hi8(RX_BUFFER+DDDD) // X pointer to DDDD buffer position ldi XL,lo8(RX_BUFFER+DDDD) // (should be 0x0065 or 0x0105) add XL,index // add index (only add to XL, since will never overflow) movw ZL,address // Z pointer to record address ld r0,X+ // get low byte from buffer ld r1,X+ // get high byte from buffer ldi r22,BOOT_PAGE_FILL // command for spm rcall do_spm2 // do page buffer fill (Z already setup) ADDI index,0x02 // next DDDD (1 word,2 bytes) adiw address,0x02 // next word address subi reclength,0x02 // byte length -2 bytes (-1 word) /*............................................................................................... end of page loop, do while more data words AND page not crossed ...............................................................................................*/ breq write_page // if 0, recordlength now 0 so jump ahead mov temp,address // get low byte of address andi temp,(SPM_PAGESIZE-1) // check if on a new page brne pgm_loop // not on a new page, do next byte /*............................................................................................... no more data bytes OR a page boundary was crossed so write the page (Z is still on address of last page fill, so will be on correct page if a page was crossed) ...............................................................................................*/ write_page: ldi r22,BOOT_PAGE_WRITE // command for spm rcall do_spm2 // do page write (Z still on address-2, nice) /*............................................................................................... now read back/verify what was just programmed addressV holds the address before the page loading started indexV holds the rx_buffer.DDDD position before the page loading started do until we get to current DDDD position (indexV will = index again) ...............................................................................................*/ verify: ldi XH,hi8(RX_BUFFER+DDDD) // buffer address to X (0x0105 or 0x0065) ldi XL,lo8(RX_BUFFER+DDDD) add XL,indexV // add indexV to buffer (only need to add to XL) movw ZL,addressV // get addressV into Z 1: #if defined RAMPZ elpm r18,Z+ elpm r19,Z+ #else lpm r18,Z+ // get flash byte low lpm r19,Z+ // get flash byte high #endif ld r24,X+ // get low byte from buffer ld r25,X+ // get high byte from buffer cp r18,r24 // check if buffer equal to flash cpc r19,r25 breq 2f rjmp _error 2: ADDI indexV,0x02 // indexV += 2 cp indexV,index brcs 1b // if indexV < index, check next word movw addressV,ZL // save Z position back to addressV /*............................................................................................... end of program flash loop if a page was crossed, recordlength will not be 0, so there will be more data to program ...............................................................................................*/ tst reclength // check recordlength brne pgm_loop // if not 0, do next page (page was crossed) /*............................................................................................... Yeah!, record programmed without error, go do it again, and again, and again... ...............................................................................................*/ rjmp _prompt // else done, back to command prompt /*=========================================================================================================== ___ _ _ _ _ ___ _____ ___ ___ _ _ ___ | __| | | | \| |/ __|_ _|_ _/ _ \| \| / __| | _|| |_| | .` | (__ | | | | (_) | .` \__ \ |_| \___/|_|\_|\___| |_| |___\___/|_|\_|___/ ===========================================================================================================*/ /*----------------------------------------------------------------------------------------------------------- send a hex byte out as an ascii pair (0x12 -> '1' '2') void hex2ascii(uint8_t h); -> r24=h r24,r25 changed void sendchar(uint8_t c); -> r24=c r24 unchanged r25 changed -----------------------------------------------------------------------------------------------------------*/ /****************************/ #define h r24 /****************************/ hex2ascii: push h // save a copy of h swap h // swap h to get high nibble->low nibble rcall 1f // put pc on stack, skip next instruction pop h // first time ret gets us here 1: andi h,0x0F // h = h & 0x0F (strip off high nibble) ADDI h,'0' // h += '0' cpi h,'9'+1 // test if h < '9'+1 (0x3A) brcs 2f // is not > '9', skip next ADDI h,7 // h += 7 (was > '9', is now 'A'-'F') /*.........................................................................................................*/ /****************************/ #define c r24 #define tempU r25 /****************************/ sendchar: 2: LOAD_S tempU,USART_STATUS_A // get usart status a sbrs tempU,TX_BUFFER_EMPTY // skip if udr empty rjmp 2b // else keep checking STORE_S TX_DATA,c // store c to udr ret // done /*----------------------------------------------------------------------------------------------------------- SPM function r1:r0 will have to be setup before this function is called (page fill needs data in r1:r0) r25:r24 will have the address, r22 will have the spm command spm will fail if IVSEL=0 (and exit bootloader) spm will fail on any 'errant' jump into the bootloader (assuming IVSEL=0) spm will fail on any 'errant' jump into the bootloader (assuming spm register already setup) as an rjmp/rcall takes 2 cycles, so if it 'lands' on anthing other than 'spm', it will have not met the 4 cycle requirement, or it will exit the bootloader if it 'lands' on the rjmp instruction so, in order for an 'errant' jump into the bootloader to do an spm, it will either have had to set IVSEL, OR setup spm register, then 'jump' exactly on the spm instruction this function could be used in an application if wanted- save sreg,cli,get IVSEL=1,setup r1:r0 if needed, call this function,get IVSEL=0,restore sreg if cmd=__BOOT_PAGE_FILL, no spm wait or rww enable will take place- spm wait not needed for page fill, and enable of rww not wanted as it will erase the page buffer void do_spm(uint16_t addr,uint8_t cmd); r24,r25,r0 unchanged r22 changed r30,r31 changed (if using do_spm2 from bootloader, Z will not be changed) r1 cleared check if we got into the bootloader through the 'front door' (ivsel will =1) void safeT_check(void); r22 changed r1 cleared -----------------------------------------------------------------------------------------------------------*/ /****************************/ #define addr r24 #define cmd r22 /****************************/ do_spm: movw ZL,addr // addr->Z do_spm2: // from bootloader only (Z already setup) cpi cmd,BOOT_PAGE_FILL // if page fill, breq 2f // no rww enable wanted, just spm rcall 2f // call spm, return to next instruction 1: LOAD_S cmd,SPM_REG // get spm register sbrc cmd,SPMEN // if spmen=0, skip next rjmp 1b // else check again ldi cmd,BOOT_RWW_ENABLE // enable rww section 2: STORE_S SPM_REG,cmd // load spm command /*.........................................................................................................*/ safeT_check: LOAD cmd,IRQ_CONTROL // clk+1, check if ivsel=1 sbrs cmd,IVSEL // clk+2+3, skip next if ivsel=1 rjmp main+BL_SIZE_BYTES // else ivsel=0, exit bootloader spm // clk+4, one spm in bootloader, this is it clr r1 // need to clear r1 in case was page fill ret // ret (if page fill), or back to rww enable /*----------------------------------------------------------------------------------------------------------- jump table for functions to use in applications, add the following- for avr's <16k (mega8,88,etc) extern void* __vectors; #define DO_SPM(addr,cmd) ( (void(*)(uint16_t a,uint8_t c)) &__vectors-8 )(addr,cmd) #define HEX2ASCII(byte) ( (void(*)(uint8_t h)) &__vectors-6 )(byte) #define SENDCHAR(byte) ( (void(*)(uint8_t c)) &__vectors-4 )(byte) #define BOOTLOADER() ( (void(*)(void)) &__vectors-2 )() the __vectors is needed as a reference to 0x0000, otherwise the numbers will be in reference to the section you are currently in (cannot put in absolute addresses like 0x1FFE) if not using the gcc startup files, and you need a reference to 0x0000 (absolute), another option would be to pass a symbol to the linker (-Wl,--defsym=__absolute_zero=0), then use it in the macros, like- extern void* __absolute_zero; #define BOOTLOADER() ( (void(*)(void)) &__absolute_zero-2 )() so, for example, to use the sendchar function, just use it as you normally would- SENDCHAR('a'); -which will load r24 with 'a', then rcall 0x0000-6, which will be FLASHEND-5, which will rjmp to the sendchar function, and when the return is reached, it will go back to your app code for avr's >8k (mega168,16,32,etc) #define DO_SPM(addr,cmd) ( (void(*)(uint16_t a,uint8_t c)) FLASHEND-7 )(addr,cmd) #define HEX2ASCII(byte) ( (void(*)(uint8_t h)) FLASHEND-5 )(byte) #define SENDCHAR(byte) ( (void(*)(uint8_t c)) FLASHEND-3 )(byte) #define BOOTLOADER() ( (void(*)(void)) FLASHEND-1 )() since a call will be used (instead of rcall), we can use FLASHEND -----------------------------------------------------------------------------------------------------------*/ .org main+(BL_SIZE_BYTES-(4*2)),0x00 // end of bootloader, fill empty space with nop jump_table: rjmp do_spm // FLASHEND-7 (for app spm use, will need ivsel=1) rjmp hex2ascii // FLASHEND-5 (for app use, no ivsel check) rjmp sendchar // FLASHEND-3 (for app use, no ivsel check) rjmp jmp_start // FLASHEND-1 (for app jump into bootloader) //---------------------------------------------------------------------------------------------------------// // __ __ _______ __ .______ // // | | | | | ____|| | | _ \ // // | |__| | | |__ | | | |_) | // // | __ | | __| | | | ___/ // // | | | | | |____ | `----.| | // // |__| |__| |_______||_______|| _| // // // //---------------------------------------------------------------------------------------------------------// // 1. general information // // 2. avr studio settings // // 3. bootloader protection from bad apps // // 4. reset flags logic // // 5. application jump into bootloader // // 6. encryption // // 7. eeprom programming // // 8. program and data memory lock bits // //---------------------------------------------------------------------------------------------------------// /* ------------------------------------------------------------------------------------------------------------- 1. general information ------------------------------------------------------------------------------------------------------------- Upload intel hex file as a text file using any terminal program set terminal program to 38400 baud (or whatever baud value you set) 8 bits, 1 stop bit, no parity, 20msec transmit line delay, incoming cr=cr+lf, outgoing cr only, no local echo make sure boot reset fuse set to correct setting only 4 things possible with this bootloader- 1. erase flash 2. program flash from an intel hex record 3. read program flash 4. 'go' (run application) that's it. program flash loop just processes an individual intel hex record that is done over and over (line by line) to enter bootloader, press (hold down) 'trigger' key in terminal program while powering on the avr (trigger key set to 'escape' key here, can change in defines) command prompt will show up > there are 3 command prompts- > = < which indicate the bootloader status, and what you can do you can ALWAYS erase or read program flash, anytime command prompt- > can go, cannot program command prompt- = can program, cannot go command prompt- < error(s), can only erase now first command prompt > (indicates you can 'go' because app still in place, but cannot program until erase) colon ':' character HAS to be the first character for any command, or start of hex record (':' converts to 0x0A, these also convert to 0x0A- '*','A','a','Q','q', so you can substitute these for ':') command to erase flash, can be changed in defines >:EF (erase flash) = (prompt indicates you cannot 'go' because nothing to 'go' to, can program as flash is now erased) command prompt now = to program, upload an intel hex file (text), however your terminal program handles text uploads =:1002A000892B31F488E591E0E1DE84E67BDF089577 = any errors will cause ! to be transmitted like so =:1002A000892B31F488E591E0E1DE84E67BDF089577! < (command prompt switches to < to indicate you are now in an error state, which requires an erase to clear) after you send an EOF record, and no previous errors, the command prompt will change to > again you can now 'go' ( :99 is default 'go' command, can be changed in defines, 'APP' can be used-converts to :99) >:DF (dump flash) will output a hex/ascii image of the application section, if decrypt is NOT on you can 'capture' the output to a file if needed any time you enter a 'bad' command, or after a hex record produces an error, a '?' after the command or record indicates nothing was done your APPLICATION code will need to clear the watchdog reset flag, and if not used, turn off the watchdog as recommended in the Atmel datasheet (page 51 of mega88) - because the bootloader uses the watchdog reset to start the application code, here is sample code to do that #include // read this file for more information MCUSR = 0; // clear all reset flags, or save first if needed wdt_disable(); // disable wdt (if fused on, you need to use wdt_enable(your_desired_timeout)) here is the paragraph in the datasheet page 51- (atmega88/168)- Note: If the Watchdog is accidentally enabled, for example by a runaway pointer or brown-out condition, the device will be reset and the Watchdog Timer will stay enabled. If the code is not set up to handle the Watchdog, this might lead to an eternal loop of time-out resets. To avoid this situation, the application software should always clear the Watchdog System Reset Flag (WDRF) and the WDE control bit in the initialisation routine, even if the Watchdog is not in use. ------------------------------------------------------------------------------------------------------------- 2. avr studio settings ------------------------------------------------------------------------------------------------------------- THIS WAS COMPILED USING WinAVR20060421 and Avr Sudio 4 to change the compiled bootloader to load in the bootloader section- AVR Studio - Project-Configuration Options- Memory Settings- Add -> Flash .text 0xF00 (ATMega88) ---OR--- Add -> Flash .text 0x1F00 (ATMega168) ---OR--- Add -> Flash .text 0x1F00 (ATMega16) ---OR--- for your avr- will be ((FLASHEND+1) - 0x200) / 2 (end of program memory +1, minus the bootloader size (in bytes), divided by 2 to get the word size) **AVR studio uses a word size for this address (which is converted to a byte size for gcc)** Use WORD address for this setting \ / || \------------------------------\-----------------------------------------------\ -------------------------------------||------- -------------------------------------||------- | ATMega88 | | ATMega168, ATMega16 | ---------------------------------------------- ---------------------------------------------- | Bootloader | BYTE Address | WORD Address | | Bootloader | BYTE Address | WORD Address | | Size - WORDS | Start | Start | | Size - WORDS | Start | Start | ---------------------------------------------- ---------------------------------------------- | 1024 | 1800 | C00 | | 1024 | 3800 | 1C00 | ---------------------------------------------- ---------------------------------------------- | 512 | 1C00 | E00 | | 512 | 3C00 | 1E00 | ---------------------------------------------- ---------------------------------------------- | *256 | 1E00 | -> F00 <- | | *256 | 3E00 | -> 1F00 <- | ---------------------------------------------- ---------------------------------------------- | 128 | 1F00 | F80 | | 128 | 3F00 | 1F80 | ---------------------------------------------- ---------------------------------------------- to save space/prevent interrupt vectors (not using interrupts), and normal C startup code- AVR Studio - Project-Configuration Options- Custom Options- Linker Options Add-> -nostartfiles Add-> -nodefaultlibs for avr's >8K, to make all calls 'rcall' instead of call (save some space- only need rcall in bootloader) AVR Studio - Project-Configuration Options- Custom Options- All Files Add-> -mshort-calls RESULTS COMPILED USING WinAVR20060421 and Avr Sudio 4 ------------------------------------------------------------------------------------ | Device Data Bytes Program Bytes USE_PIN_TRIGGER=1 MAX | |------------------------------------------------------------------------------------| | ATmega88 71 466 -4 466 | |------------------------------------------------------------------------------------| | ATmega168 135 466 -4 466 | |------------------------------------------------------------------------------------| | ATmega16 135 466 -4 466 | |------------------------------------------------------------------------------------| | ATmega32 135 466 -4 466 | |------------------------------------------------------------------------------------| | all avr's should end up the same, as in/out is only used for registers that are | | | | always in lower i/o, otherwise lds/sts is used (ram use depends on SPM_PAGESIZE) | ------------------------------------------------------------------------------------ note: when compiling, the build output will show 512bytes used because we have the jump table at the end, so to find out the 'true' size of the code, comment out the '.org' line in the jump table (don't forget to uncomment when done) ------------------------------------------------------------------------------------------------------------- 3. bootloader protection from bad apps ------------------------------------------------------------------------------------------------------------- the bootloader will be protected from inadvertant jumps into the bootloader, or inadvertant jumps from the app into flash space beyond the application code that would end up entering the bootloader the bootloader will also guard the spm command the mechanism to do this is the IVSEL bit, since there would be no reason an application will ever switch the vectors to the bootloader section (at least with this bootloader,which is what we are protecting here), and the switch of IVSEL is also protected by the IVCE+4cycle requirement the IVSEL bit will be set when entering the bootloader (if PORF=1 AND WDRF=0), and any loops in the bootloader will check to make sure that the IVSEL bit is set- so anywhere the jump happens to land, will end up getting to the safeT_check function, and get kicked out (to 0x0000) if IVSEL is not set if the errant jump lands on one of the first 5 instructions in the bootloader, it will still at least have to have the PORF flag still set, as the PORF check is done between the IVSEL change enable and the setting of the IVSEL bit (and 1 of those instructions will rjmp to 0x0000, so only 4) if the errant jump lands on code before the booloader, and 'enters' the bootloader from 'below', it will also need to have PORF still be set this same mechanism also protects any inadvertant jump from doing an spm command there is only 1 spm instruction in the bootloader, and a check of IVSEL is done between the spm enable and the spm instruction if the errant jump lands on or before the spm enable, it will have to pass the IVSEL=1 test for the spm instruction to execute if it lands on the first or second instruction after the enable (in addition to the requirement that spmen will have to be set one instruction before the errant jump), it will not meet the 4 cycle requirement regardless of what IVSEL may be, as the errant jump will take at least 2cycles (rjmp/rcall/ijmp), which then leaves the third instruction, but that one rjmp's back out to 0x0000, so the only way left for an inadvertant jump to cause an spm command is- the spmen bit would need to be set 1 (or 2 cycles) before the 'bad' jump, and that bad jump would have to land right on the spm instruction in the bootloader simply put- the odds are in your favor against any inadvertant jump accidentally wiping out any page in app space this may be overkill, but this will prevent any screwed up code from accidentally ending up in the bootloader and then erasing a page in the app section (if it lands in the bootloader section where the erase loop happens to be, it could be more than 1 page) ------------------------------------------------------------------------------------------------------------- 4. reset flags logic ------------------------------------------------------------------------------------------------------------- the bootloader WILL 'run' IF the watchdog flag NOT set, AND the power up flag IS set (power up) POWER UP- on a power up, the watchdog flag will NOT be set, the power up flag WILL be set, so the bootloader will then check for esc key for 1/8 second (or whatever key you define) IF no esc key is pressed, the watchdog timer will cause a watchdog reset, which brings us right back to the bootloader code, since the BOOTRST fuse will be programmed the bootloader will see the watchdog reset flag IS set, so just rjmp to 0x0000 your APPLICATION will have to clear the WDRF flag and WDE control bit (if not using wdt) on startup as recommended in the Atmel datasheet (to prevent an 'eternal loop of time-out resets' on newer avr's) IF the APPLICATION clears NO other flags (besides WDRF), on a watchdog reset (if app enabled it, or is set in fuses to be always on), the bootloader will NOT attempt to run (because WDRF is set) IF the APPLICATION clears NO other flags (besides WDRF), on a brown out or external reset, the bootloader WILL attempt to run because the application turned off the watchdog and did nothing to the power reset flags so, to prevent the bootloader from attempting to run (checking for 'trigger'), clear all reset flags in your application startup code ------------------------------------------------------------------------------------------------------------- 5. application jump into bootloader ------------------------------------------------------------------------------------------------------------- IF you want to 'jump' to the bootloader FROM your APPLICATION- turn off interrupts cli(); // no interrupts in bootloader clear MCU status register MCUSR=0; // clear reset flags (if not already cleared) IF you have watchdog on wdt_reset(); // just in case its about to timeout change vectors MCUSR = (1<>8) ); asm volatile("ldi r30,%0" : : "M" ( (((FLASHEND-1)/2) &0x00FF) ); asm volatile("ijmp"); OR goto *FLASHEND-1; OR //use macro to jump to FLASHEND-1 (see jmp_table code for more info) BOOTLOADER(); so, altogether now- cli(); // must MCUSR=0; // if not already cleared wdt_reset(); // if watchdog used in app, not a bad idea MCUSR = (1<> 8)) ); asm volatile("ldi r30,%0" : : "M" ((((FLASHEND-1)/2) & 0x00FF)) ); asm volatile("ijmp"); OR cli(); // must MCUSR=0; // if not already cleared wdt_reset(); // if watchdog used in app, not a bad idea MCUSR = (1< | 7 6 | 5 4 | 3 2 | 1 0 | |------------------------------------------------------------------------------| | name-> | - - | BLB12 BLB11 | BLB02 BLB01 | LB2 LB1 | |------------------------------------------------------------------------------| | default-> | 1 1 | 1 1 | 1 1 | 1 1 | |------------------------------------------------------------------------------| | applies to-> | unused | Bootloader | Application | Memory | |------------------------------------------------------------------------------| | app can't read the bootloader -> 0 0 <-no writing to bootloader area | |------------------------------------------------------------------------------| | bootloader is allowed to read application -> 1 1 <-write allowed | |------------------------------------------------------------------------------| | programmer is unable to read/write flash/eeprom (erase first) 0 0 | |------------------------------------------------------------------------------| | my secure-> | x x | 0 0 | 1 1 | 0 0 | -------------------------------------------------------------------------------- -Boot Loader Section- BLB1 Mode 3 (BLB12=0,BLB11=0, both programmed) this will prevent the bootloader from writing over itself (even though it already protects itself as currently programmed), and prevent the APPLICATION from reading the bootloader section (security) -Application Section- BLB0 Mode 1 (BLB02=1,BLB01=1, both unprogrammed) these have to be left unprogrammed, otherwise the bootloader won't work (can't read or write to app space) -Memory Lock Bits- LB Mode 3 (LB2=0,LB1=0, both programmed) this will prevent programming and reading by a programmer, and the only way to read/program will be a 'chip erase' command (which will wipe out the bootloader and application) for avr's (assuming all not greater than 256k), the type 2 record will be limited to 3 possibilities- ':020000021000EC' ':020000022000DC' ':020000023000CC' */