Ich versuche über den Pin Change Interrupt den Atmega88 Aufzuwecken
jedoch funktioniert dies nicht.
An Portb 0,1,2 Sind die taster schalten nach GND
Pullups sind aktieviert.
An Portd 0,1,2 Hab ich Leds dran,es geht keine LED an beim drücker der
Tasten.
könnte mir einer von ihnen weiterhelfen
danke
1
#include<avr/io.h>
2
#include<stdint.h>
3
#include<avr/interrupt.h>
4
#include<avr/sleep.h>
5
#include<util/delay.h>
6
7
8
9
//ist im Make-File schon definiert
10
//#define F_CPU 10000000 // Taktfrequenz 10MHz des Quarzes
>An Portd 0,1,2 Hab ich Leds dran,es geht keine LED an beim drücker der>Tasten.
Natürlich geht keine LED an PORTD an. Da ist kein einziger Pin
als Ausgang definiert.
Sebastian schrieb:> Ich versuche über den Pin Change Interrupt den Atmega88 Aufzuwecken> jedoch funktioniert dies nicht.> set_sleep_mode(SLEEP_MODE_PWR_DOWN);> sleep_mode(); // in den Schlafmodus wechseln
Mach mal so:
Thomas Eckmann schrieb:>> Mach mal so:> set_sleep_mode(SLEEP_MODE_PWR_DOWN);> sleep_enable();> sleep_cpu();>> mfg.
Das geht leider auch nicht.
ist denn meine Konfiguration so überhaupt richtig für den
Pin Change Interrupt an PORTB0-2
1
#include<avr/io.h>
2
#include<stdint.h>
3
#include<avr/interrupt.h>
4
#include<avr/sleep.h>
5
#include<util/delay.h>
6
7
8
9
//ist im Make-File schon definiert
10
//#define F_CPU 10000000 // Taktfrequenz 10MHz des Quarzes
Im 'Power-Down' wird die I/O Clock gestoppt und lediglich INT0 und INT1
sind als Wake-Up zu gebrauchen (im Level Modus).
Nur der 'Idle' Mode lässt die I/O Clock laufen, spart aber natürlich
nicht so viel.
Siehe Kapitel 7.1 im Datenblatt.
Du musst also bei Power-Down Taster an INT0 oder INT1 anschliessen.
Also funktioniert das so wie i h mir das dachte nicht .
Ich habe an PORTB0-2 3Taster mit denen wollte ich den Atmega88
aufwecken egal welcher Taster von den dreien gedrückt wurde.
Gibs denn da noch eine andere Möglichkeit.
Mfg
Matthias Sch. schrieb:
> Im 'Power-Down' wird die I/O Clock gestoppt und lediglich INT0 und INT1> sind als Wake-Up zu gebrauchen (im Level Modus).> Nur der 'Idle' Mode lässt die I/O Clock laufen, spart aber natürlich> nicht so viel.> Siehe Kapitel 7.1 im Datenblatt.> Du musst also bei Power-Down Taster an INT0 oder INT1 anschliessen.
Ich weiss ja nicht, welchem Datenblatt du das entnommen hast.
Jedenfalls nicht dem des Atmega88.
Natürlich lässt der sich aus allen Sleepmodes mit den Pinchange
Interrupts aufwecken.
@Sebastian
> ist denn meine Konfiguration so überhaupt richtig für den> Pin Change Interrupt an PORTB0-2
Das ist falsch, genau genommen unvollständig
> PCMSK0 |= (1 << PCINT0);
PCMSK0 |= (1 << PCINT0) | (1 << PCINT1) | (1 << PCINT2);
Bisschen verwirrende Doppelverwendung der Namen.
Damit funktioniert dein Programm auch.
1
#ifndef F_CPU
2
#error F_CPU not defined.
3
#endif
4
5
#include<avr/io.h>
6
#include<stdint.h>
7
#include<avr/interrupt.h>
8
#include<avr/sleep.h>
9
#include<util/delay.h>
10
11
//ist im Make-File schon definiert
12
//#define F_CPU 10000000 // Taktfrequenz 10MHz des Quarzes
Hallo Thomas,
Vielen dank ich wahr schon ein bischen irretiert denn mann kann den
Atmega88 ja mit fast jeden Pin
aufwecken das wahr ja beim Atmega8 nicht so da gabs ja nur int0 und
int1.
Nochmal eine kleine Verständisfrage
PCICR |= (1 << PCINT0); // externen Interrupt freigeben
wird denn hiermit die oben definierten pins den interrupt freigegeben
PCINT0-2
oder nur PCINT0
Mfg
Thomas Eckmann schrieb:> Ich weiss ja nicht, welchem Datenblatt du das entnommen hast.> Jedenfalls nicht dem des Atmega88.
Dann schau dir mal im ATMega 48/88/168/328 Datenblatt Version 05/11 die
Wakeup Sources auf Seite 39 an. Und bitte Fussnote (3) beachten.
Lediglich bei Idle gehen Pinchange Interrupts. Das leuchtet auch ein,
denn die Flankenerkennung läuft eben nur, wenn die I/O Clock an ist.
Level Interrupts auf INT0 und INT1 hingegen werden in allen Sleep Modes
als Wake-Up erkannt.
Sebastian schrieb:> Hallo Thomas,>> Vielen dank ich wahr schon ein bischen irretiert denn mann kann den> Atmega88 ja mit fast jeden Pin> aufwecken das wahr ja beim Atmega8 nicht so da gabs ja nur int0 und> int1.>> Nochmal eine kleine Verständisfrage> PCICR |= (1 << PCINT0); // externen Interrupt freigeben> wird denn hiermit die oben definierten pins den interrupt freigegeben> PCINT0-2> oder nur PCINT0>> Mfg
Das ist die blöde Doppelbezeichnung. PCINT0 bezeichnet einmal den
Vektor, der dem ersten Port zugeordnet ist. Also beim Atmega88 PORTB.
Beim Attiny4313 ist das PORTA. Ich hätte das analog zu den Ports A, B,
C... PCINTA, PCINTB usw. genannt. Aber mich haben sie nicht gefragt.
Das andere sind die Pin-Bezeichnungen. Pin0 am ersten Port also beim 88
PORTB0 heisst PCINT0. Um den scharf zu schalten, wird das entsprechende
Bit im zugehörigen Maskenregister gesetzt. Und dann wird hochgezählt.
PB1 ist PCINT1, PB7 PCINT7 usw. Das gilt aber nur fürs Maskenregister
und hat mit der Freigabe der Interrupts nichts zu tun. PCINT8, der am
PORTC liegt, wird im nächsten Maskenregister gesetzt und hat den
Interruptvektor PCINT1, also PCINT1_vect.
Da muss man also, wenn man das nicht jeden Tag macht, immer konzentriert
ins Datenblatt gucken. Wahrscheinlich wollte sich der Autor dadurch eine
besondere Anerkennung verschaffen.
mfg.
Matthias Sch. schrieb:> Dann schau dir mal im ATMega 48/88/168/328 Datenblatt Version 05/11 die> Wakeup Sources auf Seite 39 an. Und bitte Fussnote (3) beachten.> Lediglich bei Idle gehen Pinchange Interrupts. Das leuchtet auch ein,> denn die Flankenerkennung läuft eben nur, wenn die I/O Clock an ist.> Level Interrupts auf INT0 und INT1 hingegen werden in allen Sleep Modes> als Wake-Up erkannt.
dann lies doch bitte auch den Text dazu...die Fussnote bezieht sich nur
auf die Einschränkung "Level Interrupts" für INT0/1...
Matthias Sch. schrieb:> Thomas Eckmann schrieb:>> Ich weiss ja nicht, welchem Datenblatt du das entnommen hast.>> Jedenfalls nicht dem des Atmega88.>> Dann schau dir mal im ATMega 48/88/168/328 Datenblatt Version 05/11 die> Wakeup Sources auf Seite 39 an. Und bitte Fussnote (3) beachten.> Lediglich bei Idle gehen Pinchange Interrupts. Das leuchtet auch ein,> denn die Flankenerkennung läuft eben nur, wenn die I/O Clock an ist.> Level Interrupts auf INT0 und INT1 hingegen werden in allen Sleep Modes> als Wake-Up erkannt.
Du Fußnote solltest du dir nicht nur anschauen, sondern auch verstehen.
Aber ich erklär dir das mal.
Da steht nämlich, daß unter den genannten Bedingungen, INT0 und INT1 nur
im Level Mode funktionieren. Das ist auch logisch, wie du schon erkannt
hast.
Pinchange-Interrupts sind aber grundsätzlich Level-Interrupts. Deswegen
wird da nicht extra drauf hingewiesen, daß die eben nur so
funktionieren.
Und deswegen lassen sich alle AVRs mit Pinchange auch aus allen
Sleepmodes mit einem PCINT aufwecken.
Wenn dem jetzt tatsächlich nicht so sein sollte, kannst du meine
sämtlichen AVR haben. Die sind dann nämlich alle kaputt.
Großkotzmodus aus.
mfg.
PS: Soweit ich weiss, wurde die Pinchange-Geschichte ursprünglich für
batteriebetriebene Fernbedienungen entwickelt.
Thomas Eckmann schrieb:> Pinchange-Interrupts sind aber grundsätzlich Level-Interrupts.
Das glaube ich nicht. Das entsprechende PCIF wird durch die
Flankenerkennung gesetzt und nicht wieder gelöscht, bevor die passende
ISR ausgeführt wurde.
Und selbst, wenn der Pegel nicht gehalten wird, wird PCIF mit der
nächsten Flanke (egal, ob steigend oder fallend) wieder gesetzt.
Welche kaputten AVRs bekomme ich jetzt von Dir? :-)
Peter Dannegger schrieb:> PCINTs reagieren auf beide Flanken. Diese Logik arbeitet jedoch> asynchron, d.h. ohne CPU-Takt und daher kann sie aus jedem Mode> aufwecken.
Da schreibt einer der AHNUNG VON DER MATERIE HAT!!!
Genauso ist es.
Vielen Dank! Wieder was gelernt und auch gleich eine Mehrdeutigkeit (in
meinen Augen) im Datenblatt geklärt.
Peter Dannegger schrieb:> PCINTs reagieren auf beide Flanken. Diese Logik arbeitet jedoch> asynchron, d.h. ohne CPU-Takt und daher kann sie aus jedem Mode> aufwecken.
Nochmals danke. PCINTs sind eben doch nicht level-, sondern
flankengetriggert.
Peter Dannegger schrieb:> Diese Logik arbeitet jedoch> asynchron, d.h. ohne CPU-Takt
Auch das glaube ich nicht :-)
Wenn man ins Datenblatt sieht, wird ein Teilschaltbild gezeigt, wie die
Flankenänderung ausgewertet und das PCIF gesetzt werden. Die dazu
notwendigen FFs werden mit clk getaktet - folglich synchron.
Offensichtlich verschweigt uns Atmel, wie es gemacht wird.
Hallo miteinander!
Ich versuche auch gerade pin change interrupts zu verwenden. Hierzu habe
ich noch eine Verständnisfrage die hier sehr gut anschließt und nicht
wirklich einen neuen threat braucht.
Bezugnehmen auf den oben stehenden verbesserten code von Thomas Eckmann
aktiviere ich pin change interrupts in folgenden Schritten:
1.) Aktivierung des scans for pin change interrupt für PortB
1
PCICR|=(1<<PCIE0);
2.) Definition welche Pins von PortB überwacht werden sollen
1
PCMSK0|=(1<<PCINT0)|(1<<PCINT1)|(1<<PCINT2);
3.) Aktivierung globaler interrupts
1
sei();
Jetzt zu meiner Frage: Warum müssen im while-loop die Zeilen
1
PCICR|=(1<<PCINT0);
und
1
PCICR&=~(1<<PCINT0);
stehen?
Zum einen ist doch die Verwendung von PCICR in dem Fall nicht konsistent
weil sie am Anfang und im while-loop unterschiedlich verwendet werden
und zum anderen sind die interrupts doch schon aktiviert?
Vielen Dank vorab!
Florian
Florian Faessler schrieb:> Jetzt zu meiner Frage: Warum müssen im while-loop die ZeilenPCICR |= (1> << PCINT0);undPCICR &= ~(1 << PCINT0);stehen?
Müssen sie nicht und es ist purer Zufall, das PCINT0 zufällig an der
gleichen Bitposition wie PCIE0 steht - deswegen gehts doch.
Richtig wäre zum Desaktivieren des PC Interrupts auf Port B
PCICR &= ~(1 << PCIE0);
und zum Aktivieren
PCICR |= (1 << PCIE0);
Danke Matthias für die schnelle Antwort! :-)
Bei mir hat es selbst bei anderer Pin-Belegung auch zufällig
übereingestimmt und war deshalb bei mir reproduzierbar. Jetzt ist alles
klar.
Hallo,
ich versuche mich auch mit Interrupts und bin Neuling was das angeht.
Und zwar habe ich einen ATmega88, einen Taster bei PD5 und eine LED an
PB1.
Das Basisprogramm läuft schon einwandfrei. Jedoch soll dieses bei
Tastendruck entsprechend unterbrochen werden und danach natürlich
entsprechend fortgesetzt werden. Dafür habe ich vor den PinChange
Interrupt meines Controllers zu verwenden. Vereinfacht möchte ich gerne,
dass die LED bei Tastendruck kurz aufleutet und anschließend das
Programm in der While-schleife weiter läuft.
Folgendes habe ich als Code:
1
#define F_CPU 8000000UL
2
#include<avr/eeprom.h>
3
#include<avr/io.h>
4
#include<util/delay.h>
5
#include<stdint.h>
6
#include<stdlib.h>
7
#include<avr/interrupt.h>
8
#include"sensoren.h"
9
#include"uart.h"
10
11
12
#define PCIE2_vect _VECTOR(1)
13
14
voidtaster_init(void);
15
16
ISR(PCIE2_vect)
17
{
18
PORTB|=(1<<PB1);//LED an
19
_delay_ms(1000);
20
}
21
22
intmain(void)
23
{
24
uart_init();
25
SPI_init();
26
sensoren_init();
27
taster_init();
28
29
while(1)
30
{
31
PCICR|=(1<<PCINT20);
32
PORTB&=~(1<<PB1);//LED aus
33
//ab hier restliches Programm
34
_delay_ms(500);
35
PCICR&=~(1<<PCINT20);
36
}
37
}
38
39
voidtaster_init(void)
40
{
41
//Taster_test implementieren
42
DDRB|=(1<<PB1);//PB1 als Ausgang setzen. LED
43
DDRD&=~(1<<PD5);// PD5 als Eingang Taster
44
PORTD|=(1<<PD5);// Pullup am Taster einschalten
45
PCICR|=(1<<PCIE2);//
46
PCMSK2|=(1<<PCINT20);
47
sei();
48
_delay_ms(0.5);// Verzögerung
49
}
Wie gesagt, bin ich noch ein Anfänger. Ich hätte gerne hilfreiche
Hinweise, die mir sagen, warum mein Code nicht funktioniert.
Dankeschön
Noch drei Bemerkungen, die langfristig hilfreich sind:
1. In Interrupt-Routinen werden keine delays verwendet. Man will, das
ISR schnell reagieren und Ereignisse an die main-Schleife (die ggf. eine
State-Machine enthält) weiterreichen.
2. Ein sei ohne vorhergehendes cli ist zwecklos, denn die Interrupts
sind nach dem Reset ohnehin freigegeben. Siehe Datenblatt.
3. Sowas
1
while(1)
2
{
3
PCICR |= (1 << PCINT20);
4
...
5
PCICR &= ~(1 << PCINT20);
6
}
ist völlig zweckfrei und dazu noch potentielle Fehlerquelle. Überleg
einmal selbst, wie schnell, nach dem sperren des PIN-Change-Ints in der
letzten Zeile innerhalb der while-Schleife, die erneute Freigabe in der
ersten Zeile erfolgt.
Peter Ullrich schrieb:> #define PCIE2_vect _VECTOR(1)> ISR(PCIE2_vect)
Was versprichst du dir davon? Wenn du schon neue Vektornamen kreierst,
dann auch bitte richtig. Aber was soll das?
Benutze den Vektor so, wie er definiert ist:
1
ISR(PCINT2_vect)
2
{
3
4
}
Tasten fragt man allerdings ohnehin nicht so ab. Guck dir den ersten
Link von Bitflüsterer an.
mfg.
Hi
>PCICR &= ~(1 << PCINT20);
In PCICR gibt es kein PCINT20. Fall das ein Versuch zum Löschen des
Interruptflags sein soll: Das befindet sich in PCIFR und wird durch
Setzen des Bits gelöscht.
>2. Ein sei ohne vorhergehendes cli ist zwecklos, denn die Interrupts>sind nach dem Reset ohnehin freigegeben. Siehe Datenblatt.
Dann mach das mal schleunigst.
MfG Spess
spess53 schrieb:>>2. Ein sei ohne vorhergehendes cli ist zwecklos, denn die Interrupts>>sind nach dem Reset ohnehin freigegeben. Siehe Datenblatt.>> Dann mach das mal schleunigst.>> MfG Spess
Habe gemacht. Peinlich.
Die Interrupts sind, anders als ich behauptet habe, nach dem Reset
nicht freigeben.
Sorry.
Bitflüsterer schrieb:> Hinweise werden Dir nicht helfen, denn Dir fehlen wesentliche> Grundlagen.>> Lies und erprobe erstmal:>> 1. http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten> 2. http://www.mikrocontroller.net/articles/Multitasking
Danke, ich werde es mir durchlesen. Leider scheinen die Code-Beispiele
im Assembler geschrieben zu sein.
Mir geht es lediglich darum, dass ich verstehe wie ich den PCINT
rifchtig initialisiere für meine Pinbelegung von Taster und LED.
Bitflüsterer schrieb:> 1. In Interrupt-Routinen werden keine delays verwendet. Man will, das> ISR schnell reagieren und Ereignisse an die main-Schleife (die ggf. eine> State-Machine enthält) weiterreichen.
Der Code soll nur zu Übungszwecken dienen. Wenn ich kein Delay
reinschreibe, dann seh ich möglicherweise gar nicht, ob es funktioniert,
da er meine LED gleich wieder ausschaltet.
Meine Main funktion enthält noch weiteren Code, der für diese
Interrupt-Angelegenheit aber nicht relevant ist.
Wie gesagt bin ich Anfänger und habe meinen Code durch Interpretation
des Codes über mir weitestgehend angepasst, um es für meine Hardware zum
laufen zu bringen.
Pin PB1 ist eine LED angeschlossen
Pin PD5 ist der Taster (auf Masse gezogen)
Wie ist es zu initialisieren?
Peter Ullrich schrieb:> Wie gesagt bin ich Anfänger
der allerwichtigste Punkt ist, dass du auch als Anfänger irgendwann
erkennen musst, dass _delay_ms nicht die Lösung sondern oft das Problem
darstellt.
Wenn du das erkannt hast, dann kommst du auch irgendwann zur
Schlussfolgerung, dass du gar keinen Pin-Change Interrupt zur
Tastenbfrage brauchst. Denn dadurch, dass das Programm anders
organisiert ist und als Folge davon alle Delays rausfliegen, wird die
Hauptschleife schnell genug, dass man die Tastenabfrage auch per Polling
machen kann. Ganz im Gegenteil muss man die Tastenabfrage sogar des
öfteren noch bremsen, weil die Hauptschleife jetzt zu schnell geworden
ist, so dass Tastenprellen voll durchschlägt.
Was man allerdings in praktisch jedem Programm braucht, das ist ein
Timer, der einen Systemtick erzeugt. Und genau dort ist auch der Punkt
an dem man mit einer Tastenabfrage ansetzen kann.
@Karl Heinz:
Natürlich gibt es immer mehrere Lösungen für ein Problem. Leider kann
ich mit deinem Ratschlag nicht viel anfangen, sondern bin eher noch
verwirrter.
Mein Programm umfasst Zeitschleifen, da es in bestimmten Zeitabständen
Messergebnisse von Sensoren über UART ausgeben soll. Wie gesagt bin ich
da mit meiner Lösung zufrieden, bzw. möchte ich nicht alles über den
Haufen werfen. Es soll lediglich eine weitere Funktion hinzukommen,
falls der Taster gedrückt worden ist. Der einfachheit halber und wegen
der Anschaulichkeit, soll meine LED kurz aufleuchten, wenn der Taster
betätigt wurde.
Wenn dies erfolgreich geschieht, passe ich die Funktion entsprechend an.
Ich hoffe ihr versteht mein Vorhaben nun etwas besser.
Die Tutorial sind wirklich gut, leider bin ich nicht erfahren genug um
den Assembler Code in C zu übersetzen und für mich anzuwenden. Deshalb
habe ich im Forum nach ähnlichen Vorhaben gesucht und den Code darin
versucht auf meine Hardware zu übertragen und zu testen.
Peter Ullrich schrieb:> Der Code soll nur zu Übungszwecken dienen.
Nun denn.
Du willst also einen Pin Change am Pin PD5 detektieren.
Aus dem Datenblatt erfahren wird, dass am PD5 die Pin Change Quelle 21
hängt.
Weiters suchen wir uns im Datenblatt raus, dass diese Quelle 21 zum Pin
Change Interrupt 2 gehört.
Lass uns da mal anfangen. Wie immer muss ein Interrupt erst mal frei
gegegeben werden.
Aus dem Datenblatt holen wir uns
1
PCICR – Pin change interrupt control register
2
3
• Bit 2 - PCIE2: Pin change interrupt enable 2
4
When the PCIE2 bit is set (one) and the I-bit in the status
5
register (SREG) is set (one), pin change interrupt 2 is enabled.
6
Any change on any enabled PCINT23..16 pin will cause an interrupt.
7
The corresponding interrupt of pin change interrupt request is
8
executed from the PCI2 interrupt vector. PCINT23..16 pins are
9
enabled individually by the PCMSK2 register.
die Sache mit dem sei ist nicht weiter überraschend, das ist immer so
und ein entsprechender Aufruf von sei() ist normalerweise die letzte
Aktion vor der Hauptschleife.
ALso: in PCICR muss PCIE2 auf 1 gesetzt werden. Und der zuständige
Vektor ist der PCI2, im avr-gcc Jargon ist das dann der PCI2_vect.
interrupt.h inkludieren und dann müsste der Vektorname auch schon
existieren.
Weiter im Text. Da gibt es einen Verweis auf das PCMSK2 Register. Der
PCI2 feuert nicht einfach so bei jedem Pin, sondern jeder der 8
möglichen Pins, die zum Pin Change Interrupt 2 gehören, muss eigens
freigegeben werden. Also mal bei der Beschreibung des PCMSK2 nachsehen.
1
PCMSK2 – Pin change mask register 2
2
• Bit 7..0 – PCINT23..16: Pin change enable mask 23..16
3
Each PCINT23..16-bit selects whether pin change interrupt is
4
enabled on the corresponding I/O pin. If PCINT23..16 is set and
5
the PCIE2 bit in PCICR is set, pin change interrupt is enabled
6
on the corresponding I/O pin. If PCINT23..16 is cleared, pin
7
change interrupt on the corresponding I/O pin is disabled.
OK. Das Bit, das gebraucht wird, ist das Bit PCINT21, weil ja auch beim
PD5 Pin in der Übersicht dabei steht, dass es sich um den PCINT21
handelt.
Was ist also zu tun?
* Es braucht einen Interrupt Handler. Der trägt den Namen PCI2_vect
* In PCMSK2 ist das Bit PCINT21 zu setzen
* In PCICR ist das Bit PCIE2 zu setzen
* Es muss einen sei() geben
und dann sollte das eigentlich schon laufen.
Für den ersten Gehversuch empfehle ich erst mal, nicht zu künsteln. So
simpel wie nur irgendwie möglich. D.h. ohne irgendwelche Zeitsteuerungen
oder dergleichen. Einfach nur: Wenn Interrupt, dann LED einschalten
(oder ausschalten). NIcht mehr.
Und jetzt vergleich mal das was notwendig ist mit dem was du im
Eingangsposting an Code geschrieben hast.
Karl Heinz schrieb:> Und jetzt vergleich mal das was notwendig ist mit dem was du im> Eingangsposting an Code geschrieben hast.
1
...
2
ISR(PCIE2_vect)
3
{
Nope. Der heißt PCI2_vect.
Wann immer du eine Fehlermeldung wegen eines anscheinend falschen
Interrupt Namens kriegst, dann ist die richtige Aktion nicht, dass du
dir selber ein #define dafür machst, sondern dir überlegst, warum wohl
dein Interrupt Name falsch ist.
Das 'E' in PCIE2 steht für 'Enable'. In Langform bedeutet PCIE2 "Pin
Change Interrupt Enable für den Pin Change Interrupt 2". PCIE2 ist der
Name des Enable Bits. Damit wird ein Interrupt freigegeben ('enabled')
oder ausgeschaltet ('disabled'). Aber der dadurch freigegebene Interrupt
ist der PCI2, der 'Pin Change Interrupt 2'. Und genau so heißt dann auch
der Vektor.
Peter Ullrich schrieb:> Mein Programm umfasst Zeitschleifen, da es in bestimmten Zeitabständen> Messergebnisse von Sensoren über UART ausgeben soll.
Das ist kein Grund.
> Wie gesagt bin ich> da mit meiner Lösung zufrieden
Das solltest du aber nicht. Statt dessen solltest du lernen, wie man ein
Programm mittels Timer organisiert, so dass es zeitliche Dinge regel.
> bzw. möchte ich nicht alles über den> Haufen werfen.
Tja. Leider ist das aber oft unumgänglich. Vor allen Dingen wenn man
einen untauglichen Ansatz gewählt hat, der nicht zukunftsträchtig ist.
Zeitsteuerungen laufen praktisch immer über einen Timer. Der Timer
erzeugt mit seiner ISR einen Basistakt, von dem ausgehen über Vielfache
davon die entsprechenden Aktionen getriggert werden. Frei nach dem
Muster: nach 60 mal 1 Sekunde sind auch 60 Sekunden vergangen.
Dann ist auch das Hinzufügen von nahezu beliebig vielen weiteren
Funktionalitäten kein großes Problem mehr. Und vor allen Dingen: Das
Programm reagiert auch dann auf Benutzereingaben, wenn gerade
irgendwelche anderen Dinge ablaufen, wie zb Wartezeiten nach denen eine
LED wieder ausgeht.
Also etwa so
1
volatile uint8_t AbfrageDauer;
2
3
ISR( ... ) // Timer ISR, die zb alle 0.1 Sekunden aufgerufen wird
4
{
5
if( AbfrageDauer > 0 )
6
AbfrageDauer--;
7
}
8
9
int main()
10
{
11
....
12
13
Timer initialisieren, so dass die ISR alle 0.1 Sekunden aufgerufen wird
14
15
AbfrageDauer = 100; // mal 0.1 Sekunden macht 10 Sekunden
16
sei();
17
18
while( 1 ) {
19
20
if( AbfrageDauer == 0 ) {
21
AbfrageDauer = 100;
22
23
XXXXXX
24
}
25
26
.....
27
}
28
}
Der Code bei XXXXXX wird alle 10 Sekunden ausgeführt. Und zwar ohne das
das restliche Programm zeitlich blockiert ist.
FAQ: Timer
Lern mit Timern umzugehen! Davon hast du mehr Nutzen, als von allem
anderen! Timer sind deine Arbeitspferde in der µC-Programmierung.
Danke an Karl Heinz. Dieser Beitrag war sehr hilfreich und es hat auf
Anhieb funktioniert. Du Teufelskerl ;)
Der funktionierende Code sieht also so aus:
1
#define F_CPU 8000000UL
2
#include<avr/eeprom.h>
3
#include<avr/io.h>
4
#include<util/delay.h>
5
#include<stdint.h>
6
#include<stdlib.h>
7
#include<avr/interrupt.h>
8
#include"sensoren.h"
9
#include"uart.h"
10
11
voidtaster_init(void);
12
13
ISR(PCINT2_vect)
14
{
15
PORTB|=(1<<PB1);//LED an
16
_delay_ms(1000);
17
}
18
19
intmain(void)
20
{
21
uart_init();
22
SPI_init();
23
sensoren_init();
24
taster_init();
25
26
while(1)
27
{
28
PCICR|=(1<<PCIE2);
29
PORTB&=~(1<<PB1);//LED aus
30
//ab hier restliches Programm
31
//.
32
//.
33
//.
34
_delay_ms(500);
35
PCICR&=~(1<<PCIE2);
36
}
37
}
38
39
voidtaster_init(void)
40
{
41
//Taster_test implementieren
42
DDRB|=(1<<PB1);//PB1 als Ausgang setzen. LED
43
DDRD&=~(1<<PD5);// PD5 als Eingang Taster
44
PORTD|=(1<<PD5);// Pullup am Taster einschalten
45
PCICR|=(1<<PCIE2);//
46
PCMSK2|=(1<<PCINT21);
47
sei();
48
_delay_ms(0.5);// Verzögerung
49
}
Dummerweise habe ich in meinem vorherigen Code PCINT20 statt PCINT21
verwendet. Habe mich doch tatsächlich verzählt. Dennoch war ich nah
dran.
Ich bedanke mich nochmal herzlich bei Karl Heinz und allen anderen
Helfern.
Peter Ullrich schrieb:> ISR(PCINT2_vect)
Da hab ich wohl daneben gehaut.
Ich hätte doch im Datenblatt in der Interrupt Tabelle nachsehen sollen,
wie der Interrupt heißt. Dort ist er als PCINT2 gelistet und nicht als
PCI2, wie ich oben behauptet habe. Mein Fehler, aber du hast ja
gefunden, wie das Teil wirklich heisst.