hi guys,
first sorry my english.
I'm trying make bootloader self write sketch by one eeprom memory on i2c bus,
the concep is:
-
the first time:
I burn new bootloader then burn fist version of sketch. -
I send hex content to i2c memory using any comunication interface, using one routine on fist sketch to do it, check the information twice ,write flag on device eprom memory and reboot device.
-
bootloader find new version of software(flag) and selfburn new sketch using i2c eeprom and start app.
-
the new version of sketch is runing.
I can use this routine by serial, ethernet, wifi or any comunication device, only need have apropriate function on fist sketch (and updates of this) to receive new firmware and write on i2c memory.
my problems, I'm a newbie on atmegas, I have expertise with firmwares but no with atmegas and still having problens with my "hack bootloader"
I start with(maybe) the original bootloader on /hardware/arduino/avr/bootloaders/atmega
and make this changes.
on includes of ATmegaBOOT_168.c
/* some includes */
#include <util/twi.h> //TWI Library to use i2c serial eeprom
and this 2 funcions to use EEPROM
after the functions prototypes on bootloader
void EEOpen()
{
//Set up TWI Module
TWBR = 5;
TWSR &= (~((1<<TWPS1)|(1<<TWPS0)));
}
uint8_t EEReadByte(uint16_t address)
{
uint8_t data;
//Initiate a Dummy Write Sequence to start Random Read
do
{
//Put Start Condition on TWI Bus
TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
//Poll Till Done
while(!(TWCR & (1<<TWINT)));
//Check status
if((TWSR & 0xF8) != 0x08)
return FALSE;
//Now write SLA+W
//EEPROM @ 00h
TWDR=0b10100000;
//Initiate Transfer
TWCR=(1<<TWINT)|(1<<TWEN);
//Poll Till Done
while(!(TWCR & (1<<TWINT)));
}while((TWSR & 0xF8) != 0x18);
//Now write ADDRH
TWDR=(address>>8);
//Initiate Transfer
TWCR=(1<<TWINT)|(1<<TWEN);
//Poll Till Done
while(!(TWCR & (1<<TWINT)));
//Check status
if((TWSR & 0xF8) != 0x28)
return FALSE;
//Now write ADDRL
TWDR=(address);
//Initiate Transfer
TWCR=(1<<TWINT)|(1<<TWEN);
//Poll Till Done
while(!(TWCR & (1<<TWINT)));
//Check status
if((TWSR & 0xF8) != 0x28)
return FALSE;
//*************************DUMMY WRITE SEQUENCE END **********************
//Put Start Condition on TWI Bus
TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
//Poll Till Done
while(!(TWCR & (1<<TWINT)));
//Check status
if((TWSR & 0xF8) != 0x10)
return FALSE;
//Now write SLA+R
//EEPROM @ 00h
TWDR=0b10100001;
//Initiate Transfer
TWCR=(1<<TWINT)|(1<<TWEN);
//Poll Till Done
while(!(TWCR & (1<<TWINT)));
//Check status
if((TWSR & 0xF8) != 0x40)
return FALSE;
//Now enable Reception of data by clearing TWINT
TWCR=(1<<TWINT)|(1<<TWEN);
//Wait till done
while(!(TWCR & (1<<TWINT)));
//Check status
if((TWSR & 0xF8) != 0x58)
return FALSE;
//Read the data
data=TWDR;
//Put Stop Condition on bus
TWCR=(1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
//Wait for STOP to finish
while(TWCR & (1<<TWSTO));
//Return TRUE
return data;
}
then I make a changes on main code
add at_ep variable and start at_ep and ch variables;
uint8_t ch,ch2,at_ep;
at_ep=0;
ch = 0x00;
before forever loop I will test the device address to see if have version on i2c eeprom
in this case I just use the 328 to test
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__)
address.byte[0] = 0x03; //address of the last byte on device eeprom
address.byte[1] = 0xfe;
address.word = address.word << 1;
while(EECR & (1<<EEPE));
EEAR = (uint16_t)(void *)address.word;
EECR |= (1<<EERE);
if(EEDR == 0xFE){
flash_led(10); //just for I know if read the 0xFE on last byte.
at_ep = 1;
EEOpen(); //start i2c
}
length.byte[1] = 0x00;
length.byte[0] = 0x40; //fixe the length of page
#endif
next step is the first char identification by avrdude '0'
I only will wait this if not have a i2c flags
/* get character from UART */
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__)
if(at_ep != 1)
ch = getch();
#else
ch = getch();
#endif
on the write memory funcion 'd' I need make tests
first if is one i2c update I read memory by i2c and put informations on buff like by serial but...
/* Write memory, length is big endian and is in bytes */
else if((ch=='d')) || (at_ep==1)) {
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__)
if(at_ep!=1){
length.byte[1] = getch();
length.byte[0] = getch();
flags.eeprom = 0;
if (getch() == 'E') flags.eeprom = 1;
for (w=0;w<length.word;w++) {
buff[w] = getch(); // Store data in buffer, can't keep up with serial data stream whilst programming pages
}
ch = getch();
}else{
for (w=0;w<length.word;w++) {
buff[w]=EEReadByte(((uint16_t)(void *)address.word)+w);
}
ch = ' ';
}
#else
length.byte[1] = getch();
length.byte[0] = getch();
flags.eeprom = 0;
if (getch() == 'E') flags.eeprom = 1;
for (w=0;w<length.word;w++) {
buff[w] = getch(); // Store data in buffer, can't keep up with serial data stream whilst programming pages
}
ch = getch();
#endif
... //after this the processes is same, get buff bytes and burn on program memory by the asm codes.
//in the end I update to nexts adress
putch(0x14);
putch(0x10);
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__)
if(at_ep ==1){
//check if is my last memory block (remember I only test on 328)
//I clear flag of update on device eeprom for don't come on loop on next reboot
if((address.byte[1]==0x75)&&(address.byte[0]=0xc0))
{
address.byte[1]=0x03;
address.byte[0]=0xff;
while(EECR & (1<<EEPE));
EEAR = (uint16_t)(void *)address.word;
EEDR = 0xFF;
EECR |= (1<<EEMPE);
EECR |= (1<<EEPE);
app_start();
}
if(address.byte[0] == 0xC0) //check if is the last block of first byte
{
address.byte[1]++; // plus second byte
address.byte[0] += 0x00;
}else{
address.byte[0] += 0x40; //plus the first byte address.
}
}
#endif
I assume this changes is I need to work with and without memory i2c eeprom, in this case 24lc256 (32Kb)
why I don´t use the 24lc256 to store the flag information of new firmware, because I need on write memory code to clear that, if have implementation of versions maybe no use write code.
I compile it, and burn on arduino uno using another uno, the bootloader stop to work, it need work with or without eeprom and work normal on arduino IDE but no
I make this change on makefile
atmega328: TARGET = atmega328
atmega328: MCU_TARGET = atmega328p
atmega328: CFLAGS += '-DMAX_TIME_COUNT=F_CPU>>4' '-DNUM_LED_FLASHES=1' -DBAUD_RATE=9600
atmega328: AVR_FREQ = 16000000L
atmega328: LDSECTION = --section-start=.text=0x7400
atmega328: $(PROGRAM)_atmega328.hex
--section-start=.text=0x7400
because the bootloader is bigger, but I don´t know if it is all I need to make this work well
I need help to make it work, and think is a good way to remote update fimwares with low cost, only need one memory i2c and 2 resistors to work.
thank you the help
and sorry my english.