Forum: Compiler & IDEs AD-Wandlung Tastenentprellen


von Sascha S. (sands)


Lesenswert?

Hallo zusammen,

ich versuche eine 3*4 Tastenmatrix über einen Spannungsteiler mittels 
AD-Wandlung einzulesen.
Funktioniert soweit auch schon ganz gut, nur habe ich ein riesen Problem 
mit dem Prellen der Tasten.
Wie kann ich dafür sorgen das die Tasten entprellt werden sobald sie den 
Wert 0xff unterschreiten, bzw. den gelesenen Wert wieder verlassen?
Hier der Quellcode:
1
#include <avr/io.h>
2
#define F_CPU 3686400UL
3
#include <util/delay.h>
4
5
uint8_t messen(uint8_t port)
6
{
7
  ADMUX=0x00;        //MUX0 bis MUX3 zurücksetzen
8
  ADMUX|=port;    //MUX0 bis MUX3 auf gewünschten Port setzen
9
  ADMUX=(1<<ADLAR);      //linksbündige Ausgabe
10
  ADCSRA|=(1<<ADEN|1<<ADPS2|1<<ADPS0);  //ADEN=1; Teilfaktor 32
11
  ADCSRA|=1<<ADSC;      //Wandelung starten
12
13
  return(ADCH);              //Analogwert  zurücksetzen
14
}
15
16
int main (void)
17
{
18
  uint8_t wert1;        //vorzeichenlose 8bit Werte
19
  uint8_t value;
20
  DDRB|=0xFF;        // PB0...PB5 zur Ausgabe
21
  DDRD|=0xFF;                    // PD0...PD7 zur Ausgabe
22
  do{
23
  
24
    
25
    wert1=messen(PC0);    //PC0 messen
26
    
27
    if((wert1 >=0x07) && (wert1 <0x12))
28
      value = 1;
29
30
    if((wert1 >=0x12) && (wert1 <0x1C))
31
      value =2;
32
        
33
    if((wert1 >=0x1C) && (wert1 <0x2A))
34
      value =3;
35
          
36
    if((wert1 >=0x2A) && (wert1 <0x40))
37
      value =4;
38
    
39
    if((wert1 >=0x40) && (wert1 <0x5B))
40
      value =5;
41
        
42
    if((wert1 >=0x5B) && (wert1 <0x79))
43
      value =6;
44
          
45
    if((wert1 >=0x79) && (wert1 <0x9C))
46
      value =7;
47
        
48
    if((wert1 >=0x9C) && (wert1 <0xB9))
49
      value =8;
50
    
51
    if((wert1 >=0xB9) && (wert1 <0xCF))
52
      value =9;
53
    
54
    if((wert1 >=0xCF) && (wert1 <0xE1))
55
      value =10;
56
        
57
    if((wert1 >=0xE1) && (wert1 <0xED))
58
      value =11;
59
    
60
    if((wert1 >=0xED) && (wert1 <0xFA))
61
      value =12;
62
        
63
    }
64
65
  switch (value)
66
  {
67
  case 3:
68
      PORTB=0b00000110;
69
      PORTD=0b00000000;
70
      break;
71
  case 2:
72
      PORTB=0b00011011;
73
      PORTD=0b00000100;
74
      break;
75
  case 1:
76
      PORTB=0b00001111;
77
      PORTD=0b00000100;
78
      break;
79
  case 6:  
80
      PORTB=0b00100110;
81
      PORTD=0b00000100;
82
      break;
83
  case 5:
84
      PORTB=0b00101101;
85
      PORTD=0b00000100;
86
      break;
87
  case 4:
88
      PORTB=0b00111101;
89
      PORTD=0b00000100;
90
      break;
91
  case 9:
92
      PORTB=0b00000111;
93
      PORTD=0b00000000;
94
      break;
95
  case 8:
96
      PORTB=0b00111111;
97
      PORTD=0b00000100;
98
      break;
99
  case 7:
100
      PORTB=0b00101111;
101
      PORTD=0b00000100;
102
      break;
103
  case 12:
104
      PORTB=0b00100100;
105
      PORTD=0b00000100;
106
      break;
107
  case 11:
108
      PORTB=0b00111111;
109
      PORTD=0b00000000;
110
      break;
111
  case 10:
112
      PORTB=0b00110110;
113
      PORTD=0b00000000;
114
      break;
115
  }
116
  }
117
  while (1);
118
  return(0);
119
}

Ich gebe die gedrückte Taste an eine 7-Segmentanzeige aus. Solange ich 
die Taste drücke wird ja auch das richtige angezeigt, aber sobald ich 
die Taste loslasse oder nur andrücke wird mir alles angezeigt nur nicht 
der Tastenwert.Deshalb müsste ich die Tastenerkennung entprellen, nur 
weiß ich leider nicht wie ich das bewerkstelligen muss.

Danke schonmal für eure Hilfe im voraus.

von Peter D. (peda)


Lesenswert?


von Sascha S. (sands)


Lesenswert?

Danke für den Hinweis.
Hatte ich auch schon gelesen, doch ehrlich gesagt nicht ganz verstanden, 
deshalb ja nochmal meine Anfrage im Forum.
Ist es auch möglich die Tasten ohne Interrupt zu entprellen?

von Karl H. (kbuchegg)


Lesenswert?

Das Prinzip ist im Grunde immer das gleiche.

Eine festgestellte Veränderung deiner Messung wird erst dann akzeptiert, 
wenn sich ein paar mal hintereinander (in einem zeitlichen Abstand von 
jeweils ein paar Millisekunden) derselbe Wert ergibt.

Timer mit Interrupt ist einfach nur eine einfache Möglichkeit, wie man 
diese zeitliche Abtastung machen kann, so dass das nicht mit dem 
restlichen Programm beisst.

Auf Dauer wirst du um Timer sowieso nicht rumkommen. Jedes ernsthafte 
Programm, welches ein bischen komplexer ist als irgendwelche banalen 
Programmierübungen ist, kommt ohne Timer nicht aus. Also warum nicht 
jetzt lernen, wie man einen Timer einsetzt?

von MWS (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Also warum nicht
> jetzt lernen, wie man einen Timer einsetzt?

Warum 'nen Timer verschwenden, wenn man über 'ne langsame Samplerate und 
Freerun den ADC als zeitgebendes Element verwenden kann ?

In der AD-Complete ISR den aktuellen gemessenen mit dem vorherigen Wert 
innerhalb eines Fensters vergleichen. Wenn innerhalb Toleranz, 'ne 
Zählvariable erhöhen und den Mittelwert bilden. Wenn die Zählvariable 
eine bestimmte Höhe erreicht hat, sagen wir 20, dann ein Flag setzen und 
analog zu obigem Code den gemittelten Wert auswerten. Im Falle dass der 
aktuelle gemessene ADC-Wert außerhalb der Toleranz zum letzten Wert 
liegt, Zählvariable löschen und aktuelle Messung als neue Referenz 
setzen.

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


Lesenswert?

MWS schrieb:
> Warum 'nen Timer verschwenden

Naja, irgendwas wie einen "heartbeat"-Timer braucht man ohnehin in
so ziemlich jeder Applikation.  Den lässt man aller 5 oder 10 ms
ticken, und das bisschen Tastaturabfrage macht der nebenbei.

ADC free-running mode als Timer geht natürlich auch, hilft dem TE
aber nichts, denn auch da muss man sich ja mit Interrupts befassen. ;-)

von Peter D. (peda)


Lesenswert?

MWS schrieb:
> Warum 'nen Timer verschwenden, wenn man über 'ne langsame Samplerate und
> Freerun den ADC als zeitgebendes Element verwenden kann ?

Laut Datenblatt nicht unter 50kHz / 13.
Das ist zum Entprellen noch viel zu schnell, d.h. unnötig hohe CPU-Last.


Peter

von MWS (Gast)


Lesenswert?

Jörg Wunsch schrieb:
> ADC free-running mode als Timer geht natürlich auch, hilft dem TE
> aber nichts, denn auch da muss man sich ja mit Interrupts befassen. ;-)

"Nichts helfen" würd' ich jetzt nicht sagen, denn wenn er's lernen will 
oder muss, dann ist der Ansatz durchaus interessant. Der 
Schwierigkeitsgrad einen AD-complete oder Timer-overflow Interrupt 
aufzusetzen, sollte ähnlich sein.

Peter Dannegger schrieb:
> Laut Datenblatt nicht unter 50kHz / 13.

Du hast F_CPU wahrgenommen ?

> between 50kHz and 200kHz to get maximum resolution.

Maximum resolution braucht er nicht.

> Das ist zum Entprellen noch viel zu schnell, d.h. unnötig hohe CPU-Last.

Auch wenn's im momentanen Stadium nicht viel ausmachen wird, da dafür 
ca. 3-5% CPU verbraten würden. Aber Dein Argument ist gültig.

Dann soll der TE sich meinen Vorschlag merken, wenn ihm mal die Timer 
ausgehen, oder er soll's testweise implementieren, wenn er lernen will.

von Karl H. (kbuchegg)


Lesenswert?

MWS schrieb:
> Karl Heinz Buchegger schrieb:
>> Also warum nicht
>> jetzt lernen, wie man einen Timer einsetzt?
>
> Warum 'nen Timer verschwenden

Weil du von Atmel für einen nicht benutzten Timer kein Geld 
zurückbekommst.

Im übrigen gehts ja nicht um den Timer an sich, sondern um das Prinzip 
wie Entprellung funktioniert. Ob er da jetzt einen Timer benutzt oder 
sonst irgendwie eine regelmässige Abtastung erreicht, ist Jacke wie 
Hose. Du musst nur aufpassen, dass du nicht zu schnell (!) sampelst. Das 
bringt dann nämlich auch wieder nichts.

von Karl H. (kbuchegg)


Lesenswert?

MWS schrieb:

> Peter Dannegger schrieb:
>> Laut Datenblatt nicht unter 50kHz / 13.
>
> Du hast F_CPU wahrgenommen ?

Dir ist klar, dass man den ADC nicht beliebig langsam betreiben kann?


> Dann soll der TE sich meinen Vorschlag merken, wenn ihm mal die Timer
> ausgehen, oder er soll's testweise implementieren, wenn er lernen will.

Auch wenn es viele nicht wahrhaben wollen:
Ja, man kann einen Timer durchaus dazu einsetzen, MEHRERE Dinge zu 
machen. Es ist nicht in Stein gemeisselt, dass eine Timer ISR nur eine 
einzige Aufgabe erfüllen darf.

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


Lesenswert?

Karl Heinz Buchegger schrieb:
> Dir ist klar, dass man den ADC nicht beliebig langsam betreiben kann?

Beliebig langsam nicht, aber wenn man ihn mit weniger als 50 kHz
taktet, leidet erstmal nur die Genauigkeit, weil die S&H-Stufe dann
bereits während der Wandlung Ladung verliert.  Da es hier aber ohnehin
kaum auf 10 bit Wandlergenauigkeit ankommen wird (sondern eher nur
4 bit), und ein SA-ADC zuerst die MSBs wandelt, passen diese auch mit
geringer Taktrate immer noch gut genug.

von MWS (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Dir ist klar, dass man den ADC nicht beliebig langsam betreiben kann?

Ja. Aber 28,8kHz sollten schon noch mit ausreichender Genauigkeit 
klappen.

von Karl H. (kbuchegg)


Lesenswert?

Jörg Wunsch schrieb:
> Karl Heinz Buchegger schrieb:
>> Dir ist klar, dass man den ADC nicht beliebig langsam betreiben kann?
>
> Beliebig langsam nicht, aber wenn man ihn mit weniger als 50 kHz
> taktet, leidet erstmal nur die Genauigkeit, weil die S&H-Stufe dann
> bereits während der Wandlung Ladung verliert.  Da es hier aber ohnehin
> kaum auf 10 bit Wandlergenauigkeit ankommen wird (sondern eher nur
> 4 bit), und ein SA-ADC zuerst die MSBs wandelt, passen diese auch mit
> geringer Taktrate immer noch gut genug.

OK. Das kann ich akzeptieren.
Trotzdem finde ich, dass er mit dem Konzept "Heartbeat - Timer" doch 
einen wesentlich wichtigeren Schritt in seiner Programmierer-Entwicklung 
macht.

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


Lesenswert?

Karl Heinz Buchegger schrieb:
> Trotzdem finde ich, dass er mit dem Konzept "Heartbeat - Timer" doch
> einen wesentlich wichtigeren Schritt in seiner Programmierer-Entwicklung
> macht.

Das sehe ich natürlich genauso.  Ich kann mich (jenseits einfacher
Testprogramme) nur an wenige Applikationen erinnern, die sowas nicht
brauchen würden in der einen oder anderen Form.

von MWS (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Trotzdem finde ich, dass er mit dem Konzept "Heartbeat - Timer" doch
> einen wesentlich wichtigeren Schritt in seiner Programmierer-Entwicklung
> macht.

Den wird er schon machen. Dennoch ist's immer gut neben der 
eingefahrenen Spur denken zu können.

Soll er halt für sich entscheiden, meinst nicht auch ? Das Wissen darum, 
das man sowas mit der AD-complete ISR machen kann, oder ggf. den ADC als 
Timer missbrauchen kann, wird ihm jetzt nicht seine Seele kosten.

von Peter D. (peda)


Lesenswert?

Ja, man kann schon die lustigsten Sachen als Timer nehmen.
Man kann ja im ADC-Interrupt ne Variable runterzählen und nur jede 100. 
Messung auswerten.

Oder wenn ein Pin frei ist, UART-senden.
Oder den Watchdoginterrupt.

Echte Männer nehmen ja den EEPROM-Interrupt als Timer (~8ms) und lassen 
die ersten 16Byte ungenutzt, weil die irgendwann vergeßlich werden.

Und schon hat man auf den ATmega48 7 Timer mit 14 Interrupts.


Peter

von Sascha S. (sands)


Lesenswert?

Ersteinmal vielen Dank für die vielen Antworten.
Jedoch kann ich eurer Diskussion nicht ganz folgen.

Jetzt mal ganz dumm gefragt.
Wie würde denn eine festgestellte Veränderung meiner Messung akzeptiert,
wenn sich ein paar mal hintereinander (in einem zeitlichen Abstand von
jeweils ein paar Millisekunden) derselbe Wert ergibt.
Wie könnte ich das realisieren?
Habe im moment nur ??? im Kopf.

von MWS (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Echte Männer nehmen ja den EEPROM-Interrupt als Timer (~8ms) und lassen
> die ersten 16Byte ungenutzt, weil die irgendwann vergeßlich werden.

Chuck Norris kann Timer1 gleichzeitig mit 3 verschiedenen Prescalern 
laufen lassen. :D

Sascha S. schrieb:
> Wie könnte ich das realisieren?

Eine Lösung hab' ich hier gegeben:
Beitrag "AD-Wandlung Tastenentprellen"

Ob Du das per Timer oder wie von mir propagiert per ADC-complete 
Interrupt löst, bleibt sich gleich - am Prinzip ändert sich nix.

Aber zuerst brauchst eine Zeitquelle, dazu arbeite Dich mal in Timer und 
Timerinterrupt ein, so wie es Karl Heinz vorschlug.

von Peter D. (peda)


Lesenswert?

MWS schrieb:
> Chuck Norris kann Timer1 gleichzeitig mit 3 verschiedenen Prescalern
> laufen lassen. :D

Wenn man die Zeiten nicht gleichzeitig benötigt, mag das gehen.

Was ich aber schonmal gebraucht habe, mit T1 3 verschiedene Intervalle 
zu erzeugen.
Der Overflow macht 65536 Takte. Und Compare-A bzw. -B machen irgendwas 
zwischen 2 .. 65536, indem sie ihr Compareregister entsprechend 
nachladen.
Das ist ziemlich flexibel und auf den Takt genau.


Peter

von MWS (Gast)


Lesenswert?

Peter Dannegger schrieb:
> indem sie ihr Compareregister entsprechend nachladen.

Solange es ein Mode ohne Double Buffer 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.