Forum: Compiler & IDEs Inkonsistentes Verhalten einfachem avr-gcc-codes


von Sebastian Götte (Gast)


Angehängte Dateien:

Lesenswert?

Hi,
ich sitze hier seit Stunden vor einem eigenartigem Problem, dessen Grund 
ich nicht finden kann.

Der folgende Code geht (Geflacker auf PD1/TX):
1
int main(void){
2
    wdt_disable();
3
    //Serial port setup
4
    DDRD = 0x02;
5
    uart_init(UART_BAUD_SELECT_DOUBLE_SPEED(115200, F_CPU));
6
    sei();
7
    for(;;){
8
        char* s = "foobar\n";
9
        while (*s) {
10
          uart_putc('\n');
11
          *(s++);
12
        }
13
14
        //uart_putc('\n');
15
    }
16
}

Der folgende Code geht nicht (keine Aktivität auf PD1/TX)
1
int main(void){
2
    wdt_disable();
3
    //Serial port setup
4
    DDRD = 0x02;
5
    uart_init(UART_BAUD_SELECT_DOUBLE_SPEED(115200, F_CPU));
6
    sei();
7
    for(;;){
8
        char* s = "foobar\n";
9
        //while (*s) {
10
        //  uart_putc('\n');
11
        //  *(s++);
12
        //}
13
14
        uart_putc('\n');
15
    }
16
}

Der folgende Code geht wiederum:
1
int main(void){
2
    wdt_disable();
3
    //Serial port setup
4
    DDRD = 0x02;
5
    uart_init(UART_BAUD_SELECT_DOUBLE_SPEED(115200, F_CPU));
6
    sei();
7
    for(;;){
8
        char* s = "foobar\n";
9
        while (*s) {
10
          uart_putc(*(s++));
11
        }
12
13
        uart_putc('\n');
14
    }
15
}

Die verwendete avr-gcc-Kommandozeile ist
1
avr-gcc -Wall -fshort-enums -fno-inline-small-functions -fpack-struct -Wall -fno-strict-aliasing -funsigned-char -funsigned-bitfields -ffunction-sections -mmcu=atmega328p -DF_CPU=16000000 -std=gnu99 -Os -o main.elf -Wl,--gc-sections,--relax $^
2
        avr-objcopy -O ihex main.elf main.hex
3
        avr-size main.elf
Das .lst sieht allerdings im nicht funktionierenden Fall o.k. aus (alles 
vorhanden, was hinein gehört).
Die verwendeten uart.c und uart.h sind im Anhang.

Weis hier irgendjemand, woher das oben beschriebene Verhalten kommt?

von pegel (Gast)


Lesenswert?

Klammer zu, es zieht ;)

von pegel (Gast)


Lesenswert?

Mist. Verzählt. Passt doch.

von Hans (Gast)


Lesenswert?

Ich finde diese Bedingung ziemlich schräg:

> while (*s)

von Rolf Magnus (Gast)


Lesenswert?

Hans schrieb:
> Ich finde diese Bedingung ziemlich schräg:
>
>> while (*s)

Was soll daran schräg sein? Ganz normales Stringhandling. Was eher 
schräg, wenn auch nicht unbedingt falsch ist, ist das:

Sebastian Götte schrieb:
> *(s++);

Wozu dereferenzieren, wenn das Ergebnis eh nicht benutzt wird? Aber 
vermutlich ist das einfach nur copy/paste beim Experimentieren.

Ich sehe eigentlich keinen Grund, warum eine der Varianten nicht 
funktionieren sollte, wenn die anderen tun. Alle Varianten auf dem 
selben Prozessor getestest, auch mehrfach? Vielleicht ist einfach beim 
Flashen was  schiefgegangen?

von Floh (Gast)


Lesenswert?

Sebastian Götte schrieb:
> Der folgende Code geht (Geflacker auf PD1/TX):

Hast du nur eine LED oder überprüfst du auch die ankommenden Zeichen?
Im ersten Fall könnte ich mit vorstellen, das '\n' einfach zu kurz (zu 
wenig Bits gesetzt) ist, um es an einer LED zu erkennen.

von Thomas F. (tomasf)


Lesenswert?

Das macht bestimmt nicht das, was du willst:
1
char* s = "foobar\n"

Versuch mal dies:
1
char s[] = "foobar\n"

von Peter II (Gast)


Lesenswert?

Thomas F. schrieb:
> Das macht bestimmt nicht das, was du willst:

doch macht es, ist ist beides das gleiche.

von Karl H. (kbuchegg)


Lesenswert?

Und um dem auch gleich (nochmal) den Wind aus den Segeln zu nehmen:
Die ersten beiden C Programme sind absolut gleichwertig. Es gibt keinen 
ersichtlichen Grund für unterschiedliches externes Verhalten.

von Oliver (Gast)


Lesenswert?

Thomas F. schrieb:
> Das macht bestimmt nicht das, was du willst:char* s = "foobar\n"

Das macht seit Tag 0 der Erfindung von C das, was du willst, und wurde 
bisher aus Kompatibilitätsgründen mit alten Programmen aus dieser Zeit 
auch nie abgeschafft. Schöner ist allerdings heutzutage wirklich

>char s[] = "foobar\n"

Oliver

von Programmierer (Gast)


Lesenswert?

Sebastian Götte schrieb:
> //Serial port setup
>     DDRD = 0x02;

Lass das mal weg. Wahrscheinlich ist TX ein input pin in open collector 
Beschaltung. Die Leitung wird auf GND gezogen, um ein Startbit 
anzukündigen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Thomas F. schrieb:
> Das macht bestimmt nicht das, was du willst:
> char* s = "foobar\n"
>
> Versuch mal dies:
> char s[] = "foobar\n"

Peter II schrieb:
> doch macht es, ist ist beides das gleiche.

Es ist nicht das gleiche; es hat in dem Programm oben lediglich die 
gleiche Wirkung.

Die erste Variante definiert ein char* Obkekt und initialisiert dies mit 
der Adresse eines String-Lietrals wie .LC0.

Die zweite Variante legt ein Array an un initialisiert dies.

von Karl H. (kbuchegg)


Lesenswert?

Programmierer schrieb:
> Sebastian Götte schrieb:
>> //Serial port setup
>>     DDRD = 0x02;
>
> Lass das mal weg.

Schaden kanns nicht. Ändern wirds nix.
Sobald die UART eingeschaltet wird, krallt sich diese die beiden Pins 
und übernimmt die Kontrolle über die Datenrichtung.

von Sebastian Götte (Gast)


Lesenswert?

Floh schrieb:
> Hast du nur eine LED oder überprüfst du auch die ankommenden Zeichen?
> Im ersten Fall könnte ich mit vorstellen, das '\n' einfach zu kurz (zu
> wenig Bits gesetzt) ist, um es an einer LED zu erkennen.

Ich teste den Code auf einem Arduino-Board (benutze aber nicht die 
Arduino-"Entwicklungsumgebung"). Dort flackert weder die tx-led (die in 
Software auch bei einzelnen Zeichen auf wahrnehmbare Pulsweiten 
gestreckt wird) noch sehe ich auf dem Computer irgendwas.

Thomas F. schrieb:
> Das macht bestimmt nicht das, was du willst:char* s = "foobar\n"
Kurioserweise macht der Code, in dem das nicht dristeht Probleme, der 
Code mit dieser unmodernen Formulierung funktioniert. Ich werd's aber 
gleich nochmal anders probieren.

Rolf Magnus schrieb:
> Wozu dereferenzieren, wenn das Ergebnis eh nicht benutzt wird? Aber
> vermutlich ist das einfach nur copy/paste beim Experimentieren.
Ursprünglich war das "*(s++)" Argument vom uart_putc in der Schleife. 
Der Code ist direkt aus der uart_puts-Funktion aus uart.c kopiert.

von Xenu (Gast)


Lesenswert?

Bei mir geht Dein Code. Wie Du das Led-Flackern sehen willst ist mir 
schleierhaft.

>Dort flackert weder die tx-led (die in Software auch bei einzelnen Zeichen
>auf wahrnehmbare Pulsweiten gestreckt wird)

Wo wird in Deinem Code irgendwas gestreckt?
Benutzt Du HTerm oder ein Terminal dass Dir die Newlines wegschluckt und 
nicht anzeigt?

von Bronco (Gast)


Lesenswert?

Sebastian Götte schrieb:
1
for(;;){
2
        char* s = "foobar\n";
3
        uart_putc('\n');
4
    }

Kann es sein, daß da vielleicht der Stack überläuft?

von Peter II (Gast)


Lesenswert?

Bronco schrieb:
> Kann es sein, daß da vielleicht der Stack überläuft?

nein, warum sollte er das?

von Sebastian Götte (Gast)


Lesenswert?

Xenu schrieb:
> Wo wird in Deinem Code irgendwas gestreckt?
> Benutzt Du HTerm oder ein Terminal dass Dir die Newlines wegschluckt und
> nicht anzeigt?

Auf dem Arduino Uno, den ich verwende wird ein ATMega16u2 als 
USB-Seriell-Wandler missbraucht. Dessen Firmware lässt die LEDs blinken.

Hier der letzte Testcode (geht auch nicht, ob mit oder ohne delays):
1
int main(void){
2
    wdt_disable();
3
    uart_init(UART_BAUD_SELECT_DOUBLE_SPEED(115200, F_CPU));
4
    sei();
5
    for(;;){
6
        char* s = "foobar\n";
7
        //uart_puts("foobar\n");
8
        
9
        //while (*s) {
10
        //  uart_putc(*(s++));
11
        //}
12
13
        uart_putc('f');
14
        _delay_us(1);
15
        uart_putc('o');
16
        _delay_us(1);
17
        uart_putc('o');
18
        _delay_us(1);
19
        uart_putc('b');
20
        _delay_us(1);
21
        uart_putc('a');
22
        _delay_us(1);
23
        uart_putc('z');
24
        _delay_us(1);
25
        uart_putc('\n');
26
        _delay_us(1);
27
    }
28
}

Als "Terminal" benutze ich einen Python-Dreizeiler:
1
#!/usr/bin/env python2
2
import serial
3
ser = serial.Serial("/dev/ttyACM0", 115200)
4
while True:
5
    print ser.readline()

Sobald ich die drei Zeilen
1
while (*s) {
2
    uart_putc(*(s++));
3
}
reinnehme, gehts wieder (und auf dem Terminal sehe ich "foobar" und 
"foobaz").

von Karl H. (kbuchegg)


Lesenswert?

> USB-Seriell-Wandler missbraucht. Dessen Firmware lässt die LEDs blinken.

Dann würde ich mich allerdings nicht auf das Flackern verlassen. Was 
immer die Firmware macht, du hast es nicht unter Kontrolle.

Wenn schon, dann eine LED+220 Ohm an die Tx Leitung. Damit kannst du 
eine Aussage treffen, aber auf irgendwelche Wandler, die irgendwie 
flackern und keiner weiß wie genau, würde ich nicht setzen.

von Markus (Gast)


Lesenswert?

Das Problem könnte der Python-Dreizeiler sein.

Das "nicht" funktionierende Programm gibt ja nur Leerzeilen aus. Was 
macht jetzt das Pythonprogramm beim Empfang von Leerzeilen?

von Sebastian Götte (Gast)


Lesenswert?

Das Pythonprogramm gibt dann "\n\n" aus. Also zwei Leerzeilen. Es 
funktioniert aber auch nicht, wenn ich wie im letzten Post beschrieben 
außer '\n' noch Buchstaben auf die Leitung spamme.

von ronny, malte und jaqueline (Gast)


Lesenswert?

Typischer Anfängerfehler. Nimm doch statt diesem fehleranfälligen C 
Gefrickel eine richtige Programmiersprache.

von Sebastian Götte (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Dann würde ich mich allerdings nicht auf das Flackern verlassen. Was
> immer die Firmware macht, du hast es nicht unter Kontrolle.

Ich vertrau dem schon, hier ist der Code:
https://github.com/arduino/Arduino/blob/master/hardware/arduino/firmwares/arduino-usbserial/Arduino-usbserial.c
Allerdings gibt ja auch das Python-Script keine Ausgabe.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

ronny, malte und jaqueline schrieb:
> Typischer Anfängerfehler. Nimm doch statt diesem fehleranfälligen C
> Gefrickel eine richtige Programmiersprache

Genau.  Nimm einfach Python.

von Volker U. (volkeru)


Lesenswert?

Johann L. schrieb:
> Thomas F. schrieb:
>> Das macht bestimmt nicht das, was du willst:
>> char* s = "foobar\n"
>>
>> Versuch mal dies:
>> char s[] = "foobar\n"
>
> Peter II schrieb:
>> doch macht es, ist ist beides das gleiche.
>
> Es ist nicht das gleiche; es hat in dem Programm oben lediglich die
> gleiche Wirkung.

Genau, eine sehr kluge Aussage! Da hat jemand das Pointer-Konzept bis in 
die Tiefe verstanden. Das muss man doch mal explizit loben. :)

> Die erste Variante definiert ein char* Obkekt und initialisiert dies mit
> der Adresse eines String-Lietrals wie .LC0.

Abhängig vom Compiler und der System-Architektur kann diese 
String-Konstante an unterschiedlichen Stellen im Speicher liegen. Häufig 
wird sie am Ende des Programm-Codes mit eingebunden, wo alle Konstanten 
liegen und befindet sich daher folglich im Speicherbereich für den Code. 
Sie ist daher u.U. nicht änderbar.

> Die zweite Variante legt ein Array an un initialisiert dies.

Da dieses Array dann eine Variable ist (und keine Konstante wie beim 
ersten Beispiel), liegt sie in dem Bereich, der für Variablen vorgesehen 
ist, also z.B. im Stack und ist daher auch stets änderbar.

Die Unterschiede in der Wirkung der beiden Definitionen können in der 
Praxis also ganz erheblich sein.

von MalteKnallte (Gast)


Lesenswert?

ronny, malte und jaqueline schrieb:
> Typischer Anfängerfehler. Nimm doch statt diesem fehleranfälligen C
> Gefrickel eine richtige Programmiersprache.

Würde eher sagen typisch dämliche, arrogante Anfängerbemerkung.

von Volker U. (volkeru)


Lesenswert?

Naja, wenn man das Konzept von C (noch) nicht richtig verstanden hat, 
kann man zu solchen Aussagen kommen. Ich erinnere mich noch lebhaft an 
die Zeit Ende der 80er, in der ich angefangen habe, mit dem 
Kernighan&Ritchie C zu lernen. Zuerst fand ich es super, als ich dann 
etwas mehr verstanden hatte, habe ich es verflucht und das Buch in die 
Ecke geworfen. Etwa ein Jahr später habe ich es dann wieder vorgeholt 
und ab dann erst begann das echte Verstehen. Das Konzept von C muss sich 
wirklich erst "setzen". Einige Jahre später erst begann die Begeisterung 
für die Sprache, weil man merkt, was man damit alles machen kann und wie 
extrem hoch sie sich aufgrund ihrer Architektur optimieren lässt.

Da C so völlig offen ist, kann man natürlich durch defines auch so viel 
vermurksen, dass es tatsächlich unübersichtlich und scheinbar unlogisch 
wird. Das hängt aber dann nicht mit der Sprache zusammen. Natürlich ist 
ein System, das sehr stark beschränkt ist, immer konsistenter und 
logischer. Ein Apple ist auch konsistenter als ein PC, aber auch nicht 
so flexibel. Es ist immer eine Frage der Philosophie und des 
Einsatzzwecks.

Ich habe schon von vielen gehört, dass das Erlernen von C in diesen drei 
oben genannten Phasen abläuft. Es gibt sogar Leute die sagen "Wer diese 
Phasen nicht durchlaufen und C nicht mindestens einmal verflucht hat, 
der hat die Sprache nicht wirklich verstanden" ;-).

von Karl H. (kbuchegg)


Lesenswert?

Volker U. schrieb:

> Die Unterschiede in der Wirkung der beiden Definitionen können in der
> Praxis also ganz erheblich sein.

Schon.
Aber nicht auf einem AVR mit avr-gcc.

So gesehen, war dieser Rat in der konkreten Situation nichts wert, weil 
irrelevant.

von Volker U. (volkeru)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Volker U. schrieb:
>
>> Die Unterschiede in der Wirkung der beiden Definitionen können in der
>> Praxis also ganz erheblich sein.
>
> Schon.
> Aber nicht auf einem AVR mit avr-gcc.

Deshalb schrieb ich auch "können" und "unter Umständen" sowie "Abhängig 
vom Compiler und der System-Architektur".

> So gesehen, war dieser Rat in der konkreten Situation nichts wert, weil
> irrelevant.

Ich fand es keineswegs irrelevant, darauf hinzuweisen. Auch wenn man mit 
avr-gcc arbeitet, sollte man sich über die Bedeutung und Hintergründe 
bestimmter Arten der Definitionen im Klaren sein. Selbst dann, wenn es 
im konkreten Fall keine praktische Bedeutung hat. Wechselt man den 
Compiler oder die Plattform, kann es plötzlich sehr bedeutungsvoll sein. 
Im Hinblick auf Portabilität und universelle Verwendbarkeit sollte man 
sich immer darüber im Klaren sein, was man tut.

Ich erinnere gern mal an das von Brian W. Kernighan (einem der 
Mitentwickler der Sprache C) 1999 veröffentlichte Buch "The Practice of 
Programming" (deutsch: Programmierpraxis). Dort werden die Prinzipien 
"Simplicity, Clarity und Generality" als grundlegend für die 
Programmierung dargestellt. Dazu gehört, dass man immer genau weiß, was 
man tut und nicht nach dem Prinzip "Trial and Error" verfährt.

von Karl H. (kbuchegg)


Lesenswert?

Volker U. schrieb:

>> So gesehen, war dieser Rat in der konkreten Situation nichts wert, weil
>> irrelevant.
>
> Ich fand es keineswegs irrelevant, darauf hinzuweisen. Auch wenn man mit
> avr-gcc arbeitet, sollte man sich über die Bedeutung und Hintergründe
> bestimmter Arten der Definitionen im Klaren sein. Selbst dann, wenn es
> im konkreten Fall keine praktische Bedeutung hat. Wechselt man den
> Compiler oder die Plattform, kann es plötzlich sehr bedeutungsvoll sein.
> Im Hinblick auf Portabilität und universelle Verwendbarkeit sollte man
> sich immer darüber im Klaren sein, was man tut.
>
> Ich erinnere gern mal an das von Brian W. Kernighan (einem der
> Mitentwickler der Sprache C) 1999 veröffentlichte Buch "The Practice of
> Programming" (deutsch: Programmierpraxis). Dort werden die Prinzipien
> "Simplicity, Clarity und Generality" als grundlegend für die
> Programmierung dargestellt. Dazu gehört, dass man immer genau weiß, was
> man tut und nicht nach dem Prinzip "Trial and Error" verfährt.


Alles richtig.

Aber in der konkreten Situation hat der Rat
"Ändere das mal so um, vielleicht ist ja ...."
keine Bedeutung gehabt. Denn daran ist es ganz sicher in dieser 
konkreten Situation nicht gelegen.
Wenn ein Audi-Fahrer nachfrägt, wie man beim A80 das Waschwasser 
nachfüllt, macht es keine Sinn, ihm einen Vortrag über die allgemeinen 
Gefahren beim Nachfüllen von Flüssigkeiten in allen Automarken zu 
halten. Damit ist ihm nicht geholfen.

Und nein. Ich hab auch keine Idee, warum das eine präsentierten 
Programme sich wie beschrieben verhält. Eigentlich dürften es das nicht.

von Karl H. (kbuchegg)


Lesenswert?

Sebastian Götte schrieb:
> Karl Heinz Buchegger schrieb:
>> Dann würde ich mich allerdings nicht auf das Flackern verlassen. Was
>> immer die Firmware macht, du hast es nicht unter Kontrolle.
>
> Ich vertrau dem schon, hier ist der Code:
> 
https://github.com/arduino/Arduino/blob/master/hardware/arduino/firmwares/arduino-usbserial/Arduino-usbserial.c


An das Teil komm ich nicht ran, kann dahr auch nicht sagen, ob an dieser 
Stelle in der Kette µC - Serial - USB - PC irgendwas passiert.

Allerdings ist es ja auch nicht gerade Raketentechnik, an einen Mega328 
Pin mal eine LED+330Ohm drannzuhalten um zu sehen ob dort was flackert. 
Der Test dauert keine 5 Sekunden und dann weißt du, ob beim fraglichen 
Programm der 328 überhaupt etwas von sich gibt und der Mega16 in seiner 
Eigenschaft als RS232/USB Wandler die Sache verbockt.

von Volker U. (volkeru)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Aber in der konkreten Situation hat der Rat
> "Ändere das mal so um, vielleicht ist ja ...."
> keine Bedeutung gehabt. Denn daran ist es ganz sicher in dieser
> konkreten Situation nicht gelegen.

Da sind wir uns einig.

> Wenn ein Audi-Fahrer nachfrägt, wie man beim A80 das Waschwasser
> nachfüllt, macht es keine Sinn, ihm einen Vortrag über die allgemeinen
> Gefahren beim Nachfüllen von Flüssigkeiten in allen Automarken zu
> halten. Damit ist ihm nicht geholfen.

Der Vergleich hinkt aber etwas. Es ging ja darum, dass die beiden 
Definitionen grundsätzlich nicht identisch sind. Man konnte die Aussage 
"die sind gleich" so verstehen. Sie sind im vorliegenden, speziellen 
Fall gleichwertig, aber nicht identisch. Und ich fand es daher schon 
richtig, dies klarzustellen.

> Und nein. Ich hab auch keine Idee, warum das eine präsentierten
> Programme sich wie beschrieben verhält. Eigentlich dürften es das nicht.

Deshalb denke ich auch, dass das Problem eher in uart.c oder der 
Testumgebung zu suchen ist.

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.