Forum: Mikrocontroller und Digitale Elektronik externer Interrupt an PIC18


von Unterbrecher (Gast)


Lesenswert?

hallo,

ich arbeite mit einem PIC18LF25K50 MPLABX v2.25 und C18 Compiler 
(v.3.47).
Ich lese von einem externen Device den Status an RB2 aus. Hierfür möchte 
ich gerne den externen Interrupt 2 an RB2 nutzen. Der Interrupt soll bei 
ansteigender Flanke Triggern, d.h. wenn ich der Pin von LOW auf HIGH 
wechselt. RB2 habe ich als Input deklariert. Das ganze sieht in etwa wie 
folgt aus:
1
#pragma interrupt extern_interrupt
2
void extern_interrupt(void) {
3
4
    if(INTCON3bits.INT2IF)
5
    {
6
        // tue irgendetwas
7
        INTCON3bits.INT2IF = 0;     // cleare Interrupt Flag wieder, damit dieser wieder ausgelöst werden kann
8
    }
9
    
10
}
11
#pragma code
12
13
void main(void) {
14
...
15
16
 // interrupts / USART interrupts configuration
17
    RCONbits.IPEN   = 0;    // disable interrupt priority
18
    INTCONbits.GIE  = 1;    // enable interrupts
19
    INTCONbits.PEIE = 1;    // enable peripheral interrupts.
20
    PIE1bits.RCIE   = 1;    // enable USART receive interrupt
21
    PIE1bits.TXIE   = 0;    // disable USART TX interrupt
22
//    PIR1bits.RCIF = 0;    // make sure the RX flag is clear
23
24
    TRISBbits.TRISB2 = 1;       // Input
25
    INTCON3bits.INT2IE = 1;     // Enables the INT2 external interrupt
26
    INTCON3bits.INT2IF = 0;     // Internal 2 external interrupt Flag bit zurücksetzen
27
    INTCON2bits.INTEDG2 = 1;    // INT2 bei ansteigender Flanke, d.h. er triggert wenn er von LOW auf HIGH
28
}

Ich nutze also noch gleichzeitig den UART Interrupt, dieser funktioniert 
tadellos.

Wenn ich jetzt debugge und sehe dass sich  der Status an RB2 ändert wird 
jedoch Nie die Interruptroutine ausgelöst. Woran kann dies liegen?
Was fällt auf?

Danke

von Stampede (Gast)


Lesenswert?

Sind die Analogfuntkionen am RB2 deaktiviert? ANSELB Regstier sollte das 
sein.

von Unterbrecher (Gast)


Lesenswert?

Stampede schrieb:
> Sind die Analogfuntkionen am RB2 deaktiviert? ANSELB Regstier
> sollte das
> sein.

Jop, habe ich vergessen dazuzuschreiben:

 ANSELB = 0x00;

Daran kann es also nicht liegen, aber guter Hinweis!

von Witkatz :. (wit)


Lesenswert?

Für IOC wird der Inhalt des PORTB mit dem gelatchten Wert der letzten 
Leseroutine verglichen. Vor dem Löschen des INT2IF sollte PORTB immer 
gelesen (oder geschrieben) werden.

Gruß
witkatz

: Bearbeitet durch User
von Unterbrecher (Gast)


Lesenswert?

Witkatz :. schrieb:
> Für IOC wird der Inhalt des PORTB mit dem gelatchten Wert der
> letzten
> Leseroutine verglichen. Vor dem Löschen des INT2IF sollte PORTB immer
> gelesen (oder geschrieben) werden.
>
> Gruß
> witkatz

HMM ok,
habe es editiert:
1
void extern_interrupt(void) {
2
3
    if(INTCON3bits.INT2IF)
4
    {
5
        PORTBbits.RB2;    // Lese den Wert immer bevor Flag Bit gelöscht
6
        INTCON3bits.INT2IF = 0;
7
    }
8
    
9
}

Dies dürfte aber nicht der Grund dafür sein, dass die Interruptroutine 
gar nicht erst auslöst. Habe RB2 einmal als Output gesetzt und dann über 
Latch Register den Eingang manuell von LOW auf HIGH gezogen um zu testen 
ob der Interrupt dann auslöst. Ebenfalls ohne Erfolg!

von Volker S. (vloki)


Lesenswert?

INT2 ist doch gar kein IOC.

Wie sehen denn die Pegel für high und low genau aus? (und was für VDD)

Der Code für RX und INT2 ist natürlich in der gleichen Routine, oder?

: Bearbeitet durch User
von Unterbrecher (Gast)


Lesenswert?

Volker S. schrieb:
> INT2 ist doch gar kein IOC.
>
> Wie sehen denn die Pegel für high und low genau aus? (und was für VDD)
>
> Der Code für RX und INT2 ist natürlich in der gleichen Routine, oder?

VDD ist 3,3V
Ich habe den UART Interrupt jetzt erst einmal komplett auskommentiert, 
um wirklich nur den externen Interrupt zu testen. RB2 ist in der Tat ein 
EXT Interrupt (INT 2) Pin, d.h. KEIN Interruput on Change Pin -> IOC 
Register müßte demzufolge wegfallen.

Mich interessiert gerade nur warum der Interrupt nicht triggert, obwohl 
der Pin nachweislich von Low auf High gezogen wird!

von Witkatz :. (wit)


Lesenswert?

Volker S. schrieb:
> INT2 ist doch gar kein IOC.

Sorry, mein Fehler.

Du könntest in main das INT2IF auf einem Testausgang ausgeben, um zu 
schauen ob das Flag gesetzt wird.

Ich bin mit C18 nicht so vetrtraut, aber sollte nicht vor dem #pragma 
interrupt der Funktionsprototyp void extern_interrupt(void); deklariert 
werden?

: Bearbeitet durch User
von Hagi (Gast)


Lesenswert?

Stimmt auch das "Interrupt Priority" INTCON3bits.INT2IP ?

von Volker S. (vloki)


Lesenswert?

Unterbrecher schrieb:
> VDD ist 3,3V

low = ?
high = ?

Hagi schrieb:
> Stimmt auch das "Interrupt Priority" INTCON3bits.INT2IP ?
Ist völlig egal, da IPEN = 0...

: Bearbeitet durch User
von Volker S. (vloki)


Lesenswert?

Witkatz :. schrieb:
> Ich bin mit C18 nicht so vetrtraut, aber sollte nicht vor dem #pragma
> interrupt der Funktionsprototyp void extern_interrupt(void); deklariert
> werden?

Nein, nicht unbedingt. Leider fehlt der Teil im Post :-(
Im "compatibility mode" (nur eine Ebene/Vektor) muss man nur dafür 
sorgen, dass die Funkion an der richtigen Stelle steht.
1
#pragma interrupt extern_interrupt
2
3
4
#pragma code high_vector=0x0008
5
void extern_interrupt(void) {
6
...

Im "priority mode" kann ja am high-priority Vektor nur ein Sprung zur 
Funktion stehen. Dann braucht man den Prototyp. (wenn der Vektor nicht 
nach der Routine seht)

: Bearbeitet durch User
von Unterbrecher (Gast)


Lesenswert?

Volker S. schrieb:

> #pragma code high_vector=0x0008


Daran scheint es gelegen zu haben. Wird hier die Prirität des Interrupts 
angegeben, obwohl ich diese enablet habe? Warum ist diese Zeile für die 
Funktion so wichtig?

Nutze ich für den UART und externen Interrupt jetzt am besten dieselbe 
ISR oder zwei verschiedene Interrupt Routinen schreiben?

von Witkatz :. (wit)


Lesenswert?

Unterbrecher schrieb:
> Warum ist diese Zeile für die
> Funktion so wichtig?

C18 User's Guide Kap. 2.9.2.3 INTERRUPT VECTORS:
"MPLAB C18 does not automatically place an ISR at the interrupt vector."

: Bearbeitet durch User
von Volker S. (vloki)


Lesenswert?

Puuuhhhh, ganz schön viele Fragen auf einmal;-)

Also die 0x0008 ist eine der beiden Adressen im Programmspeicher auf die 
gesprungen wird, wenn ein Interrupt ausgelöst wird. Da du den Priority 
Mode nicht aktiviert hast, ist es sogar die einzige Adresse.

An dieser Adresse muss jetzt ein Sprung zu deiner IR-Service Routine 
oder eben die Routine selbst stehen.
Somit kann es auch nur eine Funktion geben in der alle deine IRs 
abgearbeitet werden...

Etwas ausführlicher ist das Thema im Kapitel 5 von uC_Quick-X.pdf 
dargestellt. (google...)

Mit was programmierst du deinen PIC? PICKITx?

: Bearbeitet durch User
von Unterbrecher (Gast)


Lesenswert?

Volker S. schrieb:
> Puuuhhhh, ganz schön viele Fragen auf einmal;-)
>
> Also die 0x0008 ist eine der beiden Adressen im Programmspeicher auf die
> gesprungen wird, wenn ein Interrupt ausgelöst wird. Da du den Priority
> Mode nicht aktiviert hast, ist es sogar die einzige Adresse.
>
> An dieser Adresse muss jetzt ein Sprung zu deiner IR-Service Routine
> oder eben die Routine selbst stehen.
> Somit kann es auch nur eine Funktion geben in der alle deine IRs
> abgearbeitet werden...

Super vielen Dank. Ich beschäftige mich erst seit kurzen mit Interrupts, 
deswegen ist jeder Input willkommen :)

Vielleicht noch eine Frage:
Wenn ich den PIC vorher in den Sleep Modus versetzt habe, wird der Pic 
wenn der externe Interrupt über RB2 ausgelöst wird dann automatisch aus 
dem Sleep Modus herausgeholt? Dies ist nämlich genau das, was ich 
realisieren möchte. Was anderes soll der externe Interrupt eigentlich 
nicht tun. Getestet habe ich dies nun wie folgt:
1
// start ISR code
2
#pragma interrupt isr   // let the compiler know that the function isr() is an interrupt handler
3
#pragma code isr = 0x08 // store the below code at address 0x08
4
void isr(void)
5
{
6
7
    Nop();
8
    Nop();
9
10
    if(INTCON3bits.INT2IF)
11
    {
12
        // bei Triggerung des externen Interrupts erwacht der Controller automatisch aus dem Sleep Modus
13
        INTCON3bits.INT2IF = 0;
14
    }
15
16
    if(PIR1bits.RCIF == 1)  // if the USART receive interrupt flag has been set
17
    {
18
        
19
            rx_char = getcUSART1();                             
20
        
21
    }
22
}
23
24
25
void main(void) {
26
...
27
#pragma code // return to the default code section
28
     // interrupts / USART interrupts configuration
29
    RCONbits.IPEN   = 0;    // disable interrupt priority
30
    INTCONbits.GIE  = 1;    // enable interrupts
31
    INTCONbits.PEIE = 1;    // enable peripheral interrupts.
32
    PIE1bits.RCIE   = 1;    // enable USART receive interrupt
33
    PIE1bits.TXIE   = 0;    // disable USART TX interrupt
34
//    PIR1bits.RCIF = 0;    // make sure the RX flag is clear
35
36
    TRISBbits.TRISB2 = 1;       // Input an RB2
37
    ANSELBbits.ANSB2 = 0;       // digitaler pin
38
39
    INTCON2bits.INTEDG2 = 1;    // INT2 bei ansteigender Flanke, d.h. er triggert wenn er von LOW auf HIGH
40
//    INTCON3bits.INT2IP = 1;     //High Priority
41
    INTCON3bits.INT2IE = 1;     // Enables the INT2 external interrupt
42
    INTCON3bits.INT2IF = 0;     // Internal 2 external interrupt Flag bit zurücksetzen
43
    INTCON2bits.INTEDG2 = 0;
44
45
46
...
47
while(1)
48
{
49
Sleep();    // Sleep Modus
50
}
51
}

von Volker S. (vloki)


Lesenswert?

Unterbrecher schrieb:
> Vielleicht noch eine Frage:
> Wenn ich den PIC vorher in den Sleep Modus versetzt habe, wird der Pic
> wenn der externe Interrupt über RB2 ausgelöst wird dann automatisch aus
> dem Sleep Modus herausgeholt? Dies ist nämlich genau das, was ich
> realisieren möchte. Was anderes soll der externe Interrupt eigentlich
> nicht tun. Getestet habe ich dies nun wie folgt:

Ja, das sollte so sein ;-) Was ist bei deinem Test heraus gekommen?

von Unterbrecher (Gast)


Lesenswert?

Der externe Interrupt löst wie gewünscht an RB2 aus. Leider funktioniert 
dann der UART Interrupt nicht mehr wie gewünscht :(
Ohne Sleep() Funktion läuft es einwandfrei.

Wie implementiert: Wird der PIC bei externen Interrupt immer aus dem 
Sleep Modus geholt? Ist dies richtig so? Gewollt ist:

1. Setze PIC in Sleep
2. Durch externen Interrupt PIC "awaken"
3. Wach bleiben und über UART Interrupt Daten einlesen
4. Wenn fertig PIC wieder in Sleep Modus setzen und auf externen 
Interrupt warten!

von Volker S. (vloki)


Lesenswert?

In dem Test-Programm, das du gepostet hast, geht der PIC doch sofort 
wieder in den Sleep (wenn er aus dem Pin-IR zurück in main kommt)

von Unterbrecher (Gast)


Lesenswert?

Is natürlich quatsch :)

Habe es jetzt wie folgt:
1
// start ISR code
2
#pragma interrupt isr   // let the compiler know that the function isr() is an interrupt handler
3
#pragma code isr = 0x08 // store the below code at address 0x08
4
void isr(void)
5
{
6
7
    if(INTCON3bits.INT2IF)
8
    {
9
10
        // bei Triggerung des externen Interrupts erwacht der Controller automatisch aus dem Sleep Modus
11
        INTCON3bits.INT2IF = 0;
12
    }
13
14
    if(PIR1bits.RCIF == 1) // if the USART receive interrupt flag has been set
15
    {
16
         Nop();
17
         Nop();
18
        
19
         rx_char = getcUSART1();  
20
21
        
22
    }
23
}
24
#pragma code // return to the default code section
25
26
27
void main(void) {
28
    
29
    ...
30
31
     // interrupts / USART interrupts configuration
32
    RCONbits.IPEN   = 0;    // disable interrupt priority
33
    INTCONbits.GIE  = 1;    // enable interrupts
34
    INTCONbits.PEIE = 1;    // enable peripheral interrupts.
35
    PIE1bits.RCIE   = 1;    // enable USART receive interrupt
36
    PIE1bits.TXIE   = 0;    // disable USART TX interrupt
37
38
    INTCON2bits.INTEDG2 = 1;    // INT2 bei ansteigender Flanke, d.h. er triggert wenn er von LOW auf HIGH
39
//    INTCON3bits.INT2IP = 1;     //High Priority
40
    INTCON3bits.INT2IE = 1;     // Enables the INT2 external interrupt
41
    INTCON3bits.INT2IF = 0;     // Internal 2 external interrupt Flag bit zurücksetzen
42
    INTCON2bits.INTEDG2 = 0;
43
44
    Sleep();   // gehe per default in Sleep Modus
45
    while(1)
46
    {
47
     // wenn Uart Verbindung beendet gehe wieder in Sleep Mode
48
     if(UART_END)
49
            Sleep();
50
51
}

Das scheint per Software schon einmal so zu funktionieren. Dumme Frage 
zum Schluß: Kann ich per Software kontrollieren, ob der PIC sich 
wirklich im Sleep Modus befindet oder ist dies nur meßtechnisch zu 
ermitteln? Habe leider nur nen Voltmeter und kein Oszi oder ähnliches 
hier.

von Noch einer (Gast)


Lesenswert?

> ob der PIC sich wirklich im Sleep Modus befindet

Einfach schauen, wie schnell die Spannung sinkt, nachdem du das 
Netzgerät ausschaltest. Größenordnung - Ohne sleep() nicht mal eine 
Sekunde, mit sleep() 20 Sekunden.

von Unterbrecher (Gast)


Lesenswert?

Hmm in dem Zeitintervall kann ich leider keinen großen Unterschiede im 
Sleep oder ohne Sleep entdecken. Die Spannung fällt innerhalb einer 
Sekunde auf 0,5V rapide ab und dann in etwa 10 Sec gegen 0V (sowohl im 
Sleep Mod als auch im "normalen" Modus).

Das ist natürlich sehr unprofessionell ohne Oszi aufgenommen.

von vloki (Gast)


Lesenswert?

Warim sollte der pic nicht in den sleep gehen? Setz einfach einen 
Ausgang auf high nach dem Sleep und vor dem while.
Ob man das auch mit dem Debugger testen kann, weiss ich jetzt nicht...

von Noch einer (Gast)


Lesenswert?

> keinen großen Unterschiede

Hmm... wer weiß, was das Netzgerät macht. Besser nur einen Elko 
entladen. Elko an die Pic-Platine und einen Schalter in die Zuleitung.

> Warim sollte der pic nicht in den sleep gehen?

Die Frage ist eher, bring es was? Wieso mit sleep() und Interrupts 
rumärgern, wenn da ein Spannungsteiler mehr Strom verbraucht, als der 
Controller.

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.