PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Tasten an einem ADCPin, Versuch zu entprellen



newbie1982
07.08.2008, 13:14
Hallo Leute,
ich versuche grade drei Tasten die ich an einem ADC pin eines Tiny 85
angehägt habe zu entprellen.
(Tastenwert) wird duch eine ADKanallesenfunktion bestimmt und liegt
zwischen 0 und 5
zuerst habe ich die 3 Zustände in einer funktion definiert

uint8_t Tastenabfrage(void){
uint8_t Taste;

Taste = 0;
if (Tastenwert > 4) Taste=1; //Werte grösser als 4, schalter mit
einem KOhm wurde gedrückt
else if (Tastenwert > 2) Taste=10;//Werte grösser als 2, schalter mit
10 KOhm wurde gedrückt
else if (Tastenwert > 1) Taste=33;//Werte grösser als 1, schalter mit
33 KOhm wurde gedrückt
else Taste = 0;
return Taste;
}

danach habe ich versucht die Tasten so zu entprellen sodass ein
Tastendruck was ein Wert zwischen 0 und 5 erbit nicht erkannt wird,
er wird erst dann erkannt wenn der Wert zweimal rauskommt, und dann
wird 10 ms gewartet und letzendlich werden Taste als definiert.
Das Prozessflag definiert mehrere Zustände. Für Jeden Zustand wird ein
Bit reserviert in einem 8 Bit.
Ich habe das folgende Code geschrieben.

void TastenEntprellung(){

if((Tastenabfrage()==1)||(Tastenabfrage()==10)||(T astenabfrage()==33)){
// wenn nur einmal gedrückt (KeineTaste)
Anreiz=KeineTaste;

if(Tastenabfrage()==1){// zweimal erkannt TasteEins
_delay_ms(10);
Anreiz=TasteEins;
Prozessflag|= Benutzeroberflaeche;
}

if(Tastenabfrage()==10){// zweimal erkannt TasteZwei
_delay_ms(10);
Anreiz=TasteZwei;
Prozessflag|=Benutzeroberflaeche;
}

if(Tastenabfrage()==33){// zweimal erkannt TasteDrei
_delay_ms(10);
Anreiz=TasteDrei;
Prozessflag|=Benutzeroberflaeche;
}
}
}

Kann mir jemand sagen ob es richtig ist und ob es
Verbesserungsvorschläge gibt.
Mein bessten Dank

fhs
07.08.2008, 14:02
Hallo,

da Du nur einen Auszug Deines Codes eingestellt hat, ist keine Aussage möglich. Z.B. ist nicht klar, wann der A/D-Wandler-Kanal gelesen wird, wo Du "Anreiz" deklarierst und wo und wie "Anreiz" ausgewertet wird.

Wenn Du noch einen Timer frei hast, schlage ich vor, Du "baust" Dir einen Zustandsautomaten ("state machine"): alle 10-20ms per Timer einen Zustand (in "main()" auswerten!) setzen, der dann zu einer A/D-Wandlung führt. Dann mit dem Ergebnis der letzten A/D-Wandlung vergleichen, wie Du es oben beschreibst.

Denk' auch an die Möglichkeiten der "switch()"-Funktion in C!

Viele Grüße

Fred

robocat
07.08.2008, 14:19
https://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=42075

zerush
07.08.2008, 14:29
Es gibt da noch einen Denkfehler in TastenEntprellung().
Wenn die erste Auswertung von Tastenabfrage() 1 ergibt, und die zweite 10, dann wertet es dein Programm so aus, als würde zweimal die 10 rauskommen.

Code nächstes mal am besten in [c o d e]-Blocks packen:


void TastenEntprellung(){

if((Tastenabfrage()==1)||(Tastenabfrage()==10)||(T astenabfrage()==33)){
// wenn nur einmal gedrückt (KeineTaste)
Anreiz=KeineTaste;

if(Tastenabfrage()==1){// zweimal erkannt TasteEins
_delay_ms(10);
Anreiz=TasteEins;
Prozessflag|= Benutzeroberflaeche;
}

if(Tastenabfrage()==10){// zweimal erkannt TasteZwei
_delay_ms(10);
Anreiz=TasteZwei;
Prozessflag|=Benutzeroberflaeche;
}

if(Tastenabfrage()==33){// zweimal erkannt TasteDrei
_delay_ms(10);
Anreiz=TasteDrei;
Prozessflag|=Benutzeroberflaeche;
}
}

newbie1982
07.08.2008, 15:09
Hi,
//Wenn die erste Auswertung von Tastenabfrage() 1 ergibt, und die zweite 10, //dann wertet es dein Programm so aus, als würde zweimal die 10 //rauskommen.

ich habe vercuht nur die Fälle auseinander zu halten, wenn es nicht mit 1,10,33 geht kann ich dafür Zeichen nehmen, oder 1,2,3.


hier sind die Codes.
Danke

ADKanallesen

#include <avr/io.h>
#include "Initialisierung.h"
#include <util/delay.h>
#include <avr/interrupt.h>


//variablen
uint16_t U_analog;
uint16_t Tastenwert;
uint16_t Stromwert;
extern uint8_t Prozessflag;

//Funktion zur A/D-Wandlung
uint16_t ADkanalLesen() {

uint8_t i;
uint16_t ergebnis = 0;//A/D-Wert
unsigned char Durchlaeufe =4;




for (i=0; i<Durchlaeufe; i++)
{
ADCSRA |= (1<<ADSC); //Startet eine A/D-Wandlung (Single Conversion)

while (ADCSRA & (1<<ADSC))
{
; // auf Abschluss der Konvertierung warten
}
ergebnis += ADCW; // Wandlungsergebnisse werden aufaddieren

}

ADCSRA &= ~(1<<ADEN); //A/D-Wandler wird deaktiviert

ergebnis /= Durchlaeufe; // Summe durch 4 teilen (Mittelwert)

U_analog = (ergebnis*u_ref)/1024;

return U_analog; //Analogwert
}

Intteruptroutine



#include <avr/io.h>
#include "Initialisierung.h"
#include <util/delay.h>
#include <avr/interrupt.h>


extern uint8_t Prozessflag;
//Globale Variable
uint8_t Zustand;
uint8_t Anreiz;
uint16_t Zaehler=0;
extern uint16_t Tastenwert;
extern uint16_t Stromwert;

// InterruptVectoren
ISR( TIM0_OVF_vect ){ // Interruptroutine bei Timeroverflow
TCNT0 = TCNT0init; //Counter zurück auf Startwert 58 gesetzt
ADCSRA |= (1<<ADEN) | (1<<ADPS2) | (1<<ADPS0)| (1<<ADIE); //AD-Wandler aktiviert(ADEN), Prescaler auf 32(ADSPSO,ADPS2)
//Interrupt enable aktiv
ADMUX |= (0<<REFS1) | (0<<REFS0); // Vcc als Referenzspannung die getrennt von AREF ist
// Kanal waehlen und Vcc als Referenzspannung
if(Zaehler%2){//bei 50ms und in einem 100ms Zeitabstand eine ADWandlung ausführen und ein ADInterrupt auszulösen
ADMUX|=PINTASTEN; // Kanal auswählen(PIn wo die Tasten angehängt sind)
ADCSRA |= (1<<ADSC) ; //Startet eine A/D-Wandlung (Single Conversion)
}
Zaehler++;
}

ISR( ADC_vect){
if(Zaehler%2){
Tastenwert=ADkanalLesen();//Tastenwert nimmt den Wert von der ADWandlung an (analogwert)
Prozessflag|= Tasten;//Prozessflag wird auf Tasten gesetzt (fragt die tasten ab bei der Funktion Tastenabfrage)
}
}




initialisierung.h


#include <avr/io.h>
#include <stdint.h>

/Prozessflags


#define Benutzeroberflaeche (1<<0)
#define Tasten (1<<1)
#define Rechnen (1<<2)

// Tasten //


#define KeineTaste 1
#define TasteEins 1
#define TasteZwei 2
#define TasteDrei 3

in initialisierung sind andere Funktionen deklariert die ich da nicht zugefügt habe

fhs
07.08.2008, 17:22
Hallo,

ich mache es sehr kurz: das hat rein gar nichts mehr mit Entprellen zu tun, wonach Du im ersten Posting gefragt hast. Du bildest Mittelwerte aus den A/D-Ergebnissen; wenn es dann beim Tastendruck prellt, bekommst Du einfach total verkehrte Resultate. Hast Du die Idee mit dem Entprellen ganz aufgegeben?

Ansonsten gilt immer noch der Rat, der Dir mehrfach in dem anderen Thread gegeben worden ist. Einfach Code hier einzustellen mit der Bitte, dass andere herausfinden,
ob es richtig ist, funktioniert nicht gut. Wenn etwas konkret nicht läuft oder Du fassbare Fragen hast, dann bekommst Du sicher bessere Antworten. Zum Testen gibt es 1. Hardware und
2. den Simulator!

Viele Grüße

Fred

PS: Gerade sehe ich, dass Du Deine Fragen schon gestern in einem anderen Forum (http://www.mikrocontroller.net/topic/107562) gestellt hast. Ich schlage vor, den Thread dort fortzusetzen.

BurningWave
08.08.2008, 12:33
Wieso machst du denn alles so kompliziert??? :wink:
Nehm doch einfach einen größeren µC (z.B. einen AtTiny2313 oder AtTiny26), dann kannst du für jede Taste eine eigene Pin nehmen und die dann mit _delay_ms() entprellen.

mfg

Felix G
09.08.2008, 07:14
ich mache es sehr kurz: das hat rein gar nichts mehr mit Entprellen zu tun, wonach Du im ersten Posting gefragt hast. Du bildest Mittelwerte aus den A/D-Ergebnissen; wenn es dann beim Tastendruck prellt, bekommst Du einfach total verkehrte Resultate. Hast Du die Idee mit dem Entprellen ganz aufgegeben?Ähm...
also eine gleitende Mittelung (moving average) würde sich sehr wohl zum entprellen eignen, sowas ist nämlich im Allgemeinen auch als Tiefpass bekannt der die hochfrequenten Anteile (Prellen) einfach wegfiltert. Wenn man die Grenzfrequenz sinnvoll wählt, und bei der weiteren Auswertung noch eine Hysterese mit einbaut, sollte das eigentlich recht zuverlässig funktionieren.


Allerdings, newbie1982, ist das so leider noch keine gleitende Mittelung.

Ich würde es etwa so implementieren (dient nur als Beispiel)

#define AD_EXP 4 //Mittelung über 16 Werte
#define AD_BUF_LEN (1 << AD_EXP)

volatile uint16_t AD_Result;

SIGNAL(SIG_ADC)
{
uint8_t n;
static uint8_t Buffer_index = 0;
static uint16_t AD_Buffer[AD_BUF_LEN];

// Buffer zyklisch beschreiben
AD_Buffer[Buffer_index++] = ADCW;
Buffer_index = Buffer_index % AD_BUF_LEN;

// Mittelung
ADC_Result = 0;
for(n=0;n<AD_BUF_LEN;n++)
AD_Result += AD_Buffer[n];

AD_Result >>= AD_EXP;
}

So hast du in AD_Result immer den aktuellen gefilterten Wert, und kannst im Hauptprogramm ganz bequem darauf zugreifen.

In diesem Beispiel wird jeweils über 16 Werte gemittelt, ob das sinnvoll ist oder nicht, hängt vom ADC-Takt ab. Wenn dieser bekannt ist, kannst du die Grenzfrequenz des TP berechnen (im Prinzip könnte man da problemlos auf 10Hz runter gehen, wer muss schon 10x pro Sekunde auf so eine Taste drücken?).



PS: wenn der Buffer bei der richtigen Grenzfrequenz zu groß wird, würde ich ihn als globale volatile Variable auslagern, und ihn in der ISR zwar zyklisch beschreiben, jedoch die Mittelung lieber im Hauptprogramm durchführen (damit die ISR nicht zu lange braucht). Diese Mittelung könnte man ja dann auch in eine nette kleine Funktion verpacken, irgendwas wie "getadc()" oder so, dann ist es auch sehr bequem zu nutzen.

fhs
09.08.2008, 08:53
Hi,
wenn man wie der OP mehrere Tasten abfragen möchte, wobei jede Taste durch einen Widerstand (=verschiedene Spannungen am A/D-Wandler) repräsentiert wird, ist die Mittelwertbildung zum Zwecke der Entprellung (zumindestens bei der Abtastrate, die der OP verwendet hat) unsinnig, da jedes Prellen den Mittelwert verändert, so dass schließlich ein anderes Ergebnis als das gewünschte herauskommen kann (=falsche Taste). Natürlich könnte man über eine so lange Zeit mitteln, dass das kaum noch einen Faktor darstellt; aber da sind andere Methoden doch besser geeignet.

Trotzdem schlage ich noch einmal vor, die Diskussion im anderen Forum weiterzuführen, wo der OP seine Frage zuerst gestellt hat.

Gruß

Fred

newbie1982
09.08.2008, 13:34
Danke erstmal,
im Code von Felix habe ich s nicht so kapiert, wie die Werte dann aus dem ADwandler abgeholt werden

ist es mit //static uint16_t AD_Buffer[AD_BUF_LEN];
da ich eine Funktion dafür habe ADKanallesen()

danke nochmal.