Forum: Compiler & IDEs Bitweises vergleichen von Variablen


von Alex (Gast)


Lesenswert?

Hallo!

Ich versuche mich gerade an einem Kabeltester und bin am verzweifeln. 
Ich habe ein Kabel mit 10 Leitungen. Also habe ich 10 Ausgänge und 10 
Eingänge. Ich gebe auf die Ausgänge nacheinander ein 5V-Signal und messe 
bei den Eingängen was ankommt. Manchmal ist in den Steckern gewollt eine 
Brücke. Um sämtliche Kabelbelegungen nicht softwaretechnisch speichern 
zu müssen gibt es einen Lernmodus. Man steckt ein Musterkabel auf uns 
drückt auf einen Taster. Der Atmega8535 merkt sich das was beim Muster 
zurückkommt als Referenz. Dann soll man mit einem Taster "Messen" andere 
Kabel auf Unterschiede zum Muster durchmessen können.

Nun zu meinem Problem: Ich speichere die 10 Eingangssignale in einem 10 
Felder großen uint16_t array ab. Bei der Messung werden wie bei dem 
Lernmodus ebenso auf alle Leitungen nacheinander 5V gegeben und dann 
soll der Wert an PINC bzw. PIND mit dem gespeicherten Wert verglichen 
werden. Wenn das Programm im Debugger in die Funktion vergleiche_pin() 
reinspringt zeigt er mir für die übergebene Variable "vergleichswert" 0 
an. An PD3 liegt ebenfalls 0V an. Trotzdem springt die Result-Variable 
"res" auf 0. wieso? Mache ich beim Bitvergleich irgendwas falsch? 
Verträgt er evtl den Vergleich eines 8-Bit mit einem 16-Bit Wert nicht? 
Speichert er den 16-Bit Wert anders ab (LSB, MSB)? Ich komm nicht 
weiter!

Danke für eure Antworten!

Gruß,

Alex


1
void naechster_pin(volatile int akt_pin) {
2
//Gibt immer auf den nächsten Pin eine 1 und setzt alle anderen Pins auf 0
3
if (akt_pin==1) {
4
PORTB=0b00010000;
5
PORTA=0b00000000;
6
}
7
8
... usw. ...
9
10
if (akt_pin==10) {
11
PORTB=0b00000000;
12
PORTA=0b00010000;
13
}
14
15
}
16
//Ende function naechster_pin()
17
18
19
20
volatile uint16_t lerne_pin(void) {
21
//Liest die Eingänge und schreibt Bitweise 
22
volatile uint16_t res;
23
24
if (PIND & (1<<PD3) ) res |= (1<<0); else res &= ~(1<<0);
25
if (PIND & (1<<PD4) ) res |= (1<<1); else res &= ~(1<<1);
26
if (PIND & (1<<PD5) ) res |= (1<<2); else res &= ~(1<<2);
27
if (PIND & (1<<PD6) ) res |= (1<<3); else res &= ~(1<<3);
28
if (PIND & (1<<PD7) ) res |= (1<<4); else res &= ~(1<<4);
29
if (PINC & (1<<PC0) ) res |= (1<<5); else res &= ~(1<<5);
30
if (PINC & (1<<PC1) ) res |= (1<<6); else res &= ~(1<<6);
31
if (PINC & (1<<PC2) ) res |= (1<<7); else res &= ~(1<<7);
32
if (PINC & (1<<PC3) ) res |= (1<<8); else res &= ~(1<<8);
33
if (PINC & (1<<PC4) ) res |= (1<<9); else res &= ~(1<<9);
34
35
return res;
36
37
}
38
39
40
41
42
volatile int vergleiche_pin(volatile uint16_t vergleichswert) {
43
//Liest die Eingänge und vergleicht mit Speicher
44
volatile int res; //result
45
res=1;
46
if ( (PIND & (PD3)) !=(vergleichswert & 0b0000000000000001)) res=0;
47
if ( (PIND & (PD4)) !=(vergleichswert & 0b0000000000000010)) res=0;
48
if ( (PIND & (PD5)) !=(vergleichswert & 0b0000000000000100)) res=0;
49
if ( (PIND & (PD6)) !=(vergleichswert & 0b0000000000001000)) res=0;
50
if ( (PIND & (PD7)) !=(vergleichswert & 0b0000000000010000)) res=0;
51
if ( (PIND & (PC0)) !=(vergleichswert & 0b0000000000100000)) res=0;
52
if ( (PIND & (PD1)) !=(vergleichswert & 0b0000000001000000)) res=0;
53
if ( (PIND & (PD2)) !=(vergleichswert & 0b0000000010000000)) res=0;
54
if ( (PIND & (PD3)) !=(vergleichswert & 0b0000000100000000)) res=0;
55
if ( (PIND & (PD4)) !=(vergleichswert & 0b0000001000000000)) res=0;
56
return res;
57
}
58
//Ende function vergleiche_pin()
59
60
61
62
63
int main(void) {
64
volatile int i=0;
65
volatile int gelernt=0;
66
volatile uint16_t p[11]={1};
67
int j=1;
68
while (j=1) {
69
//Datenrichtungen bestimmen
70
DDRA = 0b11111111;
71
DDRB = 0b11111111;
72
DDRC = 0b00000000;
73
DDRD = 0b00000111;
74
PORTC = 0b01100000; //Pullup Wahltaster und 12Pin
75
76
while (!( PINC & (1<<PC6))) { //Lerntaster
77
78
for (i=1; i<11; i++) {
79
naechster_pin(i);
80
p[i-1]=lerne_pin();
81
}
82
gelernt=1;
83
} //Ende Lerntaster
84
85
while (!( PINC & (1<<PC5))) { //Messtaster
86
if (gelernt==1) {  //Abbruch wenn nicht gelernt
87
for (i=1; i<10; i++) {
88
naechster_pin(i);
89
if (vergleiche_pin(p[i-1])==1) {
90
PORTD |= (1 << PD2);  // LED OK an
91
PORTD &= ~(1 << PD1);  // LED FAIL aus
92
} else {
93
PORTD |= (1 << PD1);  // LED FAIL an
94
PORTD &= ~(1 << PD2);  // LED OK aus
95
96
}
97
}
98
} //Abbruch wenn nicht gelernt
99
} //Ende Lerntaster
100
}
101
}

von Stefan E. (sternst)


Lesenswert?

1
if ( (PIND & (PC0)) !=(vergleichswert & 0b0000000000100000)) res=0;

PIND -> PINC

1
if ( (PIND & (PD1)) !=(vergleichswert & 0b0000000001000000)) res=0;
2
if ( (PIND & (PD2)) !=(vergleichswert & 0b0000000010000000)) res=0;
3
if ( (PIND & (PD3)) !=(vergleichswert & 0b0000000100000000)) res=0;
4
if ( (PIND & (PD4)) !=(vergleichswert & 0b0000001000000000)) res=0;

Und hier wolltest du wohl auch eher PINC und PCx haben.
Außerdem überall "(PXx)" durch "(1<<PXx)" ersetzen.
Wird aber trotzdem nicht funktionieren, denn da die Bitpositionen von 
den Ports und in res nicht übereinstimmen, kannst du nicht mit einem 
direkten Vergleich arbeiten.

(Rest des Codes habe ich mir nicht weiter angeschaut)

PS: Du hast offensichtlich die Bedeutung von "volatile" nicht 
verstanden. Das bitte nochmal nachlesen.

von Alex (Gast)


Lesenswert?

Hallo!

Vielen Dank für den Hinweis. Du hast aber Recht - es funktioniert 
trotzdem nicht.

volatile bedeutet doch, dass der Compiler die Variablen nicht optimieren 
(kürzen) soll, damit diese verändert werden können und kein Speicher 
überschrieben wird, oder? Deswegen habe ich jede verändernde Variable so 
deklariert. Ich werde es mir aber nochmal durchlesen.

Trotzdem bleibt mir ein Verständnisproblem. Mit
1
((PIND & (1<<PD1))

selektiere ich doch das Bit und es sollte als Ergebnis 0 oder 1 geben. 
Mit
1
(vergleichswert & 0b0000000001000000))

wird ebenfalls ein Bit ausgewertet was zu einem Ergebnis 0 oder 1 führen 
sollte. Diese sollte das Programm doch auswerten können, oder?

von Fred S. (Gast)


Lesenswert?

Hallo Alex,

> überschrieben wird, oder? Deswegen habe ich jede verändernde Variable so
> deklariert. Ich werde es mir aber nochmal durchlesen.
Vor Deine Funktionen gehört kein "volatile", auch nicht vor die 
Variablen innerhalb der Funktionen (falls eine Variable von einem zum 
nächsten Aufruf erhalten bleiben soll, sollte sie "static" sein).  Es 
wird nicht schaden, alle globalen Variablen "volatile" zu machen. Das 
ist stark vereinfacht, sollte aber am Anfang OK sein.

> Trotzdem bleibt mir ein Verständnisproblem. Mit
>
1
((PIND & (1<<PDx))
> selektiere ich doch das Bit und es sollte als Ergebnis 0 oder 1 geben.
Ja, Du selektierst das Bit; nein, das Ergebnis kann zwischen 0 und 128 
liegen!
Stell Dir vor, bit 3 in PIND ist gesetzt und Du verknüpfst
1
PIND&(1<<PD3)
Also bekommst Du 0b1000 als Ergebnis; nicht 1 und nicht 0!

> mit
1
(vergleichswert & 0b0000000001000000))
> wird ebenfalls ein Bit ausgewertet was zu einem Ergebnis 0 oder 1 führen
> sollte.
Siehe oben -- gleiches Problem! Und dann vergleichst Du 8-Bit mit 
16-Bit-Werten; Gleichheit kann nur dann gegeben sein, wenn der 16-Bit 
Wert <256 ist.

Gruß

Fred

von Alex (Gast)


Lesenswert?

aah - jetzt verstehe ich es.

Ich habe das Problem mittlerweile so gelöst, dass ich wie in der 
Lernphase die Pegel an PINC und PIND in einer 16-Bit Variable speichere 
und beide Werte zum Schluß vergleiche. So funktioniert es.

Vielen Dank für die schnelle Hilfe(n).

Gruß,

Alex

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Fred S. wrote:

> Es
> wird nicht schaden, alle globalen Variablen "volatile" zu machen.

Doch: die fehlende Optimierung kann man schon als Schaden ansehen,
das ist einfach lausig.

Selbst solche Variablen, die volatile sein müssen, cachet man u. U.
besser in einer separaten (lokalen) Variable, bspw. innerhalb einer
ISR, um die durch volatile entstehende Pessimierung der Zugriffe
abzufedern.

von Fred S. (Gast)


Lesenswert?

Hallo Jörg,

erklär das mal bitte dem OP so, dass er es auch versteht. Ich habe extra 
dazugeschrieben, dass das "stark vereinfacht" ist und ich dies "am 
Anfang" empfehle und hatte mir sogar überlegt, eine Warnung 
dazuzuschreiben, um nicht gleich eine Flame wie Deine auszulösen. Deine 
in meinen Augen überaggressive Reaktion (als "einfach lausig" lasse ich 
meine Hilfestellung, die eindeutig der Erfahrung des OP angepasst war, 
nicht sehr gerne bezeichnen!), wo ich nicht verallgemeinert habe, finde 
ich sehr bedauerlich. Wo siehst Du ein Problem durch "fehlende 
Optimierung" beim obigen Code?

Gruß

Fred

von Oliver (Gast)


Lesenswert?

Ich kann Jörg nur beipflichten: volatile sollte man ganz bewusst 
einsetzen, auch als Anfänger. Pauschal alle externen Variablen damit zu 
schmücken, ist blanker Unsinn. Auch für Anfänger.

volatile sagt dem Compiler, daß sich diese Variable ändern kann, ohne 
das dieses für ihn aus dem Programm ersichtlich ist. Prinzipiell "weiss" 
der gcc aber immer ganz genau, ob sich der Wert einer Variable seit der 
letzten Benutzung geändert haben kann. Kann sich der Wert nicht geändert 
haben, nutzt er alle Optimierungsmöglichkeiten, z.B, den Wert in einem 
Register zu halten, anstelle den Speicher bei jeder Verwendung neu zu 
lesen.

Die einzigen Fälle, die der Compiler übersieht (weil C das per 
Definition gar nicht kennt), sind Variablen, die in einer ISR UND in 
einem Hauptprogramm verwendet werden, sowie Adressen im Speicherbereich, 
die sich selbständig ändern (IO-Register, Hardwarezähler, usw.). Dafür 
ist volatile da.

Im o.a. Programm gibt es gar keine ISR. Insofern braucht es da nirgends 
ein volatile. Die IO-Register sind in den ioxxx.h-headern sowieso 
volatile definiert.

Oliver

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Fred S. wrote:

> ..., um nicht gleich eine Flame wie Deine auszulösen.

Naja, irgendwie übertreibst du ganz schön.

> Deine
> in meinen Augen überaggressive Reaktion (als "einfach lausig" lasse ich

Sorry.  Es war wohl leider doch etwas zu früh am morgen. :-(  Das
sollte eigentlich heißen: ,,der dabei generierte Code ist einfach
lausig''.  Das war keineswegs auf deine Hilfestellung bezogen.

> Wo siehst Du ein Problem durch "fehlende
> Optimierung" beim obigen Code?

Wenn jemand nicht optimieren will, soll er einfach mit -O0 arbeiten.

Wenn aber jemand offensichtlich keine rechte Vorstellung davon hat,
was volatile bewirkt (einerseits, warum man es manchmal braucht,
andererseits, wie schlecht der generierte Code davon wird), dann
ist es in meinen Augen halt kein guter Rat, ihm zu schreiben: ,,lass
es erstmal, kann ja nichts schaden''.  Der in meinen Augen bessere
Rat wäre es gewesen: ,,lass volatile erstmal weg, bis du weißt, warum
und wofür man es braucht''.

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.