Forum: Compiler & IDEs Problem mit einem Code (Atinny2313)


von Titan (Gast)


Lesenswert?

Hallo Mikrocontroller Community!

Ich bin so langsam am verzweifeln...
Ich möchte ein Programm für einen Fernsehlifter schreiben, ich hatte das 
Programm auch schon fast fertig, bis ich Probleme mit nicht korrekt 
ausgeführten Objekten bekommen hatte.

Hier mein code:
1
#include <avr/io.h>
2
#include <stdint.h>
3
4
#ifndef F_CPU
5
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz  */
6
#endif
7
#include <util/delay.h>
8
9
10
11
uint8_t stellung;
12
13
char taster (void);
14
uint8_t tastenwechsel (uint8_t);
15
16
17
18
19
int main ()
20
{
21
22
  DDRB = (1<<PB3) | (1<<PB4);  
23
24
  uint8_t a;
25
  uint8_t vtast;
26
27
28
  while (1)
29
  {
30
    vtast = taster();
31
    a = tastenwechsel(vtast);
32
33
    if(a == 0)
34
    {
35
      PORTB = (1<<PB3) | (0<<PB4);
36
    }
37
38
    if(a == 1)
39
    {
40
      PORTB = (0<<PB3) | (1<<PB4);
41
    }
42
43
  }
44
}
45
46
47
48
char taster (void)
49
{
50
    static unsigned char zustand;
51
    char rw = 0;
52
 
53
    if(zustand == 0 && (PINB & (1<<PB1)))   //Taster wird gedrueckt (steigende Flanke)
54
    {
55
        zustand = 1;
56
        PORTB ^= (1<<PB7);
57
    rw = 1;
58
    }
59
    else if (zustand == 1 && (PINB & (1<<PB1)))   //Taster wird gehalten
60
    {
61
         zustand = 2;
62
         rw = 0;
63
    }
64
    else if (zustand == 2 && !(PINB & (1<<PB1)))   //Taster wird losgelassen (fallende Flanke)
65
    {
66
        zustand = 3;
67
        rw = 0;
68
    }
69
    else if (zustand == 3 && !(PINB & (1<<PB1)))   //Taster losgelassen
70
    {
71
        zustand = 0;
72
        rw = 0;
73
    }
74
    return rw;
75
}
76
77
78
79
uint8_t tastenwechsel (uint8_t i)
80
{
81
82
83
  if ( i == 1 )
84
  {
85
    switch ( stellung )
86
    {
87
      case 0: stellung = 1;break;
88
89
      case 1:stellung = 0;break;
90
91
    }
92
  }
93
94
95
  return stellung;
96
}

Ich habe den Code mal ein wenig vereinfacht, wegen der Übersicht.
Wenn ich das ganze nun auf einen Atinny 2313 überspiele, wird bei 
mehrmaligen betätigen des Tasters PB1, der Ausgang PB3 ausgeschaltet und 
dafür der Ausgang PB4 eingeschaltet.

Wie schaffe ich es, das jeder einzelne Tastendruck gut erkannt wird und 
der Ausgang PB4 wider auf PB3 wechselt(was aber Wahrscheinlich daran 
liegt, das die Tastendrücke nicht richtig erkannt werden)?

Bitte um Hilfe!

MfG Titan

von Krapao (Gast)


Lesenswert?

DIe fehlende Entprellung ist ein Problem deines Codes. Du versuchst 
zwar in taster() exakt einen Tastendruck zu erkennen. Das hilft aber nix 
wenn eine Prellsequenz reinkommt. Die Prellsequenz kann von deiner 
taster() als Folge von Tastendrücken erkannt werden mit den 
entsprechenden Konsequenzen in  tastenwechsel().

von usr (Gast)


Lesenswert?


von Volkmar D. (volkmar)


Lesenswert?

Oder im entsprechenden Artikel: Entprellung

von XXX (Gast)


Lesenswert?

Hallo

Und diese Stelle würde ich nochmal überarbeiten:

PORTB = (1<<PB3) | (0<<PB4);
                    ^
Da ist garantiert eine Differenz zwischen dem, was du vorhast und
dem, was ausgeführt wird.

Gruß
Joachim

von Karl H. (kbuchegg)


Lesenswert?

XXX schrieb:
> Hallo
>
> Und diese Stelle würde ich nochmal überarbeiten:
>
> PORTB = (1<<PB3) | (0<<PB4);
>                     ^
> Da ist garantiert eine Differenz zwischen dem, was du vorhast und
> dem, was ausgeführt wir

In seinem Fall funktioniert es zwar so wie er das vorhatte, aber den 
kompletten Port neu zu beschreiben ist nicht unbedingt die feine 
englische Art. Da hast du schon recht.



PS:
Das hier
1
    switch ( stellung )
2
    {
3
      case 0: stellung = 1;break;
4
5
      case 1:stellung = 0;break;
6
7
    }

ist eine unheimlich komplizierte Variante von
1
  stellung = 1 - stellung;

oder
1
  stellung = stellung ? 0 : 1;

oder
1
  if( stellung )
2
    stellung = 0;
3
  else
4
    stellung = 1;

Noch ein 2.tes PS

In C ist weniger oft mehr. Wenn eine Variable nur 0 und 1 (also nicht 0) 
sein kann, dann solltest du dir explizite Vergleiche in den if sparen

    if(a == 0)

...

    if(a == 1)


entweder die Variable ist 'nicht 0', dann heißt es

   if( a )

oder sie ist 0, dann heißt es

   if( !a )

und wenn eine Variable sowieso nur 0 oder nicht 0 sein kann, dann ist es 
eine ziemlich schlechte Idee, 2 getrennte if dafür einzusetzen. Dafür 
gibt es das else.

   if( a )     // wenn a nicht 0
     ...
   else        // a war 0
     ...

oder eben anders rum

   if( !a )    // wenn a gleich 0
     ...
   else        // a war nicht 0
     ...


Interessanterweise sind solche 'weniger ist mehr' Umformungen nämlich 
auch oft Fehlerverhinderer.

und wenn du jetzt anstelle von a noch einen vernünftigen Variablennamen 
benutzt, wie zb MoveUp

     MoveUp = tastenwechsel(vtast);

dann steht da

     if( MoveUp )
       PORTB = (1<<PB3) | (0<<PB4);
     else
       PORTB = (0<<PB3) | (1<<PB4);

und das liest sich schon viel besser als deine Version mit dem a. Der 
Code erzählt mir schon fast ganz von alleine, was da passiert: Wenn die 
Liftrichtung nach oben ist (wegen MoveUp), dann machst du eine bestimmte 
externe Schaltung und wenn sie es nicht ist (dann soll offenbar nach 
Unten verfahren werden) dann machst du eine andere Verschaltung. Da 
brauch ich den ganzen anderen Rest des Programmes nicht zu verstehen um 
mit dem Schaltplan feststellen zu können: OK, kann stimmen oder kann 
nicht stimmen.

Scheu dich nicht, Variablennamen auch im Nachhinein noch umzubennen, 
wenn du draufkommst, dass sie schlecht gewählt sind. Einbuchstabige 
Variablennamen solltest du ausschliesslich für Hilfsvariablen benutzen, 
die keine spezielle Bedeutung haben. Sobald du aber für eine Variable 
eine einigermassen brauchbare und wichtige Bedeutung festpinnen kannst, 
ist das dein Kandidat für den Variablennamen.

Und ja: es sind auch solche Kleinigkeiten, die über die Qualität und 
damit auch über die potentielle Fehlerfreiheit eines Programmes 
entscheiden können. Programmieren ist schon schwer genug. Also benutz 
jede Hilfe, die du kriegen kannst! Sinnvolle Variablennamen gehören 
(unter anderem) dazu und kosten dir nichts.

von egal (Gast)


Lesenswert?

Titan schrieb:
> char taster (void)
> {
>     static unsigned char zustand;
>     char rw = 0;

Sollte in diesem Abschnitt nicht zustand sicherheitshalber auch mit 0 
initialisiert werden? Also
1
static unsigned char zustand = 0;
?

von Karl H. (kbuchegg)


Lesenswert?

egal schrieb:
> Titan schrieb:
>> char taster (void)
>> {
>>     static unsigned char zustand;
>>     char rw = 0;
>
> Sollte in diesem Abschnitt nicht zustand sicherheitshalber auch mit 0
> initialisiert werden? Also
>
1
> static unsigned char zustand = 0;
2
>
?

Sinnvoll ist sowas immer (auch bei globalen Variablen) und sei es nur zu 
Dokumentationszwecken.
Aber für statische Variablen gilt dasselbe wie für globale Variablen: 
Wenn nicht anders vereinbart, werden sie automatisch mit 0 
initialisiert.

von egal (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Sinnvoll ist sowas immer (auch bei globalen Variablen) und sei es nur zu
> Dokumentationszwecken.
> Aber für statische Variablen gilt dasselbe wie für globale Variablen:
> Wenn nicht anders vereinbart, werden sie automatisch mit 0
> initialisiert.

Danke Karl Heinz. Sicher war ich mir nämlich nicht. Macht das auch 
wirklich jeder Compiler so?

von Karl H. (kbuchegg)


Lesenswert?

egal schrieb:
> Karl Heinz Buchegger schrieb:
>> Sinnvoll ist sowas immer (auch bei globalen Variablen) und sei es nur zu
>> Dokumentationszwecken.
>> Aber für statische Variablen gilt dasselbe wie für globale Variablen:
>> Wenn nicht anders vereinbart, werden sie automatisch mit 0
>> initialisiert.
>
> Danke Karl Heinz. Sicher war ich mir nämlich nicht. Macht das auch
> wirklich jeder Compiler so?

Wenn er sich 'C-Compiler' nennen will, muss er es machen.

Du kannst dir funktionslokale static-Variablen als Sonderform von 
globalen Variablen vorstellen, deren Sichtbarkeit ganz einfach auf nur 
diese eine Funktion eingeschränkt wurde.

In C hat das Schlüsselwort 'static' nur diese eine Bedeutung: die 
Sichtbarkeit von etwas (Variable oder Funktion) einzuschränken.

von Titan (Gast)


Lesenswert?

Tut mir leid wegen der späten Rückmeldung...
Vielen dank schon mal für die vielen Antworten!
Ich hatte das mal ebend so in einfacher form hinprogrammiert, damit ihr 
euch wie bereits gesagt nicht mit irgendwelchem Code abquälen müsst, der 
keinem interessiert :)

Mein Klassenlehrer kennt sich sehr gut mit der Programmierung von 
Mikrocontrollern aus und meinte, das ein externer Interrupt nötig ist, 
was mir auch nach ein wenig Recherche sinnvoll erscheint, da der Code ja 
eigentlich funktioniert, nur der Tastendruck einfach nicht registriert 
wird.
Jedoch hat der Attiny2313 nur 2 externe Interrupt Pins(INT0, INT1) aber 
ich müsste 3 Taster anschließen. Dafür würde ja eine tasten Matrix in 
frage kommen, mit der ich theoretisch 4 Taster benutzen könnte...oder?!?
Aber wie stelle ich das am besten an? <- sry aber mein Kopf ist vorhin 
kaputt gegangen :P
Muss ich sonst noch etwas besonderes beachten?

PS:
Also ich muss schon sagen, ich programmiere seit ca. 1 1/2 Jahren in 
C++, aber da hatte ich niemals Probleme mit irgendwelchen Tastern :D

von Oliver (Gast)


Lesenswert?

Titan schrieb:
> Mein Klassenlehrer kennt sich sehr gut mit der Programmierung von
> Mikrocontrollern aus und meinte, das ein externer Interrupt nötig ist,

Hm. Bitte deinen Klassenlehrer doch mal, sich dazu hier im Forum und im 
Tutorial schlau zu machen. Noch besser, du machst das selber, und 
erklärst ihm, wie es ohne exteren Interrupts viel besser geht.

usr schrieb:
> guckst du hier : Beitrag "Re: Universelle Tastenabfrage" :)

Oliver

von Karl H. (kbuchegg)


Lesenswert?

Oliver schrieb:
> Titan schrieb:
>> Mein Klassenlehrer kennt sich sehr gut mit der Programmierung von
>> Mikrocontrollern aus und meinte, das ein externer Interrupt nötig ist,
>
> Hm. Bitte deinen Klassenlehrer doch mal, sich dazu hier im Forum und im
> Tutorial schlau zu machen. Noch besser, du machst das selber, und
> erklärst ihm, wie es ohne exteren Interrupts viel besser geht.

Oder er soll mal vorbeikommen. Dann erklären wir ihm warum externe 
Interrupts eine schlechte Idee sind, warum da kein Mensch externe 
Interrupts braucht und wie man es besser machen kann.

von Titan (Gast)


Lesenswert?

Weiß nicht, villeicht habe ich etwas falsch verstanden oder mein Lehrer. 
Wir haben uns nur kurz darüber unterhalten.

Krapao schrieb:
> DIe fehlende Entprellung ist ein Problem deines Codes. Du versuchst
> zwar in taster() exakt einen Tastendruck zu erkennen. Das hilft aber nix
> wenn eine Prellsequenz reinkommt. Die Prellsequenz kann von deiner
> taster() als Folge von Tastendrücken erkannt werden mit den
> entsprechenden Konsequenzen in  tastenwechsel().

Flankenerkennung

Bei einem Taster gibt es insgesamt 4 Zustände:

1. war nicht gedrückt und ist nicht gedrückt
2. war nicht gedrückt und ist gedrückt (steigende Flanke)
3. war gedrückt und ist immer noch gedrückt
4. war gedrückt und ist nicht mehr gedrückt (fallende Flanke)
Diese einzelnen Zustände lassen sich jetzt bequem abfragen/durchlaufen. 
Die Entprellung geschieht dabei durch die ganze Laufzeit des Programms.

Die Taster werden hierbei als Active-Low angeschlossen um die internen 
Pull-Ups zu nutzen.

Diese Routine gibt für den Zustand "steigende Flanke" den Wert "1" 
zurück, sonst "0"
1
#define TASTERPORT PINC
2
#define TASTERBIT PINC1
3
 
4
char taster(void)
5
{
6
    static unsigned char zustand;
7
    char rw = 0;
8
 
9
    if(zustand == 0 && !(TASTERPORT & (1<<TASTERBIT)))   //Taster wird gedrueckt (steigende Flanke)
10
    {
11
        zustand = 1;
12
        rw = 1;
13
    }
14
    else if (zustand == 1 && !(TASTERPORT & (1<<TASTERBIT)))   //Taster wird gehalten
15
    {
16
         zustand = 2;
17
         rw = 0;
18
    }
19
    else if (zustand == 2 && (TASTERPORT & (1<<TASTERBIT)))   //Taster wird losgelassen (fallende Flanke)
20
    {
21
        zustand = 3;
22
        rw = 0;
23
    }
24
    else if (zustand == 3 && (TASTERPORT & (1<<TASTERBIT)))   //Taster losgelassen
25
    {
26
        zustand = 0;
27
        rw = 0;
28
    }
29
 
30
    return rw;
31
}

Warum steht diese Entprellung dann im Tutorial und funktioniert bei 
einem noch kleinerem Code?

usr schrieb:
> guckst du hier : Beitrag "Re: Universelle Tastenabfrage" :)

Das funktioniert bei mir komischerweise nicht...
Beim start leuchten einfach alle LEDs und reagieren nicht auf die 
Tasten...

XXX schrieb:
> Und diese Stelle würde ich nochmal überarbeiten:
>
> PORTB = (1<<PB3) | (0<<PB4);
>                     ^
> Da ist garantiert eine Differenz zwischen dem, was du vorhast und
> dem, was ausgeführt wird.

PB3 soll eingeschaltet werden und PB4 aus. Wie mache ich dies denn 
anders?

von Karl H. (kbuchegg)


Lesenswert?

Titan schrieb:

> Warum steht diese Entprellung dann im Tutorial

Wo genau?

> und funktioniert bei
> einem noch kleinerem Code?

Je nachdem was runderhum passiert und wenn sie nicht zu häufig 
aufgerufen wird, kann das schon klappen. Ist die Aufrufsequenz 
allerdings zu schnell, dann kann ein Tastenpreller dazu führen, dass die 
Funktion beim Niederdrücken der Taste alle Zustände durchläuft und dann 
einen erneuten Preller mit dem Niederdrücken verwechselt.

Auf deutsch:
Die Funktion darf nur benutzt werden, wenn ausserhalb der Funktion sagen 
wir mal 5 Millisekunden von einem Aufruf zum nächsten vergehen. Das ist 
aber nicht das, was ich von einer zuverlässigen Entprellung erwarten 
würde. Das kanns nicht sein.

von Karl H. (kbuchegg)


Lesenswert?

> PB3 soll eingeschaltet werden und PB4 aus. Wie mache ich dies denn
> anders?


Ein Bit wird auf 1 gesetzt

   PORTB |= ( 1 << PB3 );

Ein Bit wird auf 0 gesetzt

   PORTB &= ~( 1 << PB3 );


gewöhn dir solche Rundumschläge ala

   PORTB = ( 1 << PB3 );

(also mit einer Zuweisung) erst gar nicht an. Das führt zu sehr schwer 
zu entdeckenden indirekten Zusammenhängen, wenn plötzlich der Summer am 
Port PB7 anfängt zu tröten, nur weil du die LED ausschalten wolltest. Du 
musst dir immer im klaren sein, dass eine derartige Zuweisung ALLE Bits 
des Ports beeinflusst. Solange dort nichts angeschlossen ist, ist das 
noch ok. Aber diesen Luxus wirst du nicht lange haben.

von Titan (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
>> Warum steht diese Entprellung dann im Tutorial
>
> Wo genau?

ja gut ist zwar nicht das Tutorial, aber ein Artikel:
http://www.mikrocontroller.net/articles/Entprellung

in dem Artikel sind noch mehrere entpreller, aber irgendwie sehen mir 
alle nicht sicher genug aus um damit einen Fernseher zu stoppen :S

@Karl Heinz Buchegger, danke schonmal für deine Tipps, ich werde 
versuchen sie zu beachten :)

Ich versuche gerade folgende Entprellung:
1
#include <stdint.h>
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
 
5
#ifndef F_CPU
6
#define F_CPU           1000000                   // processor clock frequency
7
#warning kein F_CPU definiert
8
#endif
9
 
10
#define KEY_DDR         DDRB
11
#define KEY_PORT        PORTB
12
#define KEY_PIN         PINB
13
#define KEY0            0
14
#define KEY1            1
15
#define KEY2            2
16
#define ALL_KEYS        (1<<KEY0 | 1<<KEY1 | 1<<KEY2)
17
 
18
#define REPEAT_MASK     (1<<KEY1 | 1<<KEY2)       // repeat: key1, key2
19
#define REPEAT_START    50                        // after 500ms
20
#define REPEAT_NEXT     20                        // every 200ms
21
 
22
#define LED_DDR         DDRA
23
#define LED_PORT        PORTA
24
#define LED0            0
25
#define LED1            1
26
#define LED2            2
27
 
28
volatile uint8_t key_state;                                // debounced and inverted key state:
29
                                                  // bit = 1: key pressed
30
volatile uint8_t key_press;                                // key press detect
31
 
32
volatile uint8_t key_rpt;                                  // key long press and repeat
33
 
34
 
35
ISR( TIMER0_OVF_vect )                            // every 10ms
36
{
37
  static uint8_t ct0, ct1, rpt;
38
  uint8_t i;
39
 
40
  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload for 10ms
41
 
42
  i = key_state ^ ~KEY_PIN;                       // key changed ?
43
  ct0 = ~( ct0 & i );                             // reset or count ct0
44
  ct1 = ct0 ^ (ct1 & i);                          // reset or count ct1
45
  i &= ct0 & ct1;                                 // count until roll over ?
46
  key_state ^= i;                                 // then toggle debounced state
47
  key_press |= key_state & i;                     // 0->1: key press detect
48
 
49
  if( (key_state & REPEAT_MASK) == 0 )            // check repeat function
50
     rpt = REPEAT_START;                          // start delay
51
  if( --rpt == 0 ){
52
    rpt = REPEAT_NEXT;                            // repeat delay
53
    key_rpt |= key_state & REPEAT_MASK;
54
  }
55
}
56
 
57
///////////////////////////////////////////////////////////////////
58
//
59
// check if a key has been pressed. Each pressed key is reported
60
// only once
61
//
62
uint8_t get_key_press( uint8_t key_mask )
63
{
64
  cli();                                          // read and clear atomic !
65
  key_mask &= key_press;                          // read key(s)
66
  key_press ^= key_mask;                          // clear key(s)
67
  sei();
68
  return key_mask;
69
}
70
 
71
///////////////////////////////////////////////////////////////////
72
//
73
// check if a key has been pressed long enough such that the
74
// key repeat functionality kicks in. After a small setup delay
75
// the key is reported being pressed in subsequent calls
76
// to this function. This simulates the user repeatedly
77
// pressing and releasing the key.
78
//
79
uint8_t get_key_rpt( uint8_t key_mask )
80
{
81
  cli();                                          // read and clear atomic !
82
  key_mask &= key_rpt;                            // read key(s)
83
  key_rpt ^= key_mask;                            // clear key(s)
84
  sei();
85
  return key_mask;
86
}
87
 
88
///////////////////////////////////////////////////////////////////
89
//
90
uint8_t get_key_short( uint8_t key_mask )
91
{
92
  cli();                                          // read key state and key press atomic !
93
  return get_key_press( ~key_state & key_mask );
94
}
95
 
96
///////////////////////////////////////////////////////////////////
97
//
98
uint8_t get_key_long( uint8_t key_mask )
99
{
100
  return get_key_press( get_key_rpt( key_mask ));
101
}
102
 
103
int main( void )
104
{
105
  LED_PORT = 0xFF;
106
  LED_DDR = 0xFF;                     
107
 
108
  // Configure debouncing routines
109
  KEY_DDR &= ~ALL_KEYS;                // configure key port for input
110
  KEY_PORT |= ALL_KEYS;                // and turn on pull up resistors
111
 
112
  TCCR0 = (1<<CS02)|(1<<CS00);         // divide by 1024
113
  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload for 10ms
114
  TIMSK |= 1<<TOIE0;                   // enable timer interrupt
115
 
116
  sei();
117
 
118
  while(1){
119
    if( get_key_short( 1<<KEY1 ))
120
      LED_PORT ^= 1<<LED1;
121
 
122
    if( get_key_long( 1<<KEY1 ))
123
      LED_PORT ^= 1<<LED2;
124
 
125
    // single press and repeat
126
 
127
    if( get_key_press( 1<<KEY2 ) || get_key_rpt( 1<<KEY2 )){
128
      uint8_t i = LED_PORT;
129
 
130
      i = (i & 0x07) | ((i << 1) & 0xF0);
131
      if( i < 0xF0 )
132
        i |= 0x08;
133
      LED_PORT = i;      
134
    }
135
  }
136
}

Jedoch habe ich das selbe Problem wie bei diesem Entpreller:

usr schrieb:
> guckst du hier : Beitrag "Re: Universelle Tastenabfrage" :)

Die LEDs leuchten einfach durchgehend -.-

Ich habe die Taster auf PB0, PB1 und PB2.
Eine LED ist zu test zwecken auf PD6 geschaltet, und 5 weitere auf PB3, 
PB4, PB5, PB6 und PB7

somit müsste der Code ja in etwa so aussehen:

1
#include <stdint.h>
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
 
5
#ifndef F_CPU
6
#define F_CPU           1000000                   // processor clock frequency
7
#warning kein F_CPU definiert
8
#endif
9
 
10
#define KEY_DDR         DDRB
11
#define KEY_PORT        PORTB
12
#define KEY_PIN         PINB
13
#define KEY0            0
14
#define KEY1            1
15
#define KEY2            2
16
#define ALL_KEYS        (1<<KEY0 | 1<<KEY1 | 1<<KEY2)
17
 
18
#define REPEAT_MASK     (1<<KEY1 | 1<<KEY2)       // repeat: key1, key2
19
#define REPEAT_START    50                        // after 500ms
20
#define REPEAT_NEXT     20                        // every 200ms
21
 
22
#define LED_DDR         DDRD
23
#define LED_PORT        PORTD
24
#define LED0            0
25
#define LED1            6
26
#define LED2            2
27
 
28
volatile uint8_t key_state;                                // debounced and inverted key state:
29
                                                  // bit = 1: key pressed
30
volatile uint8_t key_press;                                // key press detect
31
 
32
volatile uint8_t key_rpt;                                  // key long press and repeat
33
 
34
 
35
ISR( TIMER0_OVF_vect )                            // every 10ms
36
{
37
  static uint8_t ct0, ct1, rpt;
38
  uint8_t i;
39
 
40
  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload for 10ms
41
 
42
  i = key_state ^ ~KEY_PIN;                       // key changed ?
43
  ct0 = ~( ct0 & i );                             // reset or count ct0
44
  ct1 = ct0 ^ (ct1 & i);                          // reset or count ct1
45
  i &= ct0 & ct1;                                 // count until roll over ?
46
  key_state ^= i;                                 // then toggle debounced state
47
  key_press |= key_state & i;                     // 0->1: key press detect
48
 
49
  if( (key_state & REPEAT_MASK) == 0 )            // check repeat function
50
     rpt = REPEAT_START;                          // start delay
51
  if( --rpt == 0 ){
52
    rpt = REPEAT_NEXT;                            // repeat delay
53
    key_rpt |= key_state & REPEAT_MASK;
54
  }
55
}
56
 
57
///////////////////////////////////////////////////////////////////
58
//
59
// check if a key has been pressed. Each pressed key is reported
60
// only once
61
//
62
uint8_t get_key_press( uint8_t key_mask )
63
{
64
  cli();                                          // read and clear atomic !
65
  key_mask &= key_press;                          // read key(s)
66
  key_press ^= key_mask;                          // clear key(s)
67
  sei();
68
  return key_mask;
69
}
70
 
71
///////////////////////////////////////////////////////////////////
72
//
73
// check if a key has been pressed long enough such that the
74
// key repeat functionality kicks in. After a small setup delay
75
// the key is reported being pressed in subsequent calls
76
// to this function. This simulates the user repeatedly
77
// pressing and releasing the key.
78
//
79
uint8_t get_key_rpt( uint8_t key_mask )
80
{
81
  cli();                                          // read and clear atomic !
82
  key_mask &= key_rpt;                            // read key(s)
83
  key_rpt ^= key_mask;                            // clear key(s)
84
  sei();
85
  return key_mask;
86
}
87
 
88
///////////////////////////////////////////////////////////////////
89
//
90
uint8_t get_key_short( uint8_t key_mask )
91
{
92
  cli();                                          // read key state and key press atomic !
93
  return get_key_press( ~key_state & key_mask );
94
}
95
 
96
///////////////////////////////////////////////////////////////////
97
//
98
uint8_t get_key_long( uint8_t key_mask )
99
{
100
  return get_key_press( get_key_rpt( key_mask ));
101
}
102
 
103
int main( void )
104
{
105
  LED_PORT = 0xFF;
106
  LED_DDR = 0xFF;                     
107
 
108
  // Configure debouncing routines
109
  KEY_DDR &= ~ALL_KEYS;                // configure key port for input
110
  KEY_PORT |= ALL_KEYS;                // and turn on pull up resistors
111
 
112
  TCCR0 = (1<<CS02)|(1<<CS00);         // divide by 1024
113
  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload for 10ms
114
  TIMSK |= 1<<TOIE0;                   // enable timer interrupt
115
 
116
  sei();
117
 
118
  while(1){
119
120
 
121
    if( get_key_press( 1<<KEY1 ))
122
    {
123
       PORTD ^= (1<<PD6);
124
    }
125
  }
126
}

von Walter S. (avatar)


Lesenswert?

Titan schrieb:
> ja gut ist zwar nicht das Tutorial, aber ein Artikel:
> http://www.mikrocontroller.net/articles/Entprellung

da steht aber auch:
"Die Entprellung geschieht dabei durch die ganze Laufzeit des 
Programms."

und du bei dir geht die Laufzeit gegen 0

von Titan (Gast)


Lesenswert?

Ich weiß leider immernoch nicht wo das Problem bei dem Code ist -.-*

von Karl H. (kbuchegg)


Lesenswert?

Titan schrieb:
> Ich weiß leider immernoch nicht wo das Problem bei dem Code ist -.-*


In dienem letzten Code hier

> somit müsste der Code ja in etwa so aussehen:

kann ich beim Drüberlesen nichts schlimmes finden. Müsste eigentlich 
funktionieren. Mit dem Taster an PB1 wird die LED an PD6 umegschaltet. 
(Die Entprellsachen hast du ja nicht angegriffen, sind noch immer 
Original)
Welche Taktfrequenz hat dein µC?

von Volkmar D. (volkmar)


Lesenswert?

Hallo,

welchen µC verwendest Du, oben habe ich ATtiny2313 gelesen. Richtig?

Titan schrieb:
>   TCCR0 = (1<<CS02)|(1<<CS00);         // divide by 1024
>   TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload for 10ms

Wenn ich in das Datenblatt des 2313 schaue, dann finde ich kein TCCR0, 
nur TCCR0A und TCCR0B.

Und wenn ich mich nicht verrechnet habe und F_CPU = 1000000 ist, dann 
wird TCNT0 mit 255 geladen, damit paßt der Timer nicht.

Bau doch mal in die ISR noch folgendes ein:
- Eine LED die mit jedem ISR-Aufruf toggelt
- Einen Zähler und laß eine 2. LED alle 50 Aufrufe toggeln (entspricht 
dann 1Hz)

Volkmar

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

>In C hat das Schlüsselwort 'static' nur diese eine Bedeutung: die
>Sichtbarkeit von etwas (Variable oder Funktion) einzuschränken.

Und noch eine:
1
char * fu() {
2
  static char buf[256];
3
4
  strcpy buf, "Hallo");
5
  return buf;
6
}

Hier wird ein Pointer auf buf zurückgegeben, liegt buf auf dem Stack
geht's ins Nirwana. static legt da fest, daß buf weiter erreichbar
bleibt.

von Karl H. (kbuchegg)


Lesenswert?

Volkmar Dierkes schrieb:

> Und wenn ich mich nicht verrechnet habe und F_CPU = 1000000 ist, dann
> wird TCNT0 mit 255 geladen, damit paßt der Timer nicht.

hab ich auch zuerst gedacht.
Macht aber nichts, dann löst der Timer eben bei jedem mal hochzählen 
einen Interrupt aus. Durch den Vorteiler ist das immer noch alle 1024 
Taktzyklen. Also grob 1ms.


Die 10ms sind nicht so genau. Ob das 2ms oder 40ms sind, spielt so gut 
wie keine Rolle (und darum stell ich mir für meine Programme den 
Vorteiler so ungefähr in diesen Zeitbereich ein und lass das Vorladen 
des TCNT komplett weg).

von Karl H. (kbuchegg)


Lesenswert?

Gehn wir auch mal in eine andere Richtung

> Ich habe die Taster auf PB0, PB1 und PB2.
> Eine LED ist zu test zwecken auf PD6 geschaltet, und 5 weitere auf PB3,
> PB4, PB5, PB6 und PB7

OK. Aber WIE sind die Einzelteile angeschlossen?
Der Code geht zb davon aus, dass die Taster nach Masse schalten. Tun sie 
das bei dir? In deinem ersten Code sieht das nämlich nicht danach aus, 
muss jetzt aber erst mal nicht viel heissen.

von Volkmar D. (volkmar)


Lesenswert?

Volkmar Dierkes schrieb:
> Und wenn ich mich nicht verrechnet habe und F_CPU = 1000000 ist, dann
> wird TCNT0 mit 255 geladen, damit paßt der Timer nicht.

Ich habe mich verrechnet, TCNT0 wird mit 0xF6 geladen. 10e-3 = 10*10^-3 
= 0,01!

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.