| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717 | /**********************************************************//* Serial Bootloader for Atmel megaAVR Controllers        *//*                                                        *//* tested with ATmega644 and ATmega644P                   *//* should work with other mega's, see code for details    *//*                                                        *//* ATmegaBOOT.c                                           *//*                                                        *//* 20090131: Added 324P support from Alex Leone           *//*           Marius Kintel                                *//* 20080915: applied ADABoot mods for Sanguino 644P       *//*           Brian Riley                                  *//* 20080711: hacked for Sanguino by Zach Smith            *//*           and Justin Day                               *//* 20070626: hacked for Arduino Diecimila (which auto-    *//*           resets when a USB connection is made to it)  *//*           by D. Mellis                                 *//* 20060802: hacked for Arduino by D. Cuartielles         *//*           based on a previous hack by D. Mellis        *//*           and D. Cuartielles                           *//*                                                        *//* Monitor and debug functions were added to the original *//* code by Dr. Erik Lins, chip45.com. (See below)         *//*                                                        *//* Thanks to Karl Pitrich for fixing a bootloader pin     *//* problem and more informative LED blinking!             *//*                                                        *//* For the latest version see:                            *//* http://www.chip45.com/                                 *//*                                                        *//* ------------------------------------------------------ *//*                                                        *//* based on stk500boot.c                                  *//* Copyright (c) 2003, Jason P. Kyle                      *//* All rights reserved.                                   *//* see avr1.org for original file and information         *//*                                                        *//* This program is free software; you can redistribute it *//* and/or modify it under the terms of the GNU General    *//* Public License as published by the Free Software       *//* Foundation; either version 2 of the License, or        *//* (at your option) any later version.                    *//*                                                        *//* This program is distributed in the hope that it will   *//* be useful, but WITHOUT ANY WARRANTY; without even the  *//* implied warranty of MERCHANTABILITY or FITNESS FOR A   *//* PARTICULAR PURPOSE.  See the GNU General Public        *//* License for more details.                              *//*                                                        *//* You should have received a copy of the GNU General     *//* Public License along with this program; if not, write  *//* to the Free Software Foundation, Inc.,                 *//* 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA *//*                                                        *//* Licence can be viewed at                               *//* http://www.fsf.org/licenses/gpl.txt                    *//*                                                        *//* Target = Atmel AVR m128,m64,m32,m16,m8,m162,m163,m169, *//* m8515,m8535. ATmega161 has a very small boot block so  *//* isn't supported.                                       *//*                                                        *//* Tested with m168                                       *//**********************************************************//* $Id$ *//* some includes */#include <inttypes.h>#include <avr/io.h>#include <avr/pgmspace.h>#include <avr/interrupt.h>#include <avr/wdt.h>#include <avr/boot.h>#ifdef ADABOOT    #define NUM_LED_FLASHES 3    #define ADABOOT_VER	1#endif/* 20070707: hacked by David A. Mellis - after this many errors give up and launch application */#define MAX_ERROR_COUNT 5/* set the UART baud rate *//* 20080711: hack by Zach Hoeken */#define BAUD_RATE   38400/* SW_MAJOR and MINOR needs to be updated from time to time to avoid warning message from AVR Studio *//* never allow AVR Studio to do an update !!!! */#define HW_VER	 0x02#define SW_MAJOR 0x01#define SW_MINOR 0x10/* onboard LED is used to indicate, that the bootloader was entered (3x flashing) *//* if monitor functions are included, LED goes on after monitor was entered */#define LED_DDR  DDRB#define LED_PORT PORTB#define LED_PIN  PINB#define LED      PINB0/* define various device id's *//* manufacturer byte is always the same */#define SIG1	0x1E	// Yep, Atmel is the only manufacturer of AVR micros.  Single source :(#if defined(__AVR_ATmega1284P__)#define SIG2	0x97#define SIG3	0x05#elif defined(__AVR_ATmega644P__)#define SIG2	0x96#define SIG3	0x0A#elif defined(__AVR_ATmega644__)#define SIG2	0x96#define SIG3	0x09#elif defined(__AVR_ATmega324P__)#define SIG2	0x95#define SIG3	0x08#endif#define PAGE_SIZE		0x080U   //128 words#define PAGE_SIZE_BYTES	0x100U   //256 bytes/* function prototypes */void putch(char);char getch(void);void getNch(uint8_t);void byte_response(uint8_t);void nothing_response(void);char gethex(void);void puthex(char);void flash_led(uint8_t);/* some variables */union address_union{    uint16_t word;    uint8_t  byte[2];} address;union length_union{    uint16_t word;    uint8_t  byte[2];} length;struct flags_struct{    unsigned eeprom : 1;    unsigned rampz  : 1;} flags;uint8_t buff[256];uint8_t error_count = 0;uint8_t sreg;void (*app_start)(void) = 0x0000;/* main program starts here */int main(void){    uint8_t ch,ch2;    uint16_t w;	uint16_t i;	    asm volatile("nop\n\t");#ifdef ADABOOT		// BBR/LF 10/8/2007 & 9/13/2008    ch = MCUSR;    MCUSR = 0;    WDTCSR |= _BV(WDCE) | _BV(WDE);    WDTCSR = 0;    // Check if the WDT was used to reset, in which case we dont bootload and skip straight to the code. woot.    if (! (ch &  _BV(EXTRF))) // if it's a not an external reset...      app_start();  // skip bootloader#endif	//initialize our serial port.    UBRR0L = (uint8_t)(F_CPU/(BAUD_RATE*16L)-1);    UBRR0H = (F_CPU/(BAUD_RATE*16L)-1) >> 8;    UCSR0B = (1<<RXEN0) | (1<<TXEN0);    UCSR0C = (1<<UCSZ00) | (1<<UCSZ01);    /* Enable internal pull-up resistor on pin D0 (RX), in order    to supress line noise that prevents the bootloader from    timing out (DAM: 20070509) */    DDRD &= ~_BV(PIND0);    PORTD |= _BV(PIND0);    /* set LED pin as output */    LED_DDR |= _BV(LED);	    /* flash onboard LED to signal entering of bootloader                   */	    /* ADABOOT will do two series of flashes. first 4 - signifying ADABOOT  */	    /* then a pause and another flash series signifying ADABOOT sub-version */	flash_led(NUM_LED_FLASHES);	#ifdef	ADABOOT		flash_led(ADABOOT_VER);		// BBR 9/13/2008	#endif     /* forever loop */    for (;;)	{		/* get character from UART */		ch = getch();		/* A bunch of if...else if... gives smaller code than switch...case ! */		/* Hello is anyone home ? */ 		if(ch=='0')		    nothing_response();		/* Request programmer ID */		/* Not using PROGMEM string due to boot block in m128 being beyond 64kB boundry  */		/* Would need to selectively manipulate RAMPZ, and it's only 9 characters anyway so who cares.  */		else if(ch=='1')		{		    if (getch() == ' ')			{				putch(0x14);				putch('A');				putch('V');				putch('R');				putch(' ');				putch('I');				putch('S');				putch('P');				putch(0x10);		    }			else			{				if (++error_count == MAX_ERROR_COUNT)				    app_start();		    }		}		/* AVR ISP/STK500 board commands  DON'T CARE so default nothing_response */		else if(ch=='@')		{		    ch2 = getch();		    if (ch2 > 0x85)				getch();		    nothing_response();		}		/* AVR ISP/STK500 board requests */		else if(ch=='A')		{		    ch2 = getch();		    if(ch2 == 0x80)				byte_response(HW_VER);		// Hardware version		    else if(ch2==0x81)				byte_response(SW_MAJOR);	// Software major version		    else if(ch2==0x82)				byte_response(SW_MINOR);	// Software minor version		    else if(ch2==0x98)				byte_response(0x03);		// Unknown but seems to be required by avr studio 3.56		    else				byte_response(0x00);		// Covers various unnecessary responses we don't care about		}		/* Device Parameters  DON'T CARE, DEVICE IS FIXED  */		else if(ch=='B')		{		    getNch(20);		    nothing_response();		}		/* Parallel programming stuff  DON'T CARE  */		else if(ch=='E')		{		    getNch(5);		    nothing_response();		}		/* Enter programming mode  */		else if(ch=='P')		{		    nothing_response();		}		/* Leave programming mode  */		else if(ch=='Q')		{		    nothing_response();#ifdef ADABOOT					// autoreset via watchdog (sneaky!) BBR/LF 9/13/2008	  		WDTCSR = _BV(WDE);	  		while (1); // 16 ms#endif				}		/* Erase device, don't care as we will erase one page at a time anyway.  */		else if(ch=='R')		{		    nothing_response();		}		/* Set address, little endian. EEPROM in bytes, FLASH in words  */		/* Perhaps extra address bytes may be added in future to support > 128kB FLASH.  */		/* This might explain why little endian was used here, big endian used everywhere else.  */		else if(ch=='U')		{		    address.byte[0] = getch();		    address.byte[1] = getch();		    nothing_response();		}		/* Universal SPI programming command, disabled.  Would be used for fuses and lock bits.  */		else if(ch=='V')		{		    getNch(4);		    byte_response(0x00);		}		/* Write memory, length is big endian and is in bytes  */		else if(ch=='d')		{		    length.byte[1] = getch();		    length.byte[0] = getch();			    flags.eeprom = 0;		    if (getch() == 'E')				flags.eeprom = 1;			for (i=0; i<PAGE_SIZE; i++)				buff[i] = 0;				    for (w = 0; w < length.word; w++)			{				// Store data in buffer, can't keep up with serial data stream whilst programming pages				buff[w] = getch();		    }			    if (getch() == ' ')			{				if (flags.eeprom)				{		                					//Write to EEPROM one byte at a time				    for(w=0;w<length.word;w++)					{						while(EECR & (1<<EEPE));											EEAR = (uint16_t)(void *)address.word;						EEDR = buff[w];						EECR |= (1<<EEMPE);						EECR |= (1<<EEPE);						address.word++;				    }							}				else				{					//address * 2 -> byte location				    address.word = address.word << 1;			    					//Even up an odd number of bytes					if ((length.byte[0] & 0x01))						length.word++;									// HACKME: EEPE used to be EEWE				    //Wait for previous EEPROM writes to complete					//while(bit_is_set(EECR,EEPE));					while(EECR & (1<<EEPE));								    asm volatile(						 "clr	r17		\n\t"	//page_word_count						 "lds	r30,address	\n\t"	//Address of FLASH location (in bytes)						 "lds	r31,address+1	\n\t"						 "ldi	r28,lo8(buff)	\n\t"	//Start of buffer array in RAM						 "ldi	r29,hi8(buff)	\n\t"						 "lds	r24,length	\n\t"	//Length of data to be written (in bytes)						 "lds	r25,length+1	\n\t"						 "length_loop:		\n\t"	//Main loop, repeat for number of words in block							 							 						 "cpi	r17,0x00	\n\t"	//If page_word_count=0 then erase page						 "brne	no_page_erase	\n\t"						 						 "wait_spm1:		\n\t"						 "lds	r16,%0		\n\t"	//Wait for previous spm to complete						 "andi	r16,1           \n\t"						 "cpi	r16,1           \n\t"						 "breq	wait_spm1       \n\t"						 "ldi	r16,0x03	\n\t"	//Erase page pointed to by Z						 "sts	%0,r16		\n\t"						 "spm			\n\t"							 						 "wait_spm2:		\n\t"						 "lds	r16,%0		\n\t"	//Wait for previous spm to complete						 "andi	r16,1           \n\t"						 "cpi	r16,1           \n\t"						 "breq	wait_spm2       \n\t"									 						 "ldi	r16,0x11	\n\t"	//Re-enable RWW section						 "sts	%0,r16		\n\t"						 			 						 "spm			\n\t"						 "no_page_erase:		\n\t"							 						 "ld	r0,Y+		\n\t"	//Write 2 bytes into page buffer						 "ld	r1,Y+		\n\t"							 							 						 "wait_spm3:		\n\t"						 "lds	r16,%0		\n\t"	//Wait for previous spm to complete						 "andi	r16,1           \n\t"						 "cpi	r16,1           \n\t"						 "breq	wait_spm3       \n\t"						 "ldi	r16,0x01	\n\t"	//Load r0,r1 into FLASH page buffer						 "sts	%0,r16		\n\t"						 "spm			\n\t"							 						 "inc	r17		\n\t"	//page_word_count++						 "cpi r17,%1	        \n\t"						 "brlo	same_page	\n\t"	//Still same page in FLASH						 "write_page:		\n\t"						 "clr	r17		\n\t"	//New page, write current one first						 "wait_spm4:		\n\t"						 "lds	r16,%0		\n\t"	//Wait for previous spm to complete						 "andi	r16,1           \n\t"						 "cpi	r16,1           \n\t"						 "breq	wait_spm4       \n\t"						 							 						 "ldi	r16,0x05	\n\t"	//Write page pointed to by Z						 "sts	%0,r16		\n\t"						 "spm			\n\t"						 "wait_spm5:		\n\t"						 "lds	r16,%0		\n\t"	//Wait for previous spm to complete						 "andi	r16,1           \n\t"						 "cpi	r16,1           \n\t"						 "breq	wait_spm5       \n\t"									 						 "ldi	r16,0x11	\n\t"	//Re-enable RWW section						 "sts	%0,r16		\n\t"						 			 						 "spm			\n\t"					 		 						 "same_page:		\n\t"							 						 "adiw	r30,2		\n\t"	//Next word in FLASH						 "sbiw	r24,2		\n\t"	//length-2						 "breq	final_write	\n\t"	//Finished						 "rjmp	length_loop	\n\t"						 "final_write:		\n\t"						 "cpi	r17,0		\n\t"						 "breq	block_done	\n\t"						 "adiw	r24,2		\n\t"	//length+2, fool above check on length after short page write						 "rjmp	write_page	\n\t"						 "block_done:		\n\t"						 "clr	__zero_reg__	\n\t"	//restore zero register						 : "=m" (SPMCSR) : "M" (PAGE_SIZE) : "r0","r16","r17","r24","r25","r28","r29","r30","r31"						 );				}				putch(0x14);				putch(0x10);		    }			else			{				if (++error_count == MAX_ERROR_COUNT)				    app_start();		    }				}    		/* Read memory block mode, length is big endian.  */		else if(ch=='t')		{			length.byte[1] = getch();			length.byte[0] = getch();			if (getch() == 'E')				flags.eeprom = 1;			else			{				flags.eeprom = 0;				address.word = address.word << 1;	        // address * 2 -> byte location			}			// Command terminator			if (getch() == ' ')			{				putch(0x14);				for (w=0; w<length.word; w++)				{					// Can handle odd and even lengths okay				    if (flags.eeprom) 					{						// Byte access EEPROM read						while(EECR & (1<<EEPE));						EEAR = (uint16_t)(void *)address.word;						EECR |= (1<<EERE);						putch(EEDR);						address.word++;				    }				    else					{						if (!flags.rampz)							putch(pgm_read_byte_near(address.word));						address.word++;				    }				}				putch(0x10);		    }		}		/* Get device signature bytes  */		else if(ch=='u')		{			if (getch() == ' ')			{				putch(0x14);				putch(SIG1);				putch(SIG2);				putch(SIG3);				putch(0x10);			}			else			{				if (++error_count == MAX_ERROR_COUNT)					app_start();			}		}		/* Read oscillator calibration byte */		else if(ch=='v')			byte_response(0x00);		else if (++error_count == MAX_ERROR_COUNT)		    app_start();	}    /* end of forever loop */}char gethex(void){    char ah,al;    ah = getch();	putch(ah);    al = getch();	putch(al);    	if(ah >= 'a')		ah = ah - 'a' + 0x0a;	else if(ah >= '0')		ah -= '0';    if(al >= 'a')		al = al - 'a' + 0x0a;	else if(al >= '0')		al -= '0';    return (ah << 4) + al;}void puthex(char ch){    char ah,al;    ah = (ch & 0xf0) >> 4;	if(ah >= 0x0a)		ah = ah - 0x0a + 'a';	else		ah += '0';    al = (ch & 0x0f);	if(al >= 0x0a)		al = al - 0x0a + 'a';	else		al += '0';    putch(ah);    putch(al);}void putch(char ch){    while (!(UCSR0A & _BV(UDRE0)));    UDR0 = ch;}char getch(void){    uint32_t count = 0;#ifdef ADABOOT	LED_PORT &= ~_BV(LED);          // toggle LED to show activity - BBR/LF 10/3/2007 & 9/13/2008#endif    while(!(UCSR0A & _BV(RXC0)))	{    	/* 20060803 DojoCorp:: Addon coming from the previous Bootloader*/                   	/* HACKME:: here is a good place to count times*/    	count++;    	if (count > MAX_TIME_COUNT)    		app_start();     }#ifdef ADABOOT	LED_PORT |= _BV(LED);          // toggle LED to show activity - BBR/LF 10/3/2007 & 9/13/2008#endif    return UDR0;}void getNch(uint8_t count){    uint8_t i;    for(i=0;i<count;i++)	{		while(!(UCSR0A & _BV(RXC0)));		UDR0;    }}void byte_response(uint8_t val){    if (getch() == ' ')	{		putch(0x14);		putch(val);		putch(0x10);    }	else	{		if (++error_count == MAX_ERROR_COUNT)		    app_start();    }}void nothing_response(void){    if (getch() == ' ')	{		putch(0x14);		putch(0x10);    }	else	{		if (++error_count == MAX_ERROR_COUNT)		    app_start();    }}#ifdef ADABOOTvoid flash_led(uint8_t count){    /* flash onboard LED count times to signal entering of bootloader */	/* l needs to be volatile or the delay loops below might get      */	/* optimized away if compiling with optimizations (DAM).          */	    volatile uint32_t l;    if (count == 0) {      count = ADABOOT;    }    	int8_t i;    for (i = 0; i < count; ++i) {		LED_PORT |= _BV(LED);					// LED on		for(l = 0; l < (F_CPU / 1000); ++l);		// delay NGvalue was 1000 for both loops - BBR		LED_PORT &= ~_BV(LED);					// LED off		for(l = 0; l < (F_CPU / 250); ++l);		// delay asymmteric for ADA BOOT BBR 	}	for(l = 0; l < (F_CPU / 100); ++l);		    // pause ADA BOOT BBR 		}#elsevoid flash_led(uint8_t count){    /* flash onboard LED three times to signal entering of bootloader */	/* l needs to be volatile or the delay loops below might get	optimized away if compiling with optimizations (DAM). */    volatile uint32_t l;    if (count == 0) {      count = 3;    }    	int8_t i;    for (i = 0; i < count; ++i) {		LED_PORT |= _BV(LED);		for(l = 0; l < (F_CPU / 1000); ++l);		LED_PORT &= ~_BV(LED);		for(l = 0; l < (F_CPU / 1000); ++l); 	}		}#endif/* end of file ATmegaBOOT.c */
 |