Forum: Mikrocontroller und Digitale Elektronik Enhanced Capture mit PIC18f448


von Christoph H. (obbedair)


Lesenswert?

Hallo Forum,
Für einen Drehzahlmesser habe ich eine Schaltung entworfen bei der ich 
den PORTC2 als PWM-Ausgang nutze und den PORTD4 für den Capture-Modus um 
die Periode der Frequenz zu ermitteln.Zum programmieren nutze ich C18 
von Microchip. (PIC18F448)

Sind die Einstellungen für diesen Modus denn so richitg?
es scheint als wird kein Interrupt ausgelöst.

OpenTimer3( TIMER_INT_OFF &
      T3_SOURCE_INT );

OpenECapture1(  CAPTURE_INT_ON &
    EC1_EVERY_FALL_EDGE);

TRISD = 0x10;  //PD4:Frequenzzähler



vorrübergehend habe ich einen Taster zum erzeugen der Flanken in 
Betrieb.

Bei der Interrupt-routine bin ich mir nicht sicher ob die so auszusehen 
hat.
Woher weiß ich welche Priorität der Capture-Interrupt hat.

in C und mit C18 habe ich leider noch keine Interrupts programmiert, nur 
in Assambler , gibt es eine möglichkeit die Konfiguration des 
Capture-Modus in Assambler zu schreiben?

/** I N T E R R U P T S  *******************************************/

#pragma interruptlow InterruptHandlerlow
void InterruptHandlerlow (void)
{

  LED_Toggle();
}

 #pragma code _HIGH_INTERRUPT_VECTOR = 0x000008
 void _high_ISR (void)
 {
     _asm
         goto InterruptHandlerlow      // Sprung zur Interruptroutine
     _endasm
 }

 #pragma code _LOW_INTERRUPT_VECTOR = 0x000018
 void _low_ISR (void)
 {
     _asm
         goto InterruptHandlerlow       // Sprung zur Interruptroutine
     _endasm
 }

vielen Dank für jede Hilfe

von PIC N. (eigo) Benutzerseite


Lesenswert?

Hi,
ich bin mir gerade nicht sicher aber muss das GIE eventuell separat 
eingestellt werden?

Die Priorität kannst Du einstellen (im IPR Register) aber spielt bei dir 
keine Rolle, da beide Interrupts zum selben Ziel führen:
1
goto InterruptHandlerlow       // Sprung zur Interruptroutine

Hier zur Priorität:
1
IPR1bits.CCP1IP = 1; //High priority

Und dann evtl. nochmal das:
1
INTCONbits.GIE = 1; //Interrupts enable

von Christoph H. (obbedair)


Lesenswert?

ich hab diese Konfiguration jetzt. Kann das hinhauen?

void main(void)
  {
  INTCONbits.GIE  = 1; // Interrupts erlaubt
  INTCONbits.PEIE = 1; // Peripheral Interrupts erlaubt
  INTCONbits.INT0IE = 1; //INT0 Interr..

  PIE2bits.ECCP1IE = 1; // Interrupt on ECCP1 aktiv
  RCONbits.IPEN = 1;  // high/low Prioritäten aktiv

  ECCP1CONbits.ECCP1M3 = 0;// Capture-Modi, fallende Flanke
  ECCP1CONbits.ECCP1M2 = 1;
  ECCP1CONbits.ECCP1M1 = 0;
  ECCP1CONbits.ECCP1M0 = 0;

dazu diese Interrupt-routine, aber irgendwie kommt wieder kein 
interrupt:-((

#pragma interruptlow InterruptHandlerlow
void InterruptHandlerlow (void)
{

  Schaltblitz_Toggle();
}

 #pragma code _HIGH_INTERRUPT_VECTOR = 0x000008
 void _high_ISR (void)
 {
     _asm
         goto InterruptHandlerlow      // Sprung zur Interruptroutine
     _endasm
 }

 #pragma code _LOW_INTERRUPT_VECTOR = 0x000018
 void _low_ISR (void)
 {
     _asm
         goto InterruptHandlerlow       // Sprung zur Interruptroutine
     _endasm
 }

die Prioritäten spielen später eine Rolle, da kommt noch bissel was 
dazu. aber erstmal soll der interrupt funktionieren...:-(

von PIC N. (eigo) Benutzerseite


Lesenswert?

Poste bitte mal den gesamten Code und zwar in die Klammern dafür
[ c ] Code [ / c ] (Dann natürlich ohne Leerzeichen)

Gruß Nico

von Christoph H. (obbedair)


Lesenswert?

hier ist der gesamte Code. hoffe das hilft mir zu helfen:-)
1
 /** I N C L U D E S **********************************************************/
2
#include "p18cxxx.h"
3
#include "delays.h"                        // für die Warteschleife
4
#include "adc.h"
5
#include "pwm.h"
6
#include "timers.h"
7
#include "capture.h"
8
#include "stdio.h"
9
#include "portb.h"
10
11
#define   Schaltblitz    LATBbits.LATB7      // reine Erleichterung im Codê
12
#define    R_4    LATEbits.LATE2
13
#define    R_3    LATEbits.LATE1          // reine Erleichterung im Codê
14
#define    R_2    LATEbits.LATE0
15
#define    R_1    LATAbits.LATA5
16
#define    Ge_4  LATAbits.LATA3
17
#define    Ge_3  LATAbits.LATA2
18
#define    Ge_2  LATAbits.LATA1
19
#define    Ge_1  LATCbits.LATC0
20
#define    G_17  LATCbits.LATC1
21
#define    G_16  LATCbits.LATC3
22
#define    G_15  LATDbits.LATD0
23
#define    G_14  LATDbits.LATD1
24
#define    G_13  LATDbits.LATD2
25
#define    G_12  LATCbits.LATC4
26
#define    G_11  LATCbits.LATC5
27
#define    G_10  LATBbits.LATB5
28
#define    G_9    LATBbits.LATB4
29
#define    G_8    LATBbits.LATB1
30
#define    G_7    LATDbits.LATD7
31
#define    G_6    LATDbits.LATD6
32
#define    G_5    LATDbits.LATD5
33
#define    G_4    LATAbits.LATA4
34
#define    G_3    LATCbits.LATC7
35
#define    G_2    LATCbits.LATC6
36
#define    G_1    LATBbits.LATB6
37
#define    Zuendunterbrecher  LATDbits.LATD3         // reine Erleichterung im Codê
38
#define   Schaltblitz_Toggle()     Schaltblitz = !Schaltblitz;            // reine Erleichterung im Codê
39
40
41
void main (void);
42
void InterruptHandlerhigh (void);
43
void InterruptHandlerlow (void);
44
45
46
/** Configuration ********************************************************/
47
#pragma config OSC = HS   //CPU=20 MHz
48
#pragma config PWRT = ON
49
#pragma config BOR = OFF
50
#pragma config WDT = OFF  //Watchdog Timer
51
#pragma config LVP = OFF  //
52
 
53
54
/** I N T E R R U P T S  *******************************************/
55
56
#pragma interruptlow InterruptHandlerlow
57
void InterruptHandlerlow (void)
58
{
59
    
60
  Schaltblitz_Toggle();  
61
}
62
63
 #pragma code _HIGH_INTERRUPT_VECTOR = 0x000008
64
 void _high_ISR (void)
65
 {
66
     _asm
67
         goto InterruptHandlerlow      // Sprung zur Interruptroutine
68
     _endasm
69
 }
70
71
 #pragma code _LOW_INTERRUPT_VECTOR = 0x000018
72
 void _low_ISR (void)
73
 {
74
     _asm
75
         goto InterruptHandlerlow       // Sprung zur Interruptroutine
76
     _endasm
77
 }
78
79
80
/** D E C L A R A T I O N S **************************************************/
81
#pragma code
82
83
84
85
86
void main(void)
87
  {
88
    INTCONbits.GIE  = 1; // Interrupts erlaubt
89
    INTCONbits.PEIE = 1; // Peripheral Interrupts erlaubt
90
    INTCONbits.INT0IE  = 1; //INT0 External Interrupt erlaubt          
91
    PIE2bits.ECCP1IE = 1; // Interrupt on ECCP1 aktiv
92
    RCONbits.IPEN = 1;  // high/low Prioritäten aktiv
93
    
94
    ECCP1CONbits.ECCP1M3 = 0;
95
    ECCP1CONbits.ECCP1M2 = 1;
96
    ECCP1CONbits.ECCP1M1 = 0;
97
    ECCP1CONbits.ECCP1M0 = 0;
98
99
        
100
101
     OpenTimer1( TIMER_INT_OFF &
102
          T1_SOURCE_EXT &
103
          T1_PS_1_8 &
104
          T1_OSC1EN_OFF &
105
          T1_SYNC_EXT_OFF &
106
          T1_CCP1_T3_CCP2);
107
  
108
    OpenPWM1 (0xff);
109
    SetDCPWM1(0x0200);
110
    
111
    OpenTimer3( TIMER_INT_OFF &
112
          T3_SOURCE_INT );
113
114
  
115
    
116
            
117
    TRISA = 0x41;  //RA0:Öltemp RA6:Ozillator
118
    TRISB = 0x0D;  //PB1:Fussschalter RB2/RB3:Can-Bus
119
    TRISC = 0x00;  //RC2:PWM 
120
    TRISD = 0x10;  //PD4:Frequenzzähler PD3:Zündunterbrecher
121
    TRISE = 0x00;  //
122
    
123
  
124
    while(1)
125
    {
126
    Delay10KTCYx(100);
127
    G_1 = !G_1;  
128
    }//while(1)-Schleife
129
  }// Main-Schleife

von PIC N. (eigo) Benutzerseite


Lesenswert?

Also in der ISR fehlt, dass Du das IF-Flag, welches den Interrupt 
auslöst wieder löschst.

Deine Version:
1
void InterruptHandlerlow (void)
2
{
3
    
4
  Schaltblitz_Toggle();  
5
}

Ich würde es so schreiben:
1
void InterruptHandlerlow (void)
2
{
3
  if(INTCONbits.INTCON)INTCONbits.INTCON=0;
4
5
    
6
  if(PIR2bits.ECCP1IF)
7
  {
8
    Schaltblitz^=Schaltblitz;
9
    PIR2bits.ECCP1IF=0;
10
  }
11
}

Sonst hast Du das Problem, dass wenn der Interrupt vorbei ist, das Flag 
ja immer noch anzeigt, dass es einen Interrupt gibt und somit kommt das 
Programm nicht mehr aus der ISR heraus.

Probiere das erst mal, wenn es dann immer noch nicht klappt, sehen wir 
weiter.


PS: Warum ist der INT0 erlaubt? Für später, wenn ja hat er dann einen 
definierten Zustand z.Z - sprich pull up/down?

Gruß Nico

von PIC N. (eigo) Benutzerseite


Lesenswert?

Ups hab da einen Tippfehler, muss so heißen:
1
if(INTCONbits.INT0IF)INTCONbits.INT0IF=0;


Ist das hier so gewollt?:
1
TRISB = 0x0D;  //PB1:Fussschalter RB2/RB3:Can-Bus
Denn Eingänge sind hier B2,B3 und B0 nicht B1

Außerdem fällt mir gerade noch auf, dass Du für den Timer1 die externe 
Quelle gewählt hast, dann musst du an C0 und C1 einen Takt anschließen 
und außerdem die entsprechenden Tris Bits auf 1 setzten (input). Das 
stimmt bei dir nicht überein.

Gruß Nico

von Christoph H. (obbedair)


Lesenswert?

also endlich funktioniert der interrupt:-)

vielen dank dafür.

aber er funzt nich immer:-( bzw. ich glaube es liegt vielleicht an dem 
nichtentprellten Taster.

das werd ich später im motorrad genau sehen.

wegen dem PORTB, falsches Kommentar von mir, RB0 ist der fussschalter

die Sache mit dem Timer muss ich natürlich berichtigen.

und für die die es Interresiert: INT0 ist für später schon mal 
aktiviert.Ziel: ein Schaltautomat> Hochschalten ohne Kumpeln und Gas 
wegnehmen, einfach den nächsten gang "reinziehen". das ermöglicht 
schaltzeiten jenseits von gut und böse:-))

Eine verständisfrage drängt sich mir dann doch aber noch auf.

ich möchte die Zeit zwischen zwei Flanken messen.
Das heißt doch: eine Flanke kommt> Interrupt wird ausgelöst>Zeit beginnt 
zu zählen> nächster Interrupt>??wo ist das ergebnis jetzt zu finden. 
muss ich den Timer auslesen oder das Register vom ECCP? das hab ich 
leider noch nich ganz verstanden?

von PIC N. (eigo) Benutzerseite


Lesenswert?

Das Ergebnis steht in CCPR1L und CCPR1H ;-)

von Christoph H. (obbedair)


Lesenswert?

Das Programm läuft danke eurer Hilfe jetzt fast.

setzt sich der Timer von allein wieder auf null oder sollte ich das 
direkt als ersten schritt im Interrupt machen?

kann ich mir irgendwie in c18 anschauen wie viel befehle in welcher zeit 
abgearbeitet werden.
scheinbar gibts da irgendwo probleme, denn bei gleichbleibender Drehzahl 
schwankt meine Anzeige über den kompletten Bereich.ich dacht erst das 
der PIC dann von einem Interrupt zum nächsten nicht hinterher kommt aber 
ich hab mindestens 25000 Befehle Zeit das sollte doch genügen?


Interrupt-Service-Routine
1
#pragma interruptlow InterruptHandlerlow
2
void InterruptHandlerlow (void)
3
{
4
  if(INTCONbits.INT0IF)
5
  {
6
  INTCONbits.INT0IF=0;  // Externer Interrrupt an INT0
7
  }
8
9
    
10
  if(PIR2bits.ECCP1IF)    // Externer Interrupt an ECCP1
11
  {
12
    
13
  WriteTimer3(0x0000);
14
  Ergebnis = (ECCPR1H << 8) + ECCPR1L;
15
16
  Merker = 1;
17
18
    PIR2bits.ECCP1IF=0;
19
  }  
20
    
21
}


der Teil der einmal nach dem Interrupt ausgeführt wird.
1
  while(Merker)
2
        {
3
          
4
          Zahl = -150000/Ergebnis + 33;
5
          if (Zahl<0)
6
            {
7
            Zahl= 0;
8
            }
9
          if (Zahl>25)
10
            {
11
            Zahl = 25;
12
               }
13
            
14
          LATA = 0x01;
15
          LATB = 0x0D;
16
          LATC = 0x00;
17
          LATD = 0x00;
18
          LATE = 0x00;        
19
  
20
          switch(Zahl)
21
             {
22
            case 1:  R_4 =1;
23
            case 2: R_3 =1;
24
            case 3: R_2 =1;
25
            case 4: R_1 =1;
26
            case 5: Ge_4=1;
27
            case 6: Ge_3=1;
28
            case 7: Ge_2=1;
29
            case 8: Ge_1=1;  
30
            case 9: G_17=1;
31
            case 10:G_16=1;
32
            case 11:G_15=1;
33
            case 12:G_14=1;  
34
            case 13:G_13=1;
35
            case 14:G_12=1;  
36
            case 15:G_11=1;
37
            case 16:G_10=1;
38
            case 17:G_9  =1;
39
            case 18:G_8  =1;
40
            case 19:G_7  =1;
41
            case 20:G_6  =1;
42
            case 21:G_5  =1;  
43
            case 22:G_4  =1;
44
            case 23:G_3  =1;
45
            case 24:G_2  =1;  
46
            case 25:G_1  =1;
47
            }
48
        Merker = 0;
49
        } // while(Merker)

von PIC N. (eigo) Benutzerseite


Lesenswert?

Hi Christoph,
mich irritieren jetzt noch zwei Dinge.

Erstens: Ich bin mir nicht sicher ob das richtig funktioniert:
1
Ergebnis = (ECCPR1H << 8) + ECCPR1L;
Mach das mal zum Testen lieber so:
1
Ergebnis = ECCPR1H;
2
Ergebnis = Ergebnis<<8;
3
Ergebnis = Ergebnis + ECCPR1L;

Zweitens: Wieso setzt Du hier den Timer3 zurück?
1
WriteTimer3(0x0000);

...denn Du verwendest den Timer1 als Zähler für das CCP:
1
     OpenTimer1( TIMER_INT_OFF &
2
          T1_SOURCE_EXT &
3
          T1_PS_1_8 &
4
          T1_OSC1EN_OFF &
5
          T1_SYNC_EXT_OFF &
6
          T1_CCP1_T3_CCP2);
Durch T1_CCP1_T3_CCP2 wird Timer1 die Quelle für das CCP1 Modul.

Viele Grüße
Nico

von PIC N. (eigo) Benutzerseite


Lesenswert?

Nachtrag: Der Timer wird automatisch zurück gesetzt. (siehe: 
http://www.pic-projekte.de/Bilder/PIC_Tutorial/Timer_CCP_Reset.PNG)

von Christoph H. (obbedair)


Lesenswert?

ich benutzte für den Capture-Modus doch ECCP1

werd die vorschläge gleich mal ausprobieren

danke

von PIC N. (eigo) Benutzerseite


Lesenswert?

Christoph Herzog schrieb:
> ich benutzte für den Capture-Modus doch ECCP1

Ja genau und deshalb fragte ich mich warum Du den Timer3 zurück setzt, 
denn der Timer1 ist die Quelle für das ECCP1 Modul bei Dir, siehe:

>> T1_CCP1_T3_CCP2

Gruß Nico

von Christoph H. (obbedair)


Lesenswert?

das Zurücksetzten ist jetzt beseitigt und an sich funktioniert auch 
alles.

Nur das Auslesen der beiden ECCP1-Register ist noch nich richtig.


1
  long short    Ergebnis;
2
  unsigned int   Zahl;
3
  unsigned char  Merker;
4
5
/** I N T E R R U P T S  *******************************************/
6
7
#pragma interruptlow InterruptHandlerlow
8
void InterruptHandlerlow (void)
9
{
10
 
11
  if(PIR2bits.ECCP1IF)    // Externer Interrupt an ECCP1
12
  {
13
  Ergebnis = 0;  
14
  Ergebnis = ECCPR1H;
15
  Ergebnis = Ergebnis<<8;
16
  Ergebnis = Ergebnis + ECCPR1L;
17
  if ( Ergebnis > 30000)
18
    Schaltblitz = 1; 
19
  else 
20
    Schaltblitz = 0;
21
22
  
23
  Zahl = 150000/Ergebnis ;
24
  Merker = 1;
25
26
    PIR2bits.ECCP1IF=0;
27
  }  
28
    
29
}
die if(Ergebnis >30000)-Schleife soll nur eine Kontrolle sein.
Das ankommende Signal ist konstant und der zähler müsste immer etwa auf 
32500 kommen. Aber der Schaltblitz flackert. Das heißt also das die Zahl 
andauert kleiner ist. ich kann mir aber noch nicht erklären warum. Ich 
glaube es gibt dafür nur noch zwei gründe. Entweder übertrage ich das 
Ergebnis falsch aus dem ECCP1modul in meine Variable oder der Timer 
läuft ständig über und hat dadurch auch mal werte unter 30000. Die 
Rechnung und Anzeige funzt<<getestet...

von Christoph H. (obbedair)


Lesenswert?

kann der Fehler vielleicht daran liegen das ich CCP1 für eine 
Pulsweitenmodulation nehme und gleichzeitig ECCP1 für den Capture modus?

von PIC N. (eigo) Benutzerseite


Lesenswert?

Hi Christoph,
also das Auslesen des ECCPR1L/H Register ist auf jeden Fall korrekt. Was 
mir gerade ein bisschen Kopfschmerzen bereitet ist folgendes:

1. Versuche doch mal wirklich in der ISR, den Timer1 zu resetten. Ich 
glaube meine Aussage zum autom. Reset gilt nur beim Special Event 
Trigger (ist bei dir nicht so konfiguriert). Also in die ISR nochmal den 
Code einbauen und zwar als erstes in die if Schleife:
1
TMR1L = TMR1H = 0; // Timer1 Reset


2. Schau mal:
1
     OpenTimer1( TIMER_INT_OFF &
2
          T1_SOURCE_EXT &
3
          T1_PS_1_8 &
4
          T1_OSC1EN_OFF &
5
          T1_SYNC_EXT_OFF &
6
          T1_CCP1_T3_CCP2);  // <----- 2xCCP und kein ECCP?
Im Prinzip ist das ja wunderbar aber Du hast ja ein CCP und ein ECCP 
Modul in deinem PIC. Vielleicht geht die Verwendung der C18 Librarie ja 
nur wenn man zwei CCP Module hat, denn hier steht nur 2xCCP und nicht 
ECCP:
1
T1_CCP1_T3_CCP2
Aber das ist nur eine Vermutung.

3.Und wenn das auch nicht hilft, vermute ich, dass Du ne Null in Mathe 
bist ;) Spaß... Kannst du messen in Welchen Abständen (genau) ein Signal 
(Tacho..) am Pin ankommt?

Gruß Nico

von Christoph H. (obbedair)


Lesenswert?

die Abstände betragen 50ms.
das heißt bei 20Mhz /4 = 5Mhz> vorteiler 8 dann sind wir bei 1,6µs Pro 
Digit.
und bei 50ms/1,6µ sind das für den Timer 31250.

Die Sache mit der Librarie werd ich gleich mal unter die Lupe nehmen,

danke für die tips:-)

von PIC N. (eigo) Benutzerseite


Lesenswert?

Okay die Rechnung stimmt, wenn wirklich alle 50ms ein Impuls kommt. Wie 
gesagt versuch zuerst mal den Timer1 zu resetten..

Nachtrag: Man muss den TMR1 wirklich zurück setzten!!

von Christoph H. (obbedair)


Lesenswert?

scheinbar funzt die ganze geschichte jetzt. leider kann ich das 
eingangssignal nicht wirklich ändern.

also werde ich mich morgen aufs fahrrad schwingen die 15km bis zu meinem 
motorrad in angriff nehmen und die ganze schaltung am lebenden objekt 
ausprobieren. sollte die geschichte funktionieren stell ich den Code für 
ähnliche grünschnäbel wie mich hier bereit.

von PIC N. (eigo) Benutzerseite


Lesenswert?

Freut mich.. Viel Spaß noch!
Gruß Nico

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.