PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [ERLEDIGT] ATmega8 ext. Interrupt



Liquidator
08.05.2015, 10:43
Hallo liebe Gemeinde,

es handelt sich um ein Problem bei der Tasterabfrage per Interrupt. Ich möchte einen Tasterzustand auf INT1 abfragen und auf low level triggern.
Hier der grundsätzliche Programmaufbau, mal eben eingetippt:



ISR(INT1_vect) {...}

int main (void)
{
DDRD = 0xF7; // PD3 als Eingang definiert
PORTD |= (1<<PD3); // Pullups aktiviert
...
MCUCR &= ~((1<<ISC11)|(1<<ISC10); // auf low-level Zustand getriggert
GICR |= (1<<INT1); // INT1 aktiviert

sei();

while(1) {...}
...
}


Nach meinem Verständnis sollte doch, solange der low level- Zustand am Taster herrscht (und der herrscht immer, wenn der Taster nicht gedrückt ist) ständig der INterrupt ausgelöst werden und somit nur die ISR ausgeführt werden?
Der Taster ist per Tiefpass entprellt und wechselt von HIGH auf LOW und umgekehrt in 500ns.

Der eigentlich Fehler ist, dass der ext. Interrupt nie durchgeführt wird und ich nicht weiß, woran es noch liegen könnte.

Ich verwende neben der Funktion auch die beiden Timer, deshalb dachte ich erstmal, dass es an dem im Kap."Errata" aufgeführten bug liegt...


Grüße,
Nik

sast
08.05.2015, 13:52
Woher weißt du ob der Interrupt auslöst? Wenn der Taster im nicht geschalteten Zustand auf Low liegt, muss es ein Öffner sein, der an Masse angeschlossen ist. Ansonsten hast du vielleicht ein Verständnisproblem. Taster ist geöffnet bedeutet durch den Pullup ja, dass der Eingang PD3 auf High liegt und nur wenn du über einen gegen Masse schaltenden Taster den Eingang auf Low ziehst, kommt es zu einer Änderung des Eingangspegels. Hast du eventuell den Taster gegen +UB verschaltet? Dann kannst du nämlich lange drücken bzw nicht drücken. Am Eingang wäre dann immer High Pegel.

sast

Liquidator
08.05.2015, 14:18
Danke für die Antwort sast,

ich habe beide Tastervarianten ausprobiert (gegen Masse, gegen 5V) und der vorliegende Code war für die Variante für einen auf GND schaltenden Taster. Dass der Interrupt nicht ausgeführt wird, erkenne ich daran, dass die testweise in ISR(INT1_vect) eingebaute Verzögerung delay_ms(10000) nicht ausgeführt wird und das in main() befindende Programm abläuft.

Ich habe das Bild am Mikrocontroller-Eingang oszillographiert und lade das mal mit hoch.

30137

021aet04
08.05.2015, 14:52
Hast du schon versucht den INT umzustellen (auf z.b. Falling)?

MfG Hannes

Liquidator
10.05.2015, 00:37
Hallo Hannes,

funktionieren tut leider keins davon. Morgen werde ich mal einzelne Prozeduren ausschalten und feststellen, mit welchem Prozess der externe Interrupt in Konflikt kommt. Was ich allerdings nicht verstehe ist: Der INT0/1 hat doch (außer RESET) die höchste Priorität laut der Interruptvektorliste im Datenblatt. Also müsste sich doch eigentlich genau dieser Interrupt durchsetzen?

Grüße,
Nik

oberallgeier
10.05.2015, 09:01
... funktionieren tut leider keins ... einzelne Prozeduren ausschalten und feststellen ... externe Interrupt in Konflikt kommt ...

Hallo Nik, vor Jahren hatte ich mal unglaublich zähe Probleme mit Interrupts. Abhilfe schaffte ich mit einem kompletten Satz ISRs - für jeden Vektor nach dem Muster
// === Nicht unterbrechbare ISR für Vektor "EINERNACHDEMANDERN"
ISR(EINERNACHDEMANDERN) // Vektor ij,
{ //
return; //
} // Ende ISR(EINERNACHDEMANDERN)
// ================================================== =========================== =

Wenn ich mich richtig erinnere, war das schon mal die halbe Miete. Dann wurden die ISR der Reihe nach mit einem Toggel-Bit zu einer LED versehen. Damit (und mit nem Oszilloskop) war der Schuldige schnell identifizierbar.


... nicht verstehe ist: Der INT0/1 ... höchste Priorität ... Also müsste sich doch ... genau dieser Interrupt durchsetzen ...Jein. Im Prinzip und theoretisch hast Du völlig Recht. Aber höchste Priorität heißt nicht, dass er - ohne besondere Softwareverfahren - sonstige InterruptServiceRoutinen unterbrechen kann. Das ist per default unterbunden, kann aber durch eine Interruptfreigabe innerhalb einer ISR ermöglicht werden; Ergebnis sind dann sogenannte nested Interrupts. Nested Interrupts sind aber oft ne äusserst hinterhältige Erfindung (ich hab einen mit bester Funktion am Laufen). Langer Rede kurzer Sinn: Wenn eine ISR gerade läuft - ohne dem nested-Trick - hat selbst eine höher priorisierter Interrupt keine Aktien zu einer Unterbrechung.

Und um die lange Post noch länger zu machen: üblicherweise habe ich an meinem Codeanfang, direkt nach den Portdefinitionen und noch vor jeglicher anderen Aktion eine Routine um "irgendwelche" Interrupts bzw. Abstürze zu erkennen:
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
for(i=0; i< 15; i++) // gLED2(PB5) blinken lassen bevor Interrupts erlaubt sind
{ // um ungewollte Resets u.ä. besser erkennen zu können
// 25 Schleifen um die Slaves SICHER hochfahren zu lassen
// Ab Version x60: 15 Schleifen um die Slaves SICHER hochfahren zu lassen
ClrBit(PORTC, LCg); // LED auf PC6 schalten EIN, HELL
waitms(3); // ###>>> Die onBoard-LEDs schalten K<->Portpin <<<###
SetBit(PORTC, LCg); // LED auf PC6 schalten AUS, Dunkel
waitms(97); //
ClrBit(PORTC, LCr); // LED auf PC5 schalten EIN, HELL
waitms(3); // ###>>> Die onBoard-LEDs schalten K<->Portpin <<<###
SetBit(PORTC, LCr); // LED auf PC5 schalten AUS, Dunkel
waitms(97); //
} // Ende von for(i=0; i<

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -... und diese ominöse Wait-Routine ist KEINE Bibliotheksroutine sondern ein selbst geschriebenes Ding (damit ich genau weiß, was da geschieht). Diese Routine ist aber KEIN präzises Uhrwerk!
// ================================================== =========================== =
// ================================================== =========================== =
//### Programm pausieren lassen !! Der Pausenwert ist nur experimentell !
void waitms(uint16_t ms) // Mit #define auch Aufruf wms ( ms );
{ //
for(; ms>0; ms--)
{
uint16_t __c = 4000;
__asm__ volatile (
"1: sbiw %0,1" "\n\t"
"brne 1b"
: "=w" (__c)
: "0" (__c)
);
} //
} // Ende void waitms(uint16_t ms)
// ================================================== =========================== =

sast
11.05.2015, 08:21
Ohne deinen konkreten Code zu sehen läßt sich da jetzt schwer was sagen. Aber ich finde 10s in einer Interruptroutine schon echt heftig.

sast

021aet04
11.05.2015, 09:28
Das war auch nur zum Testen. Beim endprogramm sollte man delays in den Interrupt vermeiden.

MfG Hannes

Liquidator
11.05.2015, 16:27
Hallo und vielen Dank für die Unterstützung,

@oberallgeier: Das ist echt 'ne gute Idee für jedes Mikrocontroller-implementiertes Projekt so eine Testroutine zu machen, spart viel Arbeit bei der Fehlersuche. Diese Idee werde ich mir mal leihen ;)

Der Code ist etwas größer, die Timer 0,1,2 inkl. der Interrrupts beim Überlauf bzw. Erreichen des Vergleichswertes werden verwendet sowie der I²C-Bus. Ich hätte nicht gedacht, dass das Ansteuern eines externen Interrupts mir diese Schmerzen bereiten wird.



#include .....

volatile uint8_t taster = 0;

ISR(INT1_vect)
{
taster = 1;
}

...

int main(void)
{
MCUCR |=(1<<ISC11)|(1<<ISC10); // auf steigende Flanke triggern
GICR |= (1<<INT1); // ext. Interrupt freigeben

...

sei();

while(1)
{
while( taster ==1)
{
//auszuführender Code

taster = 0;
}

// Rest des Codes, falls der Taster nicht 1 ist.
}

return 0;

}




Ich befürchte, mittlerweile sehe ich den Wald vor lauter Bäumen nicht. Bei fallender und steigender Flankentriggerung funktioniert es nun - jedoch nur genau ein Mal. Ist auch zeitlich unabhängig, wann ich diesen Taster drücke.

021aet04
11.05.2015, 19:09
Hast du schon simuliert oder nur am Controller getestet?

Ich habe dein erstes Programm mit einem mega88 getestet, ohne Probleme. Habe nur die Register geändert. Ich weiß wirklich nicht woran es liegt.

MfG Hannes

Wsk8
11.05.2015, 19:33
Hallo liebe Gemeinde,

es handelt sich um ein Problem bei der Tasterabfrage per Interrupt. Ich möchte einen Tasterzustand auf INT1 abfragen und auf low level triggern.
Hier der grundsätzliche Programmaufbau, mal eben eingetippt:



ISR(INT1_vect) {...}

int main (void)
{
DDRD = 0xF7; // PD3 als Eingang definiert
PORTD |= (1<<PD3); // Pullups aktiviert
...
MCUCR &= ~((1<<ISC11)|(1<<ISC10); // auf low-level Zustand getriggert
GICR |= (1<<INT1); // INT1 aktiviert

sei();

while(1) {...}
...
}


Nach meinem Verständnis sollte doch, solange der low level- Zustand am Taster herrscht (und der herrscht immer, wenn der Taster nicht gedrückt ist) ständig der INterrupt ausgelöst werden und somit nur die ISR ausgeführt werden?
Der Taster ist per Tiefpass entprellt und wechselt von HIGH auf LOW und umgekehrt in 500ns.

Der eigentlich Fehler ist, dass der ext. Interrupt nie durchgeführt wird und ich nicht weiß, woran es noch liegen könnte.

Ich verwende neben der Funktion auch die beiden Timer, deshalb dachte ich erstmal, dass es an dem im Kap."Errata" aufgeführten bug liegt...


Grüße,
Nik
So, wie wärs wenn du einfach mal den ganzen anderen Rest von deinem Programm auskommentierst, so dass da nur noch genau das da steht. In der ISR toggelst du dann einfach einen PIN und hängst diesen ans Oszi.
Und wenn dieses ganz einfach Programm dann mal läuft, sehen wir weiter.

mfg

derNeue
11.05.2015, 20:23
Ich werf auch einfach mal Tasten-Prellen in den Raum. Einen Taster an einem externen Interrupt macht man eig nicht. Wenn du eh mehrere Timer schon in Verwendung hast, nutze doch irgend einen, der so etwa alle 10ms einen Interrupt auslöst. Und in diesem fragst du den Pin ab. Wenn er beim zweiten Interrupt immer noch gedrückt ist, hast du deinen Tastendruck. So ist die Vorgehensweise, wie ich sie kenne und auch erfolgreich nutze.

Dennis

Liquidator
24.05.2015, 19:34
Hallo liebe Gemeinde,

als Erstes möchte ich mich bei oberallgeier bedanken für den tollen Einfall mit der Interruptkontroll-LEDs, ist bereits in einem anderen Projekt untergebracht worden und wird auch fortan auf Prototypen benutzt :)
Allen anderen ebenfalls vielen Dank. Und nun meine Geschichte im Zeitraum "letzter Beitrag - #define Zeit_aktuell dd:mm:yyyy"

Ich habe angefangen den kompletten Projektcode zu optimieren, vorerst ohne die Tasterfunktionen. Der Grundgedanke war in Richtung RTOS zu gehen, bzw. wenigstens einen Scheduler zu implementieren. D.h. der ganze Code wurde hinsichtlich einer quasiparallelen Signal- und Datenverarbeitung optimiert. Es wurden absolut alle delay_ms()-Funktionen entfernt und durch Abfragen des Systemtaktes eines der Timer synchronisiert. Alle großen Tasks wurden aufgeteilt und in einzelnen Funktionsprozeduren untergebracht.
Nachdem der Code geschrumpft war und tatsächlich schneller funktionierte, versuchte ich mich weiterhin an meinen Lieblingstaster. Egal ob


ISR(INT1_vect) {...}

oder


uint8_t capt_button(void) {

if (PIND & (1<<PD3))
return 1;
else
return 0;
}

verwendet wurde, der Taster konnte AN, jedoch nicht mehr ausgeschaltet werden.
Und DANN kam die Erleuchtung! Der von mir verwendete gegen +5V angeschlossene Taster durchlief einen passiven Tiefpass. Dadurch, dass der sich dort befindende Kondensator durch +5V aufgeladen wurde, jedoch nicht über den als Input definierten Pin entladen werden konnte, bliebt der Signalpegel nach dem ersten Betätigen stets auf HIGH. Ab da war es einfach: Der entsprechende Pin musste kurzzeitig als Output auf Masse gelegt werden. Et voilà!

Also im Grunde folgende Einstellung, beispielhaft für den ganzen Port.



DDRD = 0xFF; // Alle Pins als Ausgänge
PORTD= 0x00; // Alle Ausgänge auf Null ziehen


Nun kann ich ohne Probleme verschiedene Druckzeiten abfangen und ggf. verschiedene Reaktionen auslösen etc.

Es war zwar extrem zeitraubend, aber dafür konnte ich mal wieder viel auf diesem Weg lernen und bin nun dabei, ein Echtzeitsystem für 8-Bit zu schreiben. (sprich: Rad neuerfinden, um des Wissens willen.)

Grüße,
Nik