Forum: Mikrocontroller und Digitale Elektronik Problem mit Bitweiser Maskierung, Hilfe!


von Reinhard W. (freakster235)


Lesenswert?

Hallo Leute,

ich hier ein kleines Verständnisproblem.
Ich habe hier einen Code im internet gefunden für einen Timer Compare.
Ich habe den Timer2 bei einen Atmega1280 mit 11,0592 Mhz so gesetzt das 
er alle 2 ms einen Compare Interrupt auslöst.

Der Code sieht so aus:

SIGNAL(SIG_OUTPUT_COMPARE1A)
{
    static unsigned char cnt=0;
    cnt++;
    if (cnt & 0x01)        // 4ms
    {
    ... tu was 4ms
    }

    if ((cnt & 0x3F) == 0x20)    // 64ms
    {
    ... tu was nach 64ms
    }
}

Also als erstes weißt "static" eine feste Adresse zu, soweit klar. Also 
nehme ich auch mal an das "cnt" bei jeden Einsprung in die Routine nicht 
auf null gesetzt wird duch "static unsigned char cnt=0;" sondern der 
Wert erhalten bleibt bis Überlauf ?

Wieso lösen die IF abfragen bei 4ms und bei 64ms aus.

Sehe ich das so richtig:

also wenn cnt=1 ist dann:
cnt   0000 0001
&     0000 0001
----------------
      0000 0001 also Wahr

Also löst diese schleife beim ersten Durchlauf aus und dann bei jeden 
2.ten wieder oder?

Aber bei der anderen blick ich garnet durch.

cnt    0010 0000
& 0x3F 0011 1111
----------------
       0010 0000
=0x20  0010 0000

Also löst doch diese schleife nur einmal aus und dann nicht mehr. Aber 
die realität sieht anders aus, die löst alle 64ms aus.
Aber wieso??? ;(((

Bitte erleuchtet mich mal.

Danke!

Gruß

Reini!

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Reinhard Winkelmaier schrieb:

>     if ((cnt & 0x3F) == 0x20)    // 64ms
>     {
>     ... tu was nach 64ms
>     }

Hier wird erst maskiert (auf die untersten 6 Bits) und anschließend das 
6. Bit getestet. Das kann man auch einfacher schreiben:

     if (cnt & 0x20)    // 64ms
     {
     ... tu was nach 64ms
     }

Meines Erachtens ist die Maskierung hier unnötig.

Trotzdem ist mir schleierhaft, was das soll. Nach 64ms wird nämlich 
jedesmal die Bedingung wahr, jedenfalls vom Zählerstand 0010 0000 bis 
0011 1111. Erst beim Zählerstand 0100 0000 ist dann wieder Schluss mit 
dem "tu was nach 64ms". Absicht?

> Also als erstes weißt "static" eine feste Adresse zu, soweit klar. Also
> nehme ich auch mal an das "cnt" bei jeden Einsprung in die Routine nicht
> auf null gesetzt wird duch "static unsigned char cnt=0;" sondern der
> Wert erhalten bleibt bis Überlauf ?

Eine static-Variable bleibt die ganze Zeit am Leben. Sie wird hier mit 0 
initialisiert und läuft hoch bis 255. Das nächste cnt++ bewirkt, dass 
sie wieder auf 0 gesetzt wird. Dann geht das Spiel wieder von vorn los.

> Wieso lösen die IF abfragen bei 4ms und bei 64ms aus.

Siehe oben.

> Sehe ich das so richtig:
>
> also wenn cnt=1 ist dann:
> cnt   0000 0001
> &     0000 0001
> ----------------
>       0000 0001 also Wahr
>
> Also löst diese schleife beim ersten Durchlauf aus und dann bei jeden
> 2.ten wieder oder?

Richtig.

> Aber bei der anderen blick ich garnet durch.
>
> cnt    0010 0000
> & 0x3F 0011 1111
> ----------------
>        0010 0000
> =0x20  0010 0000
>
> Also löst doch diese schleife nur einmal aus und dann nicht mehr. Aber
> die realität sieht anders aus, die löst alle 64ms aus.
> Aber wieso??? ;(((

Die löst nicht nur alle 64ms aus, sondern nach 64ms mehrfach alle 2ms, 
bis dann 0100 0000 erreicht ist. Dann ist erstmal wieder Ruhe. Wenn dann 
cnt durch den Überlauf 0 geworden ist, fängt das Spiel von vorne an.

Gruß,

Frank

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Frank M. schrieb:

> Hier wird erst maskiert (auf die untersten 6 Bits) und anschließend das
> 6. Bit getestet. Das kann man auch einfacher schreiben:
>
>      if (cnt & 0x20)    // 64ms
>      {
>      ... tu was nach 64ms
>      }
>
> Meines Erachtens ist die Maskierung hier unnötig.

Das war Quatsch,

if ((cnt & 0x3F) == 0x20)    // 64ms

ist natürlich nur wahr bei:

0010 0000
0110 0000
1010 0000
1110 0000

und damit nur bei jedem 32ten Aufruf, also alle 64ms.

Gruß,

Frank

von Reinhard W. (freakster235)


Lesenswert?

Hallo Frank,

vielen Dank fuer die Antwort, aber

der 64ms Timer hat dann schon einen Aussetzer oder?

Maske  0010 0000
32     0010 0000 ist Wahr
64     0100 0000 ist nicht Wahr, also setzt hier aus
96     0110 0000 ist Wahr
128    1000 0000 wieder nicht Wahr
160    1010 0000 ist Wahr
192    1100 0000 nicht Wahr
224    1110 0000 ist Wahr
256(0) 0000 0000 nicht Wahr

Also taktet der Timer doch nur bei jeden 2.ten aml also 128ms oder?

ist eine solche Schreibweise nicht besser:

IF ( (cnt % 32) == 0 )
{
}

oder hat das Irgendwelche Nachteile?

Und nochwas, was bewirkt so eine EODER Bitmanipulation

REGISTER = 1<<BIT1 ^ 1<<BIT2

Was bewirkt das? Also ich nehme mal an beide Bits werden gesetzt, aber 
wofuer das EODER (^) ?

Vielen Dank!

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Reinhard Winkelmaier schrieb:
> vielen Dank fuer die Antwort, aber
> der 64ms Timer hat dann schon einen Aussetzer oder?
>
> Maske  0010 0000
> 32     0010 0000 ist Wahr
> 64     0100 0000 ist nicht Wahr, also setzt hier aus
> 96     0110 0000 ist Wahr
> 128    1000 0000 wieder nicht Wahr
> 160    1010 0000 ist Wahr
> 192    1100 0000 nicht Wahr
> 224    1110 0000 ist Wahr
> 256(0) 0000 0000 nicht Wahr

Doch: alle obigen Werte (bis auf die 256(0)) sind wahr. Wenn Du die 
Werte mit der Maske 0x3F verbindest, kommt immer 0x20 raus.

> Also taktet der Timer doch nur bei jeden 2.ten aml also 128ms oder?

Nö.

> ist eine solche Schreibweise nicht besser:
>
> IF ( (cnt % 32) == 0 )
> {
> }
>
> oder hat das Irgendwelche Nachteile?

Kommt auf den Compiler und die Optimierungsstufe an.

> Und nochwas, was bewirkt so eine EODER Bitmanipulation
>
> REGISTER = 1<<BIT1 ^ 1<<BIT2
>
> Was bewirkt das? Also ich nehme mal an beide Bits werden gesetzt, aber
> wofuer das EODER (^) ?

Das sieht nach Code von Peter Danegger aus, er benutzt da gern den 
EXOR-Operator '^' statt dem bitweisen ODER-Operator '|'. Ich glaube, er 
findet das lesbarer. Andere, die in der Verwendung vom EXOR-Operator 
ungeübt sind, finden das aber eher kryptisch - was ich nachvollziehen 
kann.

Gruß,

Frank



>
> Vielen Dank!

von LuXXuS 9. (aichn)


Lesenswert?

Mal ganz anders, wieso nimmst du nicht einfach zwei Timer, die 
unabhängig voneinander nen Interrupt auslösen - dann immer in der 
gewünschten Zeit. Oder keine Ressourcen vorhanden?

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

> ist eine solche Schreibweise nicht besser:
> IF ( (cnt % 32) == 0 )
Aus diesem Grund solltest du deine Bitmanipulation besser so machen:
if ((cnt & 0x1F) == 0)    // 64ms
Das hat dann die selbe Funktion...

> Also ich nehme mal an beide Bits werden gesetzt,
Wie kommst du darauf?
> Also ich nehme mal an beide Bits werden gesetzt,
Nur, wenn der Wert von BIT1 != BIT2

EDIT:
> Mal ganz anders, wieso nimmst du nicht einfach zwei Timer
Ein böser Würgaraund... :-/
Wozu denn die Timer bei einer so simplen Aufgabe so vergeuden?

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Frank M. schrieb:

> Doch: alle obigen Werte (bis auf die 256(0)) sind wahr. Wenn Du die
> Werte mit der Maske 0x3F verbindest, kommt immer 0x20 raus.

Schon wieder Unsinn, ich sollte besser erst einen Kaffee trinken, bevor 
ich lese - und vor allen Dingen - schreibe.

Du hast recht: nur bei jedem zweiten Mal kommt 0x20 raus. Insgesamt also 
nur 4 mal bei einem Durchlauf von 0 bis 255. Wenn die ISR tarsächlich 
alle 2 ms aufgerufen wird, wären das dann 256 * 2 / 4 = 128ms.

Bist Du Dir sicher, dass die ISR tatsächlich alle 2ms und nicht jede ms 
aufgerufen wird?

Gruß,

Frank

von Reinhard W. (freakster235)


Lesenswert?

Hallo Frank,

immer noch nicht durchgeblick.

If (cnt & 0x3F) == 0x20)

das "cnt & 0x3F" ist doch eine Bitweise ver-undung ?

also, hat der Counter jetzt den Wert 64 dann:

cnt        0100 0000
0x3F       0011 1111
=          0100 0000 ???

ist doch dann nicht Wahr oder?

Ich glaub ich verstehe das Und falsch?

also kann man beim setzen von meheren bits statt:

Register = (1<<BIT1) | (1<BIT2) | (1<<BIT3)

auch

Register = (1<<BIT1) ^ (1<BIT2) ^ (1<<BIT3)

Korrekt?

Gruss Reini!

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Ich hatte die Lösung schon geschrieben, nur wollt sie keiner lesen... 
:-/
1
    if ((cnt & 0x1F) == 0)    // 32*2ms
Und damit gilt:
Maske  0001 1111
32     0010 0000 ist Wahr
64     0100 0000 ist Wahr
96     0110 0000 ist Wahr
128    1000 0000 ist Wahr
160    1010 0000 ist Wahr
192    1100 0000 ist Wahr
224    1110 0000 ist Wahr
256(0) 0000 0000 ist Wahr

> also kann man beim setzen von meheren bits statt:
> Register = (1<<BIT1) | (1<BIT2) | (1<<BIT3)
> auch
> Register = (1<<BIT1) ^ (1<BIT2) ^ (1<<BIT3)
Ja, und man spart sich 3x die Alt-GR Taste...  ;-)
Das geht wie gesagt, wenn BIT1!=BIT2!=BIT3

von Karl H. (kbuchegg)


Lesenswert?

Die Idee in diesem Code ist es
1
    if ((cnt & 0x3F) == 0x20)    // 64ms
2
    {
3
    ... tu was nach 64ms
4
    }
5
}

dass der Vergleich nur dann wahr ergibt, wenn die untersten 6-Bits von 
cnt exakt 0x20 ergeben. Die obersten 2 Bits von cnt können stehen wie 
sie wollen, die sind uninteressant. Aber die restlichen müssen exakt 
0x20 ergeben.
Wenn man also vom Zähler die obersten 2 Bits auf 0 setzt (weil sie ja 
uninteressant sind), dann soll da nicht 0x21, 0x22, 0x23 .... im Zähler 
sein, sondern genau 0x20 (oder dezimal 32)

von Stefan E. (sternst)


Lesenswert?

Für die "Wahrheitshäufigkeit" ist der genaue Vergleichswert irrelevant 
(solange in dem Wert die oberen 2 Bits 0 sind). Egal ob da nun 0x20 oder 
0 oder auch 0x0a steht, es ist immer 4 mal pro 256 wahr (also alle 64 
mal). Der Code ist also für einen 1ms-Interrupt geschrieben. Der 
Vergleichswert legt nur die genaue Position dieser 4 innerhalb der 256 
fest. Man kann so z.B. ein "Zusammenfallen" von Ereignissen bei mehreren 
Intervallen vermeiden.

von Reinhard W. (freakster235)


Lesenswert?

Hallo Leute,

jetzt wirds suspekt.

Also die Loesung von Lothar Miller, klingt mir absolut einleuchtend.
1
 if ((cnt & 0x1F) == 0)    // 32*2ms

Aber das was karl Heinz Buchegger schreibt klingt auch logisch,
wuerde aber bedeuten der Timer loest nur einmal aus?
Bis zum naechsten Ueberlauf.

Das Was Stefan Ernst schreibt mit den 4 mal ausloesen klingt mir 
garnicht
mehr plausibel. wenn der Compare wert doch 32 ist ?

das
1
 if ((cnt & 0x3F) == 0x20)

erzeugt die Maske fuer die ver-undung oder?

also 0011 1111
es werden quasi die oberen 2 Bits ausgeblendet und nur diese Bits 
uebernommen bei denen 1 und 1 ist, richtig?
Aber dann ist doch die Bedingung == 32 nur einmal erfuellt oder?

Ich werd noch mischugge.

Danke!

Gruss Reini!

von Karl H. (kbuchegg)


Lesenswert?

Reinhard Winkelmaier schrieb:
> Hallo Leute,
>
> jetzt wirds suspekt.
>
> Also die Loesung von Lothar Miller, klingt mir absolut einleuchtend.
>
>
1
 if ((cnt & 0x1F) == 0)    // 32*2ms
>
> Aber das was karl Heinz Buchegger schreibt klingt auch logisch,
> wuerde aber bedeuten der Timer loest nur einmal aus?

Nein.
Er löst 4 mal bei einem Umlauf des cnt von 0 bis 255 aus.
Dies deshalb, weil ja die obersten 2 Bits wegmaskiert werden und daher 
keine Rolle spielen

   0010 0000                hier ist der Vergleich true
   0110 0000                und hier ist er true
   1010 0000                hier noch einmal
   1110 0000                und zu guter letzt hier nochmal

   ^^
   |------ diese beiden Bits werden durch das  & 0x3F wegmaskiert und
           spielen daher keine Rolle

Aber

   0010 0000                 hier ist der Vergleich true

   0010 0001                 hier aber nicht mehr!


Die obigen 4 Binärzahlen, sind die einzigen, bei denen der Vergleich 
true ergben wird. In allen anderen Fällen ist der Vergleich false.

(Alle Zahlen sind Binärzahl, wenn nicht anders durch die C-Schreibweise 
angegeben)

von Stefan E. (sternst)


Lesenswert?

Reinhard Winkelmaier schrieb:
> Also die Loesung von Lothar Miller, klingt mir absolut einleuchtend.

Wenn du einen 2ms-Interrupt hast, dann ja. Aber es ging ja um die 
Erklärung des vorhandenen Codes. Und der ist ganz offensichtlich für 
einen 1ms-Interrupt gedacht.

> Das Was Stefan Ernst schreibt mit den 4 mal ausloesen klingt mir
> garnicht mehr plausibel. wenn der Compare wert doch 32 ist ?

Ok, dann etwas ausführlicher. ;-)
1
(cnt & 0x3F) == X
Bedeutet konkret: unteren 6 Bits von cnt sind genau X, oberen 2 Bits 
sind egal (alles mal unter der Annahme cnt sei 8 Bit groß).
Aus dem "oberen 2 Bits egal" ergibt sich die Häufigkeit. Sie können 00, 
01, 10 oder 11 sein, also 4 pro 256.
Aus dem "genau X" ergibt sich die genaue Position dieser 4 innerhalb der 
256.

Hier einfach mal ein paar konkrete Beispiele:
1
if ((cnt & 0x3F) == 0)
Ist wahr bei 0, 64, 128, 192
1
if ((cnt & 0x3F) == 0x20)
Ist wahr bei 32, 96, 160, 224
1
if ((cnt & 0x3F) == 0x0a)
Ist wahr bei 10, 74, 138, 202

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

> Er löst 4 mal bei einem Umlauf des cnt von 0 bis 255 aus.
Ich hatte das genau so aufgefasst, dass ausgehend vom zuerst geposteten 
Code die eine Aktion alle 4 ms ausgeführt werden soll, und die andere 
zyklisch alle 64 ms.

Wenn eine Aktion einmalig zum Zeitpunkt 64 ms ausgelöst werden soll, 
dann wäre die Abfrage nach
1
if (cnt == 64) ...
und ein passendes Zurücksetzen der Variable cnt wohl am 
sinnvollsten...

von Karl H. (kbuchegg)


Lesenswert?

Lothar Miller schrieb:

> Wenn eine Aktion einmalig zum Zeitpunkt 64 ms ausgelöst werden soll,
> dann wäre die Abfrage nach
>
1
> if (cnt == 64) ...
2
>
> und ein passendes Zurücksetzen der Variable cnt wohl am
> sinnvollsten...

Seh ich auch so.
Aber wissen nicht, was in der ISR sonst noch so passiert und ob es einen 
guten Grund gibt, warum cnt nicht einfach auf 0 zurückgesetzt wird.

von Reinhard W. (freakster235)


Lesenswert?

Hallo Leute,

Vielen Dank nun ist es doch angekommen.

Also die weitere ISR existiert noch garnicht, es war eine reine 
Verstaendnisfrage von mir.

Die Ihr mir aber hervorragend beantwortet habt.

Vielen Dank euch allen, bis zur naechsten Frage :)

Gruss Reini!

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.