Forum: Compiler & IDEs Problem mit Interruptvektoren ATmega2560


von Andreas K. (totem)


Lesenswert?

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_t intPortB;
14
uint16_t intCounter;
15
volatile double intTimer;
16
volatile double  intZaehler;
17
volatile double intVhigh;
18
volatile double intVlow;
19
volatile uint8_t LEDCounter;
20
21
ISR(PCINT0_vect)
22
{
23
  /*Wird ausgelöst durch Flankenwechsel auf PIN B0*/
24
  /*TestCode*/
25
  
26
  LEDCounter++;
27
28
}  
29
30
ISR(PCINT1_vect)
31
{
32
  LEDCounter--;
33
  LEDCounter--;
34
}
35
36
37
int main(void)
38
{
39
    LEDCounter = 0;
40
    DDRD = 0x00;
41
42
    PCICR |= (1 << PCIE0);  //Enable Pin Change Interrupt Pin (0 = PinB0)
43
   
44
    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

von Krapao (Gast)


Lesenswert?

> 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.

von Krapao (Gast)


Lesenswert?

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

von Krapao (Gast)


Lesenswert?

Jo. "Bitnahme"... Bitname natürlich

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Andreas König schrieb:

Das hier:
1
/*Für Delay-Header*/
2
#ifndef F_CPU
3
  F_CPU = 16000000UL;
4
#endif

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.

von Andreas K. (totem)


Lesenswert?

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:
1
/*Für Delay-Header*/
2
#ifndef F_CPU
3
  F_CPU = 16000000;
4
#endif
5
6
#include <avr/io.h>
7
#include <util/delay.h>
8
#include <avr/interrupt.h>

Abermals danke!

Beste Grüße

Andreas

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Andreas K. (totem)


Lesenswert?

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 :)

von Peter II (Gast)


Lesenswert?

> volatile double intTimer;

warum eine Variable die im namen ein int hat ein double ist - könnte 
auch eine gute frage sein.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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_t pinOld;
2
3
ISR(PCINT0_vect)
4
{
5
  uint8_t pinNow = PINB;
6
7
  uint8_t diff = 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.

von Oliver (Gast)


Lesenswert?

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

von Andreas K. (totem)


Lesenswert?

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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Andreas König schrieb:

>
1
> #ifndef F_CPU
2
>     #define F_CPU 16000000UL;
3
> #endif
4
>

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.

von Andreas K. (totem)


Lesenswert?

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......

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.