PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [ERLEDIGT] Probleme mit ATtiny2313A wärend Interruptroutine



Lichti01
30.12.2017, 18:10
Hallo zusammen!

Habe mir heute ein kleines Testprogramm geschrieben, um einfach mal zu sehen ob das so funktioniert was ich mir vorstelle.

Der Gedanke dabei war in der while-Schleife die Variable c auszuwerten. In der Interruptroutine, welche durch einen Button auf INT0 ausgelöst wird, soll der Wert von 1 auf 2 bzw. von 2 auf 1 geändert werden.

c bekommt im Programm direkt nach den Interruptinitialiesierungen den Wert 2. Meine LEDs blinken auch so wie es sein soll. Wenn ich den Button nun einmal betätige blinken die LED auch wieder richtig. Wird der Interrupt erneut ausgelöst ändert sich an dem Blinkverhalten allerdings nichts. Was ist da falsch gelaufen?

Hier mal der Code

#define F_CPU 1000000UL

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

#define LED1 0
#define LED2 1
#define LED3 2
#define LED4 3

int a = 1;
int b = 2;
int c;
int d;

int main(void)
{

DDRB = 0b00001111;

//Einstellung Interrupt
PORTD = 0b00000100;
GIMSK = (1<<INT0);

sei();

c = 2;

while (1)
{

if (c == 1)

{

PORTB |= ((1<<LED1) | (1<<LED3));
PORTB &= ~((1<<LED2) | (1<<LED4));

_delay_ms(500);

PORTB |= ((1<<LED2) | (1<<LED4));
PORTB &= ~((1<<LED1) | (1<<LED3));

_delay_ms(500);

}

else if (c == 2)

{

PORTB ^= ((1<<LED1) | (1<<LED2) | (1<<LED3) | (1<<LED4));
_delay_ms(500);

}

}

}

ISR(INT0_vect)

{

if((PINB & (1<<PINB1)) == 0)
{

if (c == 1)

{

d = b;

}

else if (c == 2)

{
d = a;
}

c = d;
return;

_delay_ms(100);
}
}

Über eure Hilfe wäre ich sehr dankbar.

Gruß
Lichti01

021aet04
30.12.2017, 20:20
In Interrupts verwendet man volatile Variablen. Anstatt "int a" musst du "volatile int a" schreiben, gleiches gilt auch für char,...
Was mir auch noch auffällt ist das du auf "c==1" und "c==2" prüfst. Besser ist es wenn du auf "c==1" und auf "c!=1" prüfst. Wenn du z.B. c==3 ist wird nichts ausgeführt. Wenn du mehrere Auswahlmöglichkeiten hast könntest du auch mit "switch case" arbeiten. Also

switch(c)
{
case 1: ....
break;

case 2: ....
break;

default: ....
}


default wird ausgeführt wenn keine Auswahl zustimmt (also wenn z.B. c==3), benötigt auch kein break; mehr. Du musst auch darauf achten das du ansonsten immer ein break; einfügst. Sonst wird das switch weiter ausgeführt. Wenn du z.B. bei "case 1:" das break; vergisst wird "case 1:" und "case 2:" ausgeführt.

Ich hatte mit einem else if schon Probleme (Programm hat nicht so funktioniert wie es sollte, habe es mehrfach überprüft und auch neu geschrieben). Ich mache seit dem nur mehr if bzw if/else und switch case Abfragen, aber keine else if Abfragen mehr.

MfG Hannes

Lichti01
30.12.2017, 23:13
HI Hannes!

Danke für deine schnelle Antwort.

Habe den Variablen in volatile Variablen geändert. Die If/Else rausgeschmissen und in switch case geändert.
Leider bestand das Problem immer noch. Des Rätzels Lösung war, den Toggelbefehl in "else if (c == 2)" in - LED ein - warten - LED aus - warten - zu ändern. Jetzt läuft es fast so wie ich mir das vorgestellt habe.
Das einzige Problem das jetzt noch besteht ist, dass ich den Schalter teilweise mehrmals betätigen muss um das Blinkmuster zu verändern.
Woran kann das denn liegen?

Hier nochmal der geänderte Code:

#define F_CPU 1000000UL

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

#define LED1 0
#define LED2 1
#define LED3 2
#define LED4 3

//variablen für interrupt
volatile int a = 1;
volatile int b = 2;
volatile int c;
volatile int d;

int main(void)
{

//I/O-Ports einstellen
DDRB = 0b00001111;

//Einstellung Interrupt
PORTD = 0b00000100;
GIMSK = (1<<INT0);

//Globale interrupts aktivieren
sei();

//Wert in Variable setzen
c = 2;

while (1)
{

switch(c)

{
case 1:
PORTB |= ((1<<LED1) | (1<<LED3)); //LED1 und 3 ein
PORTB &= ~((1<<LED2) | (1<<LED4)); //LED2 und 4 aus

_delay_ms(500);

PORTB |= ((1<<LED2) | (1<<LED4)); //LED2 und 4 ein
PORTB &= ~((1<<LED1) | (1<<LED3)); //LED1 und 3 aus

_delay_ms(500);
break;
case 2:
PORTB |= ((1<<LED1) | (1<<LED2) | (1<<LED3) | (1<<LED4)); //LED1-4 ein
_delay_ms(500);
PORTB &= ~((1<<LED1) | (1<<LED2) | (1<<LED3) | (1<<LED4)); //LED1-4 aus
_delay_ms(500);
break;

}

}

}

ISR(INT0_vect)

{

{

//switch case Abfrage
switch(c)
{
case 1: d = b;
break;
case 2: d = a;
break;
default: PORTB &= ~((1<<LED1) | (1<<LED2) | (1<<LED3) | (1<<LED4));
}

c = d;
return;
_delay_ms(250);

}
}


Gruß Alex

Searcher
31.12.2017, 07:35
Das einzige Problem das jetzt noch besteht ist, dass ich den Schalter teilweise mehrmals betätigen muss um das Blinkmuster zu verändern.

Hallo Alex,
Taster prellen. d.h. sie schließen und öffnen bei einer Betätigung mehrmals sehr schnell bis die Kontakte ihre endgültige Lage einnnehmen. Die Prellzeit von der Betätigung bis die Kontakte zu Ruhe kommen ist vom Taster abhängig und dauert im Allgemeinen 5 bis 20ms.

In Deinem Programm sehe ich keine Maßnahme dagegen, so daß die ISR bei einem Tastendruck öfter ausgeführt wird und dann das Blinkmuster zufällig eingestellt wird, das dann wie das vorhergehende Blinkmuster aussieht oder eben zufällig das andere ist.

Also Tastenentprellung implementieren.






return;
_delay_ms(250);


Wozu ist das _delay_ms in der ISR gut. Es wird nie ausgeführt, da mit dem "return;" der Ablauf der ISR beendet wird und das Normalprogramm wieder aufgenommen wird.

Gruß
Searcher

021aet04
31.12.2017, 09:00
Ich vermute auch das es Tastenprellen ist.
Das mit dem delay nach dem return stimmt ebenfalls. Bei C benötigst du kein return aus der ISR (außer es ist gewollt).
Delay sollte man auch in einer ISR vermeiden. Eine ISR sollte auf ein Ereignis so schnell wie möglich reagieren, das Flag wird aber erst bei beenden der ISR zurückgesetzt. Du sperrst somit alle INT (auch Timer,....).
Außerdem gilt bei ISR: So wenig wie möglich, so viel wie nötig. Die ISR sollte so schnell wie möglich abgearbeitet werden. Langwierige Berechnungen (z.b. Berechnungen mit Float) außerhalb der ISR (wenn nötig einfach ein Flag setzten und das im Hauptprogramm abfragen).

MfG Hannes

Searcher
31.12.2017, 09:13
Hallo,
gerade fällt mir noch etwas auf. Die ISC00 und ISC01 Bits im MCUCR Register werden nicht gesetzt. Damit ist der low-Level Interrupt für INT0 eingeschaltet. Da ist also nicht nur das Tastenprellen ein Problem, sondern auch das Festhalten bzw Betätigungsdauer des Tasters. Solange ein low-Level am INT0 Pin anliegt, wird die ISR immer wieder aufgerufen, weil ja nach Abarbeitung ja immer noch der low-Level anliegt.

Gruß
Searcher

Lichti01
31.12.2017, 09:16
HI Searcher!

Danke für die Antwort.
Werde mich dann mit dem Thema Tastenentprellung auseinandersetzen und diese dann implementieren.

Gruß Alex

Searcher
31.12.2017, 09:21
Auch meine neue Antwort bitte nicht übersehen ;)
https://www.roboternetz.de/community/threads/71478-Probleme-mit-ATtiny2313A-w%C3%A4rend-Interruptroutine?p=641412&viewfull=1#post641412

Viel Erfolg
Searcher

Lichti01
31.12.2017, 09:26
Habe eure neuen Antworten eben gelesen.

Wurden mir erst nach absenden meiner Antwort angezeigt.

Vielen DANK nochmals!
und danke für den Link:)

Lichti01
31.12.2017, 16:26
so habe mich nun einmal mit dem Thema Tasterentprellung auseinandergesetzt. Habe einen Code im Netz gefunden und diesen in meinen Code eingebunden. Leider besteht das Problem "ich blink wie ich will" immer noch. Was habe ich falsch gemacht?


#define F_CPU 1000000UL

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

#define LED1 0
#define LED2 1
#define LED3 2
#define LED4 3

//variablen für entprellung
#define TASTERPORT PIND
#define TASTERBIT PIND2
volatile int r = 1;

//variablen für interrupt
volatile int a = 1;
volatile int b = 2;
volatile int c;
volatile int d;

//Entprellung
char taster(void)
{
static unsigned char zustand;
char rw = 0;

if(zustand == 0 && !(TASTERPORT & (1<<TASTERBIT))) //Taster wird gedrueckt (steigende Flanke)
{
zustand = 1;
rw = 1;
}
else if (zustand == 1 && !(TASTERPORT & (1<<TASTERBIT))) //Taster wird gehalten
{
zustand = 2;
rw = 0;
}
else if (zustand == 2 && (TASTERPORT & (1<<TASTERBIT))) //Taster wird losgelassen (fallende Flanke)
{
zustand = 3;
rw = 0;
}
else if (zustand == 3 && (TASTERPORT & (1<<TASTERBIT))) //Taster losgelassen
{
zustand = 0;
rw = 0;
}

return rw;
}
int main(void)
{

//I/O-Ports einstellen
DDRB = 0b00001111;

//Einstellung Interrupt
PORTD = 0b00000100;
GIMSK = (1<<INT0);
MCUCR = (1<<ISC01) | (1<<ISC00);

//Globale interrupts aktivieren
sei();

//Wert in Variable setzen
c = 2;

while (1)
{

switch(c)

{
case 1:
PORTB |= ((1<<LED1) | (1<<LED3)); //LED1 und 3 ein
PORTB &= ~((1<<LED2) | (1<<LED4)); //LED2 und 4 aus

_delay_ms(500);

PORTB |= ((1<<LED2) | (1<<LED4)); //LED2 und 4 ein
PORTB &= ~((1<<LED1) | (1<<LED3)); //LED1 und 3 aus

_delay_ms(500);
break;
case 2:
PORTB |= ((1<<LED1) | (1<<LED2) | (1<<LED3) | (1<<LED4)); //LED1-4 ein
_delay_ms(500);
PORTB &= ~((1<<LED1) | (1<<LED2) | (1<<LED3) | (1<<LED4)); //LED1-4 aus
_delay_ms(500);
break;

}

}

}

ISR(INT0_vect)

{
if (taster == r); //Wenn Taster gedrückt
{

//switch case Abfrage
switch(c)
{
case 1: d = b;
break;
case 2: d = a;
break;
default: PORTB &= ~((1<<LED1) | (1<<LED2) | (1<<LED3) | (1<<LED4));
}

c = d;

}

}


Ist das überhaupt der richtige Code für Tasterentprellung?

Gruß
Alex

oberallgeier
31.12.2017, 18:43
Hallo Alex!


.. Werde mich dann mit dem Thema Tastenentprellung auseinandersetzen und diese dann implementieren ..
.. besteht das Problem "ich blink wie ich will" immer noch. Was habe ich falsch gemacht? .. Ist das überhaupt der richtige Code .. ? ..Falsch gemacht? Du hattest davor geschrieben ".. auseinandersetzen .." danach ".. implementieren ..". Mir siehts umgekehrt aus: ".. implementieren .." danach ".. auseinandersetzen ..".

Beim schnellen Hinsehen fälllt mir auf, Du hast ISC00 und ISC01 gesetzt. Da kommt der Interrupt INT0 bei rising edge. Wie schaltet denn der Taster/Button? Gegen GND oder Vcc? Dass Du den INT0-Pinn (PD2) auf Ausgang setzt macht ja nix aus, denn das Datenblatt schreibt ja auch ".. even if the INT0, INT1 or PCINT17..0 pins are configured as outputs .."; es sieht für mich nur überraschend aus. Meine Taster gehen (fast) immer auf Eingänge mit PullUp und schalten gegen GND.

021aet04
31.12.2017, 21:01
Was mir auffällt ist das du in der ISR schreibst "if (taster == r) ; {". Somit wird switch immer ausgeführt. Und warum prüfst du auf "r"? Du kannst einfach auf "1" oder "!0" prüfen.

MfG Hannes

Searcher
01.01.2018, 10:14
Leider besteht das Problem "ich blink wie ich will" immer noch. Was habe ich falsch gemacht?



ISR(INT0_vect)

{
if (taster == r); //Wenn Taster gedrückt
{

//switch case Abfrage
switch(c)
.
.

Ist das überhaupt der richtige Code für Tasterentprellung?


Hallo Alex,
weil ich in Bascom programmiere, kann ich C nur recht grob verstehen. Die "taster" Routine scheint Sinn zu machen, wenn sie ständig aufgerufen wird. Du rufst sie aber nur in Deiner ISR auf. Sie ist auch für Taster ausgelegt, die nach LOW schalten.

Wie die anderen schon geschrieben haben sind dafür die ISC00 und ISC01 Bits noch nicht richtig eingestellt. Wenn die Bits dann auf fallende Flanke eingestellt sind, wird die ISR nur mit der fallenden Flanke aufgerufen und die "taster" routine kann die Zustände "Taster wird losgelassen" bzw "Taster losgelassen" nicht erreichen, da ja in dem Fall die ISR gar nicht aufgerufen wird. (Bitte ein Aufschrei wenn ich falsch liege :-) )

Die "taster" Routine muß also ständig unabhängig vom Taster Interrupt zB durch einen eigenen Timer aufgerufen werden. Bedeutet aber auch gößere Änderungen im Programm.

Zum Testen Deiner main könntest Du Dein ursprüngliches Programm nehmen und zunächst auf eine unschöne aber einfache Entprellung zurückgreifen. Dazu ganz am Ende der ISR ein delay von 20ms einsetzen und danach noch das INTF0 Flag im GIFR durch Schreiben einer 1 auf das INTF0 löschen; Ende der ISR.

Dadurch wird die ISR bei Tasterbetätigung aufgerufen, das INTF0 automatisch gelöscht, Aktionen durchgeführt und dann 20ms lang abgewartet, ob noch Tastenpreller auftreten. Wenn welche aufgetreten sind und das ist zimlich sicher, steht das INTF0 wieder. Das wird jetzt noch manuell gelöscht!, damit nicht sofort nach Beendigung der ISR diese sofort wieder aufgerufen wird.

Bleibt der Finger auf der Taste oder wird sie losgelassen, tritt keine fallende Flanke auf und es passiert nichts weiter; nur eben dann wieder bei erneutem Tastendruck.

Der Nachteil ist, daß wie schon mal von 021aet04 erwähnt wurde, das delay das Programm für 20ms aufhält und damit auch das 500ms Blinktiming während Tastendruck minimal verändern kann. Zum Testen würde ich das mal probieren und dann mich nochmal intensiv mit der Entprellung beschäftigen.

Gruß und ein Frohes Neues an alle
Searcher

Lichti01
01.01.2018, 10:45
HI zusammen und ein frohes neues Jahr,

Hab die ISC00 und ISC01 Bits nun auf fallende Flanke gesetzt. Das If (taster == r) wurde aus der ISR gelöscht. Des weitern habe ich den PullUp-Widerstand noch in meine Schaltung mit eingebaut.
Jetzt funktioniert alles prima.

@oberallgeier
Hab auf jeden Fall versucht es im voraus zu verstehen. Aber wie heißt es so schön learning by doing.;) Bin halt noch ganz am Anfang, was das programmieren von AVR's betrifft. Aber danke dir trotzdem für die Hilfreichen Tipps.

Danke euch allen für eure Hilfe.

LG
Alex