Forum: Compiler & IDEs Taste gedrückt halten - zähler++ in zwei Zeiten


von Timo S. (tom_green1980)


Lesenswert?

Hallo an Alle,

so nach vielem hin und her habe ich mich doch entschieden mich hier 
anzumelden.
ich bin zur Zeit Schüler an der Technischen Akademie und wurde dort mit 
dem AVR Virus befallen.

Ich hab mir selbst die Aufgabe gestellt, die aus der Überschrift zu 
erahnen ist... Jeder kennt die Radiowecker Funktion, wenn man den Knopf 
der Uhr drück, das sie erst langsam hoch zählt und nach einer gewissen 
zeit schneller, bzw in Sprüngen hoch zählt. Genau das habe ich vor!!!

Mein Problem ist volgendes, das ich z.Z. mit einer einfachen IF Abfrage 
und nem Zähler++ abriete.... ist zähler <x dann soll ein wert in 1ner 
schriten und wenn der zähler >x ist soll er in größeren schritten weiter 
zählen.

da ich keine schleife verwenden kann, die den taster direkt abfragt... 
bzw. ich in der schleife hängen bleibe und die INT0 nur die flanken 
zählz und nicht Impulse erzeugt hänge ich jetzt etwas fest!

ich stelle einfach mal den Teil der Main() hier rein... ich hoffe das 
einige Schlau Köpfe eine ide haben... Ach ja die geschichte mit nem 
Timer Interrupt würde ich gerne vermeiden wenn es geht!?

MFG Timo


1
int main() //Ansteuerung eines Servos
2
{
3
  int cnt=0;
4
  int stepp = 125; 
5
  int flag_hoch=0;
6
7
  char buffer[20];
8
9
  init();
10
  lcd_init();
11
12
while(1)
13
{
14
  if(SERVO_PIN & (1 << HOCH)) //Abfrage des Taster
15
  {
16
    flag_hoch=1;
17
    cnt++; //hier ist der Knackpunkt - geht nicht
18
  }
19
  else
20
  {
21
    flag_hoch=0;
22
    cnt=0;
23
  }
24
25
  _delay_ms(100);
26
27
  if(flag_hoch == 1)
28
  {
29
    if(cnt<10) //Unterscheidung der Schrittweite
30
    {
31
      stepp+=1;
32
      _delay_ms(200);
33
    }
34
    else
35
    {
36
      stepp+=15; 
37
      _delay_ms(50);
38
    }
39
40
  lcd_setcursor(0,1);  
41
  sprintf(buffer, "Wert: %d", stepp);
42
  lcd_string(buffer);
43
  position = stepp;
44
  }  
45
}

: Bearbeitet durch User
von Captain S. (captainsubtext)


Lesenswert?

Timo Solterbeck schrieb:
> Ach ja die geschichte mit nem
> Timer Interrupt würde ich gerne vermeiden wenn es geht!?

Tu' es dir trotzdem an, es ist bei weitem die eleganteste Lösung. Und 
wenn du mal über mehr als ein "Micky-Maus-Programm" machen möchtest, 
kommst du eh nicht mehr drum herum.


Ansonsten: Dein Code sollte eigentlich funktionieren. Was heißt "Geht 
nicht..." ?

Läuft der Code wirklich genau dann in den Fall
>if(SERVO_PIN & (1 << HOCH))
wenn der Taster gedrückt ist?

Du solltest übrigens auch prüfen, dass die "cnt" nicht überlauft.

von Captain S. (captainsubtext)


Lesenswert?

Captain Subtext schrieb:
> wenn du mal über mehr als ein "Micky-Maus-Programm" machen möchtest

Entschuldige bitte meine Wortwahl, jeder fängt mal "klein" an.

von Timo S. (tom_green1980)


Lesenswert?

danke das hier so promte antworten kommen.

ich hatte noch einen beitrag von "Karl Heinz Buchegger" zu meinem 
beitrag, den würde ich gerne lesen, aber er ist nicht mehr da!

@Captain Subtext

entschuldigung angenommen, aber muss man das forum mit megal langen code 
texten zu spamen.... ich denke nicht... und mein "kleines programm ist 
nur eine nachbarkeitsstudie.... testest du deine programme immer gleich 
komplett???

so nun zu meinem progrämmchen... das hochzählen der stepp-variable 
funzt... der servo fährt auch, nur leider wird die cnt nicht 
hochgezählt.... eigenlich, so hab ich mir das gedacht, müsste bei jedem 
schleifendurchlauf der main while(1) der zähler hoch gezählt werden, und 
genau das tut er nicht!!! keine ahnung warum.

vielleicht hat hier ja noch einer einen tip!?

achja warum ich den timer interrupt vermeinden will, weil ich nicht so 
viele erfahrungen mit dem habe und weil einer davon schön für den servo 
verwendet wird!!!!

mfg timo

von Karl H. (kbuchegg)


Lesenswert?

Timo Solterbeck schrieb:
> danke das hier so promte antworten kommen.
>
> ich hatte noch einen beitrag von "Karl Heinz Buchegger" zu meinem
> beitrag, den würde ich gerne lesen, aber er ist nicht mehr da!

Weil ich zu spät gemerkt habe, dass du 2 Repeat Stufen haben willst.
Mein Grundtenor ist immer noch der gleiche: Nimm die PeDa 
Entprellung und erfinde da nichts eigenes, was wieder mal nicht 
vernünftig funktioniert.
Nur dass die PeDa Entprellung keine 2 unterschiedlichen Repeat-Stufen 
hat. Da ich allerdings denke, dass man das auch nicht wirklich braucht, 
hatte ich dann aber auch keine Lust mir da was einfallen zu lassen und 
hab den Beitrag zurückgezogen.

von Timo S. (tom_green1980)


Lesenswert?

danke Karl Heinz, ich versuche mich einzulesen und hoffe das das was 
wird!
für anregungen bin ich trotzdem offen!!!!

von Timo Solterbeck (Gast)


Lesenswert?

Guten morgen an Alle,

so ich hab mir noch ein paar ratschläge zu herzen genommen und meinen 
kopf ein klein wenig mehr angestrengt ;-)

ich hatte zwei "fehlerchen" in meinem code, zum einen war tatsächlich 
die taster abfrage nicht ganz richtig, (danke @Captain Subtext) und zum 
anderen hatte ich nen denkfehler in einem 'else' teil...
naja sei es drum hich habs geschaft und möchte den code kurz 
präsentieren!
es ist ein kleines und recht einfaches programm um einen servo über 
tasten drehen zu lassen.
vielleicht ist das von der tastenbeschaltung für den einen andern 
anfänger interessant!?
mfg timo
1
#include <my_init.h>  //beinhaltet: ADC, Interrupt, Standart #include's
2
3
//servopin
4
#define SERVO    PD7
5
#define SERVO_DDR  DDRD
6
#define SERVO_PORT  PORTD
7
#define SERVO_PIN  PIND
8
#define SERVO_ON  SERVO_PORT |= (1<<SERVO)  //Makro Servo einschalten
9
#define  SERVO_OFF  SERVO_PORT &= ~(1<<SERVO)  //Makro Servo ausschalten
10
11
//Taster zur Servobedienung
12
#define HOCH    PD2  //INT0
13
#define RUNTER    PD3  //INT1
14
#define MITTE    PD4
15
#define TASTEN_DDR  DDRD
16
#define TASTEN_PIN  PIND
17
#define  TASTEN_PORT  PORTD
18
19
unsigned volatile char position=0; //Werte zwischen 0 und 255
20
21
void init(void)
22
{
23
  SERVO_DDR |= (1<<SERVO);  //Servopin als Ausgang
24
25
  TCCR1B |= (1<<CS11);    //T1 ein CPIU/8   1Mhz Taktfrequenz ungefähr 65ms
26
27
  TIMSK |= (1<<TOIE1);    //Interrupt enable T1 overflow
28
  TIMSK |= (1<<OCIE1A);    //Output CompareA Timer 1
29
30
  sei();              //Interruptfreigabe
31
}
32
33
//*** Interrupt Pulsweitensteuerung
34
35
ISR(TIMER1_OVF_vect)      //Interruptserviceroutine
36
{
37
  TCNT1 = 40000;      //nächstes Overflow in ca. 25ms
38
  SERVO_ON;      //Servo ON
39
  OCR1A = 40990 + position * 4;  //Abschaltzeitpunkt zwischen 40990 bis 42110 bedeutet 0,99ms bis 2,01ms
40
}
41
42
ISR(TIMER1_COMPA_vect)      //Interruptserviceroutine
43
{
44
  SERVO_OFF;            //Vergleicht den Wert von TCNT1 und COCR1A und Schaltet den Servo bei gleichheit OFF
45
}
46
47
48
int main()
49
{
50
  int cnt= 0;
51
  int stepp = 125;  //init des Servo
52
  int flag_hoch = 0;
53
  int flag_runter = 0;
54
55
  char buffer[20];
56
57
  init();
58
  lcd_init();
59
60
  //*** Ausgabe der Position vom Servo (inti)
61
62
  lcd_clear();
63
  lcd_setcursor(0,1);  
64
  sprintf(buffer, "Wert: %d", stepp);
65
  lcd_string(buffer);
66
  position = stepp;
67
68
69
  while(1)
70
  {
71
  //*** FLAG's für Taste Hoch und Runter
72
73
    if((TASTEN_PIN & (1 << HOCH)) && (!(TASTEN_PIN & (1 << RUNTER))))  //gegenseitige Tastensperre
74
    {
75
      flag_hoch=1;
76
      cnt++;
77
    }
78
    else
79
    {
80
      flag_hoch=0;
81
    }
82
83
84
  //*** FLAG's für Taste Runter
85
86
    if((TASTEN_PIN & (1 << RUNTER)) && (!(TASTEN_PIN & (1 << HOCH))))  //gegenseitige Tastensperre
87
    {
88
      flag_runter=1;
89
      cnt++;
90
    }
91
    else
92
    {
93
      flag_runter=0;
94
    }
95
96
    if((!(TASTEN_PIN & (1 << HOCH))) && (!(TASTEN_PIN & (1 << RUNTER))))  //wenn kein Taster gedrückt, dann internen Zähler auf 0 setzen
97
    {
98
    cnt = 0;
99
    }
100
101
102
  //*** servo in Mittenstellung bringen
103
104
    if(TASTEN_PIN & (1 << MITTE))
105
    {
106
    stepp = 128;
107
    position = stepp;
108
    lcd_clear();
109
    lcd_setcursor(0,1);
110
    sprintf(buffer, "Wert: %d", stepp);
111
    lcd_string(buffer);
112
    lcd_setcursor(10,1);  
113
    lcd_string("Mittig");
114
    }
115
116
    lcd_setcursor(0,2);  
117
    sprintf(buffer, "Count: %d", cnt);
118
    lcd_string(buffer);
119
120
      
121
  //*** Steuerung für Hoch
122
123
    if(flag_hoch == 1)
124
    {
125
      if(stepp < 250)
126
      {
127
        if(cnt<10)
128
        {
129
          stepp+=1;
130
          _delay_ms(200);
131
        }
132
        else
133
        {
134
          stepp+=5; 
135
          _delay_ms(100);
136
        }
137
      }
138
    lcd_clear();
139
    lcd_setcursor(0,1);  
140
    sprintf(buffer, "Wert: %d", stepp);
141
    lcd_string(buffer);
142
    position = stepp;
143
    }  
144
145
  //*** Steuerung für Runter
146
147
    if(flag_runter == 1)
148
    {
149
      if(stepp > 5)
150
      {
151
        if(cnt<10)
152
        {
153
          stepp-=1;
154
          _delay_ms(200);
155
        }
156
        else
157
        {
158
          stepp-=5; 
159
          _delay_ms(100);
160
        }
161
      }
162
    lcd_clear();
163
    lcd_setcursor(0,1);  
164
    sprintf(buffer, "Wert: %d", stepp);
165
    lcd_string(buffer);
166
    position = stepp;
167
    }  
168
169
  }
170
}

von Krapao (Gast)


Lesenswert?

> vielleicht ist das von der tastenbeschaltung für den einen andern
> anfänger interessant!?

Als Beispiel wie man den sinnvollen Ratschlag eine Entprellung zu 
benutzen komplett ignoriert?

Dieses Programm "funktioniert" trotz naturgemäß prellenden Tastern nur, 
weil rel. lange Wartezeiten (delay_ms, LCD Ausgabe) zwischen den 
einzelnen while-Durchläufen liegen.

Wenn sich der erste Anwender beschwert, dass wegen der rel. langen 
Wartezeiten ein schnelles Antippen der Tasten nicht registriert wird, 
kommt eine Codeänderung und nix geht mehr...

von Karl H. (kbuchegg)


Lesenswert?

Nur so als Gegenbeispiel.

Mit der PeDa Entprellung würde die Hauptschleife so aussehen
1
  ....
2
  new_pos = position = 128;
3
4
  sei();
5
6
  while( 1 ) {
7
8
    if( get_key_press( 1 << RUNTER ) ||
9
        get_key_rpt( 1 << RUNTER ) )
10
      new_pos--;
11
12
    if( get_key_press( 1 << HOCH ) ||
13
        get_key_rpt( 1 << HOCH ) )
14
      new_pos++;
15
16
    if( get_key_press( 1 << MITTE ) )
17
      new_pos = 128;
18
19
    if( new_pos < 10 )      // untere Grenze
20
      new_pos = 10;
21
    if( new_pos > 240 )     // obere Grenze
22
      new_pos = 240;
23
24
    position = new_pos;     // erst jetzt für den Interrupt bereitstellen
25
                            // damit im Interrupt keine ungültigen Werte 
26
                            // benutzt werden.
27
28
    lcd_setcursor( 0, 1);  
29
    sprintf( buffer, "Wert: %4d", (int)new_pos );
30
    lcd_string( buffer );
31
  }
32
}

ziemlich einfach zu verifizieren, dass die Auswertung der Tastenlogik 
und wie sie auf die Servoposition einwirken korrekt ist.

von Timo S. (tom_green1980)


Lesenswert?

@Karl Heinz Buchegger      ich hab die tasten entprell geschicht nicht 
genommen weil ich ja was eigenens machen wollte (von dir ja auch 
gefördert)
ich hab mir die entprellroutine angeguckt und bin aber nicht hinter die 
makro funktinen gestiegen... dein code sieht logisch aus, aber halt mit 
diesen makros.
die kritik mit den "langen" delay zeiten nehme ich zur kenntnis und ich 
weis das das nicht ganz elegant ist, aber ich hab das programm ja nur 
für diesen einen zweck des servo testens geschrieben...

ich werden mich bei gelegenheit um eine verbesserung bemühen!

mfg

von Karl H. (kbuchegg)


Lesenswert?

Timo Solterbeck schrieb:
> @Karl Heinz Buchegger      ich hab die tasten entprell geschicht nicht
> genommen weil ich ja was eigenens machen wollte (von dir ja auch
> gefördert)
> ich hab mir die entprellroutine angeguckt und bin aber nicht hinter die
> makro funktinen gestiegen...

Wie ich in diesem Forum immer wieder sage:

Das macht nichts.
Diese Entprellung hat so viele Vorteile, dass es nichts ausmacht, wenn 
man nicht dahinter steigt, wie sie eigentlich (der Code in der ISR) 
funktioniert. Der ist zweifellos ziemlich trickreich. Aber sie 
funktioniert einfach zu gut, als das man sie ignorieren könnte.

Ich versteh ja auch nicht im Detail, wie ein Autogetriebe genau 
funktioniert und warum da so viele Zahnräder drinnen sind. Deshalb geh 
ich aber auch nicht zu Fuss.

Und die Makros, na ja so schwer sind die auch nicht zu verstehen
1
#define KEY_DDR         DDRB
2
#define KEY_PORT        PORTB
3
#define KEY_PIN         PINB
4
#define KEY0            0
5
#define KEY1            1
6
#define KEY2            2
7
#define ALL_KEYS        (1<<KEY0 | 1<<KEY1 | 1<<KEY2)
8
 
9
#define REPEAT_MASK     (1<<KEY1 | 1<<KEY2)       // repeat: key1, key2
10
#define REPEAT_START    50                        // after 500ms
11
#define REPEAT_NEXT     20                        // every 200ms

Die ersten 3 legen die Konfiguration fest: AN welchem Port sind denn 
eigentlich die Tasten.
Dann kommen n Stück Tastenvereinbarungen und an welchem Pin sie sind
ALL_KEYS ist einfach nur ein Makro welches alle Tasten zusammenfasst 
(wegen der DDR und Pullup Initialisierung). Die letzten 3 Makros regeln, 
auf welche Tasten ein Autorepeat gelegt wird, wann er einsetzen soll und 
wie schnell er sein soll.

In Summe, denke ich, nicht allzuschwer zu verstehen und an seine 
Gegebenheiten anzupassen.

Bei dir wäre das
1
#define KEY_DDR         DDRD
2
#define KEY_PORT        PORTD
3
#define KEY_PIN         PIND
4
#define HOCH            2
5
#define RUNTER          3
6
#define MITTE           4
7
#define ALL_KEYS        (1<<HOCH | 1<<RUNTER | 1<<MITTE)
8
 
9
#define REPEAT_MASK     (1<<HOCH | 1<<RUNTER)
10
#define REPEAT_START    50                        // after 500ms
11
#define REPEAT_NEXT     20                        // every 200ms
12
13
volatile uint8_t key_state;                                // debounced and inverted key state:
14
                                                  // bit = 1: key pressed
15
volatile uint8_t key_press;                                // key press detect
16
 
17
volatile uint8_t key_rpt;                                  // key long press and repeat

den ISR Code noch in deine vorhandene ISR integrieren, die Funktionen 
get_key_press und get_key_rpt in den eigenen Code übernehmen und am 
Anfang vom main nich die Portinitialisierung für den Tastenport direkt 
übernehmen. Eventuell noch die Werte bei REPEAT_START und REPEAT_NEXT 
etwas verändern, so dass man das Zeitverhalten kriegt welches einem 
zusagt.
Aber damit ist die Sache im wesentlichen gegessen und man hat 
erstklassig entprellte Tasten, die man in der eigenen Anwendung leicht 
und einfach verwenden kann.
Ach ja. Irgendeinen Timer braucht man noch (damit man eine ISR hat). Den 
hat aber sowieso so gut wie jedes ernstzunehmende Programm in 
irgendeiner Form bereits im Einsatz. Ob die ISR jetzt alle 4 oder alle 
40 Millisekunden, oder irgendein Wert dazwischen, aufgerufen wird, 
spielt hingegen für die Entprellung keine wirkliche Rolle. Hier nimmt 
man, was man für den Rest der Applikation braucht. Bei dir gehts leider 
nicht, weil deine ISR (gewollt) in unregelmässigen Abständen aufgerufen 
wird. Macht aber nichts, du hast ja noch mehr Timer zur Verfügung.


Es ist schon ok, wenn du selbst dich an Tastenentprellung versuchst. 
Aber du siehst auch, dass ohne _delay_ms da nicht viel geht. Und 
_delay_ms ist (bis auf wirklich ganz kurze Zeiten) eines der Übel, die 
einem bei komplexeren Programmen alles versauen, weil es damit schwer 
bis unmöglich wird, dass das Programm scheinbar mehrere Dinge 
gleichzeitig machen kann. Eine Motorabschaltung darf nicht 100ms zu spät 
kommen, nur weil der Benutzer auf der Taste eingeschlafen ist.

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.