Hallo!
Für ein Projekt an meiner Uni haben wir ein USBasp Board entworfen,
dieses funktioniert soweit auch gut.
Nun benötigen wir allerdings noch einen USB->RS232 Wandler, dieser
Wandler soll allerdings auf dem gleichen Controller laufen! Ich habe mir
das avr-cdc Projekt angesehen, dies läuft nach Anpassungen auch auf
unserem Board.
Doch wie bekomme ich nun beide Programme gleichzeitig auf den
Controller?
Eine Möglichkeit wäre ja meiner Meinung nach, ein Programm in den
Bootloader zu packen, das andere vorne in den Flash. An den Anfang des
"normalen" Programms kommt dann eine Jumper-Abfrage, entweder läuft das
Programm normal weiter, oder aber es springt in den Bootloader und biegt
die Interrupt Tabelle in den Bootloader. Wäre dies so möglich?
Das Problem ist leider nun, dass avr-cdc ca. 2,5kB groß ist, die
maximale Bootloadergröße im Atmega8 allerdings scheinbar nur 2kB ist?!
Nun wird es etwas komplizierter:
Ich habe mir überlegt ein kleines Bootloader-Programm zu schreiben,
welches wiederum den "Programmauswahl-Jumper" abfragt und dann an die
richtige Adresse zum ausgewählten Programm im Flash springt. (um das
Programm an die richtige Stelle im Flash zu kriegen könnte ich doch ganz
"unelegant" einfach hunderte "nop's" an den Anfang des Programms setzen
oder? Die Interrupt Tabelle soll ja immer noch am Anfang stehen und auf
die richtigen Adressen verweisen).
Sollte nun das andere Programm ausgewählt werden, müsste ich an eine
andere Stelle im Flash springen und vorher noch die Interrupt Tabelle
anpassen, damit die Interrupts an die richtigen Stellen in eben diesem
anderen Programm verweisen.
Das Schreiben aus dem Bootloader heraus in die Interrupt Tabelle müsste
doch möglich sein oder?
Kann irgendjemand meine Idee nachvollziehen? ;) Wäre dies theoretisch
möglich oder gibt es noch etwas was ich nicht berücksichtig habe?
Viele Grüße
Markus
Markus H. schrieb:> Für ein Projekt an meiner Uni haben wir ein USBasp Board entworfen,> dieses funktioniert soweit auch gut
Und warum setzt ihr da so eine olle Atmega8-Gurke drauf?
Mit einem pinkompatiblen Atmega328 und 4K Bootsektor wäre das alles
einfacher.
Markus H. schrieb:> unelegant" einfach hunderte "nop's"
Dafür gibt es Sections. Damit kann man den Code da "hinschieben", wo man
ihn haben will. Die Vektoren bleiben dabei vorne.
"Attribute Section avr". Google das mal.
mfg.
Hallo,
danke für die Antwort.
Leider ist es auch eine Kostenfrage welchen Controller wir einsetzen, da
wir auch einige 100 Stück bestellt haben, und diese teilweise auch schon
ausgegeben wurden. Daher wäre es schöner das ganze in Software anpassen
zu können..
Ich habe mir mal ein paar Seiten zu "Section's" angesehen, allerdings
finde ich nur Befehle wie --section-start=.text=0x1234, welche
allerdings den gesamten Programmcode verschieben oder? Ich hätte ja
gerne die Interrupt Tabelle noch am Anfang des Flashs, nur den
"richtigen" Programmcode dann an einer anderen Stelle.
Markus H. schrieb:> finde ich nur Befehle wie --section-start=.text=0x1234,
Das ist doch schon die halbe Miete.
> welche allerdings den gesamten Programmcode verschieben oder?
Gerade nicht. Erstmal ist das nur da und macht gar nichts.
Im Makefile:
## Linker flags
...
...
...
LDFLAGS += -Wl,-section-start=.blubb=0xe00
0xe00 ist eine BYTE-Adresse. entspicht der Word-Adresse 0x700.
In AVR-Studio 4.x, Studio verweigere ich:
>>Project >>Configuration Options >>Memory Settings <ADD>
Memory Type: Flash
Name: .blubb //Der Punkt ist wichtig
Address: 0x700 //Hier wird die Wordadresse angegeben!!!
>>OK >>OK
Im Sourcecode:
_attribute_ ((section (".blubb")))
void Delay(unsigned char nDelay)
{
for (unsigned char nInd = 0; nInd < nDelay; nInd++) _delay_ms(1);
}
Das __attribute....
muß vor jede Funktion geschrieben werden.
im *.lss:
_attribute_ ((section (".blubb")))
void Delay(unsigned char nDelay)
{
for (unsigned char nInd = 0; nInd < nDelay; nInd++) _delay_ms(1);
e00: 90 e0 ldi r25, 0x00 ; 0
^^^^
ohne __attri.... sieht das so aus:
//__attribute__ ((section (".blubb")))
void Delay(unsigned char nDelay)
{
for (unsigned char nInd = 0; nInd < nDelay; nInd++) _delay_ms(1);
45c: 90 e0 ldi r25, 0x00 ; 0
^^^^
Die Vektortabelle steht vorne, wo sie hingehört.
mfg.
Also sind nur die Interrupts das Problem?
Kann man per Software verzweigen mit ein paar inline-Assembler-Befehlen.
Ein bisschen tricky, kostet auch etwas Zeit.
Oder für alle doppelt benutzten Ints beide Programme in die ISR
schreiben und direkt am Anfang Teil 1 oder 2 auswählen.
Alternativ nach Programmstart und Jumperauswahl page0 mittels SPM
passend beschreiben.
Nun kommt beim Kompilieren eine Fehlermeldung:
Error 2 section .prog loaded at [0000073e,000008c1] overlaps section
.data loaded at [0000073e,0000073f]
Wird die Position von .data schon durch irgendwas festgelegt? Oder wieso
überschneidet sich das?
Sollte der USBasp nicht sowieso irgendwann mal auch einen
USB-Seriell-Konverter beinhalten? Das lässt sich zumindest nach dem
Schaltplan auf fischl vermuten...
mfg mf
Warum meint eigentlich jeder Horst, in sein Projekt unbedingt einen
USB-RS232 Konverter reinstopfen zu müssen? USB-RS232 Kabel gibts für 1-2
EUR auf eGay, also wozu das Theater?
Oder schämen sich manche Leute, wenn ihre Superplatine einen DSUB
Stecker anstatt einer USB Buchse hat?
Naja, leider ist "irgendwann" ein wenig zu spät für mich^^
In ca. 1 Monat muss das ganze laufen.
Ich habe mal eine kleine Nachtschicht eingelegt und jetzt den USBasp,
avr-cdc und den Bootloader zusammen im Flash.
Ich habe einfach mit --section-start=.text=0x1100 den gesamten
Programmcode des avr-cdc (incl. Interrupt-Table!) verschoben. Der USBasp
liegt am Anfang des Flash, der Bootloader logischerweise ganz hinten.
Im Bootloader wird nun ein Jumper abgefragt und dann entweder
void (*start1)( void ) = 0x0000;
oder
void (*start2)( void ) = 0x0880;
aufgerufen, der USBasp funktioniert auf diese Weise ohne Probleme,
avr-cdc allerdings noch nicht, da ich die Interrupt-Tabelle am Anfang
des Flash noch nicht modifiziere wenn ich den Programmteil des avr-cdc
aufrufe. Eine Endlosschleife zum LED-Blinken am Anfang der main() des
avr-cdc funktioniert aber.
Die Modifikation der Interrupt-Table werde ich dann morgen noch
versuchen. Die Schlüsselwörter dürften wohl "boot_page_write" und
"boot_page_fill" sein oder? Oder ist es auch möglich nur einzelne Bytes
zu schreiben, da ich (wenn ich es richtig überblickt habe) nur 2
Adressen verändern muss?
So, ich habe nun einmal versucht den Flash zu beschreiben, aber aus
irgendeinem Grund schreibt er andere Daten hinein als ichs gerne hätte
:(
Das Programm lautet folgerndermaßen:
boot_spm_busy_wait();// Wait until the memory is erased.
13
14
for(i=0;i<SPM_PAGESIZE;i+=2){
15
// Set up little-endian word.
16
uint16_tw=*buf++;
17
w+=(*buf++)<<8;
18
boot_page_fill(page+i,w);
19
}
20
21
boot_page_write(page);// Store buffer in flash page.
22
boot_spm_busy_wait();// Wait until the memory is written.
23
// Reenable RWW-section again. We need this if we want to jump back
24
// to the application after bootloading.
25
boot_rww_enable();
26
// Re-enable interrupts (if they were ever enabled).
27
SREG=sreg;
28
}
29
30
31
32
intmain()
33
{
34
void(*start1)(void)=0x0000;/* Funktionspointer auf 0x0000 */
35
void(*start2)(void)=0x0880;/* Funktionspointer auf 0x0000 */
36
37
DDRC=(1<<PC1);
38
PORTC|=(1<<PC4);
39
_delay_ms(5);
40
41
42
boot_program_page(3200,it1);
43
while(1){}
44
start1();
45
46
return0;
47
}
Die Daten die dann im Flash an Stelle 0x0C80 - 0x0CBF stehen sind:
118010C654401280000001000000054000804CC048804880004000000000054002C00140
44400400000004010000250000000E00420008002000210008002400
Dies passt doch nicht?! Erkennt jemand meinen Fehler oder etwas was ich
nicht beachtet habe?
Grüße
Markus
@ Markus H. (Gast)
>Leider ist es auch eine Kostenfrage welchen Controller wir einsetzen, da>wir auch einige 100 Stück bestellt haben, und diese teilweise auch schon>ausgegeben wurden. Daher wäre es schöner das ganze in Software anpassen>zu können..
Andere Leute bauen erstmal einen Prototypen, ehe sie tonnenweise Geld
für eine (Klein)serie ausgeben.
Falk Brunner schrieb:> Andere Leute bauen erstmal einen Prototypen, ehe sie tonnenweise Geld> für eine (Klein)serie ausgeben.
Das ganze war ja erst nur als Programmer gedacht, die Erweiterung mit
dem RS232 Wandler kommt jetzt nachträglich dazu.
Markus H. schrieb:> Das ganze war ja erst nur als Programmer gedacht, die Erweiterung mit> dem RS232 Wandler kommt jetzt nachträglich dazu.
Wie wäre es denn, wenn man die Sache so usbavrlab-mäßig aufzieht?
Man könnte den usbasploader verwenden (Taster drücken beim Einstecken
aktiviert ihn) und dann über avrdude die jeweilige gewünschte Firmware
einspielen. Mache ich bei meinem usbasp so.
Gruß Oliver
Es funktioniert! ;)
War irgendwie n Krampf, ich weiß nich wieso er keine Lust auf meine
Arrays hatte, ich habs jetzt einfach ganz unschön einfach mit 32
Befehlen hintereinander hart reingecodet, jetzt läufts aber ;)
Solang keiner in den Sourcecode guckt :P
Das funktioniert jetzt eigentlich wie ich oben beschrieben habe:
Zuerst wird der Bootloader aufgerufen, dieser prüft per Jumper ob PC4
auf Masse gezogen ist oder nicht.
Falls nicht, wird Page0 mit der Interrupt-Tabelle des USBasp
beschrieben, dieser befindet sich "ganz normal" am Anfang des Flash,
danach wird an Stelle 0 im Flash gesprungen und der USBasp startet.
Sollte der Jumper PC4 auf Masse ziehen, wird Page0 mit einer
modifizierten Interrupt Tabelle beschrieben, diese leitet alle
Interrupts einfach auf die entsprechenden Einträge in der
Interrupt-Tabelle des avr-cdc um, welcher ab Stelle 0c1100 im Flash
steht. Dadurch habe ich 2 Sprünge für einen Interrupt, dies ist meiner
Meinung nach aber besser, da ich so unabhängig den Sourcecode des
avr-cdc anpassen kann und ich nicht die Sprünge vorne in der Tabelle
anpassen muss weil sich evtl die Adressen der Interrupt Routinen des
avr-cdc verändert haben.
Falls jemand Interesse hat das Programm selber zu benutzen, habe ich mal
die .hex-File angehängt.
Achso, vergessen zu erwähnen:
Es müssen natürlich noch die passenden Fusebits eingestellt werden damit
der Controller auch in den Bootloader geht.
Bootloader Size ist 1kb, für den Atmega8 habe ich daher folgende
Fusebits:
hfuse: 0xCA
lfuse: 0xFF
Markus H. schrieb:> Falls nicht, wird Page0 mit der Interrupt-Tabelle des USBasp> beschrieben, dieser befindet sich "ganz normal" am Anfang des Flash,> danach wird an Stelle 0 im Flash gesprungen und der USBasp startet.> Sollte der Jumper PC4 auf Masse ziehen, wird Page0 mit einer> modifizierten Interrupt Tabelle beschrieben, diese leitet alle> Interrupts einfach auf die entsprechenden Einträge in der> Interrupt-Tabelle des avr-cdc um,
Schöner Ansatz.
Man könnte noch vorher die bestehende und die zuschreibende
Interrupt-Vector-Tabelle vergleichen und erst bei Ungleichheit eine neue
schreiben. Das würde Schreibzyklen sparen.
> Falls jemand Interesse hat das Programm selber zu benutzen, habe ich mal> die .hex-File angehängt
Mir persönlich ist Quellcode lieber.
Gruß Oliver
Oliver J. schrieb:> Schöner Ansatz.> Man könnte noch vorher die bestehende und die zuschreibende> Interrupt-Vector-Tabelle vergleichen und erst bei Ungleichheit eine neue> schreiben. Das würde Schreibzyklen sparen.
Stimmt, gute Idee! Das könnte ich vielleicht auch noch einbauen.
Meistens wird ja auch eh nich jedes mal gewechselt.
Wobei der Atmega ja mindestens 10000 Schreibzyklen überlebt, das wäre
bei einem täglichen 10fachen Wechsel immer noch ca. 3 Jahre, also würde
auch reichen ;)
Gut, dann werde ich mir mal pgm_read_byte() ansehen und gucken ob ichs
hin kriege.
Ich habe den Quellcode jetzt auch noch angehängt, solang ich nicht
gleich von den Informatikern hingerichtet werde :D Ist äußerst unschön,
wie gesagt gings mit dem Array irgendwie nich, selbst wenn ichs direkt
in die "boot_program_page"-Funktion schreibe, ohne es als Parameter zu
übergeben. Brauch jetzt so etwas mehr Platz, passt aber zum Glück noch
alles rein.
Beim Kompilieren drauf achten dass beim Linker die Startadresse 0xE00
(word) steht.
So, ich habs angepasst, jetzt wird nur noch bei Ungleichheit der Flash
neu beschrieben.
Habe außerdem noch eine Vereinfachung vorgenommen, da ich Adresse 0
(Sprung zu <__ctors_end>) ja auch mit der passenden Adresse
überschreibe, brauche ich nicht mehr unterscheiden zu welcher Adresse
ich zum Programmstart springen muss, ich starte einfach immer von
Adresse 0, von dort wird dann an die richtige Stelle im Flash
gesprungen.
Falls jemand Lust hat den Code um 50 Bytes zu verkleinern, würde das
ganze noch in einen Bootloader mit 512kB Größe passen ;) Irgendwie muss
das mit dem Array ja doch funktionieren...
Und noch ein Update ;)
Ich habs nochmal mit einem Array versucht, jetzt hats komischerweise
geklappt.
Bin jetzt auf 430 kB herunter gekommen, also Bootloader eine Stufe
kleiner gemacht:
hfuse: 0xCC
lfuse: 0xFF
Wenn's nur der Code vom Bootloader ist, das Anbei compiliert bei mir zu
348 Bytes.
In obigem Code sind für meinen Geschmack zu viel magische Zahlen
drinne...
Wenn ich mich recht erinnere gibt's im USBasp Ansätze für einen
USB->UART Converter, die aber wohl nie komplett ausgeführt wurden.
Die Basisfunktionalität zum USB-Handling gibt's im USBasp bereits,
vielleicht ist es ja gar nicht sooo viel Arbeit, des Entsprechende
Interface und Endpoints zu ergänzen.
Am Protokoll selbst ändert sich doch nix, oder? Das zu erweitern wäre
freilich kein Spaß, weil das Taktgenau in Assembler geschieht...
Vielen Dank für die Hilfe!
Bei mir im AVR-Studio 5 kompiliert dein Code allerdings nicht, folgende
Zeile ergibt den Fehler "Error 1 expected '=', ',', ';', 'asm' or
'__attribute__' before 'uint8_t'"
static const __flash uint8_t www_fischl[] =
Und ich habe mal die lss vom Original USBasp mit den Werten verglichen
die dein Bootloader dorthin schreibt, die stimmen allerdings nicht
überein, damit dürfte das ganze doch nicht direkt so funktionieren oder?
Page0 geht ja bis Adresse 0x40.
Im Anhang ist die lss die beim Kompilieren des USBasp generiert wurde.
Markus H. schrieb:> Vielen Dank für die Hilfe!>> static const __flash uint8_t www_fischl[] =
Ah, sorry für die Zeile.
Für ältere Compilerversionen braucht du da PROGMEM + pgm_read.
> Und ich habe mal die lss vom Original USBasp mit den Werten verglichen> die dein Bootloader dorthin schreibt, die stimmen allerdings nicht> überein, damit dürfte das ganze doch nicht direkt so funktionieren oder?
Ich hatte mit Standardeinstllungen gelinkt, abgesehen von --defsym
start=0
Wenn ich's recht sehe hat der Originalcode keine Daten in .progmem, der
neue aber wohl.
Hast du ein eigenes Linkerskript oder schubst Sections per Hand duch die
Gegend, die .progmem nicht berücksichtigen?
Ausserdem ist mein Code natütlich ungetestet und kann Tippfehler
enthalten.
Johann L. schrieb:> Wenn ich's recht sehe hat der Originalcode keine Daten in .progmem, der> neue aber wohl.>> Hast du ein eigenes Linkerskript oder schubst Sections per Hand duch die> Gegend, die .progmem nicht berücksichtigen?
Tut mir leid, ich versteh nich ganz was du meinst, ist das erste Projekt
wo ich mich überhaupt mal mit dem Linker auseinander setze ;)
Also ich habe einfach den Quellcode vom USBasp heruntergeladen und
kompiliert (alles Standardeinstellungen) und mir dann die ersten 64 Byte
des Hex-Files angesehen und die einfach mehr oder weniger in meinen
Bootloader abgetippt (deshalb auch die undurchsichtigen Zahlenwerte), so
dass dieser dann immer wieder den Originalcode wiederherstellen kann.
Beim avr-cdc habe ich als Linker Option
"-Wl,--section-start=.text=0x1100" angegeben, damit wird ja einfach der
komplette Code an Stelle 0x1100 geschoben, incl. Interrupt Tabelle und
allem. Diesen Wert habe ich einfach blind festgesetzt, ein paar Byte
vorher ist ja der Code des USBasp zuende.
Damit hatte ich dann USBasp und avr-cdc zusammen auf dem Controller,
einfach hintereinander.
Ansonsten habe ich nichts mit dem Linker gemacht, alles soweit
Standardeinstellungen.
Was mich an deinem Code jetzt gewundert hat, ist dass er ja scheinbar
andere Werte an Page0 schreibt als ursprünglich vom USBasp dort
drinstehen. Ich habe ja versucht 1:1 den Zustand wiederherzustellen, das
einzige was sich beim avr-cdc Aufruf ändert sind die 2 Sprungbefehle
vorne in der Interrupt Tabelle.
Ich hoffe das war jetzt verständlich ;)
Ich habe meinen Code ja jetzt mittlerweile auch <512 Byte, d.h. eine
Verkleinerung bringt auch eh nix, solang man nicht unter die 256 Byte
kommt um den Bootloader eine Stufe kleiner zu kriegen..