PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Tastenabfrage (Kur/Lang gedrückt)



DerSchatten
10.01.2010, 17:45
Hallo Leute,

ich habe miraus der Wiki dieses Beispiel herausgesucht und versucht in meinen Aufbau zu integrieren: Taster-Abfrage in C (http://www.rn-wissen.de/index.php/Taster-Abfrage_in_C)

Nur leider tut das ganze nicht so wie es soll.
Mein Beispiel sieht folgendermaßen aus:



//----------------------------------------
// Titel : Arcade Tastenprogrammierung
//----------------------------------------
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>
#include "taster.h"

typedef unsigned char BYTE;
typedef unsigned int WORD;

BYTE bPortB;
BYTE nKeyPress;
uint8_t eeFooByte;

volatile uint8_t gKeyCounter;
volatile signed char taster = NO_TASTER;
taste_t tasten[NUM_TASTER];

const unsigned char Tabelle[] PROGMEM = {249, 164, 176, 153, 146, 130, 248, 128, 144};

SIGNAL (SIG_OVERFLOW0)
{
static unsigned char count_ovl0;
unsigned char ovl0 = count_ovl0+1;

if (ovl0 >= 39)
{
get_taster (1, PINB & (1<<PB4));
ovl0 = 0;
}
count_ovl0 = ovl0;
}

void ioinit()
{
PORTB |= 1 << PB4;
DDRD = 0xFF;
TCCR0 = 1 << CS00;
TIMSK |= (1 << TOIE0);
}

int main (void)
{
ioinit();

bPortB = 1;
nKeyPress = 0;
// nKeyPress = eeprom_read_byte(&eeFooByte);
gKeyCounter = 0;

tasten[0].mode = TM_SHORT;
tasten[1].mode = TM_LONG;
tasten[2].mode = TM_REPEAT;

while(1)
{
signed char tast = taster;
PORTD = pgm_read_byte(&Tabelle[nKeyPress]);

switch (tast)
{
default:
case NO_TASTER:
break;

case 1:
if (nKeyPress < 8)
{
nKeyPress++;
}
else
{
nKeyPress = 0;
}
break;

case 1+TASTER_LONG:
eeprom_write_byte(&eeFooByte, nKeyPress);
break;
}
if (tast != NO_TASTER)
taster = NO_TASTER;
}
return 0;
}
void get_taster (const unsigned char num, unsigned char tast)
{
const taste_t * const ptast = & tasten[num];
const unsigned char taster_old = ptast->old;
unsigned char pressed, press, release, mode, delay;

#if TASTER_LEVEL
tast = !!tast;
#else
tast = !tast;
#endif

/* Taster bleibt gedrueckt */
pressed = taster_old & tast;
/* Taster neu gedrueckt */
press = ~taster_old & tast;
/* Taster losgelassen */
release = taster_old & ~tast;

*((unsigned char *) & ptast->old) = tast;

tast = NO_TASTER;

mode = ptast->mode;
delay = ptast->delay;

if (press)
{
if (mode != TM_LONG)
tast = num;

delay = 0;
}
else if (pressed)
{
if (delay < 0xfe)
delay++;
}
else if (release)
{
if (mode == TM_LONG && delay != 0xff)
tast = num;
}

if (mode == TM_LONG)
{
if (delay == TASTER_DELAY_LONG)
{
tast = TASTER_LONG + num;
delay = 0xff;
}
}
else if (mode == TM_REPEAT)
{
if (delay == TASTER_REPEAT_DELAY)
{
tast = num;
delay = TASTER_REPEAT_DELAY - TASTER_REPEAT;
}
}

if (taster == NO_TASTER)
taster = tast;

*((unsigned char *) & ptast->delay) = delay;
}


Bei einem Tastendruck passiert leider nichts.
Hat jemand eine Idee wo da der Hund begraben sein könnte?

sternst
10.01.2010, 18:38
Hat jemand eine Idee wo da der Hund begraben sein könnte?Ich sehe keinerlei "sei()".

DerSchatten
10.01.2010, 19:29
Ich sehe keinerlei "sei()".
Du hast recht!
Habs ergänzt.

Leider wars das jedoch noch nicht. :-k

DerSchatten
10.01.2010, 21:42
Hat vielleicht jemand ein einfacheres Beispiel für mich?
Im Grunde möchte ich nur abfragen ob der Taster kurz oder lan(ca. 2 Sek) gedrückt wurde.
Und je nachdem entweder eine Variable um 1 hochzählen oder den aktuellen Wert im EEPROM speichern.

Che Guevara
10.01.2010, 22:25
Hallo DerSchatten,

da ich kein C beherrsche, werde ich mal versuchen, dir ein einfaches Beispiel (also die Funktionsweise) zu erklären:
Wenn der Taster gedrückt worden ist, geht das Programm in eine kleine Schleife, in welcher 1ms lang gewartet wird und dann wird eine Variable erhöht. Wenn der Taster wieder losgelassen wurde, verlässt der µC die Schleife. Nun wird die Variable abgefragt, ob sie größer als 2000 (also 2000ms entspricht 2s) ist. Anschließend soll der µC darauf reagieren und die Variable muss wieder auf 0 gesetzt werden. Allerdings ist das ganze nicht gerade "schnell", da der µC ziemlich lange in der Schleife feststecken kann. Außerdem musst du aufpassen, dass die Variable nicht überläuft.
Hier mal ein Pseudocode:


wenn taster1 = gedrückt dann
solange taster1 gedrückt
1ms warten
variable xy um 1 erhöhen
wenn nicht mehr gedrückt, schleife verlassen
wenn variable xy >= 2000
taster = mindestens 2s lang gedrückt
allenfalls
taster = maximal 2s lang gedrückt
ende wenn
varaible xy = 0
ende wenn

Hier mal das ganze in BASCOM, ist meiner Meinung nach leicht verständlich:


if t1 = 0 then
while t1 = 0
waitms 1
xy = xy + 1
wend
if xy >= 2000
taster = 2
else
taster = 1
endif
xy = 0
endif


Ich hoffe, ich konnte dir helfen.
Wenn du das ganze allerdings schneller programmieren möchtest, solltest du einen timer so einstellen, dass er jede millisekunde (am besten) überläuft und dann das ganze in die ISR packen.

Gruß
Chris

Thomas$
10.01.2010, 22:28
eine aufbau möglich keit wäre
do
wenn taster=gedrückt dann gedrückt dauer+1
wenn nicht dann wenn gedrück dauer >10 dann variable+1
wenn gedrückt dauer>20 dann in eepromspeichern
dedrückt dauer=0
warte 100ms
müsste so gehen

loop

DerSchatten
11.01.2010, 22:39
Hm, wo ist der Fehler?



while(1)
{
PORTD = pgm_read_byte(&Tabelle[nKeyPress]);
if (bit_is_clear (PINB, PINB0))
{
if (bPortB) // wenn Taster gedrückt
{
dauer++;
}
else // wenn nicht gedrückt
{
if (dauer > 10)
{
if (nKeyPress < 8)
{
nKeyPress++;
bPortB = 0;
}
else
{
nKeyPress = 0;
bPortB = 0;
}
}
if (dauer > 20) // wenn länger gedrückt
{
eeprom_write_byte(&eeFooByte, nKeyPress);
dauer = 0;
Warte (100);
}
}
}
else
{
bPortB = 1;
}
}
return 0;

oberallgeier
12.01.2010, 10:36
Hm, wo ist der Fehler? ...Mit meinen bescheidenen Kenntnissen in C tue ich mir immer schwer, daher auch meine Unfähigkeit (und Abneigung), Codebruchstücke auf Fehler zu durchleuchten. Vor allem bei minimalistischen Kommentaranteilen.

Bei mir gibt es Tasten, die beim Drücken den zugehörigen Eingang - mit internem PullUp - einfach auf GND legen. Ich habe mir für die Tastenverwaltung eine Timerroutine geschrieben, die mit 100 Hz eine ISR aufruft. Darin wird u.a. dies gemacht:

// Globale Deklarationen und so:

#define rLED 3 // Rote LED auf PB3
#define TAU 1 // Taster "Aufwärts", PB1
#define TAB 2 // Taster "Abwärts", PB2 = "Start"Taste

volatile uint8_t TABsts; // Status derTaste TAB
// = 0 <=> nicht gedrückt, =1 <=> gedrückt
volatile uint8_t TAUsts; // Status derTaste TAU, wie oben
volatile uint8_t TABcnt; // Counter für Taste TAB
volatile uint8_t nTABcnt; // Counter für Taste notTAB
volatile uint8_t TAUcnt; // Counter für Taste TAU, wie oben
volatile uint8_t nTAUcnt; // Counter für Taste notTAU, wie oben
//
// ================================================== ==============================
// === Nicht unterbrechbare ISR für timer1 =======================================
// Interrupt mit ca. 100 Hz/10 ms. Diese ISR wird benutzt
// A zum Auslesen der Tasten
// Nach Ende der Routine sind die Tastenstati in den Statusbytes
// ...
//
ISR(TIM1_COMPA_vect) // Vektor 4 (1 ... 4 !!!) doc S 50
{
...
// - - Zuerst für TAB - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TABsts = (!(PINB & (1<<TAB))); // Status von Taste TAB holen

if (TABsts) // Taste TAB gedrückt?
{
if (TABcnt < 254) // Wenn counter < 250
{
TABcnt ++; // dann hochtackern
nTABcnt = 0; // und Nicht-TABcount auf Null setzen
}
}
else // Taste TAB ist nicht gedrückt
{
if (nTABcnt < 254) // Wenn Not-counter < 250
{
nTABcnt ++; // dann hochtackern
TABcnt = 0; // und TABcount auf Null setzen
}
}
// .... und so weiter
// ================================================== ==============================


// ================================================== ==============================
// === Initialisierung fuer Timer1 tiny85 ========================================
// === Interner Oszillator, 8 MHz mit CKDIV8 =================================
void TC1TMR_init(void) // Init Tmr/Cntr1, 8-Bit auf ca. 100 Hz = 10 ms
{
TCCR1 |= (1<<CTC1); // Timer im CTC-Mode, Top=OCRA doc S 92
TCCR1 |= (1<<CS12)|(1<<CS11)|(1<<CS10);
// Prescaler Clock1/64 doc S 93
OCR1C = 152; // Preset/Preload = 152 => getestet ca. 0,010s @ 1,0Mhz
TIMSK |= (1<<OCIE1A); // Tmr/Cntr1 CompareA interrupt enabled
}
// ================================================== ==============================
So - damit kann ich im main oder sonstwo bequem feststellen, ob und wie lange eine Taste gedrückt wurde - alles über 2,5 sec ist eben "ewig". Ausserdem ist damit auch feststellbar, wie lange die Taste nach dem letzten Drücken gelöst wurde - auch hier beginnt die Ewigkeit schon bei 2,5 sec. Läuft problemlos - und ist recht resistent gegen Spikes und so.


Hat vielleicht jemand ein einfacheres Beispiel für mich ...Ob das für Dich nun einfach(er) ist, weiß ich natürlich nicht.

DerSchatten
12.01.2010, 18:27
Hi, vielen dank!
Ich bin jedoch langsam am verzweifeln.
Ich blick hier nicht ganz durch.

So wie ich das verstanden habe machst du alles mit Interrupts, oder?
AVRStudio meckert bei mir fast bei jeder Zeile wenn ich den Code 1:1 reinkopiere.

Was macht dieses Teil hier:
ISR(TIM1_COMPA_vect)
Wo gehört da die zugemachte Klammer hin?

Bei TCCR1 meint er das die Variable nicht definiert ist.

Verzweifel!

oberallgeier
12.01.2010, 20:33
... Ich bin jedoch langsam am verzweifeln ... Ich blick hier nicht ganz durch ...Du machst einen beliebten Anfängerfehler: Du programmierst irgendwo zusammenkopierte Codeteile ohne jeden Peil, was da passiert - und wir sollns richten.

1. Du hast nirgendwo genannt, welchen Controller Du verwendest, Takt, etc.
2. Du hast möglicherweise ein älteres AVRStudio/AVRGCC. Ich denke dass als ISR-Aufruf die Form "ISR(TIM1_COMPA_vect)" üblich ist. Die Form "SIGNAL (SIG_OVERFLOW0)" ist doch schon etwas veraltet, oder nicht ? ?
3. Du hast nicht gemerkt, dass meine Codebeispiele für einen tiny85 steht - weil Du kopierst ohne zu lesen, was da steht.
4. Hast Du eine Dokumentation zu Deinem Controller (und GELESEN) ?
Gibt es in Deinem Controller denn ein Register namens TCCR1?
5. Dein Compiler stolpert spätestens über die Stelle, an der ich geschrieben hatte:
// .... und so weiter
denn mein Code ist ein BEISPIEL - kein vollständiges Programm für einen mir garnicht bekannten Controller.
6. Lern mal die Grundlagen der Programmierung von Mikrocontrollern in C - sonst wirst Du noch mehr verzweifeln. Zu Grundlagen der Programmierung von Mikrocontrollern gibts hier etliche Empfehlungen (klick). (https://www.roboternetz.de/phpBB2/zeigebeitrag.php?p=413294&sid=aafd8dbbcb2cf373be63c4a240127aa1#413294)

Und warum das alles lesen?
... AVRStudio meckert bei mir fast bei jeder Zeile wenn ich den Code 1:1 reinkopiere ...Damit Du weisst, was Du da gemacht hast.

Viel Erfolg.

DerSchatten
12.01.2010, 21:18
Hi,
nun so ganz unwissend bin ich ja nun auch nicht.
Hab schon einiges mit C herumexperimentiert.
Ich lerne eben am besten wenn ich mir anhand von Beispielen die Funktion klar mache.

Du hast ja dein Codebeispiel gut dokumentiert und da hast du auch vermerkt das dein Beispiel für den Tiny angepasst ist.

Aber das ist ja für AVR Studio beim compilieren nicht ausschlaggebend denke ich.

Ich verwende für meinen Aufbau einen ATMEGA 8 mit internen Takt. Für dein beispiel dann 8MHz.

oberallgeier
12.01.2010, 22:18
... dein Beispiel für den Tiny angepasst ist... das ist ja für AVR Studio beim compilieren nicht ausschlaggebend denke ich ...Hmmm, das denkst Du - daher auch Deine Verzweiflung. Denn der Compiler denkt vermutlich etwas anders.

Viel Erfolg, trotz allem.

DerSchatten
13.01.2010, 13:53
Hab das ganze jetzt nochmal umgestrickt und denke das ich am richtigen Weg bin:



//----------------------------------------
// Titel : Arcade Tastenprogrammierung
//----------------------------------------
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdint.h>
#include <avr/eeprom.h>
#include <avr/pgmspace.h>

typedef unsigned char BYTE;
typedef unsigned int WORD;

BYTE bPortB;
BYTE nKeyPress;

const unsigned char Tabelle[] PROGMEM = {249, 164, 176, 153, 146, 130, 248, 128, 144};

#define CNTDEBOUNCE 10
#define CNTREPEAT 200

#define KEY_PIN PINB
#define KEY_PINNO PB6

uint8_t eeFooByte;
volatile uint8_t gKeyCounter;


ISR(TIMER1_COMPA_vect)
{
uint8_t tmp_kc;

tmp_kc = gKeyCounter;

if (!(KEY_PIN & (1<<KEY_PINNO)))
{
if (tmp_kc < CNTREPEAT)
{
tmp_kc++;
}
}
else
{
tmp_kc = 0;
}
gKeyCounter = tmp_kc;
}

void init(void)
{
PORTB |= _BV(6); // Pull-Up Port B6 aktivieren
DDRB = 0xFF; // Port B als Eingang
DDRD = 0xFF; // Port D als Ausgang
TIMSK |= (1<<TOIE1); //Timer1 Interrupt aktiviert
TCCR1B = 1; //Prescaler 1
TCNT1 = 65535-3600; //Preloader 3600
}

int main (void)
{
init();
sei();

bPortB = 1;
nKeyPress = eeprom_read_byte(&eeFooByte);

while(1)
{


if ( gKeyCounter > CNTDEBOUNCE )
{
if (gKeyCounter == CNTREPEAT) // Code fuer "Taste lange gedrueckt"
{
eeprom_write_byte(&eeFooByte, nKeyPress);
}
else // Code fuer "Taste kurz gedrueckt"
{
PORTD = pgm_read_byte(&Tabelle[nKeyPress]);
if (nKeyPress < 8)
{
nKeyPress++;

}
else
{
nKeyPress = 0;

}
}
}

}
return 0;
}


Mit den initialisieren der Interrupts bin ich nur etwas überfragt. Wie macht man das richtig?
Wie gesagt ich verwende einen ATMEGA8 mit 8MHz Takt.