Forum: Mikrocontroller und Digitale Elektronik ISR erstellen mit einem PIC18


von Armin (Gast)


Lesenswert?

Hi Leute,

ich hab hier ein PICDEM2 PLUS Demo Board vor mir das mit einem 
PIC18F4320 bestückt ist. Ich möchte jetzt testweise eine ISR schreiben, 
welche bei Overflow des Timer0 die LEDs ansteuert. Ich habe folgende 
Konfigurationen an dem Timer und Interrupt vorgenommen:

    T0CON = 0x87;

    TMR0H = 0x00;
    TMR0L = 0xFF;

    INTCON = 0xA0;
    INTCON2 = 0x84;
    INTCON3 = 0x00;

    RCONbits.IPEN = 1;
    INTCONbits.GIE = 0;

Den Timer0 habe ich dabei auf hohe Priorität gesetzt und diese auch 
aktiviert. Der Code wird so fehlerfrei compiliert.

GIE setze ich in der mainloop auf 1.

Meiner Meinung nach müssten die Register alle korrekt initialisiert 
sein, aber ich hab leider zu wenig Ahnung von dem Mechanismus wie man 
eine ISR unter MPLAB erstellt. Ich hab das mal so versucht, anhand eines 
Beispielprogrammes das ich gefunden hab:

#pragma code InterruptVectorHigh = 0x08

void InterruptVectorHigh (void){

    PORTB = LED1;

    /*
    if(PORTB == OFF)
        PORTB = LED0;
    else if(PORTB == LED0)
        PORTB = LED1;
    else if(PORTB == LED1)
        PORTB = LED2;
    else if(PORTB == LED2)
        PORTB = LED3;
    else if (PORTB == LED3)
        PORTB = LED0;
     */

    TMR0H = 0x00;
    TMR0L = 0xFF;
    INTCONbits.TMR0IF = 0;
}

Die ISR wird aber nie ausgeführt. Ich hab schon die Hilfe von MPLAB 
durchforstet, werde aber nicht wirklich schlau daraus. Könnte mir jemand 
erklären, wie man die ISR an die richtige Stelle legt?

Danke!

von holger (Gast)


Lesenswert?

So hab ich das immer gemacht:
1
void low_isr(void);
2
void high_isr(void);
3
4
/*
5
* For PIC18cxxx devices the high interrupt vector is found at
6
* 00000008h. The following code will branch to the
7
* high_interrupt_service_routine function to handle
8
* interrupts that occur at the high vector.
9
*/
10
#pragma code high_vector=0x08
11
//#pragma code high_vector=0x208 //remap for bootloader
12
void interrupt_at_high_vector(void)
13
{
14
_asm GOTO high_isr _endasm
15
}
16
17
/*
18
* For PIC18cxxx devices the low interrupt vector is found at
19
* 00000018h. The following code will branch to the
20
* low_interrupt_service_routine function to handle
21
* interrupts that occur at the low vector.
22
*/
23
#pragma code low_vector=0x18
24
//#pragma code low_vector=0x218 //remap for bootloader
25
void interrupt_at_low_vector(void)
26
{
27
_asm GOTO low_isr _endasm
28
}
29
#pragma code /* return to the default code section */
30
31
#pragma interruptlow low_isr
32
void low_isr (void)
33
{
34
 // Hier kommen deine Interruptroutinen
35
}
36
37
#pragma interrupt high_isr
38
void high_isr (void)
39
{
40
 // Hier kommen deine Interruptroutinen
41
}

von Armin (Gast)


Lesenswert?

Danke für die Hilfe! Leider funktioniert es immer noch nicht. Könntest 
du mir mal kurz erklären was da genau passiert?

#pragma code high_vector=0x08

diese Zeile bestimmt den Ort des Vektors, an den die ISR mit höherer 
Priorität verzweigt, aber wo taucht "high_vector" im Code wieder auf? 
Der Controller verzweigt doch automaisch an diese Adresse und arbeitet 
den Code dort ab, auch ohne diese Adresse explizit anzugeben, oder? Und 
ist das ein Schlüsselwort oder kann der Vector jeden beliebigen Namen 
bekommen?

void interrupt_at_high_vector(void)
{
_asm GOTO high_isr _endasm
}

Ist hier "interrupt_at_high_vector" auch ein Schlüsselwort, oder kann 
auch hier die Funktion jeden beliebigen Namen bekommen?

_asm und _endasm sind für den Assemblerbefehl GOTO notwenig, oder?

was genau macht #pragma code?

#pragma interruptlow low_isr

diese Anweisung speichert die Funktion low_isr an den Vector 0x18, der 
weiter oben angegeben ist, oder?

Irgendwie ist es mir noch nicht wirklich klar, wie genau die ISR an der 
richtigen Adresse landet. Wäre schön wenn mir das jemand noch mal genau 
anhand der ganzen Befehle erklären könnte!

Danke!

von Christian K. (Firma: Atelier Klippel) (mamalala)


Lesenswert?

Hallo Armin,

also, IRQ Routinen brauchen etwas Extra-Code am Anfang und Ende. In 
diesem werden bestimmte Register gesichert (beim Start der Routine) und 
dann wiederhergestellt (beim Verlassen der Routine). Was 
gespeichert/wiederhergestellt werden muss bestimmt dann der Compiler 
selber. Damit dieser aber weiss was denn nun eine IRQ Routine ist, und 
ob sie eine High- oder Low-Priority Routine ist, gibt es die
1
#pragma interruptlow low_isr

und
1
#pragma interrupt high_isr

Pragma's. Bzgl. der Vektoren selber gibt es noch ein weiteres Problem, 
nämlich das der High-Vektor bei 0x08 und der Low-Vektor bei 0x18 liegt. 
Da man in die 0x10 Worte kaum Code reinbekommt, sollte da immer eine 
Sprunganweisung stehen. Also das Grundgerüst wie es Holger gezeigt hat.

Zu deinem eigentlichen Problem: Du initialisierst zwar die Timer- und 
Interrupt-Register, machst das aber alles "in einem Rutsch", also durch 
zuweisen von Werten für das ganze jeweilige Register. Besser man macht 
es nacheinander.

Das ganze sollte eher so aussehen:
1
#include <p18cxxx.h>
2
3
#define LED_OFF    0x00
4
#define LED_1      0x01
5
#define LED_2      0x02
6
#define LED_3      0x04
7
#define LED_4      0x08
8
9
// IRQ Vektoren
10
11
void low_isr(void);
12
void high_isr(void);
13
14
#pragma code irq_high=0x08
15
void irq_high(void)
16
{
17
    _asm GOTO high_isr _endasm
18
}
19
20
#pragma code irq_low=0x18
21
void irq_low(void)
22
{
23
    _asm GOTO low_isr _endasm
24
}
25
26
#pragma interrupt high_isr
27
void high_isr(void)
28
{
29
    if(INTCONbits.TMR0IF)
30
    {
31
        INTCONbits.TMR0IF = 0; // TMR0 IRQ Flag löschen
32
33
        if(LATB == LED_OFF)
34
        {
35
            LATB = LED_1;
36
        }
37
        else if(LATB == LED_1)
38
        {
39
            LATB = LED_2;
40
        }
41
        else if(LATB == LED_2)
42
        {
43
            LATB = LED_3;
44
        }
45
        else if(LATB == LED_3)
46
        {
47
            LATB = LED_4;
48
        }
49
        else if(LATB == LED_4)
50
        {
51
            LATB = LED_OFF;
52
        }
53
    }
54
}
55
56
#pragma interruptlow low_isr
57
void low_isr(void)
58
{
59
    // Ggf. Code für Low-Priority IRQ's
60
}
61
62
// Init des Timers und der IRQ Register
63
64
void init_pic(void)
65
{
66
    LATB = 0;               // PORTB Ausgabe-Latch löschen
67
    TRISB = 0b11110000;     // PORTB Bits 0-3 als Ausgang
68
69
    RCONbits.IPEN = 1;      // IRQ Prioritäten erlauben
70
    INTCONbits.GIEH = 1;    // High-Priority IRQ's erlauben
71
    INTCONbits.GIEL = 0;    // Low-Priority IRQ's sperren
72
    INTCON2bits.TMR0IP = 1; // TMR0 als High-Priority IRQ
73
74
    T0CON = 0x07;           // Prescaler für TMR0 setzen
75
    TMR0H = 0x00;           // TMR0 zurücksetzen, zuerst High-Byte
76
    TMR0L = 0x00;           // Danach Low-Byte schreiben
77
78
    INTCONbits.TMR0IF = 0;  // TMR0 IRQ-Flag löschen
79
    INTCONbits.TMR0IE = 1;  // TMR0 IRQ aktivieren
80
81
    T0CON.TMR0ON = 1;       // TMR0 starten
82
}
83
84
void main(void)
85
{
86
    init_pic();
87
88
    while(1)
89
    {
90
        // Hauptprogram-Code
91
    }
92
}

(Keine Garantie auf Fehlerfreiheit, rein aus dem Kopf gemacht grad...)

Grüße,

Chris

Edit: Achja, je nach Taktquelle des PIC kann es beim 16-Bit Timer plus 
Prescaler schon recht lange dauern bis sich da was tut. Also 
entsprechend TMR0H/TMR0L setzen beim Init als auch im IRQ selber (Habe 
ich hier im IRQ-Code nicht gemacht). Oder aber den Prescaler 
entsprechend Umkonfigurieren.

Tip: TMR0H und TMR0L Register immer am Anfang der IRQ-Routine setzen. So 
hat man immer die gleiche Latenz, egal wieviel Code anschliessend noch 
folgt.

von Armin (Gast)


Lesenswert?

Ok, erst mal vorweg, die interrupts kommen jetzt. Ich hatte leider im 
ganzen wirrwarr das Richtungsregister für die LEDs auskommentiert, darum 
konnten sie nicht mehr angesteuert werden. Der Code von Holger 
funktioniert also auf meinem Board. Ich versuch jetzt noch mal 
zusammenzufassen, was ich bisher gelernt hab:
Die Zeile

#pragma code irq_high=0x08

legt fest, dass die Funktion mit dem Namen "irq_high" an die Adresse 
0x08 gelegt wird. Danach folgt die Definition dieser Funktion:

void irq_high(void)
{
    _asm GOTO high_isr _endasm
}

Diese besteht im Grunde nur aus einem Sprungbefehl, da der Platz für den 
Code nicht ausreicht um eine umfangreiche Funktion aufzunehmen. Die 
Anweisungen _asm und _endasm deuten an, dass der Befehl GOTO als 
Assemblerbefehl zu interpretieren ist. Ist das soweit richtig?

Der Sprung geht dann zu der Funktion "high_isr", die später definiert 
wird.

Dazu hätte ich aber gleich noch mal eine Frage. In dem Code von Holger 
steht

#pragma code high_vector=0x08
void interrupt_at_high_vector(void){...}

Es funktioniert trotzdem, obwohl die Funktionen verschiedene Namen 
haben, "high_vector" und "interrupt_at_high_vector". Warum ist das so?

Auf den ersten Blick wirkt es für mich doppelt gemoppelt, zunächst die 
Funktion "irq_high" an die Adresse 0x08 zu legen mit dem Sprungbefehl zu 
"high_isr" und später dann nochmal diese als ISR zu kennzeichnen. Denn 
der Prozessor springt ja beim Interrupt an die Adresse 0x08 und danach 
dann gleich zur entsprechenden Funktion. Und tatsächlich, wenn ich die 
Zeile

#pragma interrupt high_isr

weglassen, funktionert das ganze immer noch. Warum muss ich diese Zeile 
dann mit rein bringen?

Und auch die Zeile

#pragma code

die da so einzeln rumschwirrt, kann ich weglassen ohne dass sich was 
ändert. Du hast diese Zeile ja auch nicht drin. Wozu ist diese 
notwendig?

Das waren jetzt mal wieder ne ganze Menge fragen, aber ich versuche 
einfach nur zu verstehn, wie das ganze Funktionert.

Danke!

von Christian K. (Firma: Atelier Klippel) (mamalala)


Lesenswert?

Armin schrieb:
> Die Zeile
>
> #pragma code irq_high=0x08
>
> legt fest, dass die Funktion mit dem Namen "irq_high" an die Adresse
> 0x08 gelegt wird. Danach folgt die Definition dieser Funktion:
>
> void irq_high(void)
> {
>     _asm GOTO high_isr _endasm
> }
>
> Diese besteht im Grunde nur aus einem Sprungbefehl, da der Platz für den
> Code nicht ausreicht um eine umfangreiche Funktion aufzunehmen. Die
> Anweisungen _asm und _endasm deuten an, dass der Befehl GOTO als
> Assemblerbefehl zu interpretieren ist. Ist das soweit richtig?

Ja, das ist richtig.

> Dazu hätte ich aber gleich noch mal eine Frage. In dem Code von Holger
> steht
>
> #pragma code high_vector=0x08
> void interrupt_at_high_vector(void){...}
>
> Es funktioniert trotzdem, obwohl die Funktionen verschiedene Namen
> haben, "high_vector" und "interrupt_at_high_vector". Warum ist das so?

Weil der Compiler die Code-Adresse für allen nachfolgenden Code durch 
das #pragma bestimmt. Trotzdem sollte man beidem den gleichen Namen 
geben, damit man auch später noch weiss das es zusammengehört. Sonst 
kann man sich in komplexeren Projekten dann schnell verirren wenn man 
viel Code umherschiebt/umorganisiert und dann mal ein #pragma übersehen 
hat.

> Auf den ersten Blick wirkt es für mich doppelt gemoppelt, zunächst die
> Funktion "irq_high" an die Adresse 0x08 zu legen mit dem Sprungbefehl zu
> "high_isr" und später dann nochmal diese als ISR zu kennzeichnen.

Nein. Die Kennzeichnung als interrupt/interruptlow anstelle von code ist 
hier wichtig. Schau dir mal im Disassembler den generierten Programcode 
an, einmal mit #pragma interrupt und einmal mit #pragma code. Du wirst 
sehen das er beim interrupt noch extra Code am Start und Ende der 
Routine einfügt. Dört werden dann, je nach Bedarf, die Register für 
Stackzugriff, Arbeitsregister, etc. gespeichert. Auch das return selber 
ist ein anderes.

Speziell bei deinem sehr einfachen Code merkt man den Unterschied nicht, 
sweil ja sonst nichts weiter gemacht wird in der main. Sobald Du aber 
neben dem IRQ noch weiteren Code ausführst, wird es ganz schnell zu 
Problemen kommen wenn die IRQ's nicht als solche angelegt sind.

Beispiel: Du machst eine Berechnung. Währenddessen kommt der IRQ. In dem 
rechnest Du ebenfalls etwas. Wenn jetzt die temporären Register für die 
Mathefunktionen, sowie der Stackzeiger nicht gesichert und 
wiederhergestellt werden im IRQ, kommt nur Murks bei raus.

> Und auch die Zeile
>
> #pragma code
>
> die da so einzeln rumschwirrt, kann ich weglassen ohne dass sich was
> ändert. Du hast diese Zeile ja auch nicht drin. Wozu ist diese
> notwendig?

Wirklich notwendig nicht, ist aber guter Stil damit man sieht das wieder 
der normale Code weitergeht. Im Normalfall betrachtet der Compiler nur 
die Funktion, die direkt auf das #pragma interrupt bzw. interruptlow 
folgt, als IRQ Code. Aber, es kann ja auch mal ein Bug in einer 
Compilerversion sein. Oder sie ändern das. Dann sucht man sich dämlich 
und weiss nicht was der Fehler ist. Das ich das in meinem Beispiel nicht 
drin hatte war mein Fehler, ich hätte es mit dazupacken sollen.

> Das waren jetzt mal wieder ne ganze Menge fragen, aber ich versuche
> einfach nur zu verstehn, wie das ganze Funktionert.
>
> Danke!

Kein Problem!

Grüße,

Chris

von Mick M. (highlow)


Lesenswert?

All diese genannten Informationen stehen übrigens auch im C18 Compiler 
User's Guide von Microchip.

von holger (Gast)


Lesenswert?

>> Und auch die Zeile
>>
>> #pragma code
>>
>> die da so einzeln rumschwirrt, kann ich weglassen ohne dass sich was
>> ändert. Du hast diese Zeile ja auch nicht drin. Wozu ist diese
>> notwendig?

>Wirklich notwendig nicht, ist aber guter Stil damit man sieht das wieder
>der normale Code weitergeht.

@Christian
Erstmal danke das du da so viel erklärt hast.

Dieses rumschwirrende #pragma code ist schon wichtig.
Wenn man mit diesen ganzen #pragma Anweisungen Code
an feste Adressen legt sollte man tunlichst so schnell
wie möglich wieder auf die Standard Code Section umschalten.
Bei Verwendung eines Bootloaders muss auch der Startup Code
an eine feste Adresse gelegt werden. Wenn das #pragma code
nicht dort steht wo es ist wird auch der komplette Code der
folgt ab dieser Adresse eingefügt. Das kann dann mit anderen
#pragma Anweisungen kollidieren. Spätestens beim linken knallt
es dann weil sich Code Sections überschneiden.

von Armin (Gast)


Lesenswert?

Vielen Dank für die vielen Infos!!

Mick M. schrieb:
> All diese genannten Informationen stehen übrigens auch im C18 Compiler
> User's Guide von Microchip.

Ich hab mich schon gefragt, woher ihr das alles wisst. Ich hab das 
Handbuch vom Controller gelesen und die Hilfe von MPLAb durchforstet, 
aber nix gefunden was mir weiter geholfen hat.

von Christian K. (Firma: Atelier Klippel) (mamalala)


Lesenswert?

holger schrieb:
> @Christian
> Erstmal danke das du da so viel erklärt hast.

Ebenfalls Danke.

> Dieses rumschwirrende #pragma code ist schon wichtig.
> Wenn man mit diesen ganzen #pragma Anweisungen Code
> an feste Adressen legt sollte man tunlichst so schnell
> wie möglich wieder auf die Standard Code Section umschalten.
> Bei Verwendung eines Bootloaders muss auch der Startup Code
> an eine feste Adresse gelegt werden. Wenn das #pragma code
> nicht dort steht wo es ist wird auch der komplette Code der
> folgt ab dieser Adresse eingefügt. Das kann dann mit anderen
> #pragma Anweisungen kollidieren. Spätestens beim linken knallt
> es dann weil sich Code Sections überschneiden.

Da hast Du natürlich vollkommen Recht.

Und der Vollständigkeit halber: Man muss den High-ISR natürlich nicht 
unbedingt mit einem GOTO anspringen, wenn es auf jeden Takt ankommt. Da 
kann man auch fies tricksen und den Bereich ab 0x08 nutzen. Man muss nur 
Sicherstellen das dann bei 0x18 in dem Code das GOTO zum Low-ISR 
auftaucht. Z.B. sowas hier:
1
#pragma code overlay sequencer_isr = 0x08
2
void sequencer_isr(void)
3
{
4
    _asm
5
    MOVFF   next_shutter_port, PORTD
6
    BCF     PIR1, 1, ACCESS
7
    BTFSC   sequencer_status, STATUSBIT_NEXT_TRIGGER_SIG, ACCESS
8
    BSF     sequencer_status, STATUSBIT_TRIGGER_START, ACCESS
9
    MOVF    PCL, 0, 0             // load program counter latches with current address    
10
    MOVF    sequencer_step, 0, 0  // retrieve which branch to execute
11
    ADDWF   PCL, 1, 0             // add that branch-id to the current program counter 
12
    GOTO    low_isr               // this instruction must be at 0x18 to handle low priority interrupts
13
STEP_FUDGE_ADR:
14
    MOVFF   fudge_time, PR2
15
    MOVFF   sequence+0, next_shutter_port
16
17
    ....

Grüße,

Chris

von Armin (Gast)


Lesenswert?

Hi Leute!

Ich hab hier noch ein Problem mit dem Externen Interrupt an INT0. Ich 
hab folgenden Code:

void main(void)
{
    initBoard();

    while(1)
    {
        LATAbits.LATA0 = 0;
        for(i = 0; i < 5; i++);
        LATAbits.LATA0 = 1;
        for(i = 0; i < 500; i++);
    }

    return;
}

#pragma code high_vector = 0x08

void high_vector(void)
{
    _asm GOTO high_isr _endasm
}

#pragma code

void high_isr(void)
{
if(INTCONbits.INT0IF)
    {
        INTCONbits.INT0IF = 0;

        LATB = LED1;
        for(i = 0; i < 100; i++);
        LATB = OFF;
    }
}

void initBoard (void)
{
    /* Timer0 Config */
    T0CON = 0x06;               /* prescale 1:256 */
    T0CONbits.TMR0ON = 0;       /* start timer */
    T0CONbits.T08BIT = 0;       /* 16 Bit Modus */
    T0CONbits.T0CS = 0;         /* Internal Clock */
    T0CONbits.PSA = 0;          /* Prescaler active */

    TMR0H = 0xFF;               /* Timer0 High Byte */
    TMR0L = 0xF0;               /* Timer0 Low Byte */

    /* Interrupt Config */
    INTCON = 0x00;
    INTCONbits.GIE = 1;         /* Global Interrupt Enable High (IPEN = 
1)
                                 * or Global Interrupt Enable (IPEN = 0) 
*/
    INTCONbits.PEIE = 1;        /* Global Interrupt Enable Low (IPEN = 
1)
                                 * or Peripheral Interrupt Enable (IPEN 
= 0)*/
    INTCONbits.TMR0IE = 0;      /* Timer0 IE */
    INTCONbits.INT0IE = 1;      /* External Interrupt INT0 IE */
    INTCONbits.RBIE = 0;        /* PortB IE */

    RCON = 0x00;
    RCONbits.IPEN = 0;          /* Interrup Priotity */

    INTCON2 = 0x00;
    INTCON2bits.TMR0IP = 1;     /* Timer0 High Priority */
    INTCON2bits.INTEDG0 = 1;    /* Interrupt bei steigender Flanke */

    INTCON3 = 0x00;

    /* Port Config */
    TRISA = 0x00;                  /* Port A Ausgang */
    TRISB = 0x00;                  /* Port B Ausgang */
    TRISBbits.RB0 = 1;             /* INT0 Pin Eingang */
    TRISC = 0x00;
    //TRISD = 0;

    PORTB = OFF;                /* LEDs aus */
}

In der main-Schleife erzeuge ich immer an dem Pin RA0 eine steigene 
Flanke. Auf dem Board habe ich diesen Pin mit dem INT0 Pin verbunden. In 
der entsprechenden ISR soll dann beim Interrupt eine LED kurz 
aufleuchten. Das mit den for-Schleifen ist natürich kein schöner Stil, 
aber es ist nur zu Testzwecken ob der Interrupt kommt. Leider tut sich 
nichts. Sieht jemand wo der Fehler liegt? In der Init-Datei sollten alle 
Interrupts an sein und auch die Flanke an INT0 richtig einegestellt 
sein.

Danke!

von Armin (Gast)


Lesenswert?

Sorry is bei copy,paste verloren gegangen. Vor der ISR steht noch

#pragma interrupt high_isr

Auch die includes und Prototypen hab ich noch drin, hab ich jetzt aber 
der Übersichtlichkeit halber weg gelassen.

von Erich (Gast)


Lesenswert?

>    T0CONbits.TMR0ON = 0;       /* start timer */

Hmmm, wirklich ?
Wohl eher nicht... , du solltest dein Programm etwas besser selbst 
durchsehen.
In wenigen Sekunden habe ich diese merkwürdige Zeile gefunden.
Frag doch mal GOOGLE:  "T0CONbits.TMR0ON"

Gruss

von Christian K. (Firma: Atelier Klippel) (mamalala)


Lesenswert?

Also wenn das LED1 immer noch mit 0x01 definiert ist, dann wird sich da 
auch nichts tun. Denn das wäre Bit 0, was bei Ausgabe auf PORTB dann 
eben genau der Pin ist, den Du als Eingang benutzen willst...

Woher kommt die Variable "i" in den Schleifen? Global definiert etwa? 
Das dürfte daneben gehen wenn Du das dann im Program als auch im 
Interrupt benutzt. So wie Du den Code gepostet hast dürfte der nicht 
einmal Compilieren, sondern schon da Fehler ausgeben. Eben weil nirgends 
eine Definition von "i" zu sehen ist.

Grüße,

Chris

Edit: Es ist ratsam immer den gesamten Code zu posten, am besten als 
Anhang zum Beitrag, in normalem ASCII.

von Armin (Gast)


Angehängte Dateien:

Lesenswert?

Also die LED1 ist bei mir die 2. LED, also mit 0x02 definiert, der Pin 
fällt also nicht mit INT0 zusammen. Ich hab jetzt diese LED auch direkt 
angesprochen und eine zusätzliche Zählvariable für die ISR spendiert 
damit ich nicht auf das globale i zurückgreifen muss. Leider kommt der 
Interrupt immer noch nicht korrekt.

Nochmal eine kleine Zusammenfassung was der Code macht.
In der Main Loop wird PIN RA0 periodisch von 0 auf 1 gesetzt um die 
Flanke zu erzeugen. Dieser Pin ist als Ausgang definiert und am Board 
direkt mit dem RB0/INT0 Pin verbunden. RB0 ist entsprechend als Eingang 
definiert. Somit sollte dann ein Interrupt ausgelöst werden. Wenn dieser 
kommt, wird in der ISR die LED1 (RB1) eingeschaltet. Leider bleibt die 
LED aus was daruf hinweist, dass der Interrupt nicht ausgelöst wird.

Ich hab den kompletten Code angehängt.

Danke!

von Armin (Gast)


Lesenswert?

Ich push noch mal, da der INT0 immer noch nicht geht...

von Christian K. (Firma: Atelier Klippel) (mamalala)


Lesenswert?

Du hast in dem INT0 nur einen ganz, ganz kleinen Loop der bis 100 zählt. 
Das dürfte so schnell abgearbeitet sein das Du das kurze aufblitzen kaum 
bis garnicht siehst.

Versuche es mal mit:
1
if(INTCONbits.INT0IF)
2
{
3
    INTCONbits.INT0IF = 0;
4
5
    LATBbits.LATB1 = !LATBbits.LATB1;
6
}

Auch den Zähler in der main, wo Du den Pin auf LATA toggelst, kannst Du 
ruhig größer machen, so 5000 bis 10000.

Grüße,

Chris

von Armin (Gast)


Lesenswert?

Die Anzahl der Schleifendurchläufe hab ich gestestet, 100 sieht man auf 
jeden Fall, sogar 20 - 50 sind noch sichtbar. Scheinbar läuft der 
Prozessor auf einer niedrigen Taktzahl. Der Fehler muss irgendwo anders 
liegen.

von Christian K. (Firma: Atelier Klippel) (mamalala)


Lesenswert?

Achja, ganz vergessen: Es fehlt bei deinem Code die Initialisierung des 
ADC Moduls. Standardmäßig sind die Pins, die auch als ADC Eingang dienen 
können, eben als analoge und nicht als digitale Pins konfiguriert. Wenn 
der PIC einen internen Komparator hat, der die Analogpins nutzen kann, 
muss der auch auf digital gestellt werden. Also mal im DB zu dem PIC 
nach ADC und Comparator schauen.

Nehme doch aus der IRQ routine mal die timergesteuerten LED Sachen raus, 
vielleicht haste da ja ein doofes Timing erwischt. TMR0IP setzt Du auf 
1, hast aber IPEN auf 0, also IRQ Prioritäten ausgeschaltet. Auch etwas 
komisch.

Hast Du mal getestet wie der Code reagiert wenn Du auf RB0 selber mit 
einem Draht mal 0V/5V anlegst, anstatt das einen Portpin machen zu 
lassen?

Grüße,

Chris

von Armin (Gast)


Lesenswert?

Ich hab im DB mal was gelesen über die konfiguration von digitalen und 
analogen Pins, allerdings konnte ich das entsprechende Register dazu 
nicht ansprechen, warum auch immer. Darum habe ich das nicht mehr 
konfiguriert.

Den Code mit den Timergesteuerten LEDs habe ich auskommentiert, meinst 
du das?

Ich habe erst versucht, mit einem Draht den RB0 auf 5V zu legen, 
allerdings gab es die gleiche Reaktion (nämlich garkeine) und nachdem 
ich aufgrund der eng aneinanderliegenden Pins paar mal mit dem 5V Draht 
gegen Masse gekommen bin, hab ich es sein lassen und es über die Pins 
und eine feste Verdrahtung versucht.

Ich schau ob ich das mit den digitalen Pins hinbekomme und dann meld ich 
mich hier noch mal.

Danke!

von Armin (Gast)


Lesenswert?

Ach ja, Timer 0 hab ich einfach mal auf high priority gesetzt, weil bei 
abgeschalteter Priorität die ISR an der hochprioren Adresse gesucht 
wird. Ich dachte wenn ich den auf niedrig setzte dass dann vielleicht 
kein Interrupt ausgelöst wird. Dieser Teil des Codes, also die LED 
ansteuerung über Timer und Interrupt hat aber funktioniert.

von Armin (Gast)


Lesenswert?

Sorry für die Mehrfachposts, aber ich muss doch noch was loswerden. Kann 
es sein dass ich einen Eingangspin nicht mit 5V auf High schalten kann? 
ich hab nämlich ein einfachstes Programm geschrieben, dass einfach nur 
einen Pin auf High setzt und wartet, bis ein zweiter Eingangspin auf 
High geht. Beide sind auf dem Board miteinader verbunden. Leider geht 
der Eingang nie auf High.

von Holger W. (holgerw)


Lesenswert?

Warum springst du so hin und her. Lass eine LED blinken erstmal ohne IRQ 
und Timer.
Und eben die analogen Eingänge auf digital schalten.

Dein letztes Post, welche Pins denn ?

Holger

von Armin (Gast)


Lesenswert?

Also ich hatte zunächst einmal nur die LEDs blinken lassen. Im nächsten 
Schritt wollte ich dann Timer und Interrupts hinzufügen. Diese haben 
auch funktioniert, bis auf den externen Interrupt eben. Im Moment 
versuche ich so ein Capsense zum Laufen zu bringen, wofür ich eben den 
Interrupt benötige:

http://www.arduino.cc/playground/Main/CapSense

Die Pins die ich im letzten Post verwendet hatte, waren RA0 und RA1. Ich 
hab das gleiche Programm mit Pins aus dem Port D verwendet und auf 
einmal hats funktioniert. Es könnte also wirklich daran liegen, dass sie 
als analoge Pins konfiguriert sind. Ich hatte zu Beginn auch versucht, 
diese digital zu schalten, aber das Register CONFIG3H mit dem laut DB 
diese Einstellung vornimmt, ließ sich nicht ansprechen.

von Holger W. (holgerw)


Lesenswert?

Das Config Word müsste PBAD=DIG sein und im Programm selbst musst du 
ANSEL setzen, das steht im Datenblatt.

Holger

von Armin (Gast)


Lesenswert?

Nochmal kurz zur Info, der externe Interrupt funktioiert mittlerweile. 
Das Problem war wir schon vermutet, dass der Pin analog war. Leider war 
mir nicht ganz klar, wie genau man die Konfig ändert. Im Datenblatt ist 
zwar das Register erwähnt, welches zur Konfiguration dient, leider kann 
man dieses nicht direkt verwenden. Statt dessen muss man diese Konfig 
mit einem Pragma einstellen:

#pragma config PBAD = DIG

Mit dieser Zeile im Code hats dann auch geklappt. Im Datenblatt war es 
leider nicht ganz erichtlich, wie man das genau macht. Es war nur nach 
einigem experimentieren dann klar wie das geht.

Danke an alle die sich hier beteiligt haben!

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.