Bootloader


STK500 Protocol v2 Bootloader/monitor for ATmega128,AT90CAN128.
Modified by BiPOM Electronics to support ATMEGA2560

Toolkit:AVR Development System

Location:/bipom/devtools/WinAVR/minimaxavrc/Examples/Labs/bootloader_atmega2560

Code Example


#include <ctype.h>

#ifndef F_CPU
#define F_CPU 14745600UL
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#include <avrio.h>
#include <avriom2560.h>
#include <avrboot.h>
#include <avrpgmspace.h>
#include <utildelay.h>
#include <stdlib.h>

#include "command_v2.h"

/* monitor functions will only be compiled when using ATmega64/128/CAN128, 
 * due to bootblock size constraints 
*/
#if defined(__AVR_ATmega128__) || defined(__AVR_AT90CAN128__)
//#define MONITOR

#include "rtc72423.c"
#endif

#if defined(__AVR_ATmega2560__) 
#define EEMWE   2
#define EEWE    1
#endif




#define app_start()	 asm volatile("jmp 0x0000");	      

#define	STATE_READY		      0x00
#define STATE_START           0x01
#define STATE_GET_SEQ_NUM     0x02
#define STATE_GET_MSG_SIZE1   0x03
#define STATE_GET_MSG_SIZE2   0x04
#define STATE_GET_TOKEN       0x05
#define STATE_GET_DATA        0x06
#define STATE_GET_CS          0x07

/* Set values as required supress popup messages in AVR-Studio */
#define HARDWARE_VERSION      0x02
#define SOFTWARE_MAJOR        0x02
//#define SOFTWARE_MINOR        0x01

#define SOFTWARE_MINOR        0x0A
/* values for possible topcard */
#define STK501		0xAA
#define STK502		0x55
#define STK503		0xFA
#define STK504		0xEE
#define STK505		0xE4
#define STK520		0xDD		

/* value for vtarget: always return 5.0V */
#define VTARGET		0x32
/* value for vadjust: always return 5.0V */
#define VADJUST		0x32
/* prescalled clock frequesncy equal to system clock */
#define PSCALE_FSYS 0x01
#define CMATCH_DEF	0x01
#define SCK_DURATION_DEF 0x01

#define BAUD_RATE		115200

#define MAX_BUF_SIZE	300

/* Adjust to suit whatever pin your hardware uses to enter the bootloader */

/* ATmega128 has two UARTS so two pins are used to enter bootloader and select UART */
/* BL0... means UART0, BL1... means UART1 */
#ifdef __AVR_ATmega128__
#define BL_DDR  DDRF
#define BL_PORT PORTF
#define BL_PIN  PINF
#define BL0     PINF7
#define BL1     PINF6
#elif defined __AVR_AT90CAN128__
#define BL_DDR  DDRF
#define BL_PORT PORTF
#define BL_PIN  PINF
#define BL0     PINF7
#define BL1     PINF6
#else
/* other ATmegas have only one UART, so only one pin is defined to enter bootloader */
#define BL_DDR  DDRG
#define BL_PORT PORTG
#define BL_PIN  PING
#define BL      PING4
/* RTS0 pin */
#define RTS0_DDR  DDRE
#define RTS0_PORT PORTE
#define RTS0_PIN  PINE
#define RTS0      PINE3
#endif



#define SIG1	0x1E	// Always Atmel

#if defined __AVR_ATmega128__
#define SIG2	0x97
#define SIG3	0x02
#define UART0
#elif defined __AVR_AT90CAN128__
#define SIG2	0x97
#define SIG3	0x81
#define UART0
#elif defined __AVR_ATmega64__
#define SIG2	0x96
#define SIG3	0x02
#define UART0
#elif defined __AVR_ATmega32__
#define SIG2	0x95
#define SIG3	0x02
#elif defined __AVR_ATmega16__
#define SIG2	0x94
#define SIG3	0x03
#elif defined __AVR_ATmega8__
#define SIG2	0x93
#define SIG3	0x07
#elif defined __AVR_ATmega162__
#define SIG2	0x94
#define SIG3	0x04
#define UART0
#elif defined __AVR_ATmega163__
#define SIG2	0x94
#define SIG3	0x02
#elif defined __AVR_ATmega169__
#define SIG2	0x94
#define SIG3	0x05
#elif defined __AVR_ATmega8515__
#define SIG2	0x93
#define SIG3	0x06
#elif defined __AVR_ATmega8535__
#define SIG2	0x93
#define SIG3	0x08
#elif defined __AVR_ATmega2560__
#define SIG2	0x98
#define SIG3	0x01
#endif

/*
 * Put Signature to EEPROM
 */
uint8_t ee_pwm __attribute__((section(".eeprom"))) = 0xA5;


void putch(char);
char getch(void);
void initPorts(void); // init port pins for bootloader start

void initUart(void);  // check uart selected and init

void bootCheck(void); // check bootloader/application start

void handleMessage(void) ; 
void sendResponse(void);
void cmdSignOn(void);
void cmdReadSignatureIsp(void);
void cmdSetParameter(void);
void cmdEnterProgmodeIsp(void);
void cmdLeaveProgmodeIsp(void);
void cmdChipEraseIsp(void);
void cmdLoadAddress(void);
void cmdGetParameter(void);
void cmdProgramFlashIsp(void);
void cmdReadFlashIsp(void);
void cmdProgramEepromIsp(void);
void cmdReadEepromIsp(void);
void cmdReadFuseLockIsp(void);
void eeprom_wb(unsigned int uiAddress, unsigned char ucData);
unsigned char eeprom_rb(unsigned int uiAddress);
unsigned char readBits( unsigned int address ); // read lock/fuse bits


#ifdef MONITOR	
#define MONITOR_FLAG '!' 
#define MONITOR_PROMPT ':'
#define MONITOR_BUFFER_SIZE 256 
#define MONITOR_DISPLAY_LINES 8

#define MONITOR_CMD_TIME  'T'
#define MONITOR_CMD_DATE  'D'
#define MONITOR_CMD_FLASH 'F'
#define MONITOR_CMD_RAM   'X'
#define MONITOR_CMD_EEPROM  'E'
#define MONITOR_CMD_QUIT 'Q'
#define MONITOR_CMD_HELP '?'

#define MONITOR_MEM_FLASH  0
#define MONITOR_MEM_RAM    1
#define MONITOR_MEM_EEPROM 2

	void monitorMain(void); 
	void monitorInit(void);
	uint32_t monitorDump( uint32_t address, uint8_t lineNum, uint8_t memType );
	void monitorChange( uint32_t address, uint8_t value, uint8_t memType );
	void print( char *s );
	void print_P( uint32_t address );
	char  *monitorReadLine( void );
	char *getValue( char *src,  uint32_t *value, uint8_t len );
	uint8_t htoi( uint8_t val );

static uint8_t monitorWelcome[] PROGMEM = "\nMonitor\n" ;	
static uint8_t monitorQuit[] PROGMEM = "Quit...\n" ;	
static uint8_t monitorError[] PROGMEM = "Error\n" ;	
char monitorBuf[MONITOR_BUFFER_SIZE];
unsigned char monitor_cnt = 0; // check if we enter monitor

#endif

unsigned char bootuart0=1,bootuart1=0;
unsigned char rx_buffer[MAX_BUF_SIZE];  // RX buffer

unsigned char tx_buffer[MAX_BUF_SIZE];  // TX buffer

unsigned char *rx_pntr = &rx_buffer[0];  // pointer to rx buffer

unsigned char *tx_pntr = &tx_buffer[0];  // pointer to rx buffer

unsigned char *size_pntr = &tx_buffer[2];    // pointer to msg size buffer field

unsigned char *answer_id_pntr = &tx_buffer[5];    // pointer to answer id buffer field

unsigned int msg_size=0x00;  // Rx message size

unsigned char msg_cs=0x00;  // calculated message checksum

unsigned long address_flash = 0L; // actual address in flash memory

unsigned int address_eeprom = 0x00; // actual address in eeprom memory

unsigned int n_bytes = 0x00;  // number of databytes in message

unsigned char n_pages = 0x00;  // number of page to program

unsigned char sequence_number=0x00;  // sequence number from host

unsigned char answer_id=0x00;  // answer cmd id

unsigned int i=0, j=0;  // for loop variables

unsigned char echo = 0;  // rs232 terminal echo



#if 0
void printmes( char *s )  
{
   while( *s ) {
   		putch( *s );
   		s++ ;
   }	
}

void tiprintf( const char *format, ... )
{
    char msgBuf[64] = {0};
    va_list arglist;
    va_start( arglist, format );
    vsprintf(msgBuf,format,arglist );
    va_end( arglist );
    printmes(msgBuf);
}

/********************************************************************************************
;	Function:		PrintDataBlock
;
;	Description:	Prints data buffer  
;
;	Inputs: 	 	buffer - pointer to data buffer
;					length - length of data buffer 
;
;	Returns:		Nothing	 	 
**********************************************************************************************/
void PrintDataBlock (uint8_t* buffer, uint16_t length)
{
	uint16_t ndx;
	for( ndx = 0; ndx < length ; ndx++)	
	{
		if (!(ndx%16)) tiprintf ("\n\r%04x",ndx);
		tiprintf (" %02x",*buffer++);
	}
}
void testCode(void)
{
	int i=0;
	printmes ("\n\rTestCode");
	for(i = 0; i < MAX_BUF_SIZE; i++) 
	{ 
		rx_buffer[i] = 0x00;  // clear data buffer

	}

	// n_Bytes = 32;

	rx_pntr = &rx_buffer[0];
	rx_buffer[1] = SPM_PAGESIZE  256;
	rx_buffer[2] = SPM_PAGESIZE % 256;
	// address

	address_flash = 32768;
	//

	printmes ("\n\rREAD DATA BLOCK");
	cmdReadFlashIsp();
	PrintDataBlock (&tx_buffer[2], SPM_PAGESIZE);
	//

	for(i = 0; i < MAX_BUF_SIZE; i++) 
	{ 
		rx_buffer[i] = i;  // clear data buffer

	}
	rx_buffer[1] = SPM_PAGESIZE  256;
	rx_buffer[2] = SPM_PAGESIZE % 256;
	rx_pntr = &rx_buffer[0];
	address_flash = 32768;
	tx_pntr = &tx_buffer[0];

	cmdProgramFlashIsp();
	printmes ("\n\rEXIT");
	for ( ;; );
}
#endif
/*----------------------------------------------------------------------------*/ 
/* execute command  CMD_READ_FUSE_ISP,CMD_READ_LOCK_ISP                       */
/*----------------------------------------------------------------------------*/ 
 
void cmdReadFuseLockIsp(void)  {
	unsigned int command,address;

	msg_size = 4;  		// set message length

	*(tx_pntr++) = *rx_pntr;
	*(tx_pntr++) = STATUS_CMD_OK;
	
	command = *(rx_pntr+2) * 256 + *(rx_pntr+3);
	switch( command ) {
		case 0x5800: address = 0x0001; break; // lock bits		

		case 0x5000: address = 0x0000; break; // fuse low	

		case 0x5808: address = 0x0003; break; // fuse hi	

		case 0x5008: address = 0x0002; break; // fuse ext

		default: address = 0x0000;			
	}
	
	*(tx_pntr++) = readBits(address);	 

    *(tx_pntr++) = STATUS_CMD_OK;	
	
}   
 /* read lock/fuse bits */
unsigned char readBits( unsigned int address ) {
	asm volatile(
				"mov	r31,r25 \n\t"	
		       	"mov	r30,r24 \n\t"	
				"lds	r24,%0 \n\t"		       	
				"ori	r24,0x09 \n\t"
				"sts	%0,r24 \n\t"									
		       	"lpm	\n\t" 
		       	"mov	r24,r0  \n\t" 
		       	: "=m" (SPMCSR)
				);     
	return address&0xFF;
}
/*----------------------------------------------------------------------------*/ 
/* execute command  CMD_READ_FLASH_ISP                                        */
/*----------------------------------------------------------------------------*/ 
void cmdReadFlashIsp(void) {

	*(tx_pntr++) = CMD_READ_FLASH_ISP;
	*(tx_pntr++) = STATUS_CMD_OK;
	
	n_bytes=((*(rx_pntr+1)*256)+*(rx_pntr+2));  // number of databytes to read from flash 

	for(i=0;i < n_bytes; i++) { // fill data buffer with n_bytes

#if defined(__AVR_ATmega128__) || defined(__AVR_AT90CAN128__) || defined(__AVR_ATmega2560__)		
    	*(tx_pntr++) = pgm_read_byte_far(address_flash++);
#else
    	*(tx_pntr++) = pgm_read_byte(address_flash++);
#endif    	
	}
	msg_size = n_bytes + 3;  // set message length

	*(tx_pntr++) = STATUS_CMD_OK;	
	
}   // end of readFlashIsp

/*----------------------------------------------------------------------------*/ 
/* execute command  CMD_PROGRAM_FLASH_ISP                                     */
/*----------------------------------------------------------------------------*/ 
void cmdProgramFlashIsp(void) {
unsigned /*char*/int byte1,byte2;
unsigned long address, address_page1,address_page2,offset;
	
	n_bytes = ((*(rx_pntr+1)*256)+*(rx_pntr+2));  	// number of databytes to flash

	rx_pntr += 10;  								// set pointer to flash data

	address_page1 = address_flash - ( address_flash % SPM_PAGESIZE ); // addres of 1st page to program

	address_page2 = ( address_flash + n_bytes ) - 
	                ( ( address_flash + n_bytes ) % SPM_PAGESIZE ); // addres of last page to program

	n_pages = (( address_page2 - address_page1)  SPM_PAGESIZE ) + 1;  	// number of pages to flash	  

	offset = address_flash - address_page1 ; 
	#if 0
	tiprintf ("\n\r page size = %u",SPM_PAGESIZE);
	tiprintf ("\n\r address_page1 = %u",address_page1);
	tiprintf ("\n\r address_page2 = %u",address_page2);
	tiprintf ("\n\r n_pages = %u",n_pages);             		
	tiprintf ("\n\r offset = %u",offset);  
	tiprintf ("\n\r n_bytes = %u",n_bytes);  
	tiprintf ("\n\r*");
	#endif
	for( j = 0; j < n_pages; j++ )  { 					// do for all pages in message

        for(i=0; i < SPM_PAGESIZE; i += 2)  		// load old content of flash page

        {
        	address = address_page1 + i;
        	if( (address < address_flash  ) ||
        	    (address > ( address_flash + n_bytes) ) )  {  // copy to buffer old data

#if defined(__AVR_ATmega128__) || defined(__AVR_AT90CAN128__) || defined(__AVR_ATmega2560__)		
	    		byte1 = pgm_read_byte_far(address);
	    		byte2 = pgm_read_byte_far(address + 1);
#else
	    		byte1 = pgm_read_byte(address_page1 + i );
	    		byte2 = pgm_read_byte(address_page1 + i + 1);    		
#endif          
			}
			else  {			// copy to buffer new data

				byte1 = *rx_pntr;
				byte2 = *(rx_pntr+1);
				rx_pntr += 2;								
			}	
			boot_page_fill(i,/*(unsigned int)*/(byte1 |(byte2 << 8)));
        }
		boot_page_erase((unsigned long)address_page1);  	// clear page

        while(boot_spm_busy());	        				

        boot_page_write((unsigned long)address_page1);  	// write page to flash

        while(boot_spm_busy());

        boot_rww_enable();  								// enable Read-While-Write section

		address_page1 += SPM_PAGESIZE ;
	}	
	address_flash += n_bytes ;
	for(i = 0; i < MAX_BUF_SIZE; i++) { 
		rx_buffer[i] = 0xFF;  // clear data buffer

	}
	msg_size = 2;  			// set message length		

	*(tx_pntr++) = CMD_PROGRAM_FLASH_ISP;
	*(tx_pntr++) = STATUS_CMD_OK; 	
} // end of programFlashIsp

 
int main(void)
{
	unsigned char rx_data;  // received USART data byte

	unsigned char state = STATE_READY;  // actual state


	initPorts();
	bootCheck();
	initUart();
	//testCode();

	for(;;)
	{
		rx_data=getch();  // get one byte from USART

		msg_cs ^= rx_data;


		switch( state )  {
			// start processing when byte received

			case STATE_READY :	
				if(rx_data == MESSAGE_START)  // wait for message start

	      		{
			        rx_pntr = &rx_buffer[0];  // reset pointer 

			        msg_size = 0x00;  // reset message size

			        state = STATE_GET_SEQ_NUM;  // get sequence number next

	      		}
	      		else {
	      			msg_cs = 0x00;  // clear old checksum

#ifdef MONITOR	      		
	      			if ( rx_data == MONITOR_FLAG )  {
	      				monitor_cnt ++;
	      				if( monitor_cnt == 3 ) {
	      					monitorMain();	
	      				}
	      			}
	      			else {
	      				monitor_cnt = 0;	
	      			}
#endif	      			
	      		}
	        	break;		
	        	
	        // Get Sequence Number	

			case STATE_GET_SEQ_NUM :
				sequence_number = rx_data;  // store sequence number

				state = STATE_GET_MSG_SIZE1;  // get message size LSB next			

				break; 	 
				
			// Get Message Size 1	

			case STATE_GET_MSG_SIZE1:	
				msg_size = (((unsigned int)rx_data)<<8);  // store message size MSB

				state = STATE_GET_MSG_SIZE2;  // get message size LSB next			

				break;
			
			// Get Message Size 2	

			case STATE_GET_MSG_SIZE2:
				msg_size += (unsigned int) rx_data; // make message size from MSB and LSB

		                                            // +1 because we check if >0

				state = STATE_GET_TOKEN;  // get token next			

				break;
				
			// Get Token		

			case STATE_GET_TOKEN:
				if(rx_data == TOKEN)  // check if token is correct

					state = STATE_GET_DATA;  // get data next

				else
					state = STATE_READY; // wait for new message			

				break;  
				
			// Get Data	

			case STATE_GET_DATA:
				*rx_pntr++ = rx_data;
				--msg_size;
				
				if(msg_size == 0)
				{
					state = STATE_GET_CS;
				}			
				break;	
		
			// Get Checksum and handle message if correct

			case STATE_GET_CS:
				if( msg_cs == 0x00) { // check for valid checksum

					handleMessage();
				} 			
				state = STATE_READY;
				break; 					       		
		}
    
	}  // end for endless loop


	return 0; // main return value


} // end of main

 
/*----------------------------------------------------------------------------*/
/* Handle message                                                             */
/*----------------------------------------------------------------------------*/ 
void handleMessage(void) {
	 	
// new code begin  	

 		tx_pntr = &tx_buffer[0];
 		*(tx_pntr++) = MESSAGE_START ;
 		*(tx_pntr++) = sequence_number ; 
 		tx_pntr += 2;  // space reserved for message size

 		*(tx_pntr++) = TOKEN ;		
        rx_pntr = &rx_buffer[0];  // reset pointer

        
        switch( *rx_pntr ) {
        	case CMD_SIGN_ON:
				cmdSignOn();
        		break;	
        	case CMD_GET_PARAMETER:
        		cmdGetParameter();
        		break;
        	case CMD_SET_PARAMETER:
        		cmdSetParameter();
        		break;
        	case CMD_ENTER_PROGMODE_ISP:
        		cmdEnterProgmodeIsp();
        		break;
        	case CMD_LEAVE_PROGMODE_ISP:
        		cmdLeaveProgmodeIsp();
        		break;
        	case CMD_CHIP_ERASE_ISP:
        		cmdChipEraseIsp();
        		break;
        	case CMD_READ_SIGNATURE_ISP:
        		cmdReadSignatureIsp();
        		break;
        	case CMD_LOAD_ADDRESS:
        		cmdLoadAddress();
        		break;
        	case CMD_PROGRAM_FLASH_ISP:
        		cmdProgramFlashIsp();
        		break;
        	case CMD_READ_FLASH_ISP:
				cmdReadFlashIsp();        	
        		break;	
        	case CMD_PROGRAM_EEPROM_ISP:
        		cmdProgramEepromIsp();
        		break;
        	case CMD_READ_EEPROM_ISP:
        		cmdReadEepromIsp();
        		break;      
        	case CMD_READ_FUSE_ISP:
        	case CMD_READ_LOCK_ISP:        	
        		cmdReadFuseLockIsp();
        		break;         		        		  			
        			
        }
        
        sendResponse();
         
        // leave bootloader at programming end

/*
        if( *answer_id_pntr == CMD_LEAVE_PROGMODE_ISP)
        {
        	if(pgm_read_byte_near(0x0000) != 0xFF) {	
          		app_start();
        	}	
        }
*/        
       	
} // end handleMessage

 

/*----------------------------------------------------------------------------*/ 
/* send response message                                                      */
/*----------------------------------------------------------------------------*/
void sendResponse(void) {
	unsigned int msg_len, idx;

    size_pntr[0] = (unsigned char) (msg_size >> 8);
    size_pntr[1]=  (unsigned char) (msg_size & 0xFF);
		
	msg_len = tx_pntr - tx_buffer;
	msg_cs = 0;
	for( idx = 0; idx < msg_len; idx++ )  {
		putch( tx_buffer[idx] );
		msg_cs ^= tx_buffer[idx];		
	}	
	putch(msg_cs);	
	msg_cs = 0x00;  // reset checksum

}
 
/*----------------------------------------------------------------------------*/ 
/* execute command  CMD_SIGN_ON                                               */
/*----------------------------------------------------------------------------*/ 
 
void cmdSignOn(void)  {

	msg_size = 11;  		// set message length

	*(tx_pntr++) = CMD_SIGN_ON;
	*(tx_pntr++) = STATUS_CMD_OK;
	
	*(tx_pntr++) = 0x08;  	// send signature length

	*(tx_pntr++) = 'S';		// send identifier

	*(tx_pntr++) = 'T';
	*(tx_pntr++) = 'K';
	*(tx_pntr++) = '5';
	*(tx_pntr++) = '0';
	*(tx_pntr++) = '0';
	*(tx_pntr++) = '_';
	*(tx_pntr++) = '2';
	
} 
 
/*----------------------------------------------------------------------------*/ 
/* execute command  CMD_READ_SIGNATURE_ISP                                    */
/*----------------------------------------------------------------------------*/ 
 
void cmdReadSignatureIsp(void)  {

	msg_size = 4;  		// set message length

	*(tx_pntr++) = CMD_READ_SIGNATURE_ISP;
	*(tx_pntr++) = STATUS_CMD_OK;

	if( *(rx_pntr+4) == 0 )  {  // 1st sig byte

		*(tx_pntr++) = SIG1;	
	}    
	else if ( *(rx_pntr+4) == 1 ) { // 2nd sig byte

		*(tx_pntr++) = SIG2;								
	} 
	else if ( *(rx_pntr+4) == 2 ) { // 3rd sig byte

		*(tx_pntr++) = SIG3;								
	} 
	else {
		*(tx_pntr++) = 0x00;   // error 	

	}
    *(tx_pntr++) = STATUS_CMD_OK;	
	
}  
 
/*----------------------------------------------------------------------------*/ 
/* execute command  CMD_SET_PARAMETER                                         */
/*----------------------------------------------------------------------------*/ 
 
void cmdSetParameter(void)  {


	msg_size = 2;  			// set message length		

	*(tx_pntr++) = CMD_SET_PARAMETER;
	*(tx_pntr++) = STATUS_CMD_OK; 
} 

/*----------------------------------------------------------------------------*/ 
/* execute command  CMD_ENTER_PROGMODE_ISP                                    */
/*----------------------------------------------------------------------------*/ 
 
void cmdEnterProgmodeIsp(void)  {


	msg_size = 2;  			// set message length		

	*(tx_pntr++) = CMD_ENTER_PROGMODE_ISP;
	*(tx_pntr++) = STATUS_CMD_OK; 
} 

/*----------------------------------------------------------------------------*/ 
/* execute command  CMD_LEAVE_PROGMODE_ISP                                    */
/*----------------------------------------------------------------------------*/ 
 
void cmdLeaveProgmodeIsp(void)  {


	msg_size = 2;  			// set message length		

	*(tx_pntr++) = CMD_LEAVE_PROGMODE_ISP;
	*(tx_pntr++) = STATUS_CMD_OK; 
} 
 
/*----------------------------------------------------------------------------*/ 
/* execute command  CMD_CHIP_ERASE_ISP                                        */
/*----------------------------------------------------------------------------*/ 
 
void cmdChipEraseIsp(void)  {

	// clear only first page becuase of timeout in AvrStudio

	boot_page_erase((unsigned long)(0L));  	// clear 1st page

	while(boot_spm_busy());	        				
    boot_rww_enable();  			// enable Read-While-Write section

	msg_size = 2;  			// set message length		

	*(tx_pntr++) = CMD_CHIP_ERASE_ISP;
	*(tx_pntr++) = STATUS_CMD_OK; 
} 
 
/*----------------------------------------------------------------------------*/ 
/* execute command  CMD_LOAD_ADDRESS                                          */
/*----------------------------------------------------------------------------*/ 
 
void cmdLoadAddress(void)  {

    address_eeprom = ((*(rx_pntr+3)*256)+*(rx_pntr+4));
    address_flash  = ((*(rx_pntr+3)*256)+*(rx_pntr+4))*2;


	msg_size = 2;  			// set message length		

	*(tx_pntr++) = CMD_LOAD_ADDRESS;
	*(tx_pntr++) = STATUS_CMD_OK; 
}  
 
/*----------------------------------------------------------------------------*/ 
/* execute command  CMD_GET_PARAMETER                                         */
/*----------------------------------------------------------------------------*/ 
 
void cmdGetParameter(void)  {


	msg_size = 3;  			// set message length		

	*(tx_pntr++) = CMD_GET_PARAMETER;
	*(tx_pntr++) = STATUS_CMD_OK;

	switch( *(rx_pntr+1) )  {
		case PARAM_HW_VER: 
            *(tx_pntr++) = HARDWARE_VERSION;  // send hardware version          				

			break;
		case PARAM_SW_MAJOR: 
            *(tx_pntr++) = SOFTWARE_MAJOR; // send software major version         				

			break;        			
		case PARAM_SW_MINOR: 
            *(tx_pntr++) = SOFTWARE_MINOR;  // send software minor version          				

			break;			
		case PARAM_VTARGET: 
            *(tx_pntr++) = VTARGET; // target supply voltage         				

			break;        			
		case PARAM_VADJUST: 
            *(tx_pntr++) = VADJUST; // target VREF voltage          				

			break;  
		case PARAM_OSC_PSCALE: 
            *(tx_pntr++) = PSCALE_FSYS; // oscilator prescaler value         				

			break;
		case PARAM_OSC_CMATCH: 
            *(tx_pntr++) = CMATCH_DEF; // oscilator compare value         				

			break;			
		case PARAM_SCK_DURATION: 
            *(tx_pntr++) = SCK_DURATION_DEF; // oscilator compare value         				

        			break;        			       			        			        			
#if defined(__AVR_ATmega128__) || defined(__AVR_AT90CAN128__) //|| defined(__AVR_ATmega2560__)

		case PARAM_TOPCARD_DETECT: 
            *(tx_pntr++) =  STK501; // STK501 is expected          				

			break;		
#endif        			
		default: 	
            *(tx_pntr++) = 0x00; // send dummy value for not supported parameters   

        break;      			
	}			
} 

/*----------------------------------------------------------------------------*/ 
/* execute command  CMD_PROGRAM_EEPROM_ISP                                     */
/*----------------------------------------------------------------------------*/ 

void cmdProgramEepromIsp(void) {
	
	n_bytes = ((*(rx_pntr+1)*256)+*(rx_pntr+2));  	// number of databytes to flash

	rx_pntr += 10;  								// set pointer to flash data

	for( j = 0; j < n_bytes; j++ )  {
	    eeprom_wb(address_eeprom++,*(rx_pntr++));		
	}

	for(i = 0; i < MAX_BUF_SIZE; i++) { 
		rx_buffer[i] = 0xFF;  // clear data buffer

	}
		
	msg_size = 2;  			// set message length		

	*(tx_pntr++) = CMD_PROGRAM_EEPROM_ISP;
	*(tx_pntr++) = STATUS_CMD_OK; 	
	
} // end of programEepromIsp 

 
/*----------------------------------------------------------------------------*/ 
/* execute command  CMD_READ_EEPROM_ISP                                        */
/*----------------------------------------------------------------------------*/ 
 
void cmdReadEepromIsp(void) {

	*(tx_pntr++) = CMD_READ_EEPROM_ISP;
	*(tx_pntr++) = STATUS_CMD_OK;
	
	n_bytes=((*(rx_pntr+1)*256)+*(rx_pntr+2));  // number of databytes to read from flash 

	for(i=0;i < n_bytes; i++) { // fill data buffer with n_bytes

    	*(tx_pntr++) = eeprom_rb( address_eeprom++ );
	}
	msg_size = n_bytes + 3;  // set message length

	*(tx_pntr++) = STATUS_CMD_OK;	
	
}   // end of readEepromIsp 

/*----------------------------------------------------------------------------*/ 
/* set pin direction for bootloader pin and enable pullup                     */
/*----------------------------------------------------------------------------*/
void initPorts(void)  {

#if defined(__AVR_ATmega128__) || defined(__AVR_AT90CAN128__)
  	BL_DDR &= ~(1 << BL0);
  	BL_DDR &= ~(1 << BL1);
  	BL_PORT |= (1 << BL0);
  	BL_PORT |= (1 << BL1);
  	
#else
	BL_DDR &= ~(1 << BL);
	BL_PORT |= (1 << BL);
	//

	RTS0_DDR &= ~(1 << RTS0);
	RTS0_PORT |= (1 << RTS0);
	//

#endif

#ifdef MONITOR

#endif

} 
 
/*----------------------------------------------------------------------------*/ 
/* initialize UART(s) depending on CPU defined                                */
/*----------------------------------------------------------------------------*/ 
void initUart(void)  {

#if defined(__AVR_ATmega128__) || defined(__AVR_AT90CAN128__)
 	if(bootuart0) {
	    UBRR0L = (uint8_t)(F_CPU(BAUD_RATE*16L)-1);
	    UBRR0H = (F_CPU(BAUD_RATE*16L)-1) >> 8;
	    UCSR0A = 0x00;
	    UCSR0C = 0x06;
	    UCSR0B = _BV(TXEN0)|_BV(RXEN0);
	}
	if(bootuart1) {
	    UBRR1L = (uint8_t)(F_CPU(BAUD_RATE*16L)-1);
	    UBRR1H = (F_CPU(BAUD_RATE*16L)-1) >> 8;
	    UCSR1A = 0x00;
	    UCSR1C = 0x06;
	    UCSR1B = _BV(TXEN1)|_BV(RXEN1);
	}
#elif defined __AVR_ATmega163__
	UBRR = (uint8_t)(F_CPU(BAUD_RATE*16L)-1);
	UBRRHI = (F_CPU(BAUD_RATE*16L)-1) >> 8;
	UCSRA = 0x00;
	UCSRB = _BV(TXEN)|_BV(RXEN);	
#elif defined __AVR_ATmega2560__
	if(bootuart0) {
	    UBRR0L = (uint8_t)(F_CPU(BAUD_RATE*16L)-1);
	    UBRR0H = (F_CPU(BAUD_RATE*16L)-1) >> 8;
	    UCSR0A = 0x00;
	    UCSR0C = 0x06;
	    UCSR0B = _BV(TXEN0)|_BV(RXEN0);
	}
	else if(bootuart1) {
	    UBRR1L = (uint8_t)(F_CPU(BAUD_RATE*16L)-1);
	    UBRR1H = (F_CPU(BAUD_RATE*16L)-1) >> 8;
	    UCSR1A = 0x00;
	    UCSR1C = 0x06;
	    UCSR1B = _BV(TXEN1)|_BV(RXEN1);
	}
#else
  /* m8,m16,m32,m169,m8515,m8535 */
	UBRRL = (uint8_t)(F_CPU(BAUD_RATE*16L)-1);
	UBRRH = (F_CPU(BAUD_RATE*16L)-1) >> 8;
	UCSRA = 0x00;
	UCSRC = 0x86;
	UCSRB = _BV(TXEN)|_BV(RXEN);
#endif	

} // end of initUart

 

/*----------------------------------------------------------------------------*/
/* check if flash is programmed already, if not start bootloader anyway       */
/*----------------------------------------------------------------------------*/
void bootCheck(void)  {
	if(pgm_read_byte_near(0x0000) != 0xFF) {

#ifdef __AVR_ATmega128__
	    /* check which UART should be used for booting */
	    if(bit_is_clear(BL_PIN,BL0)) {
	      bootuart0 = 1;
	    }
	    else if(bit_is_clear(BL_PIN,BL1)) {
	      bootuart1 = 1;
	    } else {
	      /* if no UART is being selected, start application */

	      app_start();
	    }
#elif defined __AVR_AT90CAN128__
	    /* check which UART should be used for booting */
	    if(bit_is_clear(BL_PIN,BL0)) {
	      bootuart1 = 1;   
	    }
	    else if(bit_is_clear(BL_PIN,BL1)) {
	      bootuart0 = 1;
	    } else {
	      /* if no UART is being selected, start application */
	      app_start();
	    }
#else
	    /* check if bootloader pin is set low */
	    if(bit_is_set(BL_PIN,BL))
			if (bit_is_set(RTS0_PIN,RTS0)) 
				app_start();
#endif
  }

#ifdef __AVR_ATmega128__
  /* if no UART is being selected, default is RS232 */
  if((bootuart0 == 0) && (bootuart1 == 0)) {
    bootuart0 = 1;
  }
#elif defined __AVR_AT90CAN128__
  /* if no UART is being selected, default is USB */
  if((bootuart0 == 0) && (bootuart1 == 0)) {
    bootuart1 = 1;
  }  
#endif	
} // end of bootCheck 

 
void putch(char ch)
{
#if defined(__AVR_ATmega128__) || defined(__AVR_AT90CAN128__)
  if(bootuart0) {
    while (!(UCSR0A & _BV(UDRE0)));
    	UDR0 = ch;
  }
  else if (bootuart1) {
    while (!(UCSR1A & _BV(UDRE1)));
    	UDR1 = ch;
  }
#elif defined(__AVR_ATmega2560__)
	if (bootuart0)
	{	
     while (!(UCSR0A & _BV(UDRE0)));
   	 UDR0 = ch;
	}
	else if (bootuart1)
	{	
     while (!(UCSR1A & _BV(UDRE1)));
   	 UDR1 = ch;
	}
#else
  /* m8,16,32,169,8515,8535,163 */
  while (!(UCSRA & _BV(UDRE)));
	UDR = ch;
#endif
}  // end of putch


char getch(void)
{
	char tmp=0;
#if defined(__AVR_ATmega128__) || defined(__AVR_AT90CAN128__)
  if(bootuart0) {
    while(!(UCSR0A & _BV(RXC0)));
     tmp = UDR0;
  }
  else if(bootuart1) {
    while(!(UCSR1A & _BV(RXC1)));
     tmp = UDR1;
  }
#elif defined(__AVR_ATmega2560__)
   if(bootuart0) {
    while(!(UCSR0A & _BV(RXC0)));
     tmp = UDR0;
  }
  else if(bootuart1) {
    while(!(UCSR1A & _BV(RXC1)));
     tmp = UDR1;
  }
#else
  /* m8,16,32,169,8515,8535,163 */
  while(!(UCSRA & _BV(RXC)));
   tmp = UDR ;
#endif
  if( echo ) {
     putch( tmp ); 
  }     
  return( tmp );

}  // end of getch


void eeprom_wb(unsigned int uiAddress, unsigned char ucData)
{
/* Wait for completion of previous write */
while(EECR & (1<<EEWE))
;
/* Set up address and data registers */
EEAR = uiAddress;
EEDR = ucData;
/* Write logical one to EEMWE */
EECR |= (1<<EEMWE);
/* Start eeprom write by setting EEWE */
EECR |= (1<<EEWE);
}

unsigned char eeprom_rb(unsigned int uiAddress)
{
/* Wait for completion of previous write */
while(EECR & (1<<EEWE))
;
/* Set up address register */
EEAR = uiAddress;
/* Start eeprom read by writing EERE */
EECR |= (1<<EERE);
/* Return data from data register */
return EEDR;
} 

#ifdef MONITOR

void monitorMain( void )  {
	
    uint8_t *buf ;
    uint8_t convBuf[20];
	uint8_t quit = 0;
	uint32_t address ;
	uint8_t  value;
	uint8_t  cmd;
	uint8_t  lastCmd = 0;
	struct time t;
	struct date d;

	monitorInit();
   	print_P( (uint32_t)monitorWelcome ); 
   	echo = 1;
    while( ! quit ) {
   		putch( MONITOR_PROMPT );    	
    	buf = monitorReadLine();
    	while( isblank( *buf ) ) {
    		buf ++ ;	
    	}	
    	cmd = toupper(*(buf++));
    	switch( cmd ) {
    		case MONITOR_CMD_QUIT:
    			echo = 0;
    			quit = 1;
    			monitor_cnt = 0; 
    			print_P( (uint32_t)monitorQuit );
    			break;
    		case MONITOR_CMD_TIME:	
    		 	buf = getValue( buf, &(t.hour10), 1 );
    		 	if( buf ) {
    		 		buf = getValue( buf, &(t.hour), 1 );
    		 		buf = getValue( buf, &(t.min10), 1 );
    		 		buf = getValue( buf, &(t.min), 1 );
    		 		buf = getValue( buf, &(t.sec10), 1 );
    		 		buf = getValue( buf, &(t.sec), 1 );
    		 		set_clock( 0, &t );	
    		 	}			 			    							
				print( time2str(get_time(&t),convBuf) );
				putch('\n');
    			lastCmd = cmd;
    			break;    			
    		case MONITOR_CMD_DATE:	
    		 	buf = getValue( buf, &(d.year10), 1 );
    		 	if( buf ) {
    		 		buf = getValue( buf, &(d.year), 1 );
    		 		buf = getValue( buf, &(d.month10), 1 );
    		 		buf = getValue( buf, &(d.month), 1 );
    		 		buf = getValue( buf, &(d.day10), 1 );
    		 		buf = getValue( buf, &(d.day), 1 );
    		 		set_clock( &d, 0 );	
    		 	}			 			    							
				print( date2str(get_date(&d),convBuf) );
				putch('\n');
    			lastCmd = cmd;
    			break;    			    			
    		case MONITOR_CMD_FLASH:
    			buf = getValue( buf, &address, 5 );
    			if( buf )  {
    				address = monitorDump( address,MONITOR_DISPLAY_LINES, MONITOR_MEM_FLASH );	
    			}
    			lastCmd = cmd;
    			break;
    		
    		case MONITOR_CMD_RAM:
    			buf = getValue( buf, &address, 4 );
    			if( buf )  {
    				buf = getValue( buf, &value, 2 );
    				if( buf )  {
    					monitorChange( address, value, MONITOR_MEM_RAM );	
    					address = monitorDump( address,1, MONITOR_MEM_RAM );
    				}
    				else {    				
    					address = monitorDump( address,MONITOR_DISPLAY_LINES, MONITOR_MEM_RAM );	
    				}	
    			}
    			lastCmd = cmd;    			
    			break;   		
    			
    		case MONITOR_CMD_EEPROM:
    			buf = getValue( buf, &address, 3 );
    			if( buf )  {
    				buf = getValue( buf, &value, 2 );
    				if( buf )  {
    					monitorChange( address, value, MONITOR_MEM_EEPROM );
    					address = monitorDump( address,1, MONITOR_MEM_EEPROM );	
    				}
    				else {
    					address = monitorDump( address,MONITOR_DISPLAY_LINES, MONITOR_MEM_EEPROM );
    				}		
    			} 
    			lastCmd = cmd;    			
    			break;	
    		case MONITOR_CMD_HELP:
    			print_P( (uint32_t) PSTR("\nF AAAA    - dump flash\n") );
    			print_P( (uint32_t) PSTR("X AAAA XX - dump/modify ram\n") );
    			print_P( (uint32_t) PSTR("E AAAA XX - dump/modify eeprom\n") ); 
    			print_P( (uint32_t) PSTR("T HHMMSS - display/set time\n") ); 
    			print_P( (uint32_t) PSTR("D YYMMDD - display/set date\n") );    			   			  
				print_P( (uint32_t) PSTR("Q         - quit monitor\n") ); 			    			
    			break;    			
    		case '\0':
    		case '\n':
    		case '\r':
    			switch( lastCmd ) {
    				case MONITOR_CMD_TIME:
    					print( time2str(get_time(&t),convBuf) );
    					putch('\n');
    					break;  
    				case MONITOR_CMD_DATE:
    					print( date2str(get_date(&d),convBuf) );
    					putch('\n');
    					break;     			    					   				
 		    		case MONITOR_CMD_FLASH:
    					address = monitorDump( address,MONITOR_DISPLAY_LINES, MONITOR_MEM_FLASH );	
    					break;   
 		    		case MONITOR_CMD_RAM:
    					address = monitorDump( address,MONITOR_DISPLAY_LINES, MONITOR_MEM_RAM );	
    					break; 
 		    		case MONITOR_CMD_EEPROM:
    					address = monitorDump( address,MONITOR_DISPLAY_LINES, MONITOR_MEM_EEPROM );	
    					break;     					    									
    			} 
    			break;
    		
    		default: 
    			print_P( (uint32_t) monitorError );
    			break;
    	}

    }
}

void monitorInit( void )  {
	XMCRB = 0x00 ; // 60kb RAM available	

#if defined(__AVR_ATmega128__) 	
	XDIV  = 0x00; //xtal divider disabled

	XMCRA = 0x00; //ext ram one sector 1100-FFFF

	MCUCR = ( 1 << SRE ); // ext ram enable	

#elif defined(__AVR_AT90CAN128__)
	CLKPR = 0x00; // clock divider disabled 

	XMCRA = ( 1 << SRE ) ; // ext ram enable, one segment	

#endif	
}

uint32_t monitorDump( uint32_t address, uint8_t lineNum, uint8_t memType ) {
	uint8_t charCnt ;
	uint8_t lineCnt ;
	uint8_t memByte ;
	uint8_t bufHex[10];
	uint8_t bufAsc[20];
	
	putch('\n');
	for( lineCnt = 0; lineCnt < lineNum; lineCnt++ ) {
		print( ultoa(address,bufHex,16) );
		putch( ' ');
		for( charCnt = 0; charCnt < 16; charCnt++ ) {
			switch( memType ) {
				case MONITOR_MEM_FLASH: 
					memByte = pgm_read_byte_far( address++ );
					break;
				case MONITOR_MEM_RAM: 
					memByte = *((uint8_t *)address++);
					break;			
				case MONITOR_MEM_EEPROM: 
					memByte = eeprom_rb( address++ );
			}
			if( memByte < 0x10 )  {
				putch('0');	
			}
			print( ultoa(memByte,bufHex,16) );
			putch(' ');
			bufAsc[charCnt] = isprint(memByte) ? memByte : '.';	
		}
		bufAsc[16] = '\0';
		print( bufAsc );
		putch('\n');	
	}
	return address ;
	
}

void monitorChange( uint32_t address, uint8_t value, uint8_t memType ) {
	switch( memType ) {
		case MONITOR_MEM_RAM: 
			*((uint8_t *)address) = value;
			break;			
		case MONITOR_MEM_EEPROM: 
			eeprom_wb( address, value );
			break;		
	}	
}



char  *monitorReadLine( void ) {
	unsigned int cnt;
	char key;
	
	cnt = 0;
	while( ((key = getch()) != '\n') && cnt < ( MONITOR_BUFFER_SIZE - 1) )  {
		monitorBuf[ cnt++ ] = key ; 
	}
	monitorBuf[ cnt ] = '\0';
	return monitorBuf ; 	
}

void print( char *s )  {
   while( *s ) {
   		putch( *s );
   		s++ ;
   }	
}

void print_P( uint32_t address ) {
   uint8_t c;
   while ((c = pgm_read_byte_far(address++))) {
		putch(c);
   }				
}


char *getValue( char *src,  uint32_t *value, uint8_t len )  {
	char buf[10];
	
	*buf = '\0';
	*value = 0;
    while( isblank( *src ) && ( *src != '\0' ) ) {
    	src ++ ;	
    }	
    if ( *src == '\0' || !isxdigit(*src) )  {
    	return 0; 	
    }
    else {
    	while( isxdigit( *src ) && len ) {
    		*value = (*value << 4) | htoi( *src );  
    		len-- ;	
    		src++ ;
    	}  	
 	    return src ;				
    }   
}


uint8_t htoi( uint8_t val ) {
	if( val >= '0' && val <= '9' ) {
		return (val - '0');
	}
	else {
	    return ( toupper( val ) - 'A' + 10 );
	}
}

#endif