Forum: Compiler & IDEs Einfaches Programm tut nicht das was es soll..


von Paul H. (powl)


Lesenswert?

Hi,

besser kann ich den Titel nicht formulieren, wahrscheinlich steh ich 
einfach nur völlig auf dem Schlauch.

Folgendes Programm soll beim einfachen Druck auf einen Taster einen 
Ausgang einschalten und beim Loslassen wieder abschalten. Falls man 
jedoch innerhalb 1 Sekunde ein zweites mal drückt soll der Ausgang nicht 
wieder abgeschaltet werden.

Drückt man nun den Taster wird dabei justreleased=1 gesetzt. Lässt man 
ihn los wird geprüft ob justreleased gesetzt war und die variable wird 
resettet, ebenso auch hebercounter. Solange der Taster nicht gedrückt 
ist zählt diese Variable hoch. Wenn ich nun innerhalb dieser Zeitspanne 
auf den Taster drücke sollte heber=1 gesetzt werden.

heber=1 wird aber IMMER gesetzt, egal wie flott ich auf den taster 
drücke.

Noch dazu kommt: Wenn ich das Programm so kompiliere ergibt das 130 
Bytes. Wenn ich die Variablendeklarierung in die main reinpacke ist das 
Programm nur 68 Bytes groß aber tut genau das gleiche. Was ist denn da 
los?
1
#include <avr/io.h>
2
#include <util/delay.h>
3
4
uint8_t justreleased;
5
uint8_t heber;
6
uint16_t hebercounter;
7
8
int main(void)
9
{
10
  PORTB = 0b00000100;
11
  DDRA = 0b01000000;
12
  
13
  while(1)
14
  {
15
    if(bit_is_clear(PINB, PB2))
16
    {
17
      if(hebercounter < 100)
18
      {
19
        heber = 1;
20
      }
21
      
22
      PORTA |= (1 << PA6);
23
      
24
      justreleased = 1;
25
    }
26
    else
27
    {
28
      if(justreleased)
29
      {
30
        hebercounter = 0;
31
        justreleased = 0;
32
      }
33
      
34
      if(!heber)
35
      {
36
        PORTA &= ~(1 << PA4);
37
      }
38
      
39
      hebercounter++;
40
    }
41
  }
42
}

Übrigens kann ich das Programm nicht so einfach anders aufbauen weil 
zeitgleich noch ein zweiter Taster abgefragt wird, welcher einen zweiten 
Ausgang nach dem gleichen Prinzip schaltet. Den Code dafür habe ich hier 
aber weggelassen.

Danke für jede Hilfe, ich dreh wieder am Rad an so einem einfachen mist 
hängen zu bleiben.

Ist übrigens ein ATtiny24 @ 1Mhz.
Grüße, PoWl

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Deine Schleife läuft mit voller Prozessorgeschwindigkeit; wie willst Du 
das "innerhalb einer Sekunde"-Kriterium erfüllen?

von Paul H. (powl)


Lesenswert?

Oh sorry, kurz vorm Ende der Schleife fehlt noch ein

_delay_ms(10);

Das hatte ich versehentlich wohl rausgelöscht. Dennoch funktioniert es 
weiterhin nicht.

von Karl H. (kbuchegg)


Lesenswert?

Paul Hamacher schrieb:

> Folgendes Programm soll beim einfachen Druck auf einen Taster einen
> Ausgang einschalten und beim Loslassen wieder abschalten. Falls man
> jedoch innerhalb 1 Sekunde ein zweites mal drückt soll der Ausgang nicht
> wieder abgeschaltet werden.

Sondern?
Wie wird der Ausgang dann abgeschaltet?

> Übrigens kann ich das Programm nicht so einfach anders aufbauen weil
> zeitgleich noch ein zweiter Taster abgefragt wird, welcher einen zweiten
> Ausgang nach dem gleichen Prinzip schaltet.

Würde ich mit einem Timer machen.
Timer ISR ist so eingestellt, dass die ISR zb alle 1/100 Sekunde 
aufgerufen wird. In der ISR wird eine Variable runtergzeählt (wenn sie 
nicht sowieso schon 0 ist).
Bei einem Tastendruck wird die Variable geprüft. Ist sie 0 dann lag der 
letzte Tastendruck schon länger als 1 Sekunde zurück. Ist sie nicht 0, 
dann war der Tastendruck noch keine Sekunde her.

Entsprechend dieser Information wird dann der Ausgang geschaltet, bzw. 
die Timer Variable auf 100 gesetzt, damit die Stoppuhr anfängt zu 
laufen.


Mit deinem Schleifen Countern da oben, wirst du nie ein vernünftiges 
Timing hinkriegen. Mach dir zur Maxime: Wenn irgendwo Zeiten im Spiel 
sind, dann ist ein Timer nicht weit.

Und 'Stoppuhren' die eine bestimmte Zeitspanne abzählen, baut man am 
einfachsten eben genau so.
1
volatile uint8_t ticksRemaining;
2
3
ISR( .... )      // wird regelmässig alle x Millisekunden aufgerufen
4
{
5
  if( ticksRemaining > 0 )
6
    ticksRemaining--;
7
}
8
9
int main()
10
{
11
   ....
12
13
14
   ticksRemaining = 50;    // Stoppuhr starten, so dass sie nach
15
                           // 50 mal x Millisekunden wieder auf 0
16
                           // runtergezählt hat.
17
   ...
18
}

von Karl H. (kbuchegg)


Lesenswert?

Ausserdem musst du deinen Taster entprellen. Sonst triggert dir das 
Prellen ständig den 2.ten Tastendruck

von Paul H. (powl)


Lesenswert?

Danke für die Antwort, aber auf mein Problem bist du nicht so ganz 
eingangen :(

> Sondern?
> Wie wird der Ausgang dann abgeschaltet?

Das ist ein anderes Fass Bier, das mach ich später auf.

> Mit deinem Schleifen Countern da oben, wirst du nie ein vernünftiges
> Timing hinkriegen. Mach dir zur Maxime: Wenn irgendwo Zeiten im Spiel
> sind, dann ist ein Timer nicht weit.

Der hebercounter zählt erstmal bis um die 65000. Und das von Beginn an, 
wenn ich den AVR einschalte. D.h. wenn ich das Teil einschalte und nach 
mehreren Sekunden(!) das erste mal auf den Taster klicke kann es 
unmöglich sein, dass heber=1 gesetzt wird. Der hebercounter müsste schon 
viel weiter gezählt haben. Das kann auch unmöglich am "ungenauen 
timinig" liegen. (meinetwegen werde ich es nochmal mit timern probiern, 
aber zuerst will ich dieses Problem gelöst haben weil ich da scheinbar 
grundlegend was nicht ganz verstanden habe oder mein compiler falsch 
kompiliert. Unter der Bedingung brauch ich garnicht erst weitermachen)

> Ausserdem musst du deinen Taster entprellen. Sonst triggert dir das
> Prellen ständig den 2.ten Tastendruck

Der prellt sicher keine 10ms. Ich kanns ja nochmal mit 100ms delay 
probieren und if(hebercounter < 3)

von Karl H. (kbuchegg)


Lesenswert?

Paul Hamacher schrieb:
> Danke für die Antwort, aber auf mein Problem bist du nicht so ganz
> eingangen :(

Weißt du.
Dein Problem ist insofern nicht wirklich relevant, weil der ganze Ansatz 
schon nicht stimmt. Er ist kompliziert, unübersichtlich und 
fehlerträchtig.

1
volatile uint8_t ticksRemaining;
2
3
ISR( .... )      // wird regelmässig alle x Millisekunden aufgerufen
4
{
5
  if( ticksRemaining > 0 )
6
    ticksRemaining--;
7
}
8
9
int main()
10
{
11
   timer aufsetzen
12
13
   sei
14
15
   keyPrevious = bit_is_clear(PINB, PB2);
16
17
   while( 1 ) {
18
19
     keyNow = bit_is_clear(PINB, PB2);
20
21
     if( keyNow != keyPrevious ) {    // Taste hat sich verändert
22
23
       if( keyNow ) {                 // Taste wurde gedrückt
24
         if( ticksRemaining == 0 ) {  // war das erste mal bzw. schon
25
                                      // länger als 1 Sekunde her
26
           ticksRemaining = SEC_1;    // Stoppuhr starten
27
           PORTA |= (1 << PA6);
28
           allowShutdown = TRUE;
29
         }
30
         else {                       // die 1 Sekunde ist noch nicht um
31
                                      // 2. Druck innerhalb der Zeit
32
           allowShutdown = FALSE;
33
         }
34
       }
35
36
       else {                         // Taste wurde losgelassen
37
         if( allowShutdown )
38
           PORTA &= ~(1 << PA6);
39
       }
40
41
       keyNow = keyPrevious;
42
     }
43
   }
44
}


ungetesteter Code. Aber so in etwas müsste das klappen

von Paul H. (powl)


Lesenswert?

Das verstehe ich nicht. Ich habe doch gerade erklärt warum es nicht sein 
kann, das mein Phänomen auftritt. Widerlege meine Erklärung bitte. Das 
hast du nicht damit getan indem du behauptest, das Timing sei völlig 
daneben. So daneben kann das garnicht sein. Da können sich doch 
unmöglich mehrere Sekunden aufsummieren, findest du nicht?

Und erkläre mit bitte noch gleich folgendes: Ich habe grade mal
1
      if(hebercounter < 100)
2
      {
3
        heber = 1;
4
      }

zu
1
      if(hebercounter)
2
      {
3
        heber = 1;
4
      }

umgeändert. Ja ich weiß, das macht keinen Sinn. Resultiert aber in der 
gleichen Codegröße, und in gleichem Verhalten. Das mit der Codegröße ist 
seltsam, das Verhalten muss nun so sein da hebercounter ja definitiv 
gesetzt und >0 ist. Und danach zu
1
      if(!hebercounter)
2
      {
3
        heber = 1;
4
      }

Wieder gleiche Codegröße, gleiches Verhalten. Wie kann das denn nun aber 
sein?

Grüße, PoWl

von Karl H. (kbuchegg)


Lesenswert?

Paul Hamacher schrieb:
> Das verstehe ich nicht. Ich habe doch gerade erklärt warum es nicht sein
> kann, das mein Phänomen auftritt. Widerlege meine Erklärung bitte.

Ich mag da jetzt aber nicht nachvollziehen, wie sich 3 Variablen in 
verschiedenen Programmteilen gegenseitig beeinflussen, wenn ich es 
einfacher haben kann (Siehe code weiter oben)

von Karl H. (kbuchegg)


Lesenswert?

Paul Hamacher schrieb:

> Wieder gleiche Codegröße, gleiches Verhalten. Wie kann das denn nun aber
> sein?

Auf Assemblerebene mündet das alles in konzeptionell dem gleichen Code

    vergleiche mit einem Wert (0 oder 100)
    Springe wenn   gleich/kleiner/größer

Egal welche Kombination jetzt zum Einsatz kommt, es sind immer 2 
Befehle. Nur eben unterschidedliche Befehle, je nachdem wie du deine 
Abfrage jetzt konkret formulierst.

von Paul H. (powl)


Lesenswert?

ok, aber wie erklärst du dir den Logikfehler? Bei beiden Kompilaten 
absolut gleiche Bedingungen und gleicher Versuchsaufbau. Aber wie kann 
die if sowohl bei (!hebercounter) als auch (hebercounter) denn true 
werden? Oder bin ich denn jetzt ganz dappisch und (!variable) ist keine 
gängige Schreibweise um diese Variable auf 0 zu prüfen und (variable) 
ist keine gängige Schreibweise um zu prüfen ob die Variable nicht 0 ist?

Ich werde jetzt erstmal ins Bett gehen und meinen Kopf freischlafen. 
Morgen verwende ich gerne den von dir gemachten Codevorschlag. Trotzdem 
möchte ich wissen warum mein Code nicht funktioniert obwohl er es 
offensichtlich tun müsste und wie dieser Logikfehler zustande kommen 
kann und auch, warum die Codegröße fast doppelt so groß wird, wenn ich 
die Variablen innerhalb der main-funktion deklariere.

Danke soweit!

von Peter D. (peda)


Lesenswert?

Paul Hamacher schrieb:
>> Wie wird der Ausgang dann abgeschaltet?
>
> Das ist ein anderes Fass Bier, das mach ich später auf.

Das ist Quatsch. Ehe Du etwas anfängst zu programmieren, mußt Du es 
erstmal zuende denken.

Man macht sich erstmal nen vollständigen und vor allem logischen 
Ablaufplan, z.B. auf Papier und erst dann fängt man an, Code 
reinzuhacken.


Peter

von Justus S. (jussa)


Lesenswert?

Paul Hamacher schrieb:
> ok, aber wie erklärst du dir den Logikfehler? Bei beiden Kompilaten
> absolut gleiche Bedingungen und gleicher Versuchsaufbau. Aber wie kann
> die if sowohl bei (!hebercounter) als auch (hebercounter) denn true
> werden? Oder bin ich denn jetzt ganz dappisch und (!variable) ist keine
> gängige Schreibweise um diese Variable auf 0 zu prüfen und (variable)
> ist keine gängige Schreibweise um zu prüfen ob die Variable nicht 0 ist?
>
> Ich werde jetzt erstmal ins Bett gehen und meinen Kopf freischlafen.
> Morgen verwende ich gerne den von dir gemachten Codevorschlag. Trotzdem
> möchte ich wissen warum mein Code nicht funktioniert obwohl er es
> offensichtlich tun müsste und wie dieser Logikfehler zustande kommen
> kann und auch, warum die Codegröße fast doppelt so groß wird, wenn ich
> die Variablen innerhalb der main-funktion deklariere.
>
> Danke soweit!

schau dir doch einfach mal das Assembler-Listing zu deinem Programm(en) 
an...bei so kurzen Programmen sind die ja noch sehr übersichtlich. Sowas 
hilft oft weiter...

von Oliver (Gast)


Lesenswert?

Justus Skorps schrieb:
> schau dir doch einfach mal das Assembler-Listing zu deinem Programm(en)
> an...bei so kurzen Programmen sind die ja noch sehr übersichtlich. Sowas
> hilft oft weiter...

Aua...

Zur Lösung solcher Probleme wurden vor geschätzen 30 zillionen Jahren 
Debugger erfunden.

Lade das Programm in den Simulator, und step Schritt für Schritt durch. 
Und wenn das alles im Simulator funktioniert, in der Realität aber 
nicht, dann solltest du über das entprellen der Taste nachdenken.

Oliver

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.