PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Einfacher ADC-interrupt



RumpelHumpel
27.02.2013, 16:01
Hi zusammen,

ich habe ein wahrscheinlich relativ einfaches Problem, aber komme leider nicht weiter.
Ich verwende einen ATmega32L.
Am PINA7 ist ein einfacher Knopf angebracht, der beim Drücken einen Interrupt auslösen soll. Ich habe dann ins Datenblatt des ATmega32L geguckt und da steht neben dem PINA7 (ADC7).
Wie gesagt, ich weiß leider echt nicht viel von Interrupts, habe mir dann einige Tutorials angeguckt und da bisschen was abgeguckt:



int main()
{

DDRA &= ~((1<<PA7)); // Pin PA7 auf Eingang setzen, da hängt der Botton dran
ADCSRA = 0x8F; // ADC und Interrupts aktivieren
ADMUX |= ((1<<MUX0)|(1<<MUX1)|(1<<MUX2)); // Auf ADC7 setzen
sei();
ADCSRA |= 1<<ADSC; //starten


while(1){}
}


ISR(ADC_vect){
GREEN_LED_ON(); // Macro, schaltet LED für 1 sek an, dann wieder aus
}


Er springt beim Starten des Programms rein, aber danach nicht wieder ( also reagiert allgemein nicht auf den Button).
Kann mir da wer helfen?


Vielen Dank und sry, dass ich eine wahrscheinlich simple Frage habe.

HeXPloreR
27.02.2013, 16:38
Hallo,

und das ist der ganze Code?

Ich bin der Meinung Du beschreibst gut was Du machen willst, aber das Programm sieht etwas merkwürdig aus. Ich gebe zu ich habe noch nicht viel in C programmiert...aber ich denke das man nicht über einen ADC-Eingang gleichzeitig auch einen Interrupt erhalten kann. Was man bekommen könnte bzw daraus machen könnte wäre eine Schaltschwelle...

Einen Pin auf Ausgang zu setzen der als Eingang benutzt werden soll (- da hängt der Button dran), macht ziemlich wenig Sinn.
Prüfe auch mal ob "ADCSRA |= 1<<ADSC; " so richtig wäre. Hier vermute ich zumindest einen dreher...
Mit xxMUX stellt man soweit ich weiß die Widerstände intern ein...was 0, 1, 2 genau mit ADC7 zu tun hat würde ich gerne wissen?

Ich fürchte für Dich ist es nicht getan ein paar Tutorials anzusehen und schon läuft die Sache. C ist etwas komplizierter in solchen Dingen asl z.B. Bascom.

RumpelHumpel
27.02.2013, 16:53
Sry, war falsch kommentiert. Sollte eigentlich "Pin auf Eingang setzen" heißen

HeXPloreR
27.02.2013, 17:19
jo, das mit dem ADMUX scheint auf jedenfall schon mal irgendwie dazu zu gehören, und ist so auf "7" eingestellt ;) - mein Fehler, irgendwie mit irgendwas verwechselt.

oberallgeier
27.02.2013, 17:20
... springt beim Starten des Programms rein, aber danach nicht wieder ...Woher weißt Du, dass die ISR nicht wieder angesprungen wird? Ich nehme in solchen Fällen in der ISR meist ein zusätzliches Statement, mit dem ich nen Ausgang (üblicherweise ne LED) toggle. Damit kann man etwas sehen oder bei schnellen Zyklen evtl. auf dem Oskar verfolgen.


Code: ... wait{} ... ISR { ... GREEN_LED_ON;} ...Setzen wir mal voraus, dass das GREEN_LED_ON ein Makro ist, das auch den Ausgang so setzt, dass die LED angeht. Und wo bitte geht die wieder aus?!?!? Ich seh nur die leere wait{}. Dein Codefenster enthält 1. nicht den ganzen Code - siehe das fehlende Makro, 2. eine ISR mit einer einzigen Funktion und 3. eine leere wait-Schleife die keine Änderung des einmal hergestellten Zustandes bewirkt.

Heilung evtl. durch ein Statement in der while-Schleife: delay_ms ( 1000); LED_OFF; delay_ms ( 1000); ... Das delay evtl. als die übliche Compilerroutine oder eine eigene Konstruktion - oder irgendeine Bremsschleife . . .

Viel Erfolg

Besserwessi
27.02.2013, 18:51
Der ADC wird für eine Wandlung gestartet, entsprechend wird auch nur eine Wandlung fertig und löst einen Interrupts aus.
Der ADC kann nicht dazu genutzt werden um direkt bei einer Änderung am Eingang einen Interrupt auszulösen. Das ginge mit dem analogen Komparator, und über den MUX vom ADC könnte man hier auch den PIN PA7 als Eingng wählen.

RumpelHumpel
28.02.2013, 07:06
Das GREEN_LED_ON schaltet eine grüne LED für 1 Sek an, dann wieder aus, sry, habe ich nicht erklärt.
Ok, danke für die Info. Ich dachte, wenn ich ADMUX vom ADC auf PIN PA7 stelle merkt der, wenn da eine Änderung passiert. Dann werde ich mir den Komparator mal angucken.

oberallgeier
28.02.2013, 07:59
... GREEN_LED_ON schaltet eine grüne LED für 1 Sek an, dann wieder aus ...Es macht herzlich wenig Sinn, wenn man nur den halben Code einstellt und dann Zeile für Zeile nachbessert. Es macht nur die Lösungssuche ziemlich diffuser. Vor Allem wenn der geheimgehaltene Codeteil ("... LED für 1 Sek ...") möglicherweise den Kern des Problems in sich trägt.


... dann wieder aus, sry, habe ich nicht erklärt ...Wurde ja schon von HeXPloreR angemerkt, dass Du recht wenig Code bereitgestellt hast, vermutlich um unsere Ratemöglichkeiten zu vervielfachen.

Ich habe nen free running ADC z.B. für die Spannungscontrolle meiner Energieversorgung. Hier mal eine 1:1-Kopie des Codes für den m1284 - vielleicht hilft Dir das als Leitlinie für Deine Lösung. Übrigens siehst Du in der ISR auch zwei Zeilen für LED-Tests (wobei die Zeitmessung NICHT der tatsächliche Zeitbedarf der ISR im Target ist ! ! ! weil dabei der Overhead nicht erfasst wird). Den switch-Abschnitt kannst Du Dir ersatzlos sparen. Und nur als Warnung: keine Garantie für die Funktion - auch wenns bei mir bestens läuft - und abschreiben und experimentell weiterentwickeln hilft meist auch nicht.


// ================================================== ============================ =
// === Initialisierung fuer ADC mega1284 MIT Interrupt free running
// === ADC5/PA5 auf 10 Bit, fertige Wandlung ###>>> löst Interrupt aus
void ADC_init_10_irupt(void) // Initialisiere ADC, Kanal5, 10 Bit, MIT Interrupt
{ //
if (adcptnr > 7) adcptnr = 5; // Defaultwert adcpnr
// - - - - - - - - - - - - - - -
switch (adcptnr) // Initialisiere je nach ADC Pin Nummer
{ //
case 0: case 1: case 2: case 3: case 4: case 6: //
break; //
case 5: //
adc5_cnt = 0; // ADC-Wert wird x-fach aufaddiert
adc5tcnt = 1; //
adc5_tmp = 0; // ADC-x-fach-Speicher
ADC5bMIN = 1023; // bisheriges Minimum setzen
ADMUX |= (1<<MUX0)|(1<<MUX2); // Wandlung mit ADC5 (mega1284) doc S 259
uputs0 ("\r\tInitialisierung ADC auf ADC5/PA5=Poti\r"); //
break; //
default: //
uputs0 ("\r\tInitialisierung ADC fehlgeschlagen\r"); //
break; //
} //
// - - - - - - - - - - - - - - -
ADMUX |= (1<<REFS0); // Referenzspannung ist AVcc S 258
ADCSRA |= (1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0); // Prescaler = clock/128 S 261
// Prescaler NUR für system clck
ADCSRA |= (1<<ADATE); // Auto Triggering Enable S 245 + 260
// es wird bei clock 20 Mhz mit ca. 80 µs getriggert
ADCSRA |= (1<<ADIE); // ADC Interrupt Enable doc S 260
ADCSRA |= (1<<ADEN); // ADC Enable
ADCSRA |= (1<<ADSC); // starte gleich die erste Wandlung
} //
// ================================================== ============================ =

// ================================================== ============================ =
// === Nicht unterbrechbare ISR für ADC
// Routine übernimmt ADC-Wert und sichert Minimum
ISR(ADC_vect) // VECTOR 25, Adr $0030
{ //
//ClrBit(PORTC, 7); // LED C7 ein, Zeitmessung: LED/PIN PC7
adc5_tmp = ADC; // Hole Wert
adc_status = 170 + adcptnr; // Setze Flag "ADC-Wert abgeholt
if (adcptnr == 5) //
{if (ADC5bMIN > adc5_tmp) ADC5bMIN = adc5_tmp;} // Sichere bisheriges Minimum
//SetBit(PORTC, 7); // LED C7 aus, Zeitmessung: LED/PIN PC7
} //
// ================================================== ============================ =

Und damit Du mir nicht ne Geheimhaltung von Makros vorhalten kannst:

#define SetBit(ADDR,BIT) ((ADDR) |= (1<<(BIT))) // Setzt Bit
#define ClrBit(ADDR,BIT) ((ADDR) &= ~(1<<(BIT))) // Löscht Bit
#define ToggleBit(ADDR,BIT) ((ADDR) ^= (1<<(BIT))) // Toogelt Bit

RumpelHumpel
28.02.2013, 12:39
#define GREEN_LED_on PORTA |= (1<<PA3)
#define GREEN_LED_off PORTA &= ~(1<<PA3)




int main()
{

DDRA &= ~((1<<PA7)); // Pin PA7 auf Eingang setzen, da hängt der Botton dran
ADCSRA = 0x8F; // ADC und Interrupts aktivieren
ADMUX |= ((1<<MUX0)|(1<<MUX1)|(1<<MUX2)); // Auf ADC7 setzen
sei();
ADCSRA |= 1<<ADSC; //starten


while(1){}
}


ISR(ADC_vect){
GREEN_LED_ON(); // Macro, schaltet LED für 1 sek an, dann wieder aus
}





void GREEN_LED_ON(void)
{
GREEN_LED_on;
_delay_ms(1000);
GREEN_LED_off;
}



Dein Beispiel würde ja einen Interrupt auslösen, wenn der ADC fertig mit der Arbeit ist. Ich bräuchte aber irgendwie eine Möglichkeit, dass der ADC ständig guckt, ob sich was ändert und im Falle einer Änderung einen Interrupt wirft. Also so ne Art externer Interrupt bei Benutzereingabe. Ist sowas möglich? Also gibts es ne Möglichkeit, den ADC dauerhaft laufen zu lassen und nur bei einer Änderung einen Interrupt zu generieren?

ich habe dann mal auf den Tip von weiter oben gehört und mir den Komparator angeguckt. Der sollte ja 2 Eingänge überprüfen können und mir dann sagen, ob sich was geändert hat. Führt man den auch nur einmal aus,sprich gibt es nur eine Überprüfung oder wird der dauerhaft betrieben?

Searcher
28.02.2013, 14:30
Ich bräuchte aber irgendwie eine Möglichkeit, dass der ADC ständig guckt, ob sich was ändert und im Falle einer Änderung einen Interrupt wirft. Also so ne Art externer Interrupt bei Benutzereingabe. Ist sowas möglich? Also gibts es ne Möglichkeit, den ADC dauerhaft laufen zu lassen und nur bei einer Änderung einen Interrupt zu generieren?

Geht mit dem ADC nicht.


Der sollte ja 2 Eingänge überprüfen können und mir dann sagen, ob sich was geändert hat. Führt man den auch nur einmal aus,sprich gibt es nur eine Überprüfung oder wird der dauerhaft betrieben?

Der Analog Comparator wird dauerhaft betrieben.

Warum nimmst du nicht INT0,1 oder 2? Bist du da auf den PA7 festgenagelt?

Welche Spannungsänderungen verursacht denn der Button?

Gruß
Searcher

RumpelHumpel
28.02.2013, 15:25
Ich wurde leider drauf festgenagelt und muss den PA7 benutzen. Wenn er gedrückt wird, liegt keine Spannung mehr an.


#define GREEN_LED_on PORTA |= (1<<PA3)
#define GREEN_LED_off PORTA &= ~(1<<PA3)

int main()
{

DDRA &= ~(1<<PA7); //PA7 als Eingang

cli(); // Interrupt deaktivieren

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

ACSR |= (1<<ACIE); // Enable analog comparator interrupt

ADMUX |= ((1<<MUX1)|(1<<MUX2)|(1<<MUX0)); // Multiplexer auf ADC7 setzen

ACSR |= (1<<ACIS1); // Interrupt soll bei steigender Flanke auslösen
ACSR |= (1<<ACIS0);

ACSR |= (1<<ACBG); // Auf Referenzspannung 2.56V setzen
sei(); // Interrupt aktivieren


while(1){}
}

// Grüne LED für 1 Sek leuchten lassen
ISR(ANA_COMP_vect){
GREEN_LED_on;
_delay_ms(1000);
GREEN_LED_off;
}


Das habe ich soweit rausgesucht, um mittels Comparator vergleichen zu können. Nach meinem Verständnis nach vergleicht er jetzt die Referenzspannung 2.56V mit der Spannung am PA7 ( ADC7) und sollte bei einer steigenden Flanke (Button gedrückt) einen Interrupt generieren und die grüne LED für 1 sek leuchten.

Searcher
28.02.2013, 15:49
Ich glaub da fehlt noch das ACME Bit in SFIOR um den MUX für den Analog Comparator zu aktivieren.

Gruß
Searcher

RumpelHumpel
28.02.2013, 16:06
:D Endlich vielen vielen Dank, genau das wars!

Searcher
28.02.2013, 16:12
Wenn du Probleme mit prellenden Kontakten hast, kannst du vor Ende der ISR noch das ACI in ACSR vorsorglich durch Schreiben einer 1 löschen. (Wenn der Button prellt, was er sehr wahrscheinlich macht, könnte nach Einsprung in die ISR das Interruptflag sofort wieder gesetzt werden und steht bei Rücksprung aus der ISR wieder neu an. Effekt: ISR wird sofort wieder ein zweites Mal ausgeführt).

Besserwessi
28.02.2013, 16:48
Das Löschen des Interrupt-falgs wäre einer erster Versuch gegen Prellen, aber bei einer kurzen ISR wird das Vermutlich nicht wirklich helfen. Die konsequente Lösung um mehrere Interrupts durch eine Flanke zu vermeiden wäre eine Verzögerung per Timer: als in der Komparator ISR einen Timer so programmieren das nach etwa 1 ms ein Interrupt kommt, und dann erst einmal den Komparator Interrupt ausschalten. In der Timer ISR wird dann der Komparator wieder scharf gemacht, und dafür der Timer deaktiviert.

RumpelHumpel
01.03.2013, 10:29
Danke für die Hinweise, werde ich einbauen.

Ist es auch möglich, mit dem Komparator mehrere Eingänge zu übermachen z.B: ABC5-ABC7 und eine "pin-spezifischen" Interrupt zu generieren, wenn einer dieser Pins seinen Zustand ändert? Also das selbe, wie mein Codebeispiel weiter oben nur mit mehreren Eingängen?

Besserwessi
01.03.2013, 11:32
Der Komparator kann immer nur einen Pin zur Zeit überwachen. Je nach µC gibt es einen PIN Change Interrut, der einen ganzen Port oder ggf. auch einen Teil davon überwachen kann, und dann einen Interrupt auslöst wenn sich einer Pins als Digitaler Eingang ändert. Welcher Pin dann der Auslöser war muss man in Software in der ISR bestimmen.