Forum: Mikrocontroller und Digitale Elektronik AVR bootloader Probleme


von kevin (Gast)


Lesenswert?

Hallo mikrocontroller Gemeinde,

ich habe mir den Artikel "AVR Bootloader in C - eine einfache Anleitung" 
hier durchgelesen und versuche nun den Bootloader zum laufen zu bekommen 
um ein HEX File damit zu schreiben..

Leider macht mein µC dabei komische Sachen.

Ich verwende einen AT90CAN128, habe die fuses eingestellt und das -Ttext 
Flag beim Linker auf den richtigen Wert gesetzt, funktioniert alles 
Prima - bootloader startet und meldet sich über den UART.

Nun sobald ich versuche eine Datei zu schreiben ('p') macht er beim 
ersten Zeichen, das ich ihm sende ein Reset.

Ich habe durchgetestet und herausgefunden an welcher Stelle er resetted:

(Anfang der main Schleife)
1
do
2
    {
3
        c = uart_getc();
4
        if( !(c & UART_NO_DATA) )
5
        {
6
             /* Programmzustand: Parser */
7
             if(boot_state == BOOT_STATE_PARSER)
8
             {
9
                  // HIER KOMMT ER NOCH HIN
10
                  switch(parser_state)
11
                  {
12
                      /* Warte auf Zeilen-Startzeichen */
13
                      case PARSER_STATE_START:
14
        // HIER NICHT MEHR
15
                          if((uint8_t)c == START_SIGN) 
16
                          {

.. obwohl parser_state auf PARSER_STATE_START stehen MUSS

Nun kommt der oberwitz: frage ich direkt über dem switch(..) ab, ob 
parser_state PARSER_STATE_START ist
1
if (parser_state == PARSER_STATE_START) {
2
          PORTC ^= (1 << PC0);
3
        }

dann funktioniert alles :/

Der compiler macht dann auch einen ganz anderen assembler code daraus:

ohne IF Abfrage:
1
      /* Programmzustand: Parser */
2
      if(boot_state == BOOT_STATE_PARSER)
3
   1e274:  21 e0         ldi  r18, 0x01  ; 1
4
   1e276:  72 12         cpse  r7, r18
5
   1e278:  06 c1         rjmp  .+524      ; 0x1e486 <main+0x2b6>
6
      {
7
        switch(parser_state)
8
   1e27a:  89 2d         mov  r24, r9
9
   1e27c:  90 e0         ldi  r25, 0x00  ; 0
10
   1e27e:  87 30         cpi  r24, 0x07  ; 7
11
   1e280:  91 05         cpc  r25, r1
12
   1e282:  08 f0         brcs  .+2        ; 0x1e286 <main+0xb6>
13
   1e284:  52 c1         rjmp  .+676      ; 0x1e52a <main+0x35a>
14
   1e286:  fc 01         movw  r30, r24
15
   1e288:  e6 5b         subi  r30, 0xB6  ; 182
16
   1e28a:  ff 40         sbci  r31, 0x0F  ; 15
17
   1e28c:  9d c2         rjmp  .+1338     ; 0x1e7c8 <__tablejump2__>
18
        {
19
          /* Warte auf Zeilen-Startzeichen */
20
          case PARSER_STATE_START:
21
          if((uint8_t)c == START_SIGN)
22
   1e28e:  8a e3         ldi  r24, 0x3A  ; 58
23
   1e290:  e8 12         cpse  r14, r24
24
   1e292:  4b c1         rjmp  .+662      ; 0x1e52a <main+0x35a>

mit IF Abfrage:
1
      /* Programmzustand: Parser */
2
      if(boot_state == BOOT_STATE_PARSER)
3
   1e266:  21 e0         ldi  r18, 0x01  ; 1
4
   1e268:  32 12         cpse  r3, r18
5
   1e26a:  18 c1         rjmp  .+560      ; 0x1e49c <main+0x2da>
6
      {
7
        if (parser_state == PARSER_STATE_START) {
8
   1e26c:  71 10         cpse  r7, r1
9
   1e26e:  10 c0         rjmp  .+32       ; 0x1e290 <main+0xce>
10
          PORTC ^= (1 << PC0);
11
   1e270:  88 b1         in  r24, 0x08  ; 8
12
   1e272:  83 25         eor  r24, r3
13
   1e274:  88 b9         out  0x08, r24  ; 8
14
        }
15
        switch(parser_state)
16
        {
17
          /* Warte auf Zeilen-Startzeichen */
18
          case PARSER_STATE_START:
19
          if((uint8_t)c == START_SIGN)
20
   1e276:  8a e3         ldi  r24, 0x3A  ; 58
21
   1e278:  48 12         cpse  r4, r24
22
   1e27a:  62 c1         rjmp  .+708      ; 0x1e540 <main+0x37e>

Leider kenne ich mich mit assembler nicht so gut aus, aber ich denke die 
unterscheiden sich ganz massiv.. warum der reset stattfindet würde mich 
sehr interessieren

jemand eine Idee??

mfg kevin

von holger (Gast)


Lesenswert?

>Nun sobald ich versuche eine Datei zu schreiben ('p') macht er beim
>ersten Zeichen, das ich ihm sende ein Reset.

Folgende Dinge führen häufig zu einem Reset:

Interrupt freigeschaltet und kein Interruptvektor dafür vorhanden.
Stackoverflow.
Arraygrenzen überschritten.
Watchdog aktiv.

Mit deinen Codeschnipseln kann kein Mensch was anfangen,
oder das Problem finden. Das liegt vermutlich ganz woanders.

von kevin (Gast)


Lesenswert?

Hallo Holger

> Interrupt freigeschaltet und kein Interruptvektor dafür vorhanden.
ich habe ISR(BADISR_vect) { } definiert, sollte also nicht passieren

> Stackoverflow.
1. Wie kann ich das herausfinden? 2. Sollte avr-gcc doch so schlau sein 
keinen stack overflow zu erzeugen, oder?

> Arraygrenzen überschritten.
An der Stelle wird auf kein Array zugegriffen

> Watchdog aktiv.
Das werde ich mal prüfen, ist aber wohl auch eher unwahrscheinlich da 
der reset immer bei der gleichen aktion auftritt.

Den code habe ich 1:1 aus dem artikel übernommen
http://www.mikrocontroller.net/articles/AVR_Bootloader_in_C_-_eine_einfache_Anleitung

von kevin (Gast)


Lesenswert?

watchdog ist nicht aktiv, dann bleibt wohl nur noch stack overflow

hmm

von kevin (Gast)


Lesenswert?

Mit Atmel Studio 6.0 funktionierts, gleiches Programm, habe es vorher 
nur mit  Atmel Studio 6.2 probiert.

von kevin (Gast)


Lesenswert?

Ich habe nun die compilereinstellungen der beiden Versionen abgeglichen, 
bei 6.2 immernoch das gleiche Verhalten.

AVR GCC Versionen: 4.6.2 (Atmel Studio 6.0) und 4.8.1 (Atmel Studio 6.2)

Kann doch nur ein Compilerproblem sein, wenn alle Einstellungen + 
Programm identisch sind und es beim einen geht, beim anderen nicht, ... 
?

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

kevin schrieb:
> if((uint8_t)c == START_SIGN)
>    1e28e:  8a e3         ldi  r24, 0x3A  ; 58
>    1e290:  e8 12         cpse  r14, r24
>    1e292:  4b c1         rjmp  .+662      ; 0x1e52a <main+0x35a>

kevin schrieb:
> if((uint8_t)c == START_SIGN)
>    1e276:  8a e3         ldi  r24, 0x3A  ; 58
>    1e278:  48 12         cpse  r4, r24
>    1e27a:  62 c1         rjmp  .+708      ; 0x1e540 <main+0x37e>

 Einmal wird r14 auf empfangenes Zeichen geprüft (ohne Abfrage),
 im anderen Fall wird r4 geprüft (mit Abfrage).
 Du machst da zwar cast auf uint8, aber das sieht man nicht, also
 muss es weiter oben gemacht worden sein (wenn uberhaupt).
 Und warum Compiler das machen sollte - auch mit Optimisierung -
 ist fur mich unverständlich.
 Wie ist 'UART_NO_DATA' definiert ?

von kevin (Gast)


Lesenswert?

Es ist die UART lib von peter fleury, hier die definitionen der return 
codes:
1
#define UART_FRAME_ERROR      0x1000              /* Framing Error by UART       */
2
#define UART_OVERRUN_ERROR    0x0800              /* Overrun condition by UART   */
3
#define UART_PARITY_ERROR     0x0400              /* Parity Error by UART        */ 
4
#define UART_BUFFER_OVERFLOW  0x0200              /* receive ringbuffer overflow */
5
#define UART_NO_DATA          0x0100              /* no receive data available   */

von kevin (Gast)


Lesenswert?

Optimierung steht auf optimize for size, habe aber auch schon alles 
andere probiert.

Falls interesse besteht kann ich das programm mal hochladen

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

kevin schrieb:
> #define UART_NO_DATA          0x0100              /* no receive data

kevin schrieb:
> if( !(c & UART_NO_DATA) )

 Also, ich verstehe diese komische Abfrage überhaupt nicht...
 Ob du ein Zeichen empfangen hast oder nicht - das Ergebnis ist für
 mich immer dasselbe.
 (c & UART_NO_DATA) = (c & 0x0100) = 0x00

 somit ist deine Behauptung:
> .. obwohl parser_state auf PARSER_STATE_START stehen MUSS

 schon mal nicht zutreffend.
 Dein Programm ist ohne ein Zeichen zu empfangen zum SWITCH angelangt.
 Jedenfalls sehe ich das so.

von Stefan E. (sternst)


Lesenswert?

Marc Vesely schrieb:
> Also, ich verstehe diese komische Abfrage überhaupt nicht...
>  Ob du ein Zeichen empfangen hast oder nicht - das Ergebnis ist für
>  mich immer dasselbe.
>  (c & UART_NO_DATA) = (c & 0x0100) = 0x00

Nein, denn c ist größer als 8 Bit (sollte es jedenfalls sein). Die 
Fleury-Lib gibt nun mal in den unteren 8 Bit das empfangene Zeichen und 
in den oberen 8 Bit zusätzliche Statusinformationen zurück. Deshalb auch 
der Cast auf 8-Bit bei dem späteren Vergleich.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Stefan Ernst schrieb:
> Nein, denn c ist größer als 8 Bit (sollte es jedenfalls sein). Die
> Fleury-Lib gibt nun mal in den unteren 8 Bit das empfangene Zeichen und
> in den oberen 8 Bit zusätzliche Statusinformationen zurück. Deshalb auch
> der Cast auf 8-Bit bei dem späteren Vergleich.

 Ja, cast hab ich gesehen, aber nicht in den gesendetem assembler-code,
 also muss es schon vorher gemacht worden sein. Und wo soll der Compiler
 das gemacht haben, ausser bei der Abfrage ?

von Stefan E. (sternst)


Lesenswert?

Marc Vesely schrieb:
> Ja, cast hab ich gesehen, aber nicht in den gesendetem assembler-code,
>  also muss es schon vorher gemacht worden sein. Und wo soll der Compiler
>  das gemacht haben, ausser bei der Abfrage ?

Was soll er denn da weiter "machen"? Der Cast im Vergleich sagt doch nur
"beschneide c für diesen Vergleich auf die unteren 8 Bit". Und genau das 
macht der Assembler-Code, denn er vergleicht nur das untere Byte.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Stefan Ernst schrieb:
> Was soll er denn da weiter "machen"? Der Cast im Vergleich sagt doch nur
> "beschneide c für diesen Vergleich auf die unteren 8 Bit". Und genau das
> macht der Assembler-Code, denn er vergleicht nur das untere Byte.

 Einmal r14/r15 und das andere mal r4/r5 nehmen, obwohl der code bis zur
 Abfrage Zeile für Zeile gleich ist ?

von kevin (Gast)


Lesenswert?

Das sind verschiedene Abfragen, erst wird mit (!(c & UART_NODATA)) 
geprüft, ob ein Zeichen empfangen wurde, (c ist uint16), dann wird 
geprüft, ob das empfangene Zeichen gleich START_SIGN, also ':' ist.

Ich habe mir das auch nicht selbst ausgedacht sondern - wie gesagt - es 
ist das Programm aus dem mikrocontroller.net Artikel.

Und es funktioniert mit Atmel Studio 6 einwandfrei, mit 6.2 habe ich 
dieses komische Verhalten, leider verstehe ich den Assembler code nicht 
und habe deshalb keine Idee wo genau nun das Problem liegen könnte, 
interessieren würde es mich aber schon.

von kevin (Gast)


Angehängte Dateien:

Lesenswert?

anbei der sourcecode - das programm ist in test.c, die uart lib habe ich 
der Vollständigkeit halber mal angehängt.

Und die beiden .lss einmal mit 6.0 und einmal mit Atmel Studio 6.2 
kompiliert..

von Stefan E. (sternst)


Lesenswert?

Marc Vesely schrieb:
> Einmal r14/r15 und das andere mal r4/r5 nehmen

c steckt halt in den beiden Fällen in unterschiedlichen Registern.

Marc Vesely schrieb:
> obwohl der code bis zur
>  Abfrage Zeile für Zeile gleich ist ?

In dem gezeigten Assembler-Code vor dem Vergleich kommt c gar nicht vor, 
also was soll das "obwohl" hier bedeuten?

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Stefan Ernst schrieb:
> In dem gezeigten Assembler-Code vor dem Vergleich kommt c gar nicht vor,
> also was soll das "obwohl" hier bedeuten?

kevin schrieb:
> c = uart_getc();
>         if( !(c & UART_NO_DATA) )

 Und da du ja so ein Genie bist, kann Kevin die Lösung seines Problems
 in den nächsten 5 min. erwarten.

von Stefan E. (sternst)


Lesenswert?

Bei der nicht funktionierenden Variante wird das switch über eine 
Sprungtabelle realisiert. Diese liegt hinter den Interrupt-Vectoren. 
Beim Auslesen des Tabelleneintrags wird nur ein lpm verwendet (kein 
elpm/RAMPZ). Bei einem Bootloader geht das auf Grund seiner besonderen 
Adresslage in die Hose und es kracht.

Ich würde es mit -fno-jump-tables versuchen.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Stefan Ernst schrieb:
> Beim Auslesen des Tabelleneintrags wird nur ein lpm verwendet (kein
> elpm/RAMPZ). Bei einem Bootloader geht das auf Grund seiner besonderen
> Adresslage in die Hose und es kracht.

 Bravo.
 Hab kein Problem zuzugeben, wenn jemand Recht hat und du hast Recht.
 Obwohl es mehr als 5 min. gedauert hat ;)

von kevin (Gast)


Lesenswert?

Vielen Dank Stefan, das flag werde ich mal probieren.

Heißt also, dass solche Probleme auch mit einer älteren avr-gcc Version 
auftreten könnten und ich nur das Glück hatte, dass hier keine 
Sprungtabelle verwendet wurde, oder liege ich da falsch?

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.