[C] Merkwürdige Erscheinung

Gast #2893457
Lesenswert?

Hallo,

ich habe den ganzen Tag nach einem Bug in meiner Software gesucht und am 
Ende durch Zufall festgestellt, dass sich das Problem durch Tausch einer 
Variablen gegen eine andere gleichen Typs und gleicher Verwendung 
beheben lässt.

Hier der Programmausschnitt um den es sich handelt.
1
switch(rcvd[1])  // Option wählen
2
        {
3
          case '1':   // Duty cycle PWM1 einstellen
4
            DC2=rcvd[2]; 
5
            PDC1 =DC1*(PTPER/128);
6
          break;
7
          case '2':   // Duty cycle PWM2 einstellen
8
            DC2=rcvd[2]; 
9
            PDC2 =DC2*(PTPER/128);
10
            break; 
11
          case '3':   // Duty cycle PWM3 einstellen
12
            DC3=rcvd[2]; 
13
            PDC3 =DC3*(PTPER/128); 
14
            break;
15
          default: ;  // Nichts tun
16
        }

Die Variablen werden wie folgt deklariert
1
char DC1=0x00, DC2=0x00, DC3=0x00;    //Duty cycles

Der obige Programmausschnitt funktioniert nur bis
1
case '2'
Ab hier wird PDC3 bei Änderung von DC3 immer der Maximalwert zugewiesen, 
ganz egal, was in DC3 steht.

So, nun zur "Lösung". Wenn ich DC3 gegen DC2 austausche funktioniert das 
Programm fehlerfrei.
Ich verstehe es einfach nicht. Die Variablen sollten - abgesehen vom 
Namen - doch identisch sein!?

Vielleicht weiß einer von Euch, woran das liegen könnte.

Das Programm läuft auf einem PIC und ich benutze den CCS Compiler.

Viele Grüße
Stefan
Gast #2893472
Lesenswert?

Zunächst habe ich Zweifel, das dieser Code ohne Warnung compiliert wird.
Falls es tatsächlich Warnungen gibt, poste die bitte hier.

Wenn PTPER tatsächlich um 7 Bit nach rechts verschoben werden kann und 
das sinnvoll ist, dann ist es grösser als 8 Bit. Das Ergebnis ist aber 
nur 8 Bit (char). Was hast Du Dir als Ergebnis vorgestellt und wie soll 
es erreicht werden? Eigentlich sollten genau hier Warnungen ausgegeben 
werden.
Gast #2893548
Lesenswert?

Stefan schrieb:
> default: ;  // Nichts tun

und

Stefan schrieb:
> char DC1=0x00, DC2=0x00, DC3=0x00;    //Duty cycles

Warum schreibst du bloß sowas?
Also erstmal, die leere default-Anweisung kannst du getrost weglassen. 
Wozu außer der Selbstverwirrung sollte die gut sein?

Und dann: Ich kann es nicht leiden, wenn jemand solche Deklarationen 
schreibt wie du bei DC1..3. Sowas ist eigentlich IMMER Mist. Grund: es 
hängt gar sehr von der Toolchain, dem Startupcode und dem konkreten 
Programm ab, ob das hinhaut oder nicht. Bei manchen Konstellationen 
funktioniert es nur dann, wenn Variablen im RAM auch korrekt im 
Startupcode initialisiert worden sind. Schreib lieber
void MyBlabla(blabla)
{ int DC1, DC2, DC3;
  DC1 = DC2 = DC3 = 0;
  ...

Wenn der Compiler optimieren kann, wird er's bei Vorlage solcher Quelle 
schon richtig tun und du sparst mit deiner impliziten Vorbesetzung der 
Variablen kein einziges Codebyte. Dafür ist die Quelle sauberer und 
klarer.

Und wenn du deine lokalen Variablen als int deklarierst, dann kriegst du 
auch keinen Datencrash infolge unzureichender Bitbreite wie bei deinem 
Beispiel.

Meine Devise ist _immer_: Keinen "geniale" Quelle schreiben, sondern 
bieder und einfach bleiben. System KISS! (Keep It Small and Simple). 
Dazu gehörten eben auch explizite Initialisierungen, sofern sie nötig 
sind.

W.S.
Gast #2893571
Lesenswert?

>Die linke Seite ist ein 16bit Wort, muss ich dann trotzdem für die
>Rechnung D1 bzw. D2/D3 casten?

Und woher sollten wir das wissen? Bitte bei der nächsten Frage von 
Anfang an vollständigen Code posten resp. den Code soweit reduzieren, 
dass, obwohl noch compilierbar, der Fehler noch auftritt.

Also ich bin dann mal 'raus.
#2893578
Lesenswert?

W.S. schrieb:
> Stefan schrieb:
>> char DC1=0x00, DC2=0x00, DC3=0x00;    //Duty cycles
>
> Warum schreibst du bloß sowas?

Weil das korrekter und gut lesbarer Code ist.

W.S. schrieb:
> Schreib lieber
> void MyBlabla(blabla)
> { int DC1, DC2, DC3;
>   DC1 = DC2 = DC3 = 0;

Solche Kettenzuweisungen sind schlechter Programmierstil, die können 
böse Seiteneffekte haben.
Schreib mal beim ATmega48:
1
  PORTD = PORTC = 0xFF;
Und dann staune, warum PORTD = 0x7F ist.

Also vergiß diese fehlerträchtigen Kettenzuweisungen ganz schnell 
wieder!


Peter
Gast #2893596
Lesenswert?

Anregungen, wie du es Anderen einfacher machen kannst, hast du ja 
reichlich bekommen ...

Stefan schrieb:
> Lutz schrieb:
>> Wie is'n rcvd[] definiert?
>  char rcvd[5];

Popel, zieh, ...: Und was wird wie in rcvd[1] reingeschrieben?

Ich habe mir angewöhnt, statt char, int usw. (fast) nur noch uint8_t, 
int32_t usw. zu benutzen. Da sieht man (ich) eher, wann was krachen 
kann. Wobei man (ich) char eigentlich ziemlich selten wirklich brauche. 
Ich vermute hier eine Menüeingabe, die ein uint8_t besser erfüllt. Denn 
einen char shiften/teilen macht keinen Sinn.

Da immer noch nur der Geier weiß, wie der Rest definiert ist (z.B. 
PTPER): Ich tippe mal auf irgendeinen Überlauf. Und den Begriff casten 
kennst du auch.

Also wie schon alle anderen schreiben: Aussagekräftigen Originalcode 
her, sonst wird das nichts.
Gast #2893597
Lesenswert?

W.S. schrieb:
> Stefan schrieb:
>> default: ;  // Nichts tun
>
> und
>
> Stefan schrieb:
>> char DC1=0x00, DC2=0x00, DC3=0x00;    //Duty cycle
> ...
> Und dann: Ich kann es nicht leiden, wenn jemand solche Deklarationen
> schreibt wie du bei DC1..3. Sowas ist eigentlich IMMER Mist. Grund: es
> hängt gar sehr von der Toolchain, dem Startupcode und dem konkreten
> Programm ab, ob das hinhaut oder nicht.

Nun ja, deine Befindlichkeiten sind dein persönliches Problem. Wer 
toolchains nutzt, die korrekten C-Code nicht korrekt übersetzen, dem ist 
nicht zu helfen. Mit so etwas wird die Erstellung eines funktionierendem 
Programm zu einem Glücksspiel. Manchmal mag es ja Gründe geben, so etwas 
nutzen zu müssen, aber daraus dann angeblich allgemeingültige 
Programmierregeln abzuleiten, ist übertrieben.

Oliver
#2893665
Lesenswert?

Rene H. schrieb:
> Naja, Karl Heinz
>
> Ich würde es mir nicht antun wollen hier den kompletten Code zu posten.
> Da wird man doch direkt verrissen.  :-)
>

Dann mach ein abgespecktes Beispiel zurecht, welches den gleichen Fehler 
zeigt.
Aber ohne vollständigen Code, kann so ein Symptom schnell in Stochern im 
Nebel ausarten.
Es hat immer einen Grund, warum eine Berechnung nicht das gewünschte 
Ergebnis liefert. Und öfter als man denkt, haben dann die 
Eingangsvariablen nicht die Werte, die der Programmierer seit Stunden 
stillschweigend annimmt.

Kontrollier halt mal DC3 vor der Berechnung.
Die Annahme "müsste eigentlich 0 sein" kann richtig sein, kann aber auch 
falsch sein.
Gast #2893767
Lesenswert?

Hallo Stefan

Ich habe nicht alles gelesen, aber ich setze ein Wette und biete ein mir 
und meiner Partnerin wichtiges Körperteil. Du hast ein ernsthaftes 
Speicherproblem.
Du überschreibst da Speicher!

Zeichne mal den Speicher auf den Du alloziert hast und wie Du ihn 
verwendest.

Grüsse,
R.
#2894106
Lesenswert?

Ingo schrieb:
> @peda
> Ich würde mal tippen das einer der Ports nur 7 Bit hat?

Stimmt, PORTC ist 7 Bit breit. Und da die Kettenzuweisung ja wieder 
zurücklesen muß, kriegt PORTD auch nur 7 Bit gesetzt.

Noch lustiger wird es beim PIC, wo das Ausgangslatch gesetzt wird, aber 
die Eingangspins gelesen. Da legt man sich definitiv die Karten damit.

Die Kettenzuweisung vereint also alle Nachteile in sich (schlechte 
Lesbarkeit, fehlerhaft, mehr Code). Somit gibt es keinen Grund, sie zu 
benutzen.
Ich hab sowas allerdings auch schon in älterem Code gesehen.


Peter
#2894115
Lesenswert?

...bei den Pic 16 gab es doch garkeine getrennten latches für Ein- und 
Ausgang, wie das bei den 18nern ist habe ich ja nie ersichtet da auf 
Atmel umgestiegen...

Gut war jedenfalls bei den 16nern auch die I/O Falle, siehe Sprut. 
Klasse Sache. Da hat man sich schön Dinge eingefangen von Außen 
kommend....

DS
#2894137
Lesenswert?

Peter Dannegger schrieb:
> Stimmt, PORTC ist 7 Bit breit. Und da die Kettenzuweisung ja wieder
> zurücklesen muß, kriegt PORTD auch nur 7 Bit gesetzt.

Muss sie?

Ich finde das gerade eine ziemlich spannende Fragestellung. Leider finde 
ich nirgends den aktuellen C-Standard um das wirklich definitiv zu 
finden.

http://c.comsci.us/etymology/operator/assignment.html behauptet:
1
=
2
    name: assignment.
3
    operand types: any.
4
    result type: the type of the left operand.
5
    result value: the value of the right operand.
6
    side effect: The left operand is set to the value of the result.
7
    associativity: Right to left.

Demnach wäre das Ergebnis 0x7f eigentlich nur zu rechtfertigen, wenn 
PORTC als ein 7-Bit-Typ deklariert wäre (was ich glaube ich noch nie 
gesehen habe).

Genauso ein problematisches Verhalten würde man dann ja bei 
volatile-Variablen haben.

Mein Bauchgefühl sagt mir, dass da der Compiler Mist baut...

Viele Grüße,
        Simon
#2894170
Lesenswert?

Karl Heinz Buchegger schrieb:
>>     result value: the value of the right operand.
> ist falsch.

Ok. Habe gerade auch in einem Standard-Draft die vermutlich relevante 
Stelle gefunden:
1
An assignment operator stores a value in the object designated by
2
the left operand. An assignment expression has the value of
3
the left operand after the assignment, but is not an lvalue.

...wobei ein Freund von mir, der solche Standards in seiner Freizeit 
liest gerade nicht mit meiner Interpretation übereinstimmt...

Viele Grüße,
        Simon

PS: Oops, da ist ja noch eine Fußnote: "The implementation is permitted 
to read the object to determine the value but is not required to, even 
when the object has volatile-qualified type."
Moderator (Firma: Titel) Persönliche Seite #2894195
Lesenswert?

Stefan schrieb:
> Vielleicht weiß einer von Euch, woran das liegen könnte.
Das beschriebene Verhalten deckt sich mit dem Verhalten eines 
amoklaufenden Pointers. Zeig mal die Reihenfolge deiner 
Variablendeklarationen. Oder wenn du sie nicht zeigen 
willst/darfst/kannst: dreht mal die Reihenfolge der Deklarationen um. 
Hast du dann andere Effekte?

> Das Programm läuft auf einem PIC
Ich denke, es läuft nicht... ;-)
#2894254
Lesenswert?

Simon Budig schrieb:

> PS: Oops, da ist ja noch eine Fußnote: "The implementation is permitted
> to read the object to determine the value but is not required to, even
> when the object has volatile-qualified type."


Aaah. Die Fussnote kannte ich noch nicht.
Ich kannte nur den Haupttext, dass der Wert einer Zuweisung, der Wert 
des linken Ausdrucks nach der Zuweisung ist. D.h. das alle 
Konvertierungen bzw. sonstige Dinge, die durch die Zuweisung notwendig 
waren, sich im 'Wert der Zuweisung' wiederfinden. Von daher ist die 
Frage tatsächlich interessant, ob hier auch hardware-Restriktionen mit 
reinfallen, was ja durch die Fussnote als 'kann sein - kann auch nicht 
sein' beantwortet wird.

Von daher: Lieber nicht.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren