Forum: Compiler & IDEs Alptraum jedes Programmierer: Code kompilierbar aber doch ein Denkfehler drin wer sieht´s?


von Michal (Gast)


Lesenswert?

Hallo Leute,

ihr alle kennt sicher dieses Gefühl, ganz nah an der Lösung zu sein und 
trotzdem wie blind keinen Fehler finden zu können? Hier bin ich eben 
angelangt.

Die Variablen BDHighestFailureByte und CCHighestFailureByte sollen sich 
nur erhöhen und niemals senken können (Maximum-Tracer)...
Die gehen jedoch entsprechend mit BDStableFailureByte bzw. 
CCStableFailureByte mit, nicht nur rauf, auch runter!!!?

In den Funktionen, die ich hier aus Übersichtlichkeit-Gründen nicht 
beigefügt habe, werden sie nicht verarbeitet; könnten auch nicht weil 
sie ja nur in main deklariert sind.
Im gesamten Programm werden auch keine Pointer benützt.

Also ich finde hier keine Operation, die die BDHighestFailureByte und 
CCHighestFailureByte Variablen erniedrigen können, egal was immer mit 
allen anderen Variablen passieren

würde.

Und ihr?

Danke voraus für die Hilfe!






int main (void)

{  //############################################# Main program begin 
#############################################


unsigned char EqualFailuresCounter=0x00;      //   For checking if the 
failure is stable
unsigned char BDPreviousSingleFailureByte=0xFF;    //   For recording 
the previous single failure byte
unsigned char CCPreviousSingleFailureByte=0xFF;    //   For recording 
the previous single failure byte
unsigned char BDStableFailureByte=0x00;    //   Stable failure is 
recorded when the single failure did not change 30 times
unsigned char CCStableFailureByte=0x00;    //   Stable failure is 
recorded when the single failure did not change 30 times
unsigned char BDPreviousStableFailureByte=0xFF;    //   For recording 
the previous stable failure byte
unsigned char CCPreviousStableFailureByte=0xFF;    //   For recording 
the previous stable failure byte
unsigned char BDHighestFailureByte=0x00;      //  The highest failure 
byte ever observed
unsigned char CCHighestFailureByte=0x00;      //  The highest failure 
byte ever observed

OutputsInputsConfiguration ();

for(;;)  {  //############################ Endless loop begin 
############################

TestPattern ();
LEDsFailureSignalisation ();




    // Check if the single failure bytes changed since the last test 
pattern loop run

 if 
((BDPreviousSingleFailureByte==BDSingleFailureByte)&(CCPreviousSingleFai 
lureByte==CCSingleFailureByte))

  {EqualFailuresCounter++;}    // Increase the counter if the single 
failure bytes did not change

 else

  {EqualFailuresCounter=0;}    // Reset the counter if the single 
failure bytes changed

BDPreviousSingleFailureByte=BDSingleFailureByte;  //   Record the 
previous single failure byte for the next comparison
CCPreviousSingleFailureByte=CCSingleFailureByte;  //   Record the 
previous single failure byte for the next comparison




if (EqualFailuresCounter>30)    //   If the single failure did not 
change during the last 30 test pattern loop run...

  {
    EqualFailuresCounter=0;        //  Reset the counter
    BDStableFailureByte=BDSingleFailureByte;  //   Take over the failure 
as stable
    CCStableFailureByte=CCSingleFailureByte;  //   Take over the failure 
as stable
  }

          // Beep only once at a change of the stable failure, else too 
loud:

if 
((BDPreviousStableFailureByte!=BDStableFailureByte)|(CCPreviousStableFai 
lureByte!=CCStableFailureByte))

  {  //  Do not beep at the highest failure byte

    if 
((BDStableFailureByte<BDHighestFailureByte)|(CCStableFailureByte<CCHighe 
stFailureByte))

        BeepForPassOrFail ();  // Beep type is choosen inside of this 
function due to the global variables BD- and CCSingleFailureByte

  }

else   _delay_ms(10);                //  Do not beep at equal stable 
failures




BDPreviousStableFailureByte=BDStableFailureByte;  //   Record the 
previous stable failure byte for the next comparison
CCPreviousStableFailureByte=CCStableFailureByte;  //   Record the 
previous stable failure byte for the next comparison




if (BDStableFailureByte>BDHighestFailureByte)      //  Find out the 
highest BDFailureByte ever seen

  {BDHighestFailureByte=BDStableFailureByte;}    //  Find out the 
highest BDFailureByte ever seen

if (CCStableFailureByte>CCHighestFailureByte)      //  Find out the 
highest CCFailureByte ever seen

  {CCHighestFailureByte=CCStableFailureByte;}    //  Find out the 
highest CCFailureByte ever seen



PORTB=BDHighestFailureByte;          //  For debugging
_delay_ms(100);
PORTB=0x00;
_delay_ms(100);
PORTB=BDHighestFailureByte;
_delay_ms(100);
PORTB=0x00;
_delay_ms(100);
PORTB=BDHighestFailureByte;
_delay_ms(100);
PORTB=0x00;
_delay_ms(100);




  }  //############################ Endless loop end 
############################




}  //############################################# Main program end 
#############################################

von Michal (Gast)


Angehängte Dateien:

Lesenswert?

Natürlich, bitte, danke!

von (prx) A. K. (prx)


Lesenswert?

Wenn du für's Debugging nur einen Wert ausgibst, den aber dreimal, woher 
kennst du dann die anderen Werte?

Erkennst du an irgendwas, dass das Programm neu gestartet wurde? Das ist 
nämlich auch ein Weg, den Wert zu verringern.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

& oder && ?

von (prx) A. K. (prx)


Lesenswert?

Ist zwar ein bischen verkehrt, aber nicht wirklich falsch und für das 
Problem nicht relevant.

von Michal (Gast)


Lesenswert?

Hallo,

ja, andere Werte verfolge ich jetzt nicht, aber wie geschrieben, egal 
wie die anderen wären, können die besagten Variablen nicht erniedrigt 
werden.
Der Rest vom Programm ist übrigens funktional ok.

& oder &&, was soll ich nehmen für diesen if? Sollte auch für die Frage 
nicht relevant sein...

von (prx) A. K. (prx)


Lesenswert?

Michal schrieb:

> ja, andere Werte verfolge ich jetzt nicht, aber wie geschrieben, egal
> wie die anderen wären, können die besagten Variablen nicht erniedrigt
> werden.

Möglicherweise durch irgendwie ausgelösten aber nicht erkannten 
Neustart.

Sind Interrupts im Spiel?

> & oder &&, was soll ich nehmen für diesen if? Sollte auch für die Frage
> nicht relevant sein...

& | für Bitoperationen
&& || für logische Operationen wie hier.

von Michal (Gast)


Lesenswert?

Neustart vom Programm, das geht durch Reset z.B. durch Unterspannung?
Kaum vorstellbar...

von (prx) A. K. (prx)


Lesenswert?

Oder durch Programmfehler wie beispielsweise einen eingeschalteten aber 
nicht definierten Interrupt, durch Stacküberlauf,...

Solltest irgendeine Erkennung dafür einbauen, um sicher zu gehen. 
Beispielsweise am Anfang kurz mit anderer Frequenz blinken oder so.

von Der M. (steinadler)


Lesenswert?

Kommentier doch einfach mal die Zuweisungen wie "CCHighestFailureByte = 
... " aus und schau, was die Werte austuen.

Kannst auch versuchen, Interrupts gezielt zu sperren, beim AVR geht das 
mit CLI.

Um was für einen Prozessortyp handelt es sich? 8Bit?
Welcher Compiler?

von Michal (Gast)


Lesenswert?

Hallo,

ich verwende keine Interrupts.

Möchte euch das lesen vom ganzen Programm ersparen, oder wollt ihr euch 
doch durchkämpfen?

Es ist ein Tester mehradrigen Kabel auf Open, Short und Verwechslung.

Ich programmiere einen ATmega8 mit einem AVRISP mkII vom AVRStudio mit 
AVR GCC Plug-In.

von Stefan E. (sternst)


Lesenswert?

Michal schrieb:
> Neustart vom Programm, das geht durch Reset z.B. durch Unterspannung?
> Kaum vorstellbar...

Watchdog schon kontrolliert?

Gibt es irgendwelche Funktionen (außer main), die mit lokalen Variablen 
arbeiten, insbesondere Arrays?

von Michal (Gast)



Lesenswert?

Hallo,

da ich vor halbem Jahr angefangen habe, programmiere ich noch am 
einfachsten wie möglich.

Kein Watchdog, keine Interrupts, keine Pointer, nur elementares 
"Blinken".

Hier der ganze Code für interessierte...

Die Frage ist: gibt es irgendwo eine Operation die BDHighestFailureByte 
und
CCHighestFailureByte erniedrigen könnte?

von Gast (Gast)


Lesenswert?

Omg,
das du bei solchem Source irgendwo ne Macke reinhaust ist fast schon 
vorprogrammiert. Rück als erstes mal alles vernünftig ein, benutz keine 
kilometerlangen Variablennamen sonderen kurze Selbsterklärende und 
schmeiss die sinnfreien Kommentare raus, dann findest du den Fehler 
bestimmt selber.
So wie du jetzt schreibst ist dein Quelltext schon bei so einem Witz 
Programm unlesbar...

von X. H. (shadow0815)


Lesenswert?

Lange Variablennamen können auch ganz gut selbsterklärend sein. Ich 
möchte mir aber manchmal Tipparbeit sparen und schreibe dann in den 
Kommentar, was ich mit einem kryptischen Kürzel meinte:

unsigned char bdhfb; //BDHighestFailureByte

alternativ könnte man das Kürzel später mit "Suchen und Ersetzen" wieder 
"lang machen".

von (prx) A. K. (prx)


Lesenswert?

@Gast: Stilfragen sind so eine Sache. Man neigt dazu,den eigenen Stil zu 
bevorzugen und findet fremde Programme oft schaurig.

So schlimm finde ich den übrigens nicht, schon garnicht die Variablen. 
Den Operatoren würden ein paar Blanks nicht schaden und der eine oder 
andere Kommentar wäre als Block statt endloser Wurst am Ende der Zeile 
besser, aber was soll's.

Spitzenklasse ist allerdings der Filename.

von Michal (Gast)


Lesenswert?

Hallo,

Die selbsterklärenden langen Variablennamen erleichten dem Anfänger das 
Leben.
Um meine Tipparbeit kümmere ich mich selbst, meistens durch Copy und 
Paste...
Strukturiert ist das ganze übersichtlich in Funktionen.
Abstände bei Operatoren und Kommentare nicht in lange Zeilen ziehen: das 
lege ich mir ans Herz.

Also ausser Rüge habt ihr mich auf ein Gedanke gebracht.

Im gesanten Code gibt es nur eine einzige Zuweisung für diese Variable, 
die sie durch die eingebaute Bedingung nur erhöhen könnte:

if (BDStableFailureByte>BDHighestFailureByte)
{BDHighestFailureByte=BDStableFailureByte;}

Ein Reset gibt es ja nur am Anfang von main, bei den 
Variablen-Deklarationen:

BDHighestFailureByte=0x00

Das heisst, mein Programm MUSS von neu gestartet gewesen sein.

Aber wie kriege ich das raus? Meine Versorgungsspannung ist perfekt, die 
restliche Logik hat - bevor ich anfangen habe den BDHighestFailureByte 
zu überwachen - tadellos funktioniert, Interrupts und Watchdogs sind 
keine im Spiel.

Mal durchschlafen. Gute Nacht da drüben!

von Karl H. (kbuchegg)


Lesenswert?

Michal schrieb:
> Hallo,
>
> Die selbsterklärenden langen Variablennamen erleichten dem Anfänger das
> Leben.

Man kann alles übertreiben

So was
1
if
2
((BDPreviousSingleFailureByte==BDSingleFailureByte)&(CCPreviousSingleFailureByte==CCSingleFailureByte))

ist einfach nur bescheuert. Du musst 10 Sekunden die Zeile anstarren um 
überhaupt erst mal die einzelnen Worte zu finden
1
  if( ( BD_PrevSingleFB == BD_SingleFB ) &&
2
      ( CC_PrevSingleFB == CC_SingleFB ) )

(was auch immer das BD und CC bedeuten mag.)

Zum Problem: Sind irgendwelche Arrays im Spiel?
Das Programm jetzt genauer zu analysieren tu ich mir nicht an. Nicht 
solange das eine wüste Buchstabensuppe ist

> Das heisst, mein Programm MUSS von neu gestartet gewesen sein.
>
> Aber wie kriege ich das raus?

Schalte am Anfang alle LEDS die du hast für 1 Sekunde ein und danch 
wieder aus. Das kriegst du auf jeden Fall mit, wenn zwischendurch alle 
LEDs immer wieder mal für 1 Sekunde angehen.

von X. H. (shadow0815)


Lesenswert?

Fällt mir ein: Kann es sich vielleicht um einen Variablen-Überlauf 
handeln?
unsigned char geht ja nur bis 255

So kann der Wert ja auch erniedrigt werden.

von Bernd O. (bitshifter)


Lesenswert?

Ich empfehle Dir, die Fehlerzähler zu kapseln - und zwar in eine 
Funktion, die eine obere Grenze sicherstellt, also quasi so:

void increment_limited(unsigned char *counter)
{
    if ((*counter) < 255) {
        (*counter)++;
    }
}

Im Code rufst Du dann statt:
  EqualFailuresCounter++;
dieses auf:
  increment_limited(&EqualFailuresCounter);

Gruß,
Bernd

von P. S. (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:

> ist einfach nur bescheuert. Du musst 10 Sekunden die Zeile anstarren um
> überhaupt erst mal die einzelnen Worte zu finden
>
>
1
>   if( ( BD_PrevSingleFB == BD_SingleFB ) &&
2
>       ( CC_PrevSingleFB == CC_SingleFB ) )
3
>
>
> (was auch immer das BD und CC bedeuten mag.)

Gleiche gilt nun fuer FB - wenn das nicht mal eine bescheuerte 
Abkuerzung ist.

von Noname (Gast)


Lesenswert?

Manchmal ist ein volatile für kleine Wunder gut.
Mit volatile wird die Optimierung des Compilers unterbunden und die 
Variable behält ihre Gültigkeit. Das es sich nur um lokale Variablen 
handelt versucht der Compiler diese registeroptimal zu nutzen.
Da kann es schon mal vorkommen, daß ein Register für mehrere Variablen 
genutzt wird.

von Simon K. (simon) Benutzerseite


Lesenswert?

Noname schrieb:
> Manchmal ist ein volatile für kleine Wunder gut.
> Mit volatile wird die Optimierung des Compilers unterbunden und die
> Variable behält ihre Gültigkeit. Das es sich nur um lokale Variablen
> handelt versucht der Compiler diese registeroptimal zu nutzen.
> Da kann es schon mal vorkommen, daß ein Register für mehrere Variablen
> genutzt wird.

Du willst also sagen, dass es beim C Compiler normal ist, dass ab und zu 
mal lokale Variablen verloren gehen, weil ein Register "schon mal" von 
anderen Variablen belegt wird?

Was ist denn das für ein Unsinn ;)

von Noname (Gast)


Lesenswert?

Thematik ist
http://www.imb-jena.de/~gmueller/kurse/c_c++/c_volat.html
ganz hübsch beschrieben.

von Klaus W. (mfgkw)


Lesenswert?

gut, das hat aber nichts damit zu tun, daß ein und dasselbe Register
durch die Optimierung nacheinander für mehrere Variablen genutzt
werden kann. Vielmehr verhindert volatile, daß eine Variable
überhaupt in einem Register gepuffert wird zwecks
Beschleunigung.

von Peter D. (peda)


Lesenswert?

Also ich muß auch zugeben, daß diese Monstervariablen das Lesen 
erheblich erschweren und erst recht die Monsterzeilen.
Warum machst Du nicht wenigstens noch Leerzeichen, damit man die 
Operatoren erkennen kann?

Und Du machst nen Haufen völlig unnötiger Leerzeilen, die höchstens den 
Scrollfinger trainieren, aber überhaupt nichts lesbarer machen.

Du hast damit bestimmt hier gute Chancen:

http://www.ioccc.org/main.html


Ich hab mühsam rausgekriegt, daß es ein Leitungstester sein soll.

Ich hatte mal nen Tester für 8-poliges Patchkabel gemacht mit nem 8051.
Vielleicht kannst Du ja damit was anfangen:
1
#include <reg51.h>
2
3
#define uchar unsigned char
4
5
sbit  xLED_OK = P0^2;
6
sbit  xLED_ERROR = P0^1;
7
sbit  xLED_CROSSOVER = P0^0;
8
9
#define  LED_ON(x)  (~(x))    // low active: 0 = LED on
10
11
12
void test( void )
13
{
14
  uchar code TEST[] = { 0xF1, 0x92, 0xA4, 0x48 };
15
  uchar code CROSS[] = { 0xD6, 0xB0, 0x83, 0x48 };
16
17
  bit error = 0;
18
  bit crossover = 0;
19
  uchar i = sizeof(TEST);
20
  P1 = 0xFF;
21
  P2 = 0;
22
  if( P1 != 0xFF ){      // cable connected
23
    do{
24
      i--;
25
      P2 = TEST[i];
26
27
      if( crossover || P1 != TEST[i] ){
28
        if( P1 == CROSS[i] ){
29
          crossover = 1;
30
          continue;
31
        }
32
        error = 1;      // not or wrong connected
33
      }
34
    }while( i );
35
36
    xLED_CROSSOVER = LED_ON(  crossover );  // crossover
37
38
    xLED_OK        = LED_ON( ~error     );  // o.k.
39
40
    xLED_ERROR     = LED_ON(  error     );  // error
41
    return;
42
  }
43
  xLED_CROSSOVER = LED_ON( 1 );      // no cable
44
  xLED_OK        = LED_ON( 0 );
45
  xLED_ERROR     = LED_ON( 0 );
46
}
47
48
49
void main( void )
50
{
51
  for(;;){
52
    test();
53
  }
54
}


Peter

von Neugieriger (Gast)


Lesenswert?

Wie gut, dass es der Compiler noch lesen und zuordnen kann. Aber 
vielleicht hat er aus Rache dir einen kleinen Fehler eingebaut.

von Bernd O. (bitshifter)


Lesenswert?

Klaus Wachtler schrieb:
> gut, das hat aber nichts damit zu tun, daß ein und dasselbe Register
> durch die Optimierung nacheinander für mehrere Variablen genutzt
> werden kann. Vielmehr verhindert volatile, daß eine Variable
> überhaupt in einem Register gepuffert wird zwecks
> Beschleunigung.
Das stimmt so nicht. Volatile hat nichts mit Registern zu tun, sondern 
bedeutet, dass der Compiler keinerlei Annahmen über die Variable machen 
darf. Der Compiler muß also davon ausgehen, dass sich die Variable 
jederzeit außerhalb seiner Kontrolle ändern könnte.
Je nach Architektur legt man beispielsweise Speicher von 
Memory-Mapped-HW gerne auf volatile Variablen.

Eine Codestückchen wie dieses:

hdlc_controller_txflag = 1; /* start transmission */

delay_us(100); /* after this time flag should be cleared hy HW */

if(hdlc_controller_txflag) {
    notify_error(TXFLAG_STUCK); /* controller hangs */
}

Läuft Gefahr, dass der Compiler (Optimzier) ohne volatile für 
hdlc_controller_txflag messerscharf schließt:
"Variable wird gesetzt, durch delay_us() mit Sicherheit nicht verändert 
- wozu im nächsten Befehl prüfen, ob die Variable immer noch gesetzt 
ist. Wer hätte sie auch löschen sollen?"

Nach dem Optimzier (ohne volatile) sieht der Code dann so aus:

hdlc_controller_txflag = 1; /* start transmission */

delay_us(100); /* after this time flag should be cleared */

notify_error(TXFLAG_STUCK);

Aus Compilersicht völlig legitim - woher soll der Compiler wissen, dass 
noch jemand anders (out of scope) auf dieser Variablen arbeitet.

Wenn eine volatile-Variable aber beispielsweise als call-by-value in 
einer Funktion landet, dann steht es dem Compiler frei, den Wert vor dem 
Funktionsaufruf in ein Register zu kopieren und zu übergeben (je nach 
ABI).


Gruß,
Bernd

von Klaus W. (mfgkw)


Lesenswert?

Bernd O. schrieb:
> Klaus Wachtler schrieb:
>> gut, das hat aber nichts damit zu tun, daß ein und dasselbe Register
>> durch die Optimierung nacheinander für mehrere Variablen genutzt
>> werden kann. Vielmehr verhindert volatile, daß eine Variable
>> überhaupt in einem Register gepuffert wird zwecks
>> Beschleunigung.
> Das stimmt so nicht. Volatile hat nichts mit Registern zu tun, sondern
> bedeutet, dass der Compiler keinerlei Annahmen über die Variable machen
> darf. Der Compiler muß also davon ausgehen, dass sich die Variable
> jederzeit außerhalb seiner Kontrolle ändern könnte.

.. und der Compiler kann sie deshalb zwangsläufig nicht in
einem Register halten.

Du hast natürlich Recht, daß volatile erstmal nichts direkt mit
Registern zu tun hat.
Aber daß eine volatile-Variable nicht in einem Register landet,
ist eine zwangsläufige Folge.

Ich hatte ich insofern ungenau ausgedrückt, daß eine
volatile-Variable natürlich in ein Register kopiert werden kann
(für andere Zwecke).
Aber der nächste Zugriff auf die Variable wird wieder auf das
RAM zugreifen, und nicht auf die Kopie im Register.

(Letztlich wollte ich aber auf etwas anderes hinaus, nämlich daß
volatile nichts mit dem Umstand zu tun hat, daß gelegentlich
ein und dasselbe Register nacheinander für mehrere Variablen
verwendet werden kann.)

> ...
> Wenn eine volatile-Variable aber beispielsweise als call-by-value in
> einer Funktion landet, dann steht es dem Compiler frei, den Wert vor dem
> Funktionsaufruf in ein Register zu kopieren und zu übergeben (je nach
> ABI).

Natürlich, aber dann steht nicht die Variable im Register, sondern
eine Kopie davon, und die ist ja nicht volatile.
Die Variable selbst liegt wie sonst auch im RAM und wird bei ihrer
nächsten Verwendung von dort geholt und nicht aus der Kopie im
Register oder sonstwo.

>
>
> Gruß,
> Bernd

von Michal (Gast)


Lesenswert?

Hallo,

also da bin ich nochmal. Zurück zum Thema...

Im gesanten Code gibt es nur eine einzige Zuweisung für diese Variable,
die sie durch die eingebaute Bedingung NUR ERHÖHEN könnte 
(Maximum-Tracer):

if ( BDStableFailureByte > BDHighestFailureByte )
{ BDHighestFailureByte = BDStableFailureByte; }

Ein Reset gibt es ja nur am Anfang von main, bei den 
Variablen-Deklarationen:

unsigned char BDHighestFailureByte = 0x00;

Durch charakterisches Blinken am Anfang von main habe ich jetzt 
rausgekriegt, dass mein Programm NICHT immer wieder von neu gestartet 
wird. Trotzdem beobachte ich eine Erniedrigung des BDHighestFailureByte, 
sie geht mit BDStableFailureByte mit. Und immer weiss ich noch nicht 
warum...

Grüße

Michal

von Gast (Gast)


Lesenswert?

Wie voll ist dein Speicher? Kann es sein, das bei Funktionaufrufen der 
Stack bis in deinen Variablenbereich wächst, und so Variablen 
überschrieben werden?

Gast

von Oliver (Gast)


Lesenswert?

Hast du denn die ferhlerhaften & und | korrigiert?

Oliver

von Michal (Gast)


Lesenswert?

Hallo,

mein Speicher ist nur mit 8 % (Programm) und 0,2 % (Data) voll.
Die & und | habe alle auf && und || geändert.

Bin jetzt in der Arbeit, stelle dann das neue Code von zuhause rein.

Grüße

Michal

von Stephan V. (orca)


Lesenswert?

Das mit Codeformatierung und Variablennamen ist wie so vieles 
Geschmackssache, solange du nicht in einer SW Gruppe arbeitest, wo es 
extra Codingconventions gibt ist es im Prinzip dir überlassen wie du 
deinen Code Formatierst, solange DU damit gut zurecht kommst. Allerdings 
haben sich für verschiedene Sprachen unterschiedliche Stile eingebürgert 
(C, C++, Java, ...) und es kann nicht schaden, wenn man sich an die 
etwas anlehnt.

Etwas anderes ist es mit den Operatoren, ich meine hier die logischen 
und bitweisen Operatoren. Hier ist es wichtig, dass dir der unterschied 
von Anfang an  klar ist, und du sie gleich immer richtig verwendest. 
Sonst kommt es schnell zu Fehlern die man gerne tagelang sucht. (Auch 
wenn die Vergliche im aktuellen Bsp. auch bitweise funktionieren 
würden.)

Aber zurück zum eigentlichen Problem:

Wie testest du eigentlich welcher Wert in BDHighestFailureByte bzw. 
CCHighestFailureByte steht? Durch deine blinkenden LEDs an Port B?
Also wenn die nur 1/3 Sekunde lang blinken und dann wieder einen neuen 
Wert anzeigen, würd ich mir nicht zutrauen diesen Wert sicher erkennen 
zu können. Außerdem schaltest du in Zeile 162
1
  PORTB=BDSingleFailureByte;  //  Red LEDs at the failure bits outputs
einen Fehlerstatus(?) auf's Port B.
Der blinkt dir da auch noch zusätzlich rein.
Erhöh die Anzeigedauer mal auf 1 Sek. damit man die Werte gut erkennt 
und von den Statusanzeigen unterscheiden kann.

Ich hab mal versucht dein Programm durch den Simulator zu jagen.

1) Der Compiler optimiert echt gut :-) (mit den std.Einstellungen von 
AVR Studio)
Der Disassembler zeigt: Nachdem CCHighestFailureByte nirgends mehr 
verwendet (gelesen) wird, wird es einfach weg optimiert. Genau das soll 
ein gut optimierender Compiler auch machen! Was mich zurück zu der Frage 
von oben bringt, wie debuggst du CCHighestFailureByte und 
BDHighestFailureByte?


2) Probier mal das ganze unoptimiert zu testen: Compilerswitch –O0 statt 
–Os
(unter AVR Studio: Menu -> Project -> Configuration Options -> 
Optimization)

Dann passt das Programm allerdings nicht mehr in den Flash. Also hab ich 
die Beepfunktionen auskommentiert, da die IMHO nichts zum eigentlichen 
Kabeltest beitragen – nicht, dass sie nicht trotzdem für den Bug 
verantwortlich sein könnten, aber das kann man dann in einem weiteren 
Schritt abklären.

also die Funktionen: BeepForFail, BeepForPass und BeepForPassOrFail 
auskommentieren
und auch die Zeilen:
1
  if ((BDStableFailureByte<BDHighestFailureByte)|(CCStableFailureByte<CCHighestFailureByte))
2
        BeepForPassOrFail ();
Jetzt sollte das Programm wieder richtig compilieren und in den Speicher 
passen.

Tritt der Fehler jetzt immer noch auf, liegt es an der HW, oder der 
Testlogik, oder wir sind alle zu doof einen Alg.Bug in einer 
Maximumsfunktion zu finden ;-)

Hast du jetzt keinen Fehler mehr, ist die HW zu 99.9% OK und die 
prinzipielle Programmlogik ist damit auch verifiziert (nicht nur die 
Maximumsfunktion).


Hoffe, dass hilft dir etwas weiter.

by(e)
Stephan

von Michal (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Stephan,

danke, ich bin sprachlos dass sich jemand die Zeit genommen hat und mein 
elendige Code noch mit dem Simulator getestet hat.

Dadurch hast, nehme ich an, verstanden wie mein Kabeltester genau 
gedacht ist, aus welchen Ports (und Teil-Ports) ich die Testpattern 
schicke und an welchen ich sie empfange.

Ich werde mich mit Deinen wertwolle Tipps, u.a. zum Kompilator (-0s, 
-00) genauer auseinandersetzen.

Was ich nicht verstehe, auch bei -0s sollte der BDHighestFailureByte 
nicht wegoptimiert werden, weil er ja hier für die Entscheidung über die 
Klang-Signalisierung VERWENDET wird:

if 
((BDStableFailureByte<BDHighestFailureByte)|(CCStableFailureByte<CCHighe 
stFailureByte))
        BeepForPassOrFail ();

Es geht darum, dass **HighestFailureByte einem herausgezogenem Stecker 
entspricht und das Ding bei jedem Kabel-Wechsel nicht ärgerlich piepsen 
sollte.

Will Benützer z.B. einen 6-Adrigen Kabel testen, soll er die restlichen 
4 Testports fix verbinden. Das Programm wird dann nach einem Ein- und 
Ausstecken durch diesen Maximum-Tracer "**HighestFailureByte" lernen, 
welcher Fehler einem herausrausgezogenen Stecker entspricht und dabei 
nicht mehr piepsen (nur rote LEDs zeigen).

Das gefragte Blinken habe ich erst jetzt für Debugging eingeführt weil 
mir diese Logik mit dem herausgezogenem Kabel eben nicht funktionieren 
wollte.

Ich lege die vorherige Version bei, die TADELLOS FUNKTIONIERT, aber die 
herausgezogenen Stecker noch nicht erkennt hat, zum Vergleich bei.

Vielen Dank nocheinmal

Michal

von Stephan V. (orca)


Lesenswert?

Ich kann natürlich nicht sehen, wie gut das mit dem Blinken 
funktioniert, aber du solltest dir wirklich sicher sein, dass du genau 
weißt wann was angezeigt wird und wie es dann zu interpretieren ist. Und 
gerade wenn du Bitmuster vergleichen willst, würd ich sie mind. 3-5 Sek. 
stehen lassen, um sie zur Not auch mal abschreiben zu können.

Naja, also ehrlich gesagt, hab ich die eigentliche Testfunktion nur grob 
überflogen.
Ich hab's mir jetzt noch mal angeschaut, dabei sind mir folgende Sachen 
aufgefallen, die aber alle nix mit deinem Maximum-Problem zu tun haben.

I/O setzen:
------------
In der Funktion TestPattern() läufst du offensichtlich alle Adern eines 
10-poligen Kabels durch. Dabei verwendest du Port C sowohl als Eingang 
als auch als Ausgang. Das ist vollkommen in Ordnung, nur wenn du ein 
Port gemischt verwendest (sei es auch nur indirekt z.B. über UART, SPI, 
etc.) musst du beim setzen der Bits etwas aufpassen.
1
PORTC=0x00&CSentUsedBitsMask;

1. 0 & irgendwas  ist immer = 0
Also kannst du gleich schreiben: PORTC = 0x00;

2. Ich glaube aber, was du eigentlich machen wolltest ist: "setze alle 
Bits in CSentUsedBitsMask auf 0" das funktioniert etwas anders. Du musst 
nämlich aufpassen, dass du nur die Output-Bits setzt, und die Input-Bits 
nicht veränderst, sonst löscht du dir event. gesetzte Pull-ups (was du 
mit deinem Code gemacht hast).
Da du nicht direkt auf einzelne Bits eines Ports zugreifen kannst, musst 
du das zwischenbuffern.
1
tmp = PORTC;
2
tmp &= ^CSentUsedBitsMask;  // alle Bits in der Maske löschen, die anderen bleiben unberührt
3
tmp |= data & CSentUsedBitsMask; // Datenbits ausmaskieren und entsprechend setzen
4
PORTC = tmp;

Dieses Konstrukt musst du auch weiter unten verwenden, bei:
1
CSentByte=0x01&CSentUsedBitsMask;
2
PORTC=CSentByte;

In LEDsFailureSignalisation() hast du wieder das gesamte Port C gesetzt. 
Das mag zwar für dein Programm funktionieren, kann aber die Ursache 
einer nie enden wollenden Debug-Orgie werden. Also gewöhn die ab besten 
gleich an: nur die Bits angreifen, die du auch wirklich ändern willst.
1
PORTC |= (1<<PC5);    // Green LED on
statt
1
PORTC = 0x20; // At no failure green LED is lightning from the 00100000 from the port C

In Zeile 161 machst du das richtig:
1
PORTC &= ~(1<<PC5);    // Green LED off
Aber gleich drauf, setzt du wieder das gesamte PORT.
1
PORTC = CCSingleFailureByte;  //  Red LEDs at the failure bits 
2
outputs

Fällt im konkreten Fall nicht auf, da die grüne LED sowieso auf 0 ist. 
Wäre die, grüne LED jetzt aber low-active, würdest du dich wundern, 
warum du einen Fehler hast, und trotzdem grün leuchtet.


Debuggen
-------------
Zum Debuggen, gennerell: Wenn es sich mit deiner HW irgendwie machen 
lässt, führ die UART Schnittstelle raus und verwende irgendeinen UART 
<-> USB Adapter (z.B. FTDI & Co) um Debugmeldungen an den PC zu senden.
Ich wollte mich früher auch immer davor drücken, weil du wieder mehr 
Leitungen hast, dann brauchst wieder eine UART Lib (aber die gibt's ja 
schon fertig), usw. Aber im Endeffekt ist dann doch bei jedem Projekt 
ein UART Stecker drauf gelandet. Es lässt sich damit einfach viel besser 
debuggen als mit LED Morsecode, und ab einer gewissen Komplexität bleibt 
dir sowieso nix anderes mehr übrig.


Abgestecktes Kabel
------------------------
Ok, jetzt hab ich endlich verstanden, was du mit BDHighestFailureByte 
eigentlich bezwecken willst :-)

Ich glaub da ist die Logik etwas suboptimal. Wenn ich dein Programm 
richtig durchschaut habe, brauchst du mal 30 Testzyklen ohne Kabel nur 
mit den Leerbrücken um den Kabeltyp zu lernen. Und wenn du Kabeln 
wechselst, kannst du nur zu mehrpoligen wechseln, und nicht zu weniger 
poligen.

Was hältst du von folgendem Vorschlag. (Der spart dir sogar die 
Drahtbrücken brauchst allerdings einen Resettaster oder Ein/Ausschalter, 
aber der sollte ja eh irgendwo vorhanden sein.)

1.  Beim Einschalten ist ein geprüftes Masterkabel des jeweiligen Typs 
angeschlossen. Das wird als Sollmuster gelernt.
2.  Danach wird der Lernvorgang durch ein Piepsen oder Blinken 
quittiert.
3.  Jetzt kannst du Kabeln dieses Typs ganz normal testen
4.  Soll ein anderer Typ getestet werden: Masterkabel d'ran, Reset bzw. 
Ein-Aus; goto 1

Umsetzung:
1.  Pullups wieder einschalten
2.  Vor der Hauptschleife das aktuelle Muster lernen. Eine Maske 
erstellen, die gleich dem invertierten Muster ist.
3.  in der Hauptschleife alle Fehler mit der Maske ausmaskieren.

von Stephan V. (orca)


Lesenswert?

Ups, das mit dem Optimieren hab ich jezt ganz vergessen.

Du hast natürlich recht, ich hab bei mir alle Beep-Funktionen 
ausgeklammert, daher war auch dir Abfrage nicht drinnen und somit wurde 
BDHighestFailureByte
weg optimiert.

Wenn ich's mit deiner Originalversion durch den Dissassambler schicke, 
bleicht BDHighestFailureByte auch als Register erhalten.

by(e)
Stephan

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.