Hallo Leute,
bei einem Problem mit meinen Interrupt-Vektoren könnte ich Eure
Unterstützung brauchen. Ich brauche zwei verschiedene Interruproutinen,
die je durch eine Transistorschaltung ausgelöst werden sollen. Dafür
habe ich eine Lichtschranke gebaut, bei deren Unterbrechung das Signal
von 5 auf 0 Volt gezogen wird. Ein Pin-Change-Interrupt detektiert dies
und macht dann die ein oder andere Aktion.
Kurzum: 2 Interrupts möchte ich haben, hier mein Code:
1
#include<avr/io.h>
2
#include<util/delay.h>
3
#include<avr/interrupt.h>
4
5
6
/*Für Delay-Header*/
7
#ifndef F_CPU
8
F_CPU=16000000UL;
9
#endif
10
11
12
/*Variablen für ISR*/
13
uint8_tintPortB;
14
uint16_tintCounter;
15
volatiledoubleintTimer;
16
volatiledoubleintZaehler;
17
volatiledoubleintVhigh;
18
volatiledoubleintVlow;
19
volatileuint8_tLEDCounter;
20
21
ISR(PCINT0_vect)
22
{
23
/*Wird ausgelöst durch Flankenwechsel auf PIN B0*/
PCMSK0|=(1<<PCINT0);//Enable Pin Change Interrupt handling on PinB0
45
PCMSK0|=(1<<PCINT1);//Enable Pin Change Interrupt handling on PinB1
46
47
48
DDRC=0xff;/*Port C als Ausgangsport definieren, um mit LEDs hoch zu zählen*/
49
_delay_ms(500);
50
51
52
while(1)
53
{
54
/*Endlosschleife, die sonstigen Code enthält */
55
sei();
56
PORTC=LEDCounter/2;
57
}
58
}
Verwende ich nur PCINT0, funktioniert alles wie gehabt, möchte ich
PCINT1 hinzufügen, so gibt's Probleme.
Es wird auch auf PIN B1 ein Interrupt ausgelöst, jedoch wird jedweils
der Vektor PCINT0_vect aufgerufen. Dem Datenblatt nach bin ich der
Meinung alles korrekt eingestellt zu haben, da das Bit PCIE0 ja PCINT1:0
freischalten sollte. Die konkrete Freigabe erfolgt im Register PCMSK0
auf dem Bit PCINT1. Das Durchsuchen des Forums hat mir leider nicht
geholfen, weshalb ich meine, einen laxen Fehler gemacht zu haben. Sieht
den jemand? Bin für Hilfe dankbar!
Beste Grüße (noch ins Neue Jahr)
Andreas
> ISR(PCINT0_vect)> ISR(PCINT1_vect)
Warum zwei? Ein PCINTx_vect kann auf mehrere Pins horchen und innerhalb
der ISR wertet man aus welcher es war (z.B. PCINT0_vect PB0..PB7).
> Es wird auch auf PIN B1 ein Interrupt ausgelöst, jedoch wird jedweils> der Vektor PCINT0_vect aufgerufen.
Das ist richtig so. s. oben.
Nicht verwechseln:
1) PCINT0 als Teil des Namens des Vektors PCINT0_vect
2) PCINT0 als Pinname und symbolischer Bitnahme im PCMSK0 – Pin Change
Mask Register 0
ist — mit Verlaub — Quark. Wenn F_CPU nicht definiert ist, würdest
du stattdessen eine initialisierte Variable ohne Datentyp (damit,
je nach C-Standard-Version, implizip vom Typ `int', der jedoch
durch den Initialisierungswert überläuft) damit definieren.
Wenn schon, willst du stattdessen ein #define F_CPU 16.... schreiben.
Außerdem muss dieser Konstrukt, damit er Sinn hat, vor dem
#include <util/delay.h> stehen.
Danke soweit für die Tipps, wobei dabei neue Fragen aufgetaucht sind:
@Krapao: Wie kann ich mir das vorstellen?
1
ISR(PCINT0_vect,PB0)
2
{
3
LEDCounter++;
4
}
5
ISR(PCINT0_vect,PB1)
6
{
7
LEDCounter--;
8
LEDCounter--;
9
}
Bei dem Code (ob mit oder ohne Komma im Aufruf) produziert Fehler bei
mir...
Könntest Du mir ein kurzes Beispiel geben?
Zumindest kann ich mir das jetzt eher zusammenreimen!
@Jörg: Auch Dir danke! Diesen Codeschnipsel habe ich hier im Forum
gefunden und gemeint es wäre just der richtige, nachdem meine delay.h
den Fehler ausgibt, dass die F_CPU nicht formatiert ist.
Brauche ich dann ein long int, oder tut es (wenn ich Dich richtig
verstanden habe) ein:
Andreas König schrieb:> ISR(PCINT0_vect, PB0)
Nein. Es schmeißt dir den PCINT0_vect, egal, ob PB0 oder PB1
diesen getriggert haben. Welcher Pin es war, das muss deine
ISR schon selbst rausfinden (oder auch nicht).
> #ifndef F_CPU> F_CPU = 16000000;> #endif
Nein. *#define* F_CPU ..., nicht F_CPU = ...
Verzeihung, aber ich glaube, du solltest mit einem Buch über die
Programmiersprache C anfangen. Die Wirkungsweise des Präprozessors
gehört zwar nicht zum ersten Kapitel von C, aber spätestens zwischen
dem fünften und dem zehnten Kapitel sollte sie abgehandelt werden.
Joa, "Softwareentwicklung in C für Mikroprozessoren und Mikrokontroller"
liegt hier aufgeschlagen auf dem Tisch. Mein Problem ist, dass ich zwar
vor ein paar Jahren einiges mit C/C++ gemacht habe, seitdem aber eher
auf die weicheren Sprachen wie VBA gerutscht bin. Da ist der ein oder
andere "Hackler" jetzt unvermeidlich. Was mir ansonsten noch zu schaffen
macht, ist die Hardwareüberprüfung, also die Kenntnis, was genau die
Chips den jetzt eigentlich können und was nicht. Klassisch würde ich
beispielsweise eine IF-Abfrage starten, um zu überprüfen, ob jetzt die
Flanke des ersten oder des zweiten Interrupt-Pins ausschlaggebend war.
Da aber zum Zeitpunkt der IF-Abfrage das Ereignis bereits wieder vorbei
sein könnte, bin ich einfach auf der Suche nach anderen Wegen. Einer
davon war, zwein Interrupt-Routinen zu schreiben, die je von ihrem
zugeordneten Pin ausgeführt werden. Scheint ja nicht so die Hammer-Idee
gewesen zu sein :)
Andreas König schrieb:> Klassisch würde ich> beispielsweise eine IF-Abfrage starten, um zu überprüfen, ob jetzt die> Flanke des ersten oder des zweiten Interrupt-Pins ausschlaggebend war.> Da aber zum Zeitpunkt der IF-Abfrage das Ereignis bereits wieder vorbei> sein könnte, bin ich einfach auf der Suche nach anderen Wegen.
Genau da liegt der Hund im Pfeffer, oder wie das heißt ...
> Einer> davon war, zwein Interrupt-Routinen zu schreiben, die je von ihrem> zugeordneten Pin ausgeführt werden. Scheint ja nicht so die Hammer-Idee> gewesen zu sein :)
Geht schon, aber du darfst die beiden Pins nicht an ein und demselben
Port sitzen haben, damit sie eben nicht den gleichen Interrupt
triggern. Oder aber, du benutzt "richtige" Externinterrupts statt
der pin-change-Interrupts. Die haben dann auch wieder jeder ihren
eigenen Vektor.
Andreas König schrieb:
beispielsweise eine IF-Abfrage starten, um zu überprüfen, ob jetzt die
> Flanke des ersten oder des zweiten Interrupt-Pins ausschlaggebend war.> Da aber zum Zeitpunkt der IF-Abfrage das Ereignis bereits wieder vorbei> sein könnte,
Deine Lichtschrankenpulse sind nur µs breit?
Was misst du?
> bin ich einfach auf der Suche nach anderen Wegen. Einer> davon war, zwein Interrupt-Routinen zu schreiben, die je von ihrem> zugeordneten Pin ausgeführt werden. Scheint ja nicht so die Hammer-Idee> gewesen zu sein :)
Kann man so nicht sagen. Tasten an Interrupts sind meistens keine so
gute Idee. So schnell kannst du gar nicht drücken und wieder loslassen,
dass man das mittels Polling nicht mitkriegen würde.
Lichtschranken können durchaus ok sein, weil man kein Augenmerk auf
eventuelles Prellen legen muss.
Und das mit der Hammer Idee.
Ach komm. Du brauchst doch nur in der einen
ISR(PCINT0_vect)
den Pin einlesen und feststellen, welcher Pin den Interrupt ausgelöst
hat. Dazu vergleichst du mit dem Pin Zustand von früher und hast das
Ergebnis
1
uint8_tpinOld;
2
3
ISR(PCINT0_vect)
4
{
5
uint8_tpinNow=PINB;
6
7
uint8_tdiff=pinNow^pinOld;
8
9
// ist in diff das Bit 0 auf 1, dann hat PB0 den Interrupt ausgelöst
10
// ist in diff das Bit 1 auf 1, dann hat PB1 den Interrupt ausgelöst
11
// es können auch beide Bits gesetzt sein
12
13
if(diff&(1<<PB0))
14
LEDCounter++;
15
16
if(diff&(1<<PB1)){
17
LEDCounter--;
18
LEDCounter--;
19
}
20
21
pinOld=pinNow;// für den nächsten Interrupt merken
22
}
das ist doch keine Hexerei, wenn man seine logischen Operatoren kennt.
Andreas König schrieb:> Was mir ansonsten noch zu schaffen> macht, ist die Hardwareüberprüfung, also die Kenntnis, was genau die> Chips den jetzt eigentlich können und was nicht.
Da hilft jetzt alles nichts, du musst das Datenblatt lesen und
verstehen.
Der ATMega2560 hat zwei verschiedene external Interrupts: INT0-INT7, und
PCI0-PCI2. Die INTx-Interrupts reagieren auf jeweils einem zugeordneten
Eingang, die PCIx auf je 8 Eingängen.
Also sind die INTx die, die du brauchst.
Oliver
Sodale, jetzt erst mal Kompliment ans Forum, danke für die Diskussion!
Auf Mitleid (@Karl Heinz) wollt ich jetzt aber auch nicht machen,
trotzdem vielen Dank für den Code!
Meine Lichtschranke wird ca. 2 ms durchschnitten. Das reicht für den
Controller ja dicke aus. Für das Schalten der Spannung nehme ich jedoch
statt einem OP einen Transistor (hat sich halt so ergeben). Laut
Datenblatt bräuchte der ein 1-2 µs zum Schalten, jedoch spricht das Oszi
das eher von 1-2 ms am Ausgang des Transistors. Hier wird die LS noch
mit dem Finger oder mit einem geschnippten Lineal durchbrochen. Und da
der Ausgang der Transistors der Eingang am Controller ist, wollte ich da
erst mal kein unnötiges Risiko eingehen.
@Jörg:
habe mittlerweile auch dein "Define-Hinweis" genauer angeschaut. Ich
verstehe meinen Fehler, danke soweit.
Wie von Dir vorhergesagt, führen die Zeilen
1
2
#ifndef F_CPU
3
#define F_CPU 16000000UL;
4
#endif
zu Fehlern. Jedoch wird in der delay.h exakt das gleiche Konstrukt
verwendet nur dass "1000000UL" angegeben wird. Benutze ich den Code,
dann bekomme ich ein halbes Dutzend Fehlermeldungen aus der delay.h, in
der ich (seblstredend) nichts geändert hatte. Meist sind das ", or ;
expected", etc.
Wie läuft denn die Taktdefinition in der Regel korrekt? In der io.h,
delay.h ist sie nicht hinterlegt....
Andreas
Das ist 'ne beliebte Fratzenfalle. Wirf' mal das Semikolon am Ende
raus. Der Präprozessor macht einfache Textersetzungen (dieses
Kapitel solltest du dir in deinem C-Buch unbedingt nochmal
durchlesen), und das Semikolon dort führt dadurch in der Folge zu
den seltsamsten Fehlermeldungen, da es ja überall hinein ersetzt
wird.
Ja, der Präprozessor ist eine Krücke, aber er ist eine ziemlich
mächtige Krücke.
Jörg Wunsch schrieb:> Das ist 'ne beliebte Fratzenfalle. Wirf' mal das Semikolon am Ende> raus. Der Präprozessor macht einfache Textersetzungen (dieses> Kapitel solltest du dir in deinem C-Buch unbedingt nochmal> durchlesen), und das Semikolon dort führt dadurch in der Folge zu> den seltsamsten Fehlermeldungen, da es ja überall hinein ersetzt> wird.
ARRRGGGGG, Seite 19!!!! Dein Text kam mir jetzt seltsam bekannt
vor......