Forum: Compiler & IDEs Was mache ich mit den Bitoperatoren falsch!?


von Jan M. (jan_m)


Lesenswert?

Noch ein weiteres Problem ist aufgereten:
Ich will verschiedenen Taster und Schalterzustände an einem Port 
abfragen: Zum Beispiel: Wenn Taster gedrückt (An PD1, interner Pullup), 
Endschalter noch nicht erreicht (PD3) und der Timer noch nicht 
abgelaufen (Setzt zeit auf 0), dann....
Hab mir das dann so vorgestellt:

if (!(PIND & (1<<PIND1)) && (PIND & (1<<PIND3)) && Zeit)

Aber der Effekt ist komisch, soll so nicht sein. Ich verunde mit dem && 
doch keine Binärzahlen mehr, sondern nur nullen oder einsen!?

Wie kann ich es eleganter lösen?

Grüße!

von g457 (Gast)


Lesenswert?

..streiche '!', setzte '~'

von g457 (Gast)


Lesenswert?

..oder auch nicht ∗brilleaufsetz∗, ich nehm alles zurück</ingrid>

von (prx) A. K. (prx)


Lesenswert?

Jan M. schrieb:

> Aber der Effekt ist komisch, soll so nicht sein.

Eine ungemein erschöpfende Problembeschreibung.

> Wie kann ich es eleganter lösen?

Oder ist dein Problem ausschliesslich ästhetischer Natur?

Wobei du nicht vergessen solltest, dass Taster prellen.

von Knut (Gast)


Lesenswert?

Was geht denn nicht?
Probier mal:
1
if (!((PIND & (1<<PIND1)) || (PIND & (1<<PIND3)) || Zeit))


Knut

von Stefan E. (sternst)


Lesenswert?

Wenn ich in meine Glaskugel schaue, taucht da aus dem Nebel der 
spärlichen Informationen der Begriff "volatile" auf.

von Jan M. (jan_m)


Angehängte Dateien:

Lesenswert?

Das ganze ist mir halt auch nicht so klar, daher wollte ich es lieber 
erstmal mweglassen. Aber okay, hier was ich bisher rausfand:

Ein Fehler ist, dass der Motor direkt vom Start des µcs losgeht was 
nicht sein darf denke ich.
Eine andere komische Sache ist, dass er manchmal vom einen Endschalter 
zum anderen fuhr und direkt wieder umkehrte. Was aber eigentlich nicht 
sein kann da ich weder einen Taster/Schalter gedrückt habe und somit 
durch die Verundung keine Funktion aktiv sein sollte!?
Daher dachte ich die Bitoperationen falsch angewandt zu haben.

Danke für die Hilfe! =)

von Stefan E. (sternst)


Lesenswert?

Ich sehe auf Anhieb zwei Sachen:

1) Zeit hat einen falschen Initial-Wert.

2) Wie soll der Zähler jemals den Compare-Wert erreichen, wenn er in der 
Schleife ständig wieder auf 0 zurückgesetzt wird?

von Jan M. (jan_m)


Lesenswert?

Stefan Ernst schrieb:
> Ich sehe auf Anhieb zwei Sachen:
>
> 1) Zeit hat einen falschen Initial-Wert.
>
> 2) Wie soll der Zähler jemals den Compare-Wert erreichen, wenn er in der
> Schleife ständig wieder auf 0 zurückgesetzt wird?

Danke! Das zweite war ein Denkfehler, weiß auch nicht wie das passiert 
ist.

Was meinst du mit erstens? Welchen Initialwert sollte ich nehmen.

Gerade passiert esnoch, das wenn ich zufahre er automatisch wieder 
auffährt nach Erreichen des Endschalters. Komisch...

von Stefan E. (sternst)


Lesenswert?

Jan M. schrieb:
> Was meinst du mit erstens? Welchen Initialwert sollte ich nehmen.

Wenn die Wartezeit abgelaufen ist, wird Zeit auf Null gesetzt. Welchen 
Wert sollte Zeit also vorher haben? Null (wie jetzt) ist jedenfalls 
"ungünstig".

von der A. (the_a)


Lesenswert?

generell würde ich auch eine explizite Schreibweise bevorzugen:
statt:
    if (!(PIND & (1<<PIND1)) && (PIND & (1<<PIND3)) && Zeit)
besser:
    if (((PIND & (1<<PIND1)) == 0) && ((PIND & (1<<PIND3)) != 0) && 
(Zeit != 0))

Adib.

von der A. (the_a)


Lesenswert?

Kannst du eigentlich erkennen, ob ein Schalter neu gesetzt wurde während 
du in der while Schleife verharrst?

Adib.

von Jan M. (jan_m)


Lesenswert?

Ich werde das mit der expliziten Schreibweise mal ausprobieren, danke 
:-)

Wegen den Schleifen: Ich wollte Hardwareinterrupts INT0 und INT1 der 
Enschalter bei fallender Flanke auslösen lassen, das heißt die werden 
sicher erkannt! Aber ob andere Schalter gesetzt wurden derzeit nicht. Er 
soll ja auch beim Antasten bis in die Endstellug fahren ohne 
unterbrochen zu werden.

Da ich noch ein blutiger Anfänger bin habe ich es erst einmal mit den 
Schleifen versucht. Mit Sicherheit sehr unschön, wie könnte man es 
besser machen?
Ich habe die Flankenauswertung nur bei den Interrupts gesehen, oder wie 
geht das sonst Softwaremäßig?

Vereinfachter Ablauf:
Fallende Flanke der Taster/Schalter --> Motor an
Wenn Zeit um oder fallende Flanke der Endschalter --> Motor aus

von Karl H. (kbuchegg)


Lesenswert?

Jan M. schrieb:

> Da ich noch ein blutiger Anfänger bin habe ich es erst einmal mit den
> Schleifen versucht. Mit Sicherheit sehr unschön, wie könnte man es
> besser machen?

Das ist schon in Ordnung. Du brauchst keine Interrupts dafür verwenden. 
In deiner Anwendung sind sie mehr Ärger als es wert ist.

> Ich habe die Flankenauswertung nur bei den Interrupts gesehen, oder wie
> geht das sonst Softwaremäßig?

Was ist denn eine Flanke?
Ein Zustandswechsel. Wenn du (als Person) alle 10 Sekunden nachsiehst 
und einen 'Flankenwechsel' in der Wohnraumbeleuchtung feststellen 
sollst, wie machst du das dann? Du merkst dir einfach ob das Licht jetzt 
brennt oder nicht (Hinweis: Die Phrase 'Du merkst dir' ist immer ein 
Hinweis darauf dass du eien Variable brauchst). 10 Sekunden später 
siehst du erneut nach und vergleichst mit vorher. WEnn vorher das Licht 
nicht brannte, jetzt aber schon, dann hat offenbar in der Zwischenzeit 
jemand das Licht eingeschaltet -> es gab eine Flanke in der 
Lichtsituation. Wenn jetzt das Licht brennt und vorher auch, dann hat 
niemand am Schalter gefummelt. Genauso in der Situation jetzt brennt das 
Licht nicht und vorher hat es auch nicht gebrannt. Und dann gibt es noch 
die Situation: jetzt ist das Licht aus und vorher hat es noch gebrannt. 
Dann hat offenbar jemand in der Zwischenzeit den Lichtschalter auf "aus" 
gestellt.
1
....
2
3
uint8_t TasterLinksAlt;
4
uint8_t TasterRechtsAlt;
5
uint8_t TasterLinksNeu;
6
uint8_t TasterRechtsNeu;
7
8
...
9
10
int main()
11
{
12
   .....
13
14
15
  TasterLinksAlt =  ( PIND & (1<<PD1) );
16
  TasterRechtsAlt = ( PIND & (1<<PD7) );
17
18
  while( 1 ) {
19
20
    ....
21
22
    // jetzigen Zustand der Taster feststellen
23
    TasterLinksNeu =  ( PIND & (1<<PD1) );
24
    TasterRechtsNeu = ( PIND & (1<<PD7) );
25
    
26
    // Taster "Links" auswerten
27
    if( TasterLinksNeu != TasterLinksAlt ) {   // offenbar hat sich was verändert
28
      if( TasterLinksNeu == 0 ) {              // der Taster wurde gedrückt
29
        ....
30
      }
31
      TasterLinksAlt = TasterLinksNeu;
32
    }
33
34
    // Taster "Rechts" auswerten
35
    if( TasterRechtsNeu != TasterRechtsAlt ) {   // offenbar hat sich was verändert
36
      if( TasterRechtsNeu == 0 ) {              // der Taster wurde gedrückt
37
        ....
38
      }
39
      TasterRechtsAlt = TasterRechtsNeu;
40
    }
41
42
    ...
43
  }
44
}

von Jan M. (jan_m)


Angehängte Dateien:

Lesenswert?

Vielen Dank schonmal bis hier, tolles Forum!!

Ich habe mich nochmal an das Programm gemacht:
Habe die einfache Debounce-Routine von Peter Dannegger implementiert und 
detektiere damit hoffentlich, sofern ich es richtig verstanden habe, die 
fallenden Flanken der Taster (Active low).

Sehr ihr eine einfachere Lösung für die Schalterschleife?
Ich kann den Motor natürlich mit dem debounce befehl starten lassen, 
aber wie wieder ausschalten? Die Schalter sollte man besser auch 
entprellen oder!?
Die steigende Flanke könnte ich mit dem angepassten Skript für 
Active-high Taster ermitteln, wobei es sicher sehr ineffizient ist beide 
Routinen parallel arbeiten zu haben. Auch wegen dem Speicher:

// Modification for active high push button
#define debounce( port, pin )                                        \
({                                                                   \
  static uint8_t flag = 0; /* new variable on every macro usage */   \
  uint8_t i = 0;                                                     \
                                                                     \
  if( flag ){                  /* check for key release: */          \
    for(;;){                   /* loop... */                         \
      if( (port & 1<<pin) ){   /* ... until key pressed or ... */    \
        i = 0;                 /* 0 = bounce */                      \
        break;                                                       \
      }                                                              \
      _delay_us( 98 );         /* * 256 = 25ms */                    \
      if( --i == 0 ){          /* ... until key >25ms released */    \
        flag = 0;              /* clear press flag */                \
        i = 0;                 /* 0 = key release debounced */       \
        break;                                                       \
      }                                                              \
    }                                                                \
  }else{                       /* else check for key press: */       \
    for(;;){                   /* loop ... */                        \
      if( !(port & 1<<pin) ){  /* ... until key released or ... */   \
        i = 0;                 /* 0 = bounce */                      \
        break;                                                       \
      }                                                              \
      _delay_us( 98 );         /* * 256 = 25ms */                    \
      if( --i == 0 ){          /* ... until key >25ms pressed */     \
        flag = 1;              /* set press flag */                  \
        i = 1;                 /* 1 = key press debounced */         \
        break;                                                       \
      }                                                              \
    }                                                                \
  }                                                                  \
  i;                           /* return value of Macro */           \
})

Seht ihr ansonsten noch Fehler?

Grüße Jan.

von Jan M. (jan_m)


Angehängte Dateien:

Lesenswert?

Habe das ganze dank Karl-Heinz`s Einwand nochmal umgeschrieben.
Nun überprüfe ich die Zustandswechsel der Schalter und Endschalter.
Am WE kann ich es erst testen, dann schauen wir mal :-)

Hab schon viel gesucht aber nichts gefunden: Gibt es eine schöne 
Entprell, sowie Flankenerkennungs (fallende und steigende) Routine für 
einen gesamten Port? Will nicht jeden Schalter einzeln abfragen müssen 
mit 8 mal denselben Code.

Grüße Jan!

von Karl H. (kbuchegg)


Lesenswert?

Jan M. schrieb:

> Hab schon viel gesucht aber nichts gefunden: Gibt es eine schöne
> Entprell, sowie Flankenerkennungs (fallende und steigende) Routine für
> einen gesamten Port? Will nicht jeden Schalter einzeln abfragen müssen
> mit 8 mal denselben Code.

gibt es

Entprellung

ganz unten: die Dannegger Methode in Form der C-Komfortroutinen.
Spielen alle Stückeln, die man jemals brauchen wird.

von Jan M. (jan_m)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Jan M. schrieb:
>
>> Hab schon viel gesucht aber nichts gefunden: Gibt es eine schöne
>> Entprell, sowie Flankenerkennungs (fallende und steigende) Routine für
>> einen gesamten Port? Will nicht jeden Schalter einzeln abfragen müssen
>> mit 8 mal denselben Code.
>
> gibt es
>
> Entprellung
>
> ganz unten: die Dannegger Methode in Form der C-Komfortroutinen.
> Spielen alle Stückeln, die man jemals brauchen wird.

Hallo erneut!

Die Routine hatte ich gefunden, jedoch habe ich sie wohl nicht ganz 
verstanden. Ich dachte man kann nur erkennen, ob ein Taster/Schalter 
kurz/lang gedrückt wird oder dauerhaft.

Ich denke wohl viel zu kompliziert und sehe das ganze nicht.
Um nicht wieder in einer Schleife zu enden, will ich bei fallender 
Flanke an einem Port den Motor mit if(...) anschalten und bei steigender 
dann genauso ausschalten.

Hättest du daher ein Beispiel?

Vielen Dank!

von Karl H. (kbuchegg)


Lesenswert?

Jan M. schrieb:

> Ich denke wohl viel zu kompliziert und sehe das ganze nicht.
> Um nicht wieder in einer Schleife zu enden, will ich bei fallender
> Flanke an einem Port den Motor mit if(...) anschalten und bei steigender
> dann genauso ausschalten.
>
> Hättest du daher ein Beispiel?


Ist doch im Code dort drinnen!

Den ISR Teil übernimmst du einfach.
Die Funktionen danach ebenfalls
Genauso wie die Timerinitialisierung


Und in der Hauptschleife in main() (auch die ist im Codebeispiel 
drinnen), gehts dann ganz einfach

  while( 1 ) {

   .....

   if( get_key_press( 1 << TASTE ) ) {
     Fertig. Dieser Code hier wird ausgeführt, wenn eine Taste gedrückt
     wurde
   }
 }


Edit:
Hier musst du natürlich noch deine Konfiguration eintragen
#define KEY_DDR         DDRB
#define KEY_PORT        PORTB
#define KEY_PIN         PINB
#define KEY0            0
#define KEY1            1
#define KEY2            2
#define ALL_KEYS        (1<<KEY0 | 1<<KEY1 | 1<<KEY2)

Die ersten 3 definieren an welchem Port die Tasten hängen und danach 
kommen alles Tastem die du brauchst. Wenn du daher zb 4 Tasten am Port D 
hängen hast, die die Funktion Up, Down, Left, Right haben sollen, die an 
den Pins 0, 1, 5 und 7 hängn, dann hast du
1
#define KEY_DDR         DDRD
2
#define KEY_PORT        PORTD
3
#define KEY_PIN         PIND
4
#define KEY_UP          0
5
#define KEY_DOWN        1
6
#define KEY_LEFT        5
7
#define KEY_RIGHT       7
8
#define ALL_KEYS        (1<<KEY_UP | 1<<KEY_DOWN | 1<<KEY_LEFT | 1<<KEY_RIGHT)

Da du wahrscheinlich keinen Autorepeat auf den Tasten haben willst
1
#define REPEAT_MASK     0

und deine verwendenden Abfragen sehen so aus
1
....
2
3
  while( 1 ) {
4
5
    if( get_key_press( 1 << KEY_UP ) ) {      // Taste Up gedrückt
6
      ...
7
    }
8
9
    if( get_key_press( 1 << KEY_DOWN ) ) {    // Taste Down gedrückt
10
      ...
11
    }
12
13
    if( get_key_press( 1 << KEY_LEFT ) ) {    // Taste Left gedrückt
14
      ...
15
    }
16
17
    if( get_key_press( 1 << KEY_RIGHT ) ) {   // Taste Right gedrückt
18
      ...
19
    }
20
21
    ...
22
  }

(wenn du die restlichen Funktionen nicht brauchst, kannst du sie auch 
rauslöschen)

von Karl H. (kbuchegg)


Lesenswert?

Und deine Rollosteuerung implementierst du am besten als 
Zustandsmaschine.

http://www.mikrocontroller.net/articles/Statemachine

In welchen Zuständen kann dein Rollo sein?

* unten
* fährt nach oben
* oben
* fährt nach unten
* fehler

wie sind die Übergänge:

Zustand "unten"
-------------
  Taster "Up" gedrückt
       Aktion: Motor nach oben einschalten
               nächster Zustand  ->  "fähtr nach oben"


Zustand "fährt nach oben"
-------------------------
  Taster "Down" gedrückt
       Aktion: Motor auf nach unten schalten
               nächster Zustand   ->  fährt nach unten

  Endschalter oben betätigt
       Aktion: Motor ausschalten
               nächster Zustand   ->  oben

Zustand "oben"
--------------

    usw. usw.

von Jan M. (jan_m)


Lesenswert?

Wow super das ist wirklich alles was ich brauche.

Vielen, vielen Dank! Jetzt wirds auf jedenfall klappen, ich werde 
berichten.

von Jan M. (jan_m)


Angehängte Dateien:

Lesenswert?

Habe ein ganz neues Programm mit der Zustandsmaschine und der ISR 
Entprellroutine geschrieben. Ist denke ich echt die beste Wahl.

Es klappt allerdings noch garnichts, bin jetzt auch schon wieder seit 
ner Stunde am Fehler suchen.

Komisch ist auch, dass mir der Compiler ohne die fünfte geschlossene 
Klammer am Ende einen Error ausgibt ( Expected Declaration or statement 
at end of Input). Wenn ich richtig zählen kann müssten es aber nur vier 
sein: Main, while-Schleife,void Funktion und switch.

Vielleicht liegt da ja schon der Hund begraben. Und wie mache ich am 
besten die erste Abfrage um zu schauen in welchem Zustand ich starte? 
Ich das so gut gelöst?

Grüße Jan.

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


Lesenswert?

Du hast eine Funktion (stateMachine()) in einer Funktion (main()) ...
sowas macht man nicht, auch wenn GCC es kann. ;-)  Wenn stateMachine
schon eine Funktion sein soll, dann definiere sie oberhalb von main
(und am besten gleich "static").  Dann aber nicht vergessen, diese
Funktion auch aufzurufen!  (Genau das hast du wohl auch jetzt bereits
vergessen.)

von Karl H. (kbuchegg)


Lesenswert?

Jan M. schrieb:

> Es klappt allerdings noch garnichts, bin jetzt auch schon wieder seit
> ner Stunde am Fehler suchen.

Jörg hat dazu ja schon was gesagt

Du machst einen Kardinalfehler:
Du schreibst viel zu viel Code, ohne ihn zu testen!

> Komisch ist auch, dass mir der Compiler ohne die fünfte geschlossene
> Klammer am Ende einen Error ausgibt ( Expected Declaration or statement
> at end of Input). Wenn ich richtig zählen kann müssten es aber nur vier
> sein: Main, while-Schleife,void Funktion und switch.

Wenn du sauber arbeitest, brauchst du überhaupt nicht zählen.
Nach jeder { wird der darauffolgende Code um 2 Leerzeichen weiter nach 
rechts eingerückt. Jede } verschiebt die Einrückung wieder um 2 Zeichen 
nach links.
Am Ende einer Funktion muss daher die letzte } automatisch wieder am 
linken Rand stehen, sonst stimmt irgendwo etwas nicht ( vergessenes { 
oder } )

Und drum mag ich solche Schreibweisen auch nicht
1
  if (!(PIND & (1<<PIND1)))
2
    {
3
    state= Vorhangoffen;
4
    }

weil man die { bzw } nicht gut genug sieht
1
  if (!(PIND & (1<<PIND1)))
2
  {
3
    state= Vorhangoffen;
4
  }

jetzt stechen sie raus

> Vielleicht liegt da ja schon der Hund begraben. Und wie mache ich am
> besten die erste Abfrage um zu schauen in welchem Zustand ich starte?
> Ich das so gut gelöst?

Musst du entscheiden, was nach dem Einschalten passieren soll.
Ich würde nach dem Einschalten ganz einfach den ersten State vorgeben. 
Meiner Meinung nach sollte das sein "ROllo fährt auf" und gleichzeitig 
schalte ich den Motor ein.
Auf die Art wird dann das Rollo beim ersten Bestromen der Schaltung erst 
mal aufgefahren. Du magst das anders sehen aber ich finde das nicht 
schlecht, wenn nach einem Stromausfall erst mal das Rollo auf jeden Fall 
aufgeht und ich nicht im Dunkeln sitze. Ist aber IMHO und letzten Endes 
ist es deine Entscheidung was passieren soll.

von Karl H. (kbuchegg)


Lesenswert?

Und noch ein PS

Was ist der UNterschied zwischen
1
      case Vorhangauffahren_auto:
2
            {
3
        if ((get_key_press( 1 << Endschalteraussen )) || (Zeit))
4
                {
5
          PORTB &= ~(1 << PB1);
6
          PORTB &= ~(1 << PB2); 
7
          TCCR1B &= ~(1 << CS12);
8
          TCCR1B &= ~(1 << CS10);
9
          state= Vorhangoffen;
10
              }
11
        }
12
      break;

und
1
#define MOTOR_PORT  PORTB
2
#define MOTOR_1     PB1
3
#define MOTOR_2     PB2
4
5
void MotorStop()
6
{
7
  MOTOR_PORT &= ~(1 << MOTOR_1);
8
  MOTOR_PORT &= ~(1 << MOTOR_2); 
9
}
10
11
....
12
13
      case Vorhangauffahren_auto:
14
      {
15
        if ((get_key_press( 1 << Endschalteraussen )) || (Zeit))
16
        {
17
          MotorStop();
18
          TimerStop();
19
          state = Vorhangoffen;
20
        }
21
      }
22
      break;


richtig: In der 2. Variante kann ich mich in der Statemachine auf die 
Logik konzentrieren und muss nicht dauernd überlegen, was das jetzt 
bedeutet, wenn ich PB2 am Port B auf 0 setze. Der Code kommt mir 
entgegen, indem er in meinen (menshclichen) Ausdrücken formuliert ist 
und nicht in irgendwelchen Details, wie zb dass ich zum Stoppen des 
Motors 2 bestimmte Pins auf 0 setzen muss. An dieser Stelle ist die 
richtige Aktion, den Motor zu stoppen. Und das steht auch so dort. Was 
dazu notwendig ist, den Motor zu stoppen ist eine andere Geschichte und 
wird als Detail in einer eigenen Funktion abgehandelt.

zuverlässige Programm zu schreiben hat auch sehr viel damit zu tun, dass 
man
* eine saubere äussere Form hat (Einrückungen, etc)
* den Code so formuliert, dass er leicht ZU LESEN (!) ist. Im Idealfall
  schaut Code so aus, dass man nur noch ein paar Füllwörter einsetzen
  muss und dann steht da ein kompletter Satz dort, der in meinen
  Worten auf möglichst hoher Ebene beschreibt, was da passiert.
  Code schreibt man einmal, ändert ihn vielleicht noch ein paar mal,
  aber man liest ihn im Laufe der Zeit viele, viele male. Daher muss
  man ihn so schreiben, dass man ihn gut und einfach LESEN und das
  Gelesene schnell VERSTEHEN kann.

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.