problems after bootloading is done...

Go To Last Post
6 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have written a custom bootloader (PC app + firmware) to update my Atmega128. The program seems to write data to the flash fine and the application code seems to be running.
(e.g., it will start flashing the correct LEDs, sending back some data over the USART, etc...)

My problem is that after the bootloader reset the device to addres 0x0000 and the app code starts running, anything involving interrupts behaves irregularly! I have a button connected to INT1, and when the isr for that pin is triggered, it seems to run fine the first time, but never again!

My interrupt driven USART routines also behave strange. Anything stream over 4 bytes seems to cause problems, and data gets replaced with the values "0xA0 0x00 0x00....."

Below I have posted my bootloader code. If anyone can find problems with it please respond ASAP! I am very desperate to solve this problem...

Note: I am using byte BOOTLOAD_FLAG_ADDR of the EEPROM as a flag to tell the bootloader whether or not to enter bootloader mode or run the application. The bootloader is being called upon reset (eg. reset vector fuse is programmed) *and* is also being run from the application (using a "jmp 0x1E000" instruction) . The flag is cleared at the end of the bootload process and is used to determine whether or not a successful bootload occured, or if the bootloader somehow crashed. (Trust me, it needs to be done this way for functionality reasons...)

void run_app()
{
	RAMPZ = 0;
	MCUCR = 0x01;	   	 	// Enable interrupt vector select
	MCUCR = 0x00;	   	 	// Move interrupt vector to flash
	asm("jmp 0x0000");  	// Run application code   
};

void uart_init() 
{
	UCSR0B = 0x00;
	UCSR0A = 0x00;
	UCSR0C = (1 << UCSZ0) | (1 << UCSZ1); /* 8,N,1 */
	
	// initialize USARTs
	UBRR0H = (unsigned char)(_UBRR >> 8);
	UBRR0L = (unsigned char)(_UBRR);
	
	/* enable transmit and recieve */
	UCSR0B = (1 << RXEN) | (1 << TXEN);
};

int uart_putchar(char ch)
{ 	
	// Wait for empty transmit buffer
	while ( !(UCSR0A & (1 << UDRE)) );
	// Start transmittion
	UDR0 = ch; 		
	
	return 0;
};

int uart_getchar(void)
{
	// Wait for incoming data
	while ( !(UCSR0A & (1<<RXC)) );
	
	// Return the data
	return UDR0;
};

int start()
{
	if (bit_is_set(UCSR0A, RXC)) 
	{
		return (UDR0 == BOOT_ACK);
	}
	return 0;	
};

int enter()
{
	unsigned int cfg_block;
	
	// read bootloader flag byte in eeprom
	eeprom_busy_wait(); 
	cfg_block = eeprom_read_byte((uint8_t*)BOOTLOAD_FLAG_ADDR);	// read cfg block from EEPROM
	
	return (cfg_block == 0x00) ? 0 : 1;
};

void clear_flag()
{	
	// clear bootloader flag byte in eeprom
	eeprom_busy_wait();
	eeprom_write_byte((uint8_t*)BOOTLOAD_FLAG_ADDR, 0x00);
};

void delay()
{
	unsigned int i;
	
	for(i = 0; i < 2; i++) 
	{
		_delay_loop_2(0x0FFF);
	}
};

void init()
{
	cli();								// disable interrupts
	
	boot_spm_interrupt_disable();
	
	DDRA = 0xFC;						// ooooooii: RFID_DATA_OUT, M_ERR
	DDRB = 0xFF;						// oooooooo
	DDRC = 0xFF;						// oooooooo
	DDRD = 0xFC;						// ooooooii: INT0, INT1, 
	DDRE = 0xFE;						// oooooooi: RX0
	DDRF = 0xFF;						// oooooooo	
	PORTA 		= (1 << PINA2);		// keep power on
	
	RAMPZ = 0;
};

/**
 * main -- 
 */
int main()
{
	unsigned char data[SPM_PAGESIZE];
	unsigned char local_checksum 	= 0;
	unsigned char remote_checksum 	= 0;
	unsigned int addr 				= 0;
	unsigned char addr_lo			= 0;
	unsigned char addr_hi			= 0;
	unsigned int i 				= 0;
	int done 						= 0;
	int err							= 0;
	
	init();
	 
	if( enter() )
	{
		uart_init(); 
		
		while( !start() ) 
		{
			delay();
			uart_putchar(BOOT_NOTIFY);	// notify host of bootloader
		}
		
		uart_putchar(OK);				// tell host to start sending Addresses
		
		
		done = 0;
		while(!done)
		{
			// get page address
			addr_hi  	= uart_getchar();
			addr_lo 	= uart_getchar();
			
			addr = (addr_hi << 8) | (addr_lo);
			
			if(addr == 0xFFFF)
			{
				done = 1;
			}
			else 
			{
				// init checksum
				local_checksum = 0;
				
				local_checksum ^= addr_hi;
				local_checksum ^= addr_lo;
				
				// get data bytes
				for(i = 0; i < SPM_PAGESIZE; i++)
				{
					data[i] = uart_getchar();
					local_checksum ^= (unsigned int)data[i];
				}
				
				remote_checksum = uart_getchar();
				
				if(local_checksum == remote_checksum) 
				{
				
					boot_page_erase(addr);						// erase page
					while(boot_rww_busy())
					{
						boot_rww_enable();
					}
	
					for(i = 0; i < SPM_PAGESIZE; i += 2)
					{
						boot_page_fill((addr + i), (data[i]) + (data[i+1] << 8) );
					}
					
					// Write page.
					boot_page_write(addr);
					while(boot_rww_busy())
					{
						boot_rww_enable();
					}
					
					// verify data
					err = 0;
					for(i = 0; (i < SPM_PAGESIZE) && !err; i++)
					{
						if( data[i] != pgm_read_byte((addr + i)) )
						{
							err = 1;
						}
					}
					
					if(err)
					{
						uart_putchar(VERIFY_FAILED);
					}
					else
					{
						uart_putchar(OK);
					}
				}
				else
				{
					uart_putchar(CHECKSUM_FAILED);
					err = 1;
				}
					
			}
		}

	}
	
	if(!err)
	{
		clear_flag();
	}
	
	run_app();
	
	return 1;

}

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
void run_app()
{
   RAMPZ = 0;
   MCUCR = 0x01;             // Enable interrupt vector select
   MCUCR = 0x00;             // Move interrupt vector to flash
   asm("jmp 0x0000");     // Run application code   
}; 

Is this OK? You are writing to MCUCR 0x01, and then overwrite the value with 0x00 . (Don't remembre the function of MCUCR, and don't have the datasheet at hand )

What boot_spm_interrupt_disable() do ?

Regards,
Alejandro.
http://www.ocam.cl

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would use the watchdog to jump to the application. This ensures that the MCU is as it would be after a reset. But to do this you need a check at the start of your bootloader to decide starting application or bootloader before you are doing some settings.

Volkmar

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't know how to tie this to your symptom, but it looks as though you
start the application with an EEPROM write in progress. Does it change
anything if you insert an eeprom_busy_wait() call into run_app()?

Personally (being a superstitious sort) I'd be inclined to clear everything-
in-sight (DDRx, UCSR0x) back to its reset value in case the application
cares.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

BTW: do You have CKOPT and BODEN fuses enabled? Are you restoring all the registers from the stack before using them inside your asm routine?

Every man dies, but not every man really lives.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sheldona,
Please do NOT crosspost! I just answered your duplicate post in the AVR GCC forum (where this post should be due to the GCC code in the example).