PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Problem bei Taster Abfrage mit ADC



MartinNRW86
10.09.2006, 10:18
Guten Morgen :)

schon seit einiger zeit bin in nun an einem kleinen Projekt von mir dran, komme aber leider nicht weiter.

Die Problemstellung ist, ich habe einen ADV mit Spannungswandler nach +5V, der gegen Masse geschaltet wird. Die Werte dazu habe ich berechnet und scheint auch ganz gut zu funktionieren. Habe es kurz getestet.
Nun sollen diese 3 möglichen Felder, verschiedene funktionen haben, 2 Felder wie ein Taster und eines soll unterscheiden zwischen kurz gedrückt und gedrückt gehalten. Nun bin ich auf eure Tutorial "Taster-Abfrage in C" gestoßen. Diese ist genau das was ich suche. Also habe ich es mit meiner AD-Wandler abfrage kombiniert. Nur leider funktioniert so garnichts :/

Könnte sich vieleicht bitte mal wer meinen Code ansehen und mir vieleicht tips geben ?


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

/* Wir haben 3 Taster, in taster.h wird also angepasst zu
#define NUM_TASTER 3
*/
#include "taster.h"

#define ADC_VREF_TYPE 0x00

// Read the AD conversion result
unsigned int read_adc(unsigned char adc_input)
{
ADMUX=adc_input|ADC_VREF_TYPE;
// Start the AD conversion
ADCSRA|=0x40;
// Wait for the AD conversion to complete
while ((ADCSRA & 0x10)==0);
ADCSRA|=0x10;
return ADCW;
}


/* Bei 1MHz Grundtakt läuft Timer0 alle 256µs über.
* Um auf rund 10ms zu kommen, rufen wir get_taster nur
* jedes 39. mal auf. */
SIGNAL (SIG_OVERFLOW0)
{
static unsigned char count_ovl0;
unsigned char ovl0 = count_ovl0+1;

if (ovl0 >= 39)
{

unsigned int result = read_adc(0);

get_taster (0, ((result >= 700) && (result <= 790)) ? 1:0);

get_taster (2, ((result >= 800) && (result <= 880)) ? 1:0);

get_taster (1, ((result >= 890) && (result <= 940)) ? 1:0);

ovl0 = 0;
}

count_ovl0 = ovl0;
}

void ioinit()
{
/* Timer0 ohne Prescaler starten */
TCCR0 = 1 << CS00;


/* Timer0-Overflow-Interrupt aktivieren */
TIMSK |= (1 << TOIE0);
}

int main()
{
ioinit();

// ADC initialization
// ADC Clock frequency: 500,000 kHz
// ADC Voltage Reference: AREF pin
ADMUX=ADC_VREF_TYPE;
ADCSRA=0x81;


/* Taster konfigurieren (#define NUM_TASTER 3 in taster.h) */
tasten[0].mode = TM_SHORT;
tasten[1].mode = TM_LONG;
tasten[2].mode = TM_SHORT;

/* Interrupts global aktivieren */
sei();

// Input/Output Ports initialization
// Port B initialization
// Func7=In Func6=Out Func5=In Func4=In Func3=In Func2=Out Func1=Out Func0=Out
// State7=T State6=0 State5=T State4=T State3=T State2=0 State1=0 State0=0
PORTB=0x00;
DDRB=0x47;

/* Hauptschleife */
while (1)
{
signed char tast = taster;

switch (tast)
{
default:
case NO_TASTER:
PORTB &= ~((1<< PINB6) | (1 << PINB2) | (1 << PINB1) | (1<< PINB0));
break;
case 0:
/* Taster 0 */
PORTB = (1 << PINB0); // PB0 amschalten
break;

case 1:
/* Taster 1 kurz gedrueckt */
PORTB = (1 << PINB2); // PB2 amschalten
break;

case 1+TASTER_LONG:
/* Taster 1 lange gedrueckt */
// PORTB = (1 << PINB6);
break;

case 2:
/* Taster 2 */
PORTB = (1 << PINB1); // PB1 amschalten
break;
}

if (tast != NO_TASTER)
taster = NO_TASTER;

/* ********************************** */
/* Weiterer Code in der Hauptschleife */
}
}

taster.h:
#ifndef _TASTER_H_
#define _TASTER_H_

/* Wert fuer taster, wenn nichts gedrückt wurde */
#define NO_TASTER (-1)

/* Maximale Anzahl der Taster */
#define NUM_TASTER 3

/* 0 --> Taster sind low-aktiv */
/* 1 --> Taster sind high-aktiv */
#define TASTER_LEVEL 1

/* Dieser Offset wird zur Tasten-Nummer addiert, */
/* wenn eine Taste lange gedrückt wurde */
#define TASTER_LONG 16

/* Zeitverzögerung (in Ticks), bis zum Beginn von auto-repeat */
#define TASTER_REPEAT_DELAY (60)

/* Zeitverzögerung (in Ticks), bis zum nächsten auto-repeat */
#define TASTER_REPEAT (15)

/* Ab dieser Dauer wird der Tastendruck 'lange' */
#define TASTER_DELAY_LONG (80)

typedef struct
{
/* private */
const unsigned char delay, old;

/* Mode des Tasters aus: TM_SHORT, TM_LONG, TM_REPEAT */
unsigned char mode;
} taste_t;

extern taste_t tasten[];

/* In dieser Variable kann abgefragt werden, welche Taste gedrückt wurde.
--> NO_TASTER:
es wurde nichts gedrückt
--> 0..NUM_TASTER-1:
Taster Numero 'taster' wurde (kurz) gedrückt
--> TASTER_LONG ... TASTER_LONG + NUM_TASTER-1:
Taster Numero 'taster-TASTER_LONG' wurde lange gedrückt
*/
extern volatile signed char taster;

extern void get_taster (const unsigned char num, unsigned char tast);

enum
{
TM_SHORT,
TM_LONG,
TM_REPEAT
};

#endif /* _TASTER_H_ */


Wäre sehr nett danke schonmal :)

Ach was ich vergessen habe, Ich benutze einen ATMega8 und an den Ausgängen PB0,PB1,PB2,PB6 hängt ein Relais Treiber mit 4 Relais.

SprinterSB
12.09.2006, 08:03
Funktioniert es denn, wenn du normale digitale I/O-Ports nimmst anstatt ADC-Inputs?

Was mir auffllt ist daß du in der Tioer0-ISR ne Warteschleife auf den ADC hast (in read_adc), und zwar sogar 3 mal. Das ist nicht so toll. Wenn das dauert bekommst du die nächste Timer0-IRQ schon, wenn du noch in der Timer0-ISR bist. Dann geht kaum mehr was.

Besser wäre den ADC im free runnong mode zu betreiben und in einer ADC-ISR die Werte zu merken und den Port ein weiter zu schalten.

Nochwas zu Code: Lass die magischen Zahlen wie 0x40 weg, es ist klarer, wenn du sie Bit-Namen verwendes -- nicht nur für andere, auch für dich.

MartinNRW86
12.09.2006, 21:38
mit i/o ports läuft es.

was meinst du mit free runnong mode ? Fange grade erst an mit µC programmierung, und das ist mein erstes Projekt.

Meinst du das ich einen 2. Timer nehmen soll und dann einfach eine globale variable dazu ?

Die magischen Zahlen habe ich nur drin weil ich zu faul war, habe die mit dem CodeVisionAVR generiert :P

SprinterSB
13.09.2006, 12:22
In Free Running mode, the ADC is constantly sampling and updating the ADC Data Register. Free Running mode is selected by writing the ADFR bit in ADCSRA to one. The first conversion must be started by writing a logical one to the ADSC bit in ADCSRA. In this mode the ADC will perform successive conversions independently of whether the ADC Interrupt Flag, ADIF is cleared or not.

Aber das macht wohl nicht ganz das, was du willst... :-k

Besser ist wohl single shot. in main (Prinzip):
ADC-Port wählen
Konvertierung starten (single shot)
Wert wegwerfen
ADC-IRQ-Flag resetten
ADC-IRQ erlauben
sei()
Konvertierung starten (single shot)
Hauptschleife


In der ADC-ISR:
ADC-Wert sichern
ADC-Port wählen
ADC starten (single shot)


In deinem Fall hast du 3 ADC-Werte, die zu sichern sind.

::EDIT::

Nochwas: Der ADC sollte nich über 200kHz getaktet sein.

MartinNRW86
20.09.2006, 14:37
Ich denke du meinst das Beispiel was auch beim Roboternetz im Artikel bereich ist oder ?



uint16_t readADC(uint8_t channel) {
uint8_t i;
uint16_t result = 0;

// Den ADC aktivieren und Teilungsfaktor auf 64 stellen
ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1);

// Kanal des Multiplexers waehlen
ADMUX = channel;
// Interne Referenzspannung verwenden (also 2,56 V)
ADMUX |= (1<<REFS1) | (1<<REFS0);

// Den ADC initialisieren und einen sog. Dummyreadout machen
ADCSRA |= (1<<ADSC);
while(ADCSRA & (1<<ADSC));

// Jetzt 3x die analoge Spannung and Kanal channel auslesen
// und dann Durchschnittswert ausrechnen.
for(i=0; i<3; i++) {
// Eine Wandlung
ADCSRA |= (1<<ADSC);
// Auf Ergebnis warten...
while(ADCSRA & (1<<ADSC));

result += ADCW;
}

// ADC wieder deaktivieren
ADCSRA &= ~(1<<ADEN);

result /= 3;

return result;
}




habs mal eins zu eins kopiert, habe aber einen angepassten source (für die Parameter) noch da, nur auf dem anderen PC :P

tut mir leid das ich erst so spät wieder antworte, war leider eine Zeitlang krank im bett :(