Dag,

Ik ben op het moment bezig met het inbouwen van een bootloader in een AVR. Voorlopig is dit een atmega8, maar in de toekomst moet dit een at90can32 worden. Het protocol is vooralsnog USART, maar moet CAN worden. Ik programmeer overigens in GCC.

Maar waar het eigenlijk om gaat is de programma structuur. Het programma in de AVR bestaat vanzelfsprekend uit 2 delen: de bootloader, de programmatuur die de flash herschrijft
de firmware, de firmware die wordt geupdate door de bootloader en behoort te draaien als er geen firmware wordt geupdate/bootloadt.

Dit is allemaal logisch, maar ik zit nu met het volgende:
ik programmeer een functie genaamd bootloader op ongeveer de volgende manier:


#include <avr/boot.h> (en de andere benodigde includes of course)

void bootloader_program() BOOTLOADER_SECTION;

void bootloader_program() {
   firmwareupdate programmatuur
}

int main() {
   hoofdprogramma, kan worden veranderd door bootloader
}

\
Vanzelfsprekend zit er in de makefile een regel die er voor zorgt dat de bootloader functie in het goede gedeelte van het geheugen wordt geplaatst.

Nu zit ik met het volgende, ik wil graag dat bij een reset mijn bootloader functie wordt gedraaid. Dit dacht ik te doen door de boot reset vector fuse bit te enabl'en.
Helaas heeft dit geen effect, het programma start nog steeds op vanaf main en dus kan ik de bootloader sequentie niet inkomen.

Nu kan ik dit natuurlijk lomp oplossen door het main() programma in de bootloader sectie te knallen, maar dit is niet ideaal, bijvoorbeeld omdat ik graag main() in de "normale" firmware wil kunnen gebruiken. Mijn vraag is dus hoe ik het voor elkaar krijg dat de bootloader functie als eerste wordt aangeroepen, en dat ik vanaf daar kan beslissen (aan de hand van pinnetje natuurlijk) of ik een firmware update ga doen of het hoofdprogramma ga draaien.

b.v. _O_

mvg Tom

Op 16 januari 2009 21:47:01 schreef pcnerd:
....Dit dacht ik te doen door de boot reset vector fuse bit te enabl'en.

Dat is de enige juiste manier. Als hij weer naar je main loopt springt deugt er iets niet in je bootloader sectie.
Heb je ook de Bootsector grote fusebit goed gezet? Weet je zeker dat je bootloader voor de bootsector wordt gecompiled en ook op het boot adres wordt geprogrammerd.
Programmeer je bootloader eens in je chip en lees dan je flash weer teruguit. Het eerste deel tot aan je bootadres moet dan leeg zijn en vanaf hier moet je dan wat code zien staan.
Werkt het nog niet, dan even met de jtag er stap voor stap doorstappen.

ik heb geen jtag, ofnaja, ik geloof dat ik het wel heb maar ik gebruik het niet xD.

ik heb het .hex bestand gekeken en het adres voor de bootloader sectie komt overeen met die van het gedefinieerde adres in de ihex file. Bootloader sectie zit op de goede plek dus. Maar zoals ik het dus voordoe in de topic start zou moeten werken?

Welke bootloader gebruik je, die van atmel ?
Gebruik je flip als programmer ?

En het de code die je hebt, is dat nu de bootloader software, of je eigen applicatie software ?

Je kan ook naar de bootloader sectie gaan in je applicatie code

void (*start_bootloader) (void)=(void (*)(void))0xf000;  //declaratie, het adres 0xf000 kan bij jou anders zijn.

//in je coder

(*start_bootloader)();

punt is dus dat ik niet vanuit m'n main program naar de bootloader wil gaan om te voorkomen dat er een door een fout in het updaten van de firmware niet meer gebootloadt kan worden.

welke bootloader? nou, die schrijf ik zelf, het gaat er eerst om dat ik op de juiste manier de bootloader sequentie start om vervolgens het echte werk gaan doen. Programmer? myavrUSB, doet er weinig toe volgens mij...

Ik had het gisteravond wat druk, vandaar mijn vragen die wat kortaf waren.

Flip (flexibele In system program) is geen programmer, maar software die communiceert met de bootloader van je AVR. Deze bootloader is te verkrijgen van hun website. (mischien ook wel de source, dat is voor de can bootloader wel het geval). Je hoeft dan niet je eigen software te schrijven (tenzij je dat natuurlijk heel leuk vind).

Op 16 januari 2009 23:15:59 schreef pcnerd:
punt is dus dat ik niet vanuit m'n main program naar de bootloader wil gaan om te voorkomen dat er een door een fout in het updaten van de firmware niet meer gebootloadt kan worden.

Hier heb je een heel goed punt. Ik weet niet hoe toegankelijk je elektronica is, maar dan kun je alsnog de boot reset vector programmeren en opnieuw resetten.

Ik ben met ongeveer hetzelfde bezig en dat laatste wat zei had ik nog niet over nagedacht en dit is bij mij wel belangrijk omdat ik alles laat ingieten.

De software voor het updaten van de firmware schrijf ik zelf :).

Het punt is dus dat ik zo veel mogelijk wil voorkomen dat er geen nieuwe firmware kan worden geüploadt omdat er een fout in de firmware zit. De bootloader moet dus altijd beschermd blijven en blijven werken.

ik laat de bootloader ALTIJD starten ( dus static op 0 zetten ) .
Je weet hoe groot je bootloader is.
Het volstaat aan de compiler mee te geven dat je hoofdprogramma moet starten op een bepaald address wat hoger ligt dan de bootloader ( in een hogere pagina. zo hoef je de bootpagina nooit te wissen )

1) Processor reset
2) Bootloader start op en kijkt naar startconditie (dit zijn hoop en al 2 machinetaal instructies) is een bepaalde pin hoog dan is dat een request om in load mode te gaan -> ga in load en wacht. indine niet ga naar stap 3)

3)kijk of er een magic marker aanwezig is op een bepaald adress (ik gebruik daarvoor hoogste address in flash).
Als magic marker gebruik ik een getal. FF = lege flash. iets anders is versie nummer van firmware wat nu in flash zit.
Is de marker FF : ga in load mode en wacht.
Staat de marker op iets anders: jump naar begin van echt programma

die boot routine is hoop en al 6 machinetaal instructies.

Om een update aan te vragen doe je dit in je hoofdprogramma : erase marker address naar FF en jump naar address 0.

zo doe ik dat altijd. werkt altijd onafhankelijk van processor architectuur / flash layout etc.

je load algoritme moet dan alleen flash paginas hoger dan zichzelf wissen en binnekomende data daar in zetten. eenmaal klaar schrijf je de marker weg en voert een jump naar 0 uit. je hoofdprogramma kan zijn eigen versie rapporteren door top address te lezen en inhoud te retourneren.

Bij de HARDWARE pin wis ik de marker niet direct. alleen bij een request vanuit de hoofdsoftware. dereden is dat als het spul gestart wordt met ene hardware request ik blijkbaar naas thet bord zit. 'future debug'

Helemaal duidelijk nu. Ik moet dat bij mijn eigen software ook veranderen. Ik laat alle elektronica ingieten en is daarna alleen toegankelijk via de canbus.

Ik heb ook een tijdje terug met een at90usb162 gewerkt, deze had een HWB pin, Als je deze hoog maakte tijdens een reset ging hij naar de bootloader. Ik zie deze pin niet bij de atmega8. De enigste manier lijkt mij is controleren met een if'je of een bepaalde pin hoog of laag is, maar dat doe volgens mij al, dan moet het aan de bootloader code liggen zoals 2n3055 ook zegt. Ik dacht zelf aan zoiets

//bootloader code
include....
//zomaar een pin gebruiken hier PA0
DDRA &= ~(1<<PA0); //ingang
PORTA |= (1<<PA0); //pullup

if(!(PINA & (1<<PINA0)) //als pin Pa0: start applicatie code
{
'start applicatie code
}


while(1)
{
  firmware update
  if(firmware update == klaar)
  {
    start applicatie code
  }
}

Iets dergelijks is ook mijn plan, de bootloader sectie wordt elke keer gestart bij reset (wat dus nu nog niet op de "juiste" manier lukt) en kijkt of er om een update wordt gevraagd (pinnetje hoog/laag) en gaat anders naar het main programma toe. Alleen huidige probleem blijft, zijn er mensen die bootloaders geschreven hebben in C en het op dezelfde manier aangepakt hebben? Dan kan ik even afkijken hoe dat zit met reset boot vector en de bootloader functie.

Vergeet je niet aan het begin van je bootloader de watchdog te disabelen?
Voordat je het wiel gaat uitvinden, kijk eens op avr-freaks, daar zijn zat bootloaders (ook voor can) te vinden.
Hier kun je natuurlijk ook stukjes lenen voor je eigen bootloader.
http://www.avrfreaks.net/index.php?module=Freaks%20Academy&func=vi…
http://www.avrfreaks.net/index.php?module=Freaks%20Academy&func=vi…
http://www.avrfreaks.net/index.php?module=Freaks%20Academy&func=vi…

Ik heb even wat uitgeprobeerd, maar het werkt niet.
dit is ongeveer wat ik eigenlijk wil (stukken van de complete code):


#include <stdlib.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/boot.h>
#include <avr/interrupt.h>
#include <util/delay_basic.h>
#include <avr/sfr_defs.h>
#include "lcd.h"
#include "lcd.c"


/* Function prototypes: */
void bootloader() BOOTLOADER_SECTION;//Bootloader function
void delay_ms(unsigned int x); //delay function
void delay_us(unsigned int x); //delay function
void USART_init() BOOTLOADER_SECTION; //Initialize USART
void USART_sendstring(char *str); //Send string by USART
void USART_sendchar(char chr); //Send char by USART

int main(){
	unsigned int count=0;
	DDRB |= _BV(PB0);
	PORTD &= ~(_BV(PD7));
	DDRD |= _BV(PD7);
	PORTB |= _BV(PB0);
	lcd_init(LCD_DISP_ON);
	lcd_clrscr();
	lcd_puts_P("BTLDR Ver. 1.0\n");
	
	USART_init();
	USART_sendstring("Ready..");
while(1);
	return 0;
}

void bootloader() {
	DDRD &= ~(_BV(PD2)); //Set PD2 as input
	PORTD |= _BV(PD2); //Enable pullup on PD2
	if ((PIND & _BV(PD2)) == 0) { //Start bootloader sequence
		USART_init(); //Initiate usart connection
		loop_until_bit_is_set(UCSRA, UDRE); //Wait until transmit complete
		UDR = 'R'; //Send Ready signal
		loop_until_bit_is_set(UCSRA, UDRE); //Wait until transmit complete
	}
	else {
		main(); //Jump to main
	}
}


void USART_sendstring(char *str) {
	while (*str) { //for every char
		USART_sendchar(*str); //Send char
		str++; //Next char
    }
}

void USART_sendchar(char chr) {
	loop_until_bit_is_set(UCSRA, UDRE); //Wait until transmit complete
	UDR = chr; //Store char in buffer
	
}

void USART_init() {
	PORTD &= ~(_BV(PD1)|_BV(PD0));//Be sure to set D1 
(TXD) low and disable pullup on D0 (RXD)
	DDRD |= _BV(PD1); //Set D1 (TXD) as output
	DDRD &= ~(_BV(PD0)); //Set D0 (RXD) as input
	UCSRB |= _BV(RXEN)|_BV(TXEN); //Enable Receiver 
and Transmitter
	UCSRC |= _BV(URSEL)|_BV(UCSZ1)|_BV(UCSZ0); 
//Select UCSRC and set character size to 8
	UCSRC = 0; //Set UBBRH low
	UBRRL = 12; //Set baudrate to 38k4bps
}

void delay_ms(unsigned int x) {
	while (x>0) {
		_delay_loop_2(2000);
		x--;
	}
}
void delay_us(unsigned int x) {
	while (x>0) {
		_delay_loop_2(2);
		x--;
	}
}

wat deze code *zou* moeten doen is het volgende:
uc start op, gaat naar boot reset vector en start dus bootloader().
bootloader kijkt of er gevraagd wordt om een firmware update (Pinnetje checken)
zo ja => bootloader sequentie (nog te schrijven)
zo nee => draai main()

echter, er gebeurt iets vreemd
het ledje (dat aan PB0 zit) en normaal gesproken zou aan moeten gaan als dus main() wordt gedraaid) begint te knipperen als een gek (zichtbaar, iets van tegen de 10hz aan schat ik), en er gebeurt niks (ik ontvang ook niks over rs232).

Ik kan niet volgen wat er fout gaat helaas, maar de bovenstaande code zal de oorzaak bevatten denk ik, alleen kan ik hem er niet uit destilleren.

Fuses zien er zo uit: http://www.engbedded.com/cgi-bin/fc.cgi/?P_PREV=ATmega8&P=ATmega8&…

Probeer het eens kaal, dus zonder het lcd etc.

Ik heb de code nog niet goed bekeken, maar ik denk dat je hele structuur verkeerd is.

Zoals ik het nu zie heb je je applicatie code en bootloader code in een project staan, het is gebruikelijk dat dit 2 aparte projecten zijn, zodat je 2 hex files krijgt.

Vervolgens programmeer je je bootloader hex file via de isp in de AVR.

En dan kun je je applicatiecode hex file via de bootloader in de flash van je avr zetten.

gebruik je voor je bootloader gedeelte dan ook main() ?

Ja, net zoals je ook bij een normaal project doet. In de main wacht je bijvoorbeeld op ontvangen tekens die je in je flash moet zetten.

Uiteindelijk als je je flash geheugen weer terug leest zit de bootloader in het bootloader sectie (wat je ook al had) en je applicatie code begint dan bij adres 0

Ok, dat moet te doen zijn, eerst flash leegmaken, bootloader erin en dan ook zonder flash leeg te maken(avrdude -D), firmware erin.

Maar het moet toch mogelijk zijn beide in 1 keer erin te knallen, hex achter elkaar plakken misschien? Maar het zou mooi zijn om het ook allemaal in 1 c bestand te kunnen doen en in 1 keer te compileren, of is dat echt niet mogelijk?

Btw, dat knipperen van dat ledje lag ergens anders aan dan aan het bootloader systeem volgens mij, na het opnieuw programmeren van de fuses deed hij het weer, waarschijnlijk stond de watchdog timer aan.

Probleem is nu dat ik nu weer niet in de bootloader sectie kom met bovenstaande code...
in de hex file wordt wel adres 1800 (huidige start bootloader sectie) gegeven, ik kan niet opmaken of ook daadwerkelijk de bootloader() functie daarbij wordt gestart, hex file:
http://pastebin.com/m56822e8e

[Bericht gewijzigd door pcnerd op (41%)]

Het is toch echt handiger en gebruikelijk om je bootloader code en applicatie code apart te houden, anders zou je bij elk nieuw project je bootloader code ook weer moeten invoegen...

Je hex file lijkt er wel goed uit te zien. Waar maak je uit op dat de processor niet in bootloader functie komt ? Je krijgt het 'ready' signal niet binnen ? Wat gebeurt als je bijvoorbeeld even een ledje bijvoorbeeld 2 keer laat knipperen in de bootloader functie ? Doet hij dat wel ?

dat soort geintjes dus allemaal geprobeerd en ik kwam er dus niet in.

Maar op het moment kamp ik met héél erg vreemde problemen. Ik krijg vage device signatures terug waardoor ik niet kan proggen, code wil wel proggen maar verifieerd verkeerd, ik ontvang rare tekens etc. etc. Ongeveer waar ik normaal wel is last van heb als ik losse draadjes heb ofzo, dus binnenkort maar is opnieuwe op ander breadboard opbouwen want ik snap er de ballen niet meer van. Alles wat fout kan gaan, gaat ongeveer fout...

na op een nieuw breadboard te hebben getest werkte het nog steeds voor geen meter, dus toen maar een atmega88 gepakt en die werkte wel... Misschien perongelijk een fuse verkeerd geprogd of de programmer is een beetje gek. Iig, ik ben nu weer terug bij het begin, met de volgende code:


#include <stdlib.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/boot.h>
#include <avr/interrupt.h>
#include <util/delay_basic.h>
#include <avr/sfr_defs.h>
#include "lcd.h"
#include "lcd.c"


/* Function prototypes: */
void USART_init(); //Initialize USART
void bootloader() BOOTLOADER_SECTION;//Bootloader function
void delay_ms(unsigned int x); //delay function
void delay_us(unsigned int x); //delay function
void USART_sendstring(char *str); //Send string by USART
void USART_sendchar(char chr); //Send char by USART

int main(){
	unsigned int count=0;
	DDRB |= _BV(PB0);
	PORTD &= ~(_BV(PD7));
	PORTB |= _BV(PB0);
	
	lcd_init(LCD_DISP_ON);
	lcd_clrscr();
	lcd_puts_P("BTLDR Ver. 1.0\n");
	USART_init();
	USART_sendstring("Ready..");
	while (1) {
		loop_until_bit_is_set(UCSR0A, RXC0);
		if(count <= 15) {
			lcd_putc(UDR0);
			count++;
		}
		else {
			lcd_clrscr();
			lcd_puts_P("Tomzooi Bootldr.\n");
			count = 0;
		}		
	}
	return 0;
}

void bootloader() {
	
	DDRD |= _BV(PD6); //Set PD6 as output
	PORTD |= _BV(PD6); //Light led on PD6
}

-- functies die er niet toe doen stonden hier--

fuses: lfuse:0xF7, hfuse:0xDF, efuse:0xF8

hex file ziet er goed uit, de bootloader functie wordt op de juiste plek geprogrammeerd. Enige gekke is dat de bootloader() functie niet gaat draaien bij een reset (dat zou zichtbaar moeten zijn aan het ledje op PD6 en aan het niet draaien van het main() programma), maar hij gaat in plaats daarvan dus naar de main en niet naar de bootloader() functie. Hier licht vooralsnog het echte probleem met deze bootloader.

Zo, ik heb na een hoop gedonder maar besloten om het toch apart te doen, maar zelfs dan gaat er iets mis.

Ik compileer eerst de bootloader sectie, maak de chip leeg en programmeer de bootloader sectie (isp met avrdude).
vervolgens programmeer ik het gewone programma zonder de chip leeg te maken (eveneens met avrdude).
Na het programmeren en het uitlezen lijkt dat er prima een stukje op de juiste adressenzitten (vanaf 0000(firmware) en 1800(bootloader))

echter, het werkt niet.
Het bootloader gedeelte is nu als volgt:


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

int main(){
	PORTD |= _BV(PD7);
	if (!(PIND & _BV(PD7))) {
		DDRD |= _BV(PD6);
		PORTD |= _BV(PD6);
		delay_ms(500);
		PORTD &= ~(_BV(PD6));
		delay_ms(500);
		PORTD |= _BV(PD6);
	}
	jump_to_app();
	return 0;
}

wat er voor zou moeten zorgen om een ledje een keer te laten knipperen als een jumpertje is verwijdert. Dit werkt ook prima, totdat ik de "gewone" firmware bij de chip opzet, dan knippert er niks meer en wil hij enkel de gewone firmware draaien, het lijkt er dus gek genoeg op dat hij niet meer in de bootloader sectie komt, wat hij echter wel zou moeten doen met:
lfuse: F7
hfuse: DF
efuse: F8

Kan iemand misschien bedenken waar het misgaat? Mij lukt het niet meer.

//edit:
owja, wat ik vergeten was te zeggen is dat het ledje bij enkel de bootloader firmware blijft doorknipperen, dus niet zoals het zou moeten (1 keer knipperen en dan aan blijven), hij gaat dus weer naar het begin van de bootloader steeds oid...

woei, had een typefoutje gemaakt in de command om de fuses te programmeren waardoor de efuse niet meekwam, ff gecorrigeerd en het werkt nu, alhoewel avrdude nog wel steeds een error geeft bij het verifieren van de efuse... bekend probleem/feature/bug?