Forum: Mikrocontroller und Digitale Elektronik Fehlerhafte Ausführung eines AVR C-Programms


von Marcus Woletz (Gast)


Lesenswert?

Hallo Leute,

ich bin gerade dabei, mich mit C für AVR zu beschäftigen. In C 
porgrammiere ich schon lange, allerdings nicht für den AVR. Dort war 
bisher Assembler angesagt. Also, folgendes Programm tut nicht das, was 
ich erwarte:

#include <avr/io.h>

int main (void)
{
   DDRB  = 0x02; /* PB1 ist Ausgang */
   PORTB = 0x00; /* PB1 ausschalten */

   volatile uint8_t i = 0;

   /* müsste eigentlich Endlosschleife sein */
   while (i < 1)
   {
   }

   /* Programm kommt fälschlicherweise hier an! */
   PORTB = 0x02; /* PB1 einschalten */

   /* Endlosschleife */
   while (1)
   {
   }

   return (0);
}

PB2 wird eingeschaltet, obwohl das Programm eigentlich gar nicht in
der entsprechenden Zeile ankommen dürfte. Prinzipiell funktioniert die 
Verwendung der Variablen, z.B. sowas:

i = 0x02;
PORTB = i;

oder

i = 0;
++i;
++i;
PORTB = i;

Compiler ist avr-gcc. Download aus hex-Datei. Die avr-libc wird nicht 
eingebunden.

Der vom gcc erzeugte Assemblerquelltext sieht bis auf die beiden .global 
am Anfang eigentlich recht übersichtlich aus:


  .file  "togglefast.c"
  .arch atmega48
_SREG_ = 0x3f
_SP_H_ = 0x3e
_SP_L_ = 0x3d
_tmp_reg_ = 0
_zero_reg_ = 1
  .global __do_copy_data
  .global __do_clear_bss
  .text
.global  main
  .type  main, @function
main:
/* prologue: frame size=1 */
  push r28
  push r29
  in r28,__SP_L__
  in r29,__SP_H__
  sbiw r28,1
  in _tmp_reg_,__SREG__
  cli
  out _SP_H_,r29
  out _SREG_,__tmp_reg__
  out _SP_L_,r28
/* prologue end (size=10) */
  ldi r24,lo8(2)
  out 36-0x20,r24
  out 37-0x20,__zero_reg__
  std Y+1,__zero_reg__
.L2:
  ldd r24,Y+1
  tst r24
  breq .L2
  ldi r24,lo8(2)
  out 37-0x20,r24
.L4:
  rjmp .L4
/* epilogue: frame size=1 */
/* epilogue: noreturn */
/* epilogue end (size=0) */
/* function main size 20 (10) */
  .size  main, .-main
/* File "togglefast.c": code   20 = 0x0014 (  10), prologues  10, 
epilogues   0 */


Hat jemand eine Idee, was hier nicht funktioniert? Ich freue mich über 
jede Rückmeldung. Vielen Dank schon an dieser Stelle.

ciao

Marcus

von Kernighan (Gast)


Lesenswert?

> In C porgrammiere ich schon lange, allerdings nicht für den AVR...

LOL,
Ritchie

von Marcus Woletz (Gast)


Lesenswert?

???

war das LOL wegen des Schreibfehlers?

ciao

Marcus

von Roland P. (pram)


Lesenswert?

is schon ne Weile her mein Assembler

  std Y+1,__zero_reg__    ' Hier wird in RAM[1] eine 0 geschrieben
.L2:
  ldd r24,Y+1             ' RAM[1] auslesen
  tst r24                 ' testen
  breq .L2                ' bei gesetztem Zero-Flag zu L2

sieht auf (meinen) ersten Blick korrekt aus.

hast du den Controllertyp richtig gesetzt (lt. gcc-Output wurde dies für 
einen Atmega48 compiliert)

Du kannst dir mal das AVRStudio anschauen, da ist ein Simulator dabei.

Gruß
Roland

von Andreas W. (Gast)


Lesenswert?

nicht das du eine LED am Port hast und diese LOW-Aktiv ist. :)

von Florian P. (db1pf)


Lesenswert?

Hallo,

bau mal in die while-Schleifen ein asm("nop"); ein.

Unter umständen optimiert der Compiler die Schleife weg, da diese so ja 
anscheinend keinen Sinn haben.


Grüße,
Florian

von Marcus Woletz (Gast)


Lesenswert?

Hallo Andreas,

vielen Dank für Deine Antwort. Nein, die LED passt schon, da sich diese 
ja ein- und ausschalten lässt. Andere Testprogramm funktionieren ja auch 
wie gewünscht...

ciao

Marcus

von Marcus Woletz (Gast)


Lesenswert?

Hallo Florian,

vielen Dank für Deine Antwort. Ich werde das mit dem nop mal testen, 
allerdings sieht der Assembler-Output des Compilers ja i.O. aus, und 
auch die "while(1)"-Schleife funktioniert ja wie gewünscht. Der Compiler 
darf ja gerne optimieren, aber durch das Optimieren sollte sich das 
Programmverhalten ja eigentlich nicht ändern. Deshalb habe ich mir ja 
auch den Assembler-Output angeschaut.

ciao

Marcus

von Andreas W. (Gast)


Lesenswert?

mhh,
ich sehe keinen fehler, auch der ASM-code sieht in meinen Augen ok aus. 
ich habe aber schon ewig nichtmehr in ASM gecoded. Da verliert man dann 
leicht den Blick für eine Sprache.

von Peter (Gast)


Lesenswert?

Gibt's keine Fehlermeldungen oder Warnings beim compilieren...?

von yalu (Gast)


Lesenswert?

> Die avr-libc wird nicht eingebunden.

Mit -nostdlib? Das ist der Fehler.

Die AVR-Libc stellt u.a. den Startupcode bereit, der vor dem Aufruf von
main ausgeführt wird und verschiedene Dinge initialisiert, u.a. den
Stackpointer und statische Variablen. Insbesondere wird auch das
Register R1, das fortan als Nullregister verwendet wird, mit 0
initialisiert. Der Befehl
1
  std Y+1,__zero_reg__

benutzt nun genau dieses Register, um sie der Variable i zuzuweisen.
Ist R1 nicht initialisiert, wird i ein undefinierter Wert zugewiesen,
der in deinem Fall offensichtlich von 0 verschieden ist.

Lass das -nostdlib weg, und dein Programm wird das Gewünschte tun.

von Marcus Woletz (Gast)


Lesenswert?

Hallo yalu,

Bingo! Tja, das sind bei mir typische Startprobleme. Ich starte gerne 
minimalistisch, und daraus resultierte auch meine tool chain:

avr-gcc -S (Assembler output)
avr-as
avr-objcopy (elf nach hex)

Das ganze dann mit einem selbst erstellten Programm und passendem
Druckerport-Adapter in den AVR übertragen.

Mich hatten ja die sections

  .global __do_copy_data
  .global __do_clear_bss

etwas irritiert. Aber ich dachte: "kein Tool meckert, dann wird's schon 
passen". Ich hatte mich dann nach meinem erfolgreichen Versuch (mit gcc 
direkt das ausführbare Programm erzeugen) noch mal mit meiner ersten 
tool chain beschäftigt. Ich sehe nicht, wie ich as oder ld dazu bringen 
könnte, mir den passenden Startcode doch noch mit einzubauen. Außerdem 
ist mir momentan unklar, woher dieser Startcode kommt. Ich vermute mal 
nicht von der avr-glibc, denn die habe ich meinem Compiler (zumindest 
nicht wissentlich) bekannt gemacht. Werde aber mal schauen, ob die 
avr-glibc-Funktionen doch ohne Fehler übersetzt bzw. belinkt werden.
Ach ja: System ist openSUSE 10.3

Vielleicht kann ja noch jemand ein wenig Licht ins Dunkel bringen.
Ich werde mir dann parallel noch weiter Lektüre zusammensuchen.

Vielen Dank nochmal für alle Antworten.

ciao

Marcus

von yalu (Gast)


Lesenswert?

Der übliche (und sicherste) Weg ist es, für C-programme den Assembler
und Linker über avr-gcc aufzurufen und nicht direkt als avr-as und
avr-ld. Dann werden automatisch das richtige Startup-Modul
(avr?/crt*.o), die richtigen Libraries (avr?/libgcc.a und avr?/libc.a)
und das richtige Linker-Skript (avr?.x*) ausgewählt, passend zu dem mit
der Option -mmcu angegebenen Controllertyp. Beim direkten Aufruf von
avr-ld müssen diese Informationen von Hand zusammengetragen werden, und
die Gefahr ist groß, dass man dabei einen Fehler macht, der vom Linker
nicht gemeldet wird, aber zum Versagen des gelinkten Programms führt.

Wenn dein Programm nur aus einem Modul main.c besteht, sind die Schritte
die folgenden:
1
  avr-gcc -mmcu=atmega48 -Os -o main.elf main.c  # kompilieren und linken
2
  avr-objcopy -O ihex main.elf main.hex          # in Hex konvertieren

Besteht das Programm aus mehreren Modulen (main.c, cmod1.c und cmod2.c),
geht es folgendermaßen:
1
  avr-gcc -mmcu=atmega48 -Os -c main.c           # kompilieren
2
  avr-gcc -mmcu=atmega48 -Os -c cmod1.c          # kompilieren
3
  avr-gcc -mmcu=atmega48 -Os -c cmod2.c          # kompilieren
4
  avr-gcc -mmcu=atmega48 -Os -o main.elf main.o cmod1.o cmod2.o  # linken
5
  avr-objcopy -O ihex main.elf main.hex          # in Hex konvertieren

Beim Linken kommt zusätzlich moch die Option -lm ans Ende der Zeile,
wenn dein Programm Floating-Point verwendet.

Selbstgeschrieben Assembler-Module (amod.S) werden wie folgt
assembliert:
1
  avr-gcc -mmcu=atmega48 -c amod.S               # assemblieren

und anschließend wie oben bschrieben gelinkt.

Meist werden die genannten Schritte zum einem Makefile zusammengefasst,
das man entweder selbst schreibt oder sich mit entsprechenden Tools
(Mfile) generieren lässt.

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.