Atmega328 first time boot-loader Programming

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

Hi guys,

So I am trying to write a bootloader function so I can write to flash (Program Memory) as I recieve data via UART(though I haven't even gotten to that yet).

 

At the moment all I am trying and failing to do is write some arbitrary data to a fixed address and then read it back.

 

I am using Atmel Studio 6.2, default install. Also I am using the default Makefile it gives me.

 

I have been searching around and reading this I can't seem to understand exactly what I have done wrong :( 

 

Please help :)

 

#define F_CPU 8000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/boot.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>

//bit manipulation definitions
#define SB(x,n) (x|=(1<<n))
#define CB(x,n) (x&=~(1<<n))
#define RB(x,n) (x&(1<<n))
#define TB(x,n) (x^=(1<<n))
#define PB(x,n) do{SB(x,n);CB(x,n);}while(0)
	
//prototypes
void boot_program_page (uint32_t page, uint8_t *buf) BOOTLOADER_SECTION;

volatile uint8_t revieveBuffer[128];


int main(void)
{	
		CLKPR = (1 << CLKPCE); // Enable change of CLKPS bits
		CLKPR = 0x00; //no prescaler
		
		//Port C[3,2,1,0] as out put
		DDRC|=0x0F;
		for(int i=0; i < 128; i++){
			revieveBuffer[i] = i;
		}
		
		//write the data recieved to program memory
		boot_program_page((0x2000), revieveBuffer);
		//SB(PORTC, 3);
		uint8_t byte;
		for(int j=0; j < 128; j++){
			byte = pgm_read_byte(0x2000 + j);
			if(byte == j){
				SB(PORTC, 2);//toggle pin turning LED on
				}else{
				//CB(PORTC, 2);
				SB(PORTC, 3);//toggle pin turning LED on
			}
		}
		
		
    while(1)
    {
        _delay_ms(50);
    }
}

/*image start page should always be 0x2000 giving:
 * 0x0000 -> 0x1FFF Application memory (16KB)
 * 0x2000 -> 0x3680 Image Data (11520 Bytes)
 * 0x3681 -> 0x37FF Free space
 * 0x3800 -> 0x4000 BootLoader (4KB)
*/
void boot_program_page(uint32_t page, uint8_t *buf){
	uint16_t i;
	uint8_t sreg;
	
	//disable interupts
	sreg = SREG;
	cli();
	
	eeprom_busy_wait ();//ensure eeprom is not busy as SPM cannot operate while an eeprom operation is taking place.
	boot_page_erase (page);
	boot_spm_busy_wait (); // Wait until the memory is erased.
	
	for (i=0; i<SPM_PAGESIZE; i+=2)
	{
		// Set up little-endian word.
		uint16_t w = *buf++;
		w += (*buf++) << 8;
		boot_page_fill (page + i, w);
	}
	boot_page_write (page); // Store buffer in flash page.
	boot_spm_busy_wait(); // Wait until the memory is written.
	// Reenable RWW-section again. We need this if we want to jump back
	// to the application after bootloading.
	boot_rww_enable ();
	// Re-enable interrupts (if they were ever enabled).
	SREG = sreg;
}

 

This topic has a solution.

Last Edited: Thu. Oct 23, 2014 - 01:59 PM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Also I am using the default Makefile it gives me.

Not completely "default" I hope?

 

You aren't going to get very far without a -Ttext=0xnnnn are you? (aka -Wl,-section-start=.text=0xnnnn)

 

Assuming you are doing something to arrange for that linker directive how are you specifying the 0x3800 base address? If you specify it direct it needs to be 0x7000 (because GCC uses byte addressing) though if you use Studios "Memories" then you do specify 0x3800 because Atmel double it to 0x7000 when they pass it to the GCC tools.

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

When writing my first bootloader, I found it helpful to look at the source for other bootloaders first.

I suggest you start with optiboot:

https://code.google.com/p/optibo...

 

I have no special talents.  I am only passionately curious. - Albert Einstein

 

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

Ahh Ok thanks guys it was me not setting up the linker options properly as Clawson said.

 

Adding:

-Wl,-section-start=.text=0x7000  

to Project -> Properties -> Toolchain -> AVR/GNU Linker -> Miscellaneous 

did the trick.

 

Thanks also for the point about it being byte address not word address.

 

Hope this helps someone else as well!

 

Cheers

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

Umm ok so I am getting a weird side-effect of adding -Wl,-section-start=.text=0x7000 to the linker...

 

It will now only once fire the ISR(USART_RX_vect){} interrupt.

 

I have commented out all code  to do with the bootloader to the point all the interrupt does is toggle an LED.

 

If I remove -Wl,-section-start=.text=0x7000 the interupt goes of everytime i hit a key in putty when the compile flag is set it only goes off once? 

 

From reading the data sheet I think it is something to do with:

Notes: 1. When the BOOTRST Fuse is programmed, the device will jump to the Boot Loader address at reset, see ”Boot Loader Support
– Read-While-Write Self-Programming, ATmega88PA, ATmega168PA and ATmega328P” on page 277.
2. When the IVSEL bit in MCUCR is set, Interrupt Vectors will be moved to the start of the Boot Flash Section. The address of
each Interrupt Vector will then be the address in this table added to the start address of the Boot Flash Section.

 

So the vector should now be 0x7000 + 0x024(RX complete interupt)... But I don't understand how to refelct that in my program.

 

ISR(0x7024) gives a compile error.

 

So I solved this was super easy in the end just combing through the data sheet Doh.

 

Just run :

void Move_interrupts(void)
{
/* Enable change of Interrupt Vectors */
MCUCR = (1<<IVCE);
/* Move interrupts to Boot Flash section */
MCUCR = (1<<IVSEL);
}

Last Edited: Fri. Oct 24, 2014 - 09:47 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It will now only once fire the ISR(USART_RX_vect){} interrupt.

Interrupts? In a bootloader??

 

Oh well, if you really insist then make yourself familiar with "IVSEL" in the datasheet.

 

(but seriously - don't use interrupts in a bootloader!!)

 

EDIT: You have edited while I was replying! In which case you already know the solution - it IS to use IVSEL. The bootloader must set this bit in MCUCR before it attempts to use ISR()s so that the AVR will vector to the right place (not near 0x0000)

Last Edited: Fri. Oct 24, 2014 - 09:47 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yeah im not actually using interrupts from within the bootloader code I have written :)

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

Then why do you think you have a problem?!?

 

If the bootloader program doesn't SEI or use ISR() then obviously there's no need for it to use IVSEL.

 

Are you saying that your bootloader rewrites the app and that app then tries to use interrupts? That should just work shouldn't it?

 

(please don't say you are combining both app and bootloader in ONE program - clearly that won't work!)

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

1. Most people use a bootloader from RESET to upload a new application or run the existing app.    e.g. Arduino

 

2. Some use a small Boot section so that they can update part(s) of a running application from within the app.     (IAP)

 

3. Some use a small Boot section so that they can erase/write data to an area of Flash with SPM.    e.g. like a big flash EEPROM.

 

I suspect that the OP is attempting (3).     (1) and (3) are relatively straightforward.    (2) requires very careful design and management.

 

If you are doing (1),    just stick to one of the many proven bootloaders.

 

David.

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

I am attempting 3 which I though was clear from my initial post :S

 

I am probably misusing the term Bootloader which has confused things.

 

But it all seems to be working fine :/

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

Yes,   I thought you were intending (3).    

 

Note that your Bootloader seems a little 'greedy'.    You only need a SPM, RET in the bootloader area.     The rest of your code can live anywhere.  e.g. you could save your image data from 0x2000-0x3EFF.    With the 0x3F00-3FFF area protected by lockbits.   

 

You can afford to put more than the SPM, RET into the 256word protected Boot area.

 

David.

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

 

One may wonder... why write yet another bootloader?

 

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

Sooo I have found that using -Wl,-section-start=.text=0x7000 is putting my whole program in the bootloader section hence why I needed to change my ISR locations... this is not ideal. I really just want the function from my code :

void boot_program_page(uint32_t page, uint8_t *buf){

to be in the bootloader section so it is able to write my image file to program memory then return to the normal program. How do I tell the compiler/ linker to do this?

 

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

You just need to assign a specific section to the function(s).    then ask the linker to place that specific section at 0x7000 or wherever.

The rest of your app lives at the regular 0x0000.

 

David.

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

OK how do I assign a specific section to the function? i though i had by doing :

void boot_program_page (uint32_t page, volatile uint8_t *buf) BOOTLOADER_SECTION;

/*image start page should always be 0x2000 giving:
 * 0x0000 -> 0x1FFF Application memory (16KB)
 * 0x2000 -> 0x3680 Image Data (11520 Bytes)
 * 0x3681 -> 0x37FF Free space
 * 0x3800 -> 0x4000 BootLoader (4KB)
 *
 * NOTE page is a byte address the above example uses word addressing.
 * Requires compiler flag: -Wl,-section-start=.text=0x7000
*/
void boot_program_page(uint32_t page, volatile uint8_t *buf){
	uint16_t i;
	uint8_t sreg;
	
	//disable interupts
	sreg = SREG;
	cli();
	
	eeprom_busy_wait ();//ensure eeprom is not busy as SPM cannot operate while an eeprom operation is taking place.
	boot_page_erase (page);
	boot_spm_busy_wait (); // Wait until the memory is erased.
	
	for (i=0; i<SPM_PAGESIZE; i+=2)
	{
		// Set up little-endian word.
		uint16_t w = *buf++;
		w += (*buf++) << 8;
		boot_page_fill (page + i, w);
	}
	boot_page_write (page); // Store buffer in flash page.
	boot_spm_busy_wait(); // Wait until the memory is written.
	// Reenable RWW-section again. We need this if we want to jump back
	// to the application after bootloading.
	boot_rww_enable ();
	// Re-enable interrupts (if they were ever enabled).
	SREG = sreg;
	//sei();///Maybe remove from here
}
-Wl,-section-start=.text=0x7000 is what I am currently giving the linker, I am assuming this is not actually what I want

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

Look at http://www.nongnu.org/avr-libc/user-manual/mem_sections.html

 

I just Googled "avr-gcc section attribute" and followed the first hit.

 

You definitely should NOT choose ".text" as the name of your special section.

I would suggest a name like ".boot" or ".SPMsection"

 

David.

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

I would suggest a name like ".boot" or ".SPMsection"

I wouldn't! See line 138...

 

http://svn.savannah.nongnu.org/v...

 

So I would use BOOTLOADER_SECTION and in the linker set the address of .bootloader (even though this is a misnomer and it's not for a "bootloader" but for SPM application code!). So just use:

#include <avr/boot.h>

void BOOTLOADER_SECTION mySPM(uint16_t addr, uint8_t * data) {
  // etc.
}

then:

-Wl,-section-start=.bootloader=0x7000