Forum: Mikrocontroller und Digitale Elektronik 2 Programme in einem AVR


von Markus H. (Gast)


Lesenswert?

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

von Thomas E. (thomase)


Lesenswert?

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.

von Markus H. (Gast)


Lesenswert?

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.

von Thomas E. (thomase)


Lesenswert?

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.

von H.Joachim S. (crazyhorse)


Lesenswert?

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.

von Thomas E. (thomase)


Lesenswert?

Thomas Eckmann schrieb:
> In AVR-Studio 4.x, Studio verweigere ich:
Das sollte Studio5 heissen. Ich kann das kaum schreiben.

mfg.

von Markus H. (Gast)


Lesenswert?

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?

von Achim M. (minifloat)


Lesenswert?

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

von Okram (Gast)


Lesenswert?

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?

von Markus H. (Gast)


Lesenswert?

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?

von Markus H. (Gast)


Lesenswert?

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:

1
unsigned char it1[] = { 0x3B,0xC0,0x39,0xC7,0x54,0xC0,0x53,0xC0,0x52,0xC0,0x51,0xC0,0x50,0xC0,0x4F,0xC0,0x4E,0xC0,0x4D,0xC0,0x4C,0xC0,0x4B,0xC0,0x4A,0xC0,0x49,0xC0,0x48,0xC0,0x47,0xC0,0x46,0xC0,0x45,0xC0,0x44,0xC0,0x04,0x03,0x09,0x04,0x1C,0x03,0x77,0x00,0x77,0x00,0x77,0x00,0x2E,0x00,0x66,0x00,0x69,0x00,0x73,0x00,0x63,0x00,0x68,0x00,0x6C,0x00};
2
3
void boot_program_page (uint32_t page, uint8_t *buf) {
4
  uint8_t i;
5
  uint8_t sreg;
6
7
  // Disable interrupts.
8
  sreg = SREG;
9
  cli();
10
  eeprom_busy_wait ();
11
  boot_page_erase (page);
12
  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_t w = *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
int main()
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
    return 0;
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

von Markus H. (Gast)


Lesenswert?

Wenn ich dies:
1
for (i=0; i<SPM_PAGESIZE; i+=2) {
2
  // Set up little-endian word.
3
    uint16_t w = *buf++;
4
    w += (*buf++) << 8;
5
   
6
  }


durch folgendes ersetze:
1
for (i=0; i<SPM_PAGESIZE; i+=2) {
2
    boot_page_fill (page+i, i+((i+1)<<8));
3
  }


steht das Richtige im Flash! Also funktioniert die Übergabe des Arrays 
nicht richtig?! Doch wo ist der Fehler?

von Markus H. (Gast)


Lesenswert?

Markus H. schrieb:
> for (i=0; i<SPM_PAGESIZE; i+=2) {
>   // Set up little-endian word.
>     uint16_t w = *buf++;
>     w += (*buf++) << 8;
>
>   }


ich habe
1
 boot_page_fill (page+i, w);
 vergessen, dies steht natürlich auch noch in der For-Schleife...

von Falk B. (falk)


Lesenswert?

@  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.

von Markus H. (Gast)


Lesenswert?

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.

von Oliver J. (skriptkiddy)


Lesenswert?

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

von Markus H. (Gast)


Angehängte Dateien:

Lesenswert?

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.

von Markus H. (Gast)


Lesenswert?

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

von Oliver J. (skriptkiddy)


Lesenswert?

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

von Markus H. (Gast)


Angehängte Dateien:

Lesenswert?

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.

von Markus H. (Gast)


Angehängte Dateien:

Lesenswert?

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...

von Markus H. (Gast)


Angehängte Dateien:

Lesenswert?

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

von Johann L. (gjlayde) Benutzerseite


Angehängte Dateien:

Lesenswert?

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...

von Markus H. (Gast)


Angehängte Dateien:

Lesenswert?

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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.

von Markus H. (Gast)


Lesenswert?

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..

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.