Super simple bootloader?

Short Version: Does anyone have, or know of, a super simple AVR bootloader (i.e. C source code and explanation/tutorial)?

Long Version: I’m currently using an ATMega168 as the brains of a robotic helicopter (work in progress), which has a wireless serial radio connecting it’s UART pins to a ground computer. The helicopter is connected to a 5 degree of freedom safety stand for testing and development, and I have things like control loop gains as well as telemetry sent over the wireless serial link.

The problem is that every time I want to tweak the actual microcontroller program during an experiment (i.e. change the structure of a control loop, not just its gains) I have to walk over from the computer to the test stand, switch off the motor controller board (for safety), plug the programmer into an unfortunately cumbersomely located ISP header (my fault), walk back to the computer, download the program, walk back to the helicopter, unplug the header (also somewhat cumbersome), switch the motor controller back on, walk back over to the computer, send a command to ramp the throttle, walk back over to the helicopter, press the ARM button (only works after ramping throttle), and finally walk back over to the computer and resume my experiment. I got a wireless keyboard which makes things a little better, but this still eats up a lot of my testing time.

My dream is to turn the throttle all the way down from the ground computer, tweak and compile the AVR code, send the new hex file over the wireless radio, turn up the throttle back up, and continue the experiment all without getting up from my seat.

I’ve been looking into this just a little, and it seems like the super-basic and application specific boot loader I would need could be written in like a page of code. It also seems like figuring out how to do it will end up taking (me) way longer than all my back and forth walking and plugging and unplugging. Still, I really like the idea.

Getting the helicopter quasi-flying is far more important to me right now than neat features like wireless microcontroller reprogramming, but if anyone has some source code or can point me in a good direction that would get me there more quickly, it would totally be worth it (plus having more neat features is always good!). I know the ATMega168 on the X2 must have a UART-based bootloader of some sort, but I don’t know if it would help in this case.

Thanks for any input.

-Adam

Hi, Adam.

This is a simplified version of a bootloader I’ve used before. I haven’t tested this version, but I’m confident that the core of the bootloader works. You would send a file to the bootloader as ascii text with line delays of maybe 50ms. This code is programmed to absorb a single line-feed character. If your terminal program transmits both a line-feed and a carriage-return (or neither a LF or CR), it will cause the bootloader to fail.

- Ben

bootloader.c:

#include <avr/boot.h>


/* Select Boot Size (select one, comment out the others) */
//#define _B128 
//#define _B256 
#define _B512 			// boot size is 512 words (1024 bytes)
//#define _B1024
#include "chipdef.h"


#define SERIAL_BAUD_RATE  10	// 115200 @ 20MHz: CLOCK/BAUD/16 - 1

#define BL_VERSION_MAJOR	'1'
#define BL_VERSION_MINOR	'0'


unsigned char checksum;


//_____________________________________________________________________________
//
// Serial
//_____________________________________________________________________________

#define	serialReceiveReady()		(UCSR0A & (1<<RXC0))
#define serialDataRegisterReady()	(UCSR0A & (1<<UDRE0))
#define	serialReceiveNow()			(UDR0)
#define	serialTransmitMacro(byte) 	while (!serialDataRegisterReady());	\
									UDR0 = byte


void serialTransmit(unsigned char byte) 
{
	while (!serialDataRegisterReady());	// Wait for empty transmit buffer
	UDR0 = byte;	// send byte
}


unsigned char serialReceive(void)
{
	while (!serialReceiveReady())
		;
	return UDR0;							// Get byte
}

unsigned char serialReceiveHexNibble(void)
{
	unsigned char in = serialReceive();
	if (in >= 'A')
		in -= 'A' - 10;
	else
		in -= '0';
	return in;
}

unsigned char bootloaderGetData(void)
{
	unsigned char in;

	in = serialReceiveHexNibble() << 4;
	in |= serialReceiveHexNibble();
	checksum += in;
	return in;
}


//_____________________________________________________________________________


void (*jumpToApp)(void) = 0x0000;


int main(void)
{	
	// Serial Init 
	UBRR0 = (unsigned int)SERIAL_BAUD_RATE;	// Set baud rate
	UCSR0C = (3 << UCSZ00);				// 1 stop bit, 8-bit characters
	UCSR0B = _BV(RXEN0) | _BV(TXEN0);	// enable UART receiver and transmitter


	// *** Do something here to decide whether to remain in the bootloader
	//      to leave the bootloader, call jumpToApp()


	top:				// jump back here if there's an error receiving upload

	// *** Require some sort of handshake before entering main bootloader code


	while (1)
	{   
		serialTransmit(':'); // Notify that we are in the bootloader code

		unsigned char in = serialReceive();

		if (in == 'u')
		{
			unsigned char memory[SPM_PAGESIZE]; // create temporary page
			unsigned int address;
			unsigned char count, type, i;
			unsigned int mem_data;

			type = 0;						
			address = 0; 					// erase application portion
			while (address < APP_END)
			{
				boot_page_erase(address);	// perform page erase
				boot_spm_busy_wait();		// wait until the memory is erased.
				address += SPM_PAGESIZE;
			}

			// echo capital letter back once application portion is erased
			serialTransmit('U');		

			do
			{
				if (serialReceive() != ':')	// get the packet start
				{
					serialTransmit( 'e' );		// start byte error
					goto top;
				}
	
				checksum = 0;			
				count = bootloaderGetData();	// get the packet size
				address = bootloaderGetData();	// get the memory address
				address <<= 8;
				address |= bootloaderGetData();
				type = bootloaderGetData();		// get the packet type

				for (i = 0; i < count; i++)		// get the data
					memory[i] = bootloaderGetData();

				bootloaderGetData();			// get the checksum
	
				if (checksum)					// check the checksum
				{
					serialTransmit( 'c' );		// checksum error
					goto top;
				}
					
				serialReceive();				// get the LF
	
				if (type == 0) 
				{
					while (count < SPM_PAGESIZE)	// fill rest of buffer
						memory[count++] = 0xFF;
		
					for(i = 0; i < SPM_PAGESIZE;)	// store in temp buffer
					{
						mem_data = memory[i++];
						mem_data |= (memory[i++] << 8);
						boot_page_fill(address, mem_data); 
						address += 2; 
					}
		
					boot_page_write(address-SPM_PAGESIZE);	// write to flash
					boot_spm_busy_wait();	
					boot_rww_enable();			// we-enable the RWW section
				}
				serialTransmit('.');			// signify valid packet
			} while(type != 1);

			if (type == 1)
			{
				serialTransmit('*');			// signify valid upload
				jumpToApp();					// jump to the start of flash
			}

		}	// end if (in == 'u')
		else if (in == 'x')
		{
			serialTransmit('X');
			jumpToApp();
		}
		else if (in == 'v')
		{
			serialTransmit(BL_VERSION_MAJOR);
			serialTransmit('.');
			serialTransmit(BL_VERSION_MINOR);
		}

    }	// end while(1)
	
	
    return 0;
}

chipdef.h:

#ifndef CHIPDEF_H
#define CHIPDEF_H

#include <avr/io.h>

// TODO: make use of RAMEND in the avr-libc io-files and
// avoid a lot of by-device definitions here

#if defined(__AVR_ATmega169__)
  #define sig_byte3 0x1E
  #define sig_byte2 0x94
  #define sig_byte1 0x05
  
  #define devtype 0x79       // Mega 169 device code
  
//  #define PAGESIZE 128  // 2*64 Words = Size in Bytes
  
  #ifdef _B128
    #define APP_PAGES ((2*8192 / SPM_PAGESIZE)- (2*128 / SPM_PAGESIZE )) 
    #define APP_END APP_PAGES * SPM_PAGESIZE 
  #endif
  #ifdef _B256
    #define APP_PAGES ((2*8192 / SPM_PAGESIZE)- (2*256 / SPM_PAGESIZE )) 
    #define APP_END APP_PAGES * SPM_PAGESIZE 
  #endif
  #ifdef _B512
    #define APP_PAGES ((2*8192 / SPM_PAGESIZE)- (2*512 / SPM_PAGESIZE )) 
    #define APP_END APP_PAGES * SPM_PAGESIZE 
  #endif
  #ifdef _B1024
    #define APP_PAGES ((2*8192 / SPM_PAGESIZE)- (2*1024 / SPM_PAGESIZE )) 
    #define APP_END APP_PAGES * SPM_PAGESIZE 
  #endif
  #ifdef _B2048
    #error "_B2048 not suppoted on this device"
  #endif   

#elif defined(__AVR_ATmega16__)

  #define sig_byte3 0x1E
  #define sig_byte2 0x94
  #define sig_byte1 0x03
  
  #define devtype 0x75       // Mega16 device code
  
//  #define PAGESIZE 128       // Size in Bytes
  
  #ifdef _B128
    #define APP_PAGES ((2*8192 / SPM_PAGESIZE)- (2*128 / SPM_PAGESIZE )) 
    #define APP_END APP_PAGES * SPM_PAGESIZE 
  #endif
  #ifdef _B256
    #define APP_PAGES ((2*8192 / SPM_PAGESIZE)- (2*256 / SPM_PAGESIZE )) 
    #define APP_END APP_PAGES * SPM_PAGESIZE 
  #endif
  #ifdef _B512
    #define APP_PAGES ((2*8192 / SPM_PAGESIZE)- (2*512 / SPM_PAGESIZE )) 
    #define APP_END APP_PAGES * SPM_PAGESIZE 
  #endif
   #ifdef _B1024
    #define APP_PAGES ((2*8192 / SPM_PAGESIZE)- (2*1024 / SPM_PAGESIZE )) 
    #define APP_END APP_PAGES * SPM_PAGESIZE 
  #endif  
  #ifdef _B2048
    #error "_B2048 not suppoted on this device"
  #endif   

#elif defined(__AVR_ATmega168__)

  #define sig_byte3 0x1E
  #define sig_byte2 0x94
  #define sig_byte1 0x03
  
  #define devtype 0x75       // Mega16 device code
  
//  #define PAGESIZE 128       // Size in Bytes
  
  #ifdef _B128
    #define APP_PAGES ((2*8192 / SPM_PAGESIZE)- (2*128 / SPM_PAGESIZE )) 
    #define APP_END APP_PAGES * SPM_PAGESIZE 
  #endif
  #ifdef _B256
    #define APP_PAGES ((2*8192 / SPM_PAGESIZE)- (2*256 / SPM_PAGESIZE )) 
    #define APP_END APP_PAGES * SPM_PAGESIZE 
  #endif
  #ifdef _B512
    #define APP_PAGES ((2*8192 / SPM_PAGESIZE)- (2*512 / SPM_PAGESIZE )) 
    #define APP_END APP_PAGES * SPM_PAGESIZE 
  #endif
   #ifdef _B1024
    #define APP_PAGES ((2*8192 / SPM_PAGESIZE)- (2*1024 / SPM_PAGESIZE )) 
    #define APP_END APP_PAGES * SPM_PAGESIZE 
  #endif  
  #ifdef _B2048
    #error "_B2048 not suppoted on this device"
  #endif   

#elif defined(__AVR_ATmega8__)

  #define sig_byte3 0x1E
  #define sig_byte2 0x93
  #define sig_byte1 0x07
  
  #define devtype 0x77       // Mega8 boot device code
  
//  #define PAGESIZE 64        // Size in Bytes
  
  #ifdef _B128
    #define APP_PAGES ((2*4096 / SPM_PAGESIZE)- (2*128 / SPM_PAGESIZE )) 
    #define APP_END APP_PAGES * SPM_PAGESIZE 
  #endif
  #ifdef _B256
    #define APP_PAGES ((2*4096 / SPM_PAGESIZE)- (2*256 / SPM_PAGESIZE )) 
    #define APP_END APP_PAGES * SPM_PAGESIZE 
  #endif
  #ifdef _B512
    #define APP_PAGES ((2*4096 / SPM_PAGESIZE)- (2*512 / SPM_PAGESIZE )) 
    #define APP_END APP_PAGES * SPM_PAGESIZE 
  #endif
   #ifdef _B1024
    #define APP_PAGES ((2*4096 / SPM_PAGESIZE)- (2*1024 / SPM_PAGESIZE )) 
    #define APP_END APP_PAGES * SPM_PAGESIZE 
  #endif  
  #ifdef _B2048
    #error "_B2048 not suppoted on this device"
  #endif   
#elif defined(__AVR_ATmega162__)

  #define sig_byte3 0x1E
  #define sig_byte2 0x94
  #define sig_byte1 0x04
  
// #define devtype 0x??       // Mega162 boot device code
  
//  #define PAGESIZE 128        // Size in Bytes
  
  #ifdef _B128
    #define APP_PAGES ((2*8192 / SPM_PAGESIZE)- (2*128 / SPM_PAGESIZE )) 
    #define APP_END APP_PAGES * SPM_PAGESIZE 
  #endif
  #ifdef _B256
    #define APP_PAGES ((2*8192 / SPM_PAGESIZE)- (2*256 / SPM_PAGESIZE )) 
    #define APP_END APP_PAGES * SPM_PAGESIZE 
  #endif
  #ifdef _B512
    #define APP_PAGES ((2*8192 / SPM_PAGESIZE)- (2*512 / SPM_PAGESIZE )) 
    #define APP_END APP_PAGES * SPM_PAGESIZE 
  #endif
   #ifdef _B1024
    #define APP_PAGES ((2*8192 / SPM_PAGESIZE)- (2*1024 / SPM_PAGESIZE )) 
    #define APP_END APP_PAGES * SPM_PAGESIZE 
  #endif  
  #ifdef _B2048
    #error "_B2048 not suppoted on this device"
  #endif   

#elif defined(__AVR_ATmega32__)

  #define sig_byte3 0x1E
  #define sig_byte2 0x95
  #define sig_byte1 0x02
  
  #define devtype 0x73       // Mega32 device code
  
//  #define PAGESIZE 128       // Size in Bytes
  
  #ifdef _B128
    #define APP_PAGES ((2*16384UL / SPM_PAGESIZE)- (2*128 / SPM_PAGESIZE )) 
    #define APP_END APP_PAGES * SPM_PAGESIZE 
  #endif
  #ifdef _B256
    #define APP_PAGES ((2*16384UL / SPM_PAGESIZE)- (2*256 / SPM_PAGESIZE )) 
    #define APP_END APP_PAGES * SPM_PAGESIZE 
  #endif
  #ifdef _B512
    #define APP_PAGES ((2*16384UL / SPM_PAGESIZE)- (2*512 / SPM_PAGESIZE )) 
    #define APP_END APP_PAGES * SPM_PAGESIZE 
  #endif
   #ifdef _B1024
    #define APP_PAGES ((2*16384UL / SPM_PAGESIZE)- (2*1024 / SPM_PAGESIZE )) 
    #define APP_END APP_PAGES * SPM_PAGESIZE 
  #endif  
  #ifdef _B2048
    #define APP_PAGES ((2*16384UL / SPM_PAGESIZE)- (2*2048 / SPM_PAGESIZE )) 
    #define APP_END APP_PAGES * SPM_PAGESIZE 
  #endif  

#elif defined(__AVR_ATmega128__)

  #define sig_byte3 0x1E
  #define sig_byte2 0x97
  #define sig_byte1 0x02
  
  #define devtype 0x44      // Mega128 device code
  
//  #define PAGESIZE 128       // Size in Bytes
  
  #ifdef _B512
    #define APP_PAGES ((2*65536UL / SPM_PAGESIZE)- (2*512 / SPM_PAGESIZE )) 
    #define APP_END APP_PAGES * SPM_PAGESIZE 
  #endif
  #ifdef _B1024
    #define APP_PAGES ((2*65536UL / SPM_PAGESIZE)- (2*1024 / SPM_PAGESIZE )) 
    #define APP_END APP_PAGES * SPM_PAGESIZE 
  #endif  
  #ifdef _B2048
    #define APP_PAGES ((2*65536UL / SPM_PAGESIZE)- (2*2048 / SPM_PAGESIZE )) 
    #define APP_END APP_PAGES * SPM_PAGESIZE 
  #endif
  #ifdef _B4096
    #define APP_PAGES ((2*65536UL / SPM_PAGESIZE)- (2*4096 / SPM_PAGESIZE )) 
    #define APP_END APP_PAGES * SPM_PAGESIZE 
  #endif  

#else
	#error "no definition for MCU available in chipdef.h"
#endif

#endif

Woah, that’s exactly what I was hoping for!

Thanks Ben!

That together with this document (AVR109: Self Programming) might just do it. I’m posting the link because I had a really hard time finding it (it doesn’t have the word bootloader in the title, or show up when you search Atmel.com for “bootloader”).

-Adam

Hey Ben, I think I’m really close here, but I’m doing something wrong.

I’ve been messing around with the code you posted, but to track down this problem I’m using the original, as-posted copy. I have it compiled and downloaded to a Baby Orangutan (ATMega168), and it responds properly when I send it a hex file form a terminal program (sends back ‘.’ with each page, and ‘*’ when it’s all done) but for some reason it isn’t actually writing anything to flash. When I read back the flash contents all I get back is the original hex file (plus a lot of empty FF bytes).

I’ve tried a lot of fuse combinations, but right now the boot-related fuses I have set are:

-Boot Flash Section Size=512 words
-Application Protection Mode 1: No Lock
-Boot Loader Protection Mode 1: No Lock

I suspect I’m doing something fundamentally wrong, because the Baby-O seems to always startup running the bootloader code regardless of the Boot Reset Vector Enable fuse. The spec sheet was a little confusing as to which way was which, but my understanding is that with the fuse set one way, the AVR will start up running bootloader section code, but with the fuse set the other way the AVR will start running code from the application section, from where the bootloader can be called.

Anyway, that’s where I stand now, any thoughts?

Thanks,

-Adam

Edit: read my next post first, I just had a new idea that makes much more sense.

Hmm, that’s really odd. I’ve had plenty of problems with the hex-file-reading portion of the bootloader, but I’ve never encountered I problem with the flash-writing portion.

I’m willing to bet the reason you always start up running the bootloader code is because your application section is empty. I’ve found that the AVR just reads through blank flash until it hits an instruction, and if all you have on there is the bootloader, that’s the first instruction it will hit. Once you successfully use the bootloader to write to the application section, you’ll notice if the reset vector is enabled or not. If you want to start up running from the bootloader, you should set the BOOTRST bit of the extended fuse byte to zero (or, if you’re using AVR Studio, check the “Boot Reset Enabled” option).

Your fuse and lock-bit settings all look right, which leads me to think there are three possibilities:

  1. The bytes you’re sending are bad
  2. The bytes you’re sending are being interpretted wrong
  3. The flash-writing code is bad

I think the first step I’d take would be to try serially echoing the values of SPM_PAGESIZE, count, address, and mem_data. If the mem_data array is full of all 0xFF bytes, at least you know to look more closely at how it’s being filled.

Another test you could do is hardcoding a write to the application section. In that way you could verify that the flash-writing routines from boot.h are working right. I’m thinking something simple, like writing 0x01, 0x02, 0x03 to address 0 and then reading the flash.

Let me know what you find out.

- Ben

I neglected to mention one key thing when I posted the code in this thread: you need to explicitly instruct AVR Studio to write it to the bootloader section of the flash. Otherwise, it’ll just get written to the application section of the flash, and I’m pretty sure that it wouldn’t be able to write the hex file over itself.

If you make the bootloader into an AVR Studio project, go to the Memory Settings of its Configuration Options. The only line I have is:

Flash .text 0x1e00

The 0x1e00 is the flash address (in words, not bytes) to which it will start writing the program , and this just so happens to be the start of a 512-word bootloader.

When you program the bootloader and read back the flash, you should see that the first used address in the hex file is 0x3c00.

Now, if you were already doing all of this, I’d suggest trying what I wrote in my previous post; otherwise, maybe this would explain everything.

- Ben

Thanks, that was definitely my fundamental mistake (not pointing the compiler to the bootloader section). Somehow I had managed to convince myself that the bootloader section was at the beginning of the flash memory, when it makes so much more sense to put it at the end!

I just successfully boot-loaded a short program by copy pastinglines of hex into bray terminal. When I try to send it as a file I getchecksum errors, not sure why, but it’s a problem with the terminalprogram at this point.

Thanks again!

-Adam

That’s great to hear! Now you just need to make sure that the program you’re trying to bootload is 15,360 bytes or less.

There are two possible problems you could be having with the terminal program:

  1. You need to use line delays that give the bootloader enough time to write each line of flash. Something like 50ms should suffice. You shouldn’t need character delays.

  2. Check to see how it’s handling the line feeds. You may have to absorb one more or one fewer byte at the end of each line, depending on whether you’re getting a LF, a LF+CR, or neither. I think the program I posted is configured to just absorb a single LF at the end of each line. Or maybe you can configure your terminal program to send just a single LF at the end of each line. I’m pretty sure if you send the file as binary it’ll be transmitting both the LF and CR.

- Ben

It was totally a line delay problem. Actually I just added a routine to my flight control software that sends out a line and waits for the ‘.’ back.

I would like to officially set the “Wireless Programming of a Baby Orangutan” record at ~10 meters through two walls!

-Adam

P.S. I’m also well on my way to being the first user to destroy a Baby Orangutan’s MCU with too many flash write cycles!

Woo, that is so cool!

I’ve been using Ben’s bootloader to flash my helicopter’s Baby-O multiple times daily for a little over a month now, and it works great, but here’s something confusing: When I use an event in my code to directly ‘goto’ to the bootloader, everything works peachy:

if(event){
	goto *0x1E00;//jump to bootloader
}

But, if I have the event call a function, even when all that function contains is the exact same goto line, nothing happens:

void jumpToBoot(){
	goto *0x1E00;//jump to bootloader
}

if(event){
	jumpToBoot();
}

I know the event occurs, but the program doesn’t jump to the bootloader, or hang up for that matter, it just keeps executing as if the jumpToBoot() function wasn’t called. Then I have to get out my AVRISP.

This isn’t a big problem since I can just call the goto command directly, I’m just confused as to why the two scenarios are any different! Anyone have any thoughts on the matter?

-Adam

That sounds very strange, but there’s an easy way for you to investigate further: look at the list file that’s generated when you compile. Hopefully the assembly will make clear what the difference is between the two scenarios.

- Ben

Like most unpredictable microcontroller behavior, the problem was interrupt related. I was thinking of the bootloader as this totally self-contained thing, when of course all the hardware configurations are unchanged by the jump from the application section code to the bootloader section code. It just so happened that the extra little bit of time taken by the function call changed the code dynamics (what an odd concept!) around so that some interrupt or other (I’m using just about all of them!) was taking over before the bootloader was done reconfiguring the hardware to its liking, then dumping back into the main program. I added a global interrupt disable line to the beginning of my jump function (much better practice!) and everything is working great now.

This partial hardware reconfiguration may explain why my helicopter freaked out the other day! Good thing it’s still on the safety test stand (goggles people!).

So it’s time for another question then. I loooove the simplicity of this bootloader (i.e. directly sending the ASCII hex file), and I’m totally happy to twiddle my thumbs while it does its thing, but it seems kind of slow compared to say, updating the firmware on the X2 (to compare speeds I’m assuming that the X2 bootloader returns a period for every page of data received as well, right?). I realize that the X2 firmware file is transferred in binary, but it still seems blazingly fast. In my mind, using two ASCII characters for each eight-bit bite should only double the time the transfer takes, plus maybe an extra teensey bit to convert the data on the microcontroller side. Am I missing something?

-Adam

P.S. By the way, Ben, I thought you might like to know that I’ve been e-mailing back and forth with a guy from the Robot Association (club?) of Chalmers University in Sweden. Apparently they too were looking for a good AVR bootloader, and were as disappointed as I was with the unnecessary complexity and/or secrecy of the available offerings, until they came across this thread! Now they’re going to use a bootloader based on yours as part of their beginners kit. You’re an international programming superstar! (Hey, two nations counts!)

Ah, that makes perfect sense. Usually I only enter the bootloader right after a reset, so I never have to worry about having rogue interrupts enabled.

In my recent experience using the bootloader on the TReX it didn’t seem like the binary transfer was that much faster than the ASCII transfer (at least not more than a factor of two). One thing you can do is play around with the line delays. It’s possible you can get away with shortening them appreciably, which could do a lot to speed up the ASCII process. Other than that, the binary transfer is really the same as the ASCII except you can send the same information in half the number of bytes.

Heh, I’m very happy this is helping people all over the world!

- Ben

I’m still loving the bootloader, and I’ll probably incorporate it into other robots in the future. I’ve got another interesting question though, which really pertains more to AVRs and microcontrollers in general than to the bootloader itself.

When I bring my helicopter down to the bench to test it with an oscilloscope, I’m generally making lots of short timing tweaks which I would like to upload quickly, so I end up using the SPI programmer, and accidentally erasing the bootloader. It’s no big deal to reload it later, but I was wondering if there was a better way.

Right now I’m just un-checking the “Erase Device Before Programming” option. Almost all of my programs are self contained (i.e. end in infinite loops), so should I really worry about stray instructions that will never be reached?

Alternatively, I looked briefly at combining the program and the bootloader. Not surprisingly, just appending the two hex files didn’t do the trick, I’m guessing some of the checksum bytes indicate the end of a program as well. Is there a way to set AVRStudio to generate hex files with both the program and the bootloader to be downloaded in one shot?

-Adam

P.S. Two updates. First, I added a true software reset function, using the watchdog, to my bootloader/code transition, so no more weird interrupt/unexpected hardware state problems. It took me a while to figure out how to break out of the infinite reset spiral though!

Also, I’ve reached the huge milestone of four degree of freedom computer-controlled flight of the helicopter. Basically right now it’s sitting on a five degree of freedom boom-arm test stand where it can roll, pitch, and yaw, as well as move vertically and horizontally around the circumference of a circle (the pivot point of the boom-arm). I’m controlling the throttle right now, but all the on-board sensor polling, external telemetry processing, and control math is being done by the ATMega168. This could very well be the first flying baby Orangutan!

I think you should be just fine programming your Baby-O without erasing first; the only time you’ll ever reach unerased stray instructions is if your code is jumping somewhere it shouldn’t.

I wouldn’t be surprised at all if there is a way, but unfortunately I don’t have any expertise to offer here. Maybe someone at avrfreaks could be more helpful here? If you do figure something out, I’d love to hear about it, though.

Manually combining the two hex files seems like it should work, though. Just make sure you remove the last line from your application’s hex file (something like :00000001FF) before you add on the bootloader hex file. I’m just not 100% certain that AVR Studio will gracefully handle a hex file that has a gap in flash addresses. I’ve made my own hex file before by combining two others, but I did it by programming the application, reading it back, and then replacing the correct portion of the read file with the bootloader hex.

What was causing the hardware state problems? Can you tell me a little bit more about your solution?

That’s so cool!

- Ben

Actually with the two files combined and that one line removed from the middle, AVRStudio handles the programming very gracefully, in that it does it correctly and without complaining. That line must just be the hex for ‘nothing more to see here.’ I’ll look more into making AVRStudio generate combined hex files itself, but not erasing the entire chip before I program it is actually a better solution for me. I don’t want to erase the bootloader when I SPI program, but I also don’t want the bootloader included in the hex file I will later try to upload to the bootloader…

The hardware problems were just some unexpected behavior (i.e. bootload failures) before related to the fact that I jumped from my code to the bootloader with lots of clocks still running and lots of interrupts enabled (particularly the USART_RX vector!). For a while my quick fix was to just disable global interrupts before the jump, since your bootloader code doesn’t use any, which worked, but wasn’t as technically correct a solution. Remote true-resetting is a lot cleaner, and lets me run things like startup sensor calibration routines without actually hitting a button on the helicopter, which is good because the sensors I want to calibrate are accelerometers and gyroscopes!

-Adam

For reference, AVRStudio sometimes complains if a program gets smaller and you upload it without erasing the larger copy on the device first. I think it’s failing a checksum test on the last page, which has some stray unexpected bytes left over. Of course you could get around this by also turning off verification, but that’s getting a little sketchy.

If this becomes more of a nuisance I might look into the boot-loader write protection fuses, but in general programs get bigger anyway!

-Adam

Hmm, well one potential solution could be to use the bootloader to erase the application section before using AVR Studio to program without erasing. You can make the flash-erasing portion of the bootloader a function and use the list file to figure out its flash address. Then you can embed in your application a routine that jumps to that address in response to some input from you.

- Ben

That sounds like a better idea.

Using the bootloader write protection fuses doesn’t help, since the “erase device” command is the one thing that will reset those fuses…then erase the bootloader!

-Adam