I am trying to write a bootloader for an atmega328 (datasheet here) but am having difficulty writing a simple program from boot. Here is my setup.
In prog.c, I am setting PB1 high:
#include <avr/io.h> int main() { DDRB = (1 << DDB1); PORTB = (1 << PORTB1); while (1); }
This is then compiled as follows:
avr-gcc -g -Wall -Os -mmcu=atmega328p -c prog.c avr-gcc -g -Wall -Os -mmcu=atmega328p -o prog.elf prog.o avr-objcopy -j .text -j .data -O ihex prog.elf prog.hex
which creates a hex file here:
:100000000C9434000C943E000C943E000C943E0082 :100010000C943E000C943E000C943E000C943E0068 :100020000C943E000C943E000C943E000C943E0058 :100030000C943E000C943E000C943E000C943E0048 :100040000C943E000C943E000C943E000C943E0038 :100050000C943E000C943E000C943E000C943E0028 :100060000C943E000C943E0011241FBECFEFD8E04C :10007000DEBFCDBF0E9440000C9444000C940000F1 :0C00800082E084B985B9FFCFF894FFCF6F :00000001FF
My bootloader will eventually get data from an external source, but to simplify things I am just putting the above hex into ram. When I compile the below program, I expect to see PB2 go high for 1 second followed by PB1. Instead, PB2 just stays high.
#include <avr/io.h> #define F_CPU 1000000UL #include <util/delay.h> #include <avr/boot.h> #define PAGE_SIZE_BYTES 128 #define FLASH_APP_START_ADDR 0x0000 int main() { //saving result of prog.hex in ram //prog.hex is a program that sets PB1 high and loops forever //extra 0xFF's have been added to make sure the below fills 2 full pages uint8_t prog[] = { 0x0C,0x94,0x34,0x00,0x0C,0x94,0x3E,0x00,0x0C,0x94,0x3E,0x00,0x0C,0x94,0x3E,0x00, 0x0C,0x94,0x3E,0x00,0x0C,0x94,0x3E,0x00,0x0C,0x94,0x3E,0x00,0x0C,0x94,0x3E,0x00, 0x0C,0x94,0x3E,0x00,0x0C,0x94,0x3E,0x00,0x0C,0x94,0x3E,0x00,0x0C,0x94,0x3E,0x00, 0x0C,0x94,0x3E,0x00,0x0C,0x94,0x3E,0x00,0x0C,0x94,0x3E,0x00,0x0C,0x94,0x3E,0x00, 0x0C,0x94,0x3E,0x00,0x0C,0x94,0x3E,0x00,0x0C,0x94,0x3E,0x00,0x0C,0x94,0x3E,0x00, 0x0C,0x94,0x3E,0x00,0x0C,0x94,0x3E,0x00,0x0C,0x94,0x3E,0x00,0x0C,0x94,0x3E,0x00, 0x0C,0x94,0x3E,0x00,0x0C,0x94,0x3E,0x00,0x11,0x24,0x1F,0xBE,0xCF,0xEF,0xD8,0xE0, 0xDE,0xBF,0xCD,0xBF,0x0E,0x94,0x40,0x00,0x0C,0x94,0x44,0x00,0x0C,0x94,0x00,0x00, 0x82,0xE0,0x84,0xB9,0x85,0xB9,0xFF,0xCF,0xF8,0x94,0xFF,0xCF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, }; //setting PB2 high to indicate it is still in boot.c DDRB = (1 << DDB2); PORTB = (1 << PORTB2); _delay_ms(1000); //////////////////////////////////////////////////////////// //write prog to flash area //////////////////////////////////////////////////////////// uint8_t* flash_ptr = FLASH_APP_START_ADDR; //address used for page write uint8_t* temp_flash_ptr; //address used for page fill uint8_t* ram_ptr = prog; //address pointing to prog uint8_t i, j; //looping variables //prog is 256kb and a page is 128 bytes so loop twice for (j = 0; j < 2; j++) { temp_flash_ptr = flash_ptr; //erase the page about to be written boot_page_erase_safe(flash_ptr); boot_spm_busy_wait(); //fill a page for (i = 0; i < PAGE_SIZE_BYTES; i+=2) { uint16_t word; word = *ram_ptr++; word = *ram_ptr++ << 8; boot_page_fill_safe(temp_flash_ptr, word); boot_spm_busy_wait(); temp_flash_ptr += 2; } //write a page boot_page_write_safe(flash_ptr); boot_spm_busy_wait(); //update flash_ptr to the next page flash_ptr+=128; } //start the actual program asm("jmp 0"); while (1); }
This is then compiled and loaded using the following:
avr-gcc -g -Wall -Os -mmcu=atmega328 -c boot.c avr-gcc -g -Wall -Os -mmcu=atmega328 -Wl,-Ttext=0x7000 -o boot.elf boot.o avr-objcopy -j .text -j .data -O ihex boot.elf boot.hex avrdude -e -v -c avrisp2 -p m328 -P usb -U flash:w:boot.elf
When the program runs, it just has PB2 high. PB1 stays low. I would have thought that PB2 would go high for about 1 second (the delay function) and then PB1 would go high (the "jmp 0" statement to prog.c that has just been placed into flash. I have verified in a simpler scenario (see here) that the code starts at 0x7000 and jumps appropriately to 0x0000 so I think the issue is in the section labeled "write prog to flash area"). However, from what I can tell, I have followed AVR109 and section 27-8 of the datasheet.
Can you help me understand where my code is not writing flash appropriately? Thanks.