July 10, 2011
by Noter
|
One thing I like to do when learning a new program like the bootloader is rewrite and improve where possible. So I did this early on with the bootloader and consolidated it all into a single source file with config data in the make file for the things like the chip, frequency, and baud to use. Now to put the bootloader on a different chip I just edit the make file and go. Well for the 168 and 328 anyway because for now it is setup for those two. To add another chip will require a couple of definitions in the program for the new signature and likewise in the makefile for the bootsection start address and fuse settings. Just follow along with the existing defintions and it shouldn't be too difficult.
As usual my makefile is not in standard form but it does show the configuration settings and how they are passed to the compiler. Maybe someone that implements with a standard nerdkit type makefile will post their's once it's working.
Following is the program and then the makefile ... |
July 10, 2011
by Noter
|
//
//
// AVR109_Noter.c
//
// Noter's v3.0 AVR109 (AVRBOOT) Bootloader
//
// Based on ATmel's application notes:
// AVR109 - Using Self Programming on tinyAVR and megaAVR devices
// AVR911 - AVR Open-source Programmer.
//
#include <avr/io.h>
#include <avr/boot.h>
#include <avr/eeprom.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <stdbool.h>
// preprocessor macros for port/pin manulipulation
//
#define INPUT2(port,pin) DDR ## port &= ~_BV(pin)
#define OUTPUT2(port,pin) DDR ## port |= _BV(pin)
#define CLEAR2(port,pin) PORT ## port &= ~_BV(pin)
#define SET2(port,pin) PORT ## port |= _BV(pin)
#define TOGGLE2(port,pin) PORT ## port ^= _BV(pin)
#define READ2(port,pin) ((PIN ## port & _BV(pin))?1:0)
//
#define INPUT(x) INPUT2(x)
#define OUTPUT(x) OUTPUT2(x)
#define CLEAR(x) CLEAR2(x)
#define SET(x) SET2(x)
#define TOGGLE(x) TOGGLE2(x)
#define READ(x) READ2(x)
#define PULLUP_ON(x) INPUT2(x); SET2(x)
#define PULLUP_OFF(x) INPUT2(x); CLEAR2(x)
// define ports, pins
//
#define START_SWITCH B,0
// local function prototypes
//
void uart_write(char data);
uint8_t uart_read(void);
void (*jump_to_application)(void);
void uart_write_PSTR(char *data);
// Loader variables/values/macros
//
#define MEMORY_TYPE_FLASH 'F'
#define MEMORY_TYPE_EEPROM 'E'
// programmer info
//
#define PROGRAMMER_ID "AVRBOOT"
#define HARDWARE_VERSION "01"
#define SOFTWARE_VERSION "30"
#define SIG_LEN 3
#if defined (__AVR_ATmega168__)
prog_uint8_t signature[] = "\x1E\x94\x06";
#define DEVICE_CODE 0x02
#define PAGE_SIZE 128
#define PAGE_COUNT 128
#elif defined (__AVR_ATmega328P__)
prog_uint8_t signature[] = "\x1E\x95\x0F";
#define DEVICE_CODE 0x03
#define PAGE_SIZE 128
#define PAGE_COUNT 256
#else
#error cpu not supported by AVR109_Noter bootloader!
#endif
// buffer for block writes to eeprom
uint8_t usart_buffer[PAGE_SIZE];
// --------------------------------------------------------------------------------------------------------
//
int main(void) {
// initialize and check start switch
PULLUP_ON(START_SWITCH);
_delay_us(1); // let it rise
// if the start switch is open, start the application
if(READ(START_SWITCH)){
// put reset vector back to flash address base
MCUCR=_BV(IVCE);
MCUCR=0;
// and jump to it to start the application
jump_to_application();
}
// set baudrate, initialize usart
UCSR0A = 0;
UBRR0L = (F_CPU/(16*BAUD))-1;
// enable tx, rx,
UCSR0B = _BV(RXEN0)|_BV(TXEN0);
// set 8N1
UCSR0C = _BV(UCSZ01)|_BV(UCSZ00);
// process commands from host
uint16_t i;
uint16_t address;
uint16_t byte_count;
uint8_t command;
while(true){
// Commands | Host writes | Host reads | |
// -------- +-----+-------+------+-----+ |
// | ID | data | data | | Note |
command=uart_read();
if(command=='P'){ // | Enter programming mode | 'P' | | | 13d | 1 |
// we're always in programming mode
uart_write('\r');
}
else if(command=='L'){ // | Leave programming mode | 'L' | | | 13d | 5 |
// could jump start the application here ...
uart_write('\r');
}
else if(command=='a'){ // | Report autoincrement address | 'a' | | | 'Y' | |
uart_write('Y');
}
else if(command=='A'){ // | Set address | 'A' | ah al | | 13d | 2 |
// we get word address for flash
// or byte address for eeprom
address=(uart_read()<<8)|uart_read();
uart_write('\r');
}
else if(command=='e'){ // | Chip erase | 'e' | | | 13d | |
// erase all lower memory pages - below the bootloader
for(address=0;address<BOOT_SECTION_START;address+=PAGE_SIZE)
boot_page_erase_safe(address);
uart_write('\r');
}
else if(command=='b'){ // | check block support | 'b' | |dd dd | Y | |
// we only do block reads and writes
uart_write('Y');
// max block size is page size
uart_write(PAGE_SIZE>>8);
uart_write(PAGE_SIZE&0xFF);
}
else if(command=='B'){ // | flash or eeprom block write | 'B' |dd(dd) | | 13d | |
// get the byte count for this block
byte_count=(uart_read()<<8)|uart_read();
// is it for flash or eeprom?
if(uart_read()==MEMORY_TYPE_FLASH){
// remember, word address given for flash
// read and load the page buffer 2 bytes at a time
for(i=0;i<byte_count;i+=2)
// note: write words using byte buffer address
// but byte address must be on word boundary
// thus +2 each iteration and load two bytes
boot_page_fill_safe(i,uart_read()|(uart_read()<<8));
// but no matter what, don't overwrite self (bootloader)
if(address<(BOOT_SECTION_START-PAGE_SIZE))
// otherwise, write the page using byte address
boot_page_write_safe(address<<1);
// re-enable the Read-While_Write (application) section
boot_rww_enable_safe();
// auto-increment with count in words
address+=(byte_count>>1);
}else{
// a byte address is used for eeprom
for(i=0;i<byte_count;i++)
usart_buffer[i]=uart_read();
// write the buffer
eeprom_write_block(usart_buffer,(void*)address,byte_count);
address+=byte_count; // and auto-increment the address
}
// tell host we're ready for next block
uart_write('\r');
}
else if(command=='g'){ // | flash or eeprom block read | 'g' | |dd(dd)| | |
// get byte count to read for this block
byte_count=(uart_read()<<8)|uart_read();
// get memory type - flash or eeprom
if(uart_read()==MEMORY_TYPE_FLASH){
// word address given for flash
for(i=0;i<byte_count;i++)
// read and send all the bytes for this block
// using a byte address to read. <<1 converts
// the word address to a byte address.
uart_write(pgm_read_byte((address<<1)+i));
// and auto-increment flash word address
address+=(byte_count>>1);
}else{
// byte address given for eeprom
for(i=0;i<byte_count;i++)
// read and send bytes for this block,
// auto-increment byte address
uart_write(eeprom_read_byte((uint8_t *)(address++)));
}
}
else if(command=='T'){ // | Select device type | 'T' | dd | | 13d | 6 |
// read and ignore since we already know our
// device type.
uart_read();
uart_write('\r');
}
else if(command=='s'){ // | Read signature bytes | 's' | | 3*dd | | |
// send the string backwards, LSB first
for(i=1;i<=SIG_LEN;i++)
uart_write(pgm_read_byte(&signature[SIG_LEN-i]));
}
else if(command=='t'){ // | Return supported device codes | 't' | | n*dd | 00d | 7 |
// we only have one, terminate list with null character
uart_write(DEVICE_CODE);
uart_write(0x00);
}
else if(command=='S'){ // | Return software identifier | 'S' | | s[7] | | 8 |
uart_write_PSTR(PSTR(PROGRAMMER_ID));
}
else if(command=='V'){ // | Return sofware version | 'V' | |dd dd | | 9 |
uart_write_PSTR(PSTR(SOFTWARE_VERSION));
}
else if(command=='v'){ // | Return hardware version | 'v' | |dd dd | | 9 |
uart_write_PSTR(PSTR(HARDWARE_VERSION));
}
else if(command=='p'){ // | Return programmer type | 'p' | | dd | | 10 |
// [S]erial programmer
uart_write('S');
}
else if(command=='E'){ // | Exit bootloader | 'E' | | | 13d | |
// avrdude gives an error message if we don't do this
uart_write('\r');
}
else if(command==0x1B){ // | ESC - sync char | x1B | dd | | | |
// consume, no reply
}
}
}
// --------------------------------------------------------------------------------------------------------
// USART write a character
void __attribute__ ((noinline)) uart_write(char data) {
while ((UCSR0A & _BV(UDRE0))==0);
UDR0 = data;
}
// USART read a character
uint8_t __attribute__ ((noinline)) uart_read(void){
while(!(UCSR0A & _BV(RXC0)));
return UDR0;
}
// USART write a string from flash
void __attribute__ ((noinline)) uart_write_PSTR(char *data){
int i;
for(i=0;pgm_read_byte_near((char*)&data[i]);i++)
uart_write(pgm_read_byte_near((char*)&data[i]));
}
|
July 10, 2011
by Noter
|
PROGRAM=AVR109_Noter
# AVRBOOT (butterfly) bootloader
#
# settings for the programmer that will load the bootloader
#
PGMR_SPI_PORT=0
#PGMR_COM_PORT=COM9
#PGMR_COM_BAUD=250000
PGMR_COM_PORT=COM2
PGMR_COM_BAUD=115200
PGMR_ID="AVR HV "
#PGMR_ID="AVR ISP"
#
# settings for the bootloader compile and link
#
MCU=atmega328p
#MCU=atmega168
#F_CPU=20000000UL
F_CPU=18432000UL
#F_CPU=14745600UL
#F_CPU=8000000UL
#BAUD=9600UL
#BAUD=19200UL
#BAUD=38400UL
#BAUD=57600UL
BAUD=115200UL
#BAUD=250000UL
#
# Fuse settings and start address of the bootsection
# Use a fuse calculator like http://www.engbedded.com/fusecalc
#
ifeq ($(MCU),atmega168)
# 0x1E00 word addr is 0x3C00 byte addr (1kb bootloader) efuse 0xFA
# 0x1C00 word addr is 0x3800 byte addr (2kb bootloader) efuse 0xF8
BOOT_SECTION_START = 0x3C00
LFUSE = F7
HFUSE = D5
EFUSE = FA
LOCK = EF
else ifeq ($(MCU),atmega328p)
# 0x3E00 word addr is 0x7C00 byte addr (1kb bootloader) hfuse 0xD4
# 0x3C00 word addr is 0x7800 byte addr (2kb bootloader) hfuse 0xD2
BOOT_SECTION_START = 0x7C00
# external crystal and preserve eeprom on chip erase
LFUSE = F7
HFUSE = D4
EFUSE = FD
LOCK = EF
# 8 mhz internal oscillator, no eeprom preservation
# LFUSE = E2
# HFUSE = DA
# EFUSE = FF
# LOCK = EF
endif
all: $(PROGRAM).hex
program: $(PROGRAM).pgm
.SECONDARY:
%.o: %.c
avr-gcc -g -Os -mcall-prologues -mmcu=$(MCU) -std=gnu99 \
-L/usr/local/avr/avr/lib \
-Wl,--section-start=.text=$(BOOT_SECTION_START) \
$< -o $@ \
-DF_CPU=$(F_CPU) -DBAUD=$(BAUD) -DMCU=$(MCU) \
-DBOOT_SECTION_START=$(BOOT_SECTION_START)
%.ass: %.o
avr-objdump -S -d $< > $@
%.hex: %.o %.ass
avr-objcopy -j .text -O ihex $< $@
%.pgm: %.hex
# avr911_noter -d$(MCU) -c$(PGMR_COM_PORT) -B$(PGMR_COM_BAUD) \
# -if$< -pf -vf -e -f$(HFUSE)$(LFUSE) -l$(LOCK) -E$(EFUSE) -Z0
#
avr911_noter -d$(MCU) -c$(PGMR_COM_PORT) -B$(PGMR_COM_BAUD) \
-if$< -pf -vf -e -f$(HFUSE)$(LFUSE) -l$(LOCK) -E$(EFUSE) -D
#
# avrdude -C ../libNoter/avrdude.rs -c $(PGMR_ID) -p $(MCU) -b $(PGMR_COM_BAUD) \
# -P $(PGMR_COM_PORT) -U flash:w:$<:a \
# -U lfuse:w:0X$(LFUSE):m \
# -U hfuse:w:0X$(HFUSE):m \
# -U efuse:w:0X$(EFUSE):m \
# -U lock:w:0X$(LOCK):m
clean:
-rm $(PROGRAM).o $(PROGRAM).hex $(PROGRAM).ass
|
August 17, 2011
by missle3944
|
Hi Noter,
So what is the main difference from the other code that you posted? Does this make it easier to put another bootloader onto a new chip?
-Dan |