Forum: Mikrocontroller und Digitale Elektronik Interrupt-Routine mit C und Timer1


von Bastian B. (hobby-pic)


Lesenswert?

Hallo,

ich arbeite mich gerade in die µC-Geschichten ein.
Ein ganzschön komplexes und teilweise trockenes Thema.
So weit klappt alles ganz gut. Jetzt möchte ich aber mal eine 
Interrupt-Routine ausprobieren und eine LED mit dem 16bit Timer1 zum 
Blinken bringen.

Ich bin in ASM nicht besonders gut und es fällt mir sehr schwer die 
Erklärungen und Beispiele nach zu vollziehen.

Vielleicht gibt es ja hier einen Pfiffigen, der eine

"Keine Panink vor Mechanik"-mäßige Antwort hat :-)

Danke, Basti

Hier mein Ausgangspunkt. Müsste alles soweit konfiguriert sein.

Controller: PIC 18F2550
Programmierung: MPLAB IDE - C18-Compiler
1
//Testschaltung
2
3
//Includes
4
#include <p18f2550.h>
5
#include "delays.h"
6
7
//Config
8
9
#pragma config CPUDIV = OSC1_PLL2
10
#pragma config FOSC=HS
11
#pragma config FCMEN = OFF, IESO = OFF
12
#pragma config PWRT = ON
13
#pragma config BOR = OFF
14
#pragma config WDT = OFF
15
#pragma config LVP = OFF
16
17
18
//defines
19
#define  OUT1     LATBbits.LATB0
20
#define  OUT2     LATBbits.LATB1
21
#define  OUT3      LATBbits.LATB2
22
#define  OUT4    LATBbits.LATB3
23
#define  OUT5    LATBbits.LATB4
24
#define  OUT6    LATBbits.LATB5
25
#define  OUT7    LATBbits.LATB6
26
#define  OUT8    LATBbits.LATB7
27
28
#define  IN1      PORTAbits.RA0
29
#define  IN2      PORTAbits.RA1
30
#define  IN3      PORTAbits.RA2
31
#define   OUT9    LATAbits.LATA3
32
#define  OUT10    LATAbits.LATA4
33
34
//Declaration
35
#pragma code
36
void main (void)
37
{
38
  T1CON= 0b10110001;              //Timer1 aktivieren
39
  TMR1H=0;                  //High Byte T1
40
  TMR1L=0;                  //Low Byte T1
41
  ADCON1 = 0x0F;                  //Alle Port Pins auf digital gesetzt
42
  TRISA=0xE7;                   //PORTA Ein- und Ausgänge 
43
  TRISB=0;                   //PORTB - Ausgang
44
  PORTB=0;                  //PORTB auf Null setzen
45
  INTCON=0b10000000;              //
46
  PIE1=1;                    //bei Auslösen PIR1(bit0)=0 setzen
47
48
  while (1)
49
  {
50
    
51
  }  
52
}

von Fragezeichen (Gast)


Lesenswert?

Bastian B. schrieb:
> Hier mein Ausgangspunkt. Müsste alles soweit konfiguriert sein.

Jetzt fehlt nur noch die Frage...

von Bastian B. (hobby-pic)


Lesenswert?

Wie bekomme ich den Interrupt abgefragt?

von Peter D. (peda)


Lesenswert?

Bastian B. schrieb:
> Wie bekomme ich den Interrupt abgefragt?

Im Example-Verzeichnis Deiner Compilerinstallation hast Du schon 
nachgeschaut?


Peter

von Bastian B. (hobby-pic)


Lesenswert?

Wenn ich es richtig verstehe ist die Funktioniert die Interrupt-Routine 
wie eine Funktion.

Wie wird diese Funktion aufgebaut? Wie kann ich den Überlauf des Timers 
(PIR1(bit0)=1) genau in diesem Moment nutzen und wie kann ich ihn in 
mein Programm einarbeiten?

von Bastian B. (hobby-pic)


Lesenswert?

Das Ziel ist es, dass der PORTB im Takt des interrupts von T1 hochzählt.

Klappt noch nicht.

Danke für die Hilfe.
1
//Testschaltung
2
3
//Includes
4
#include <p18f2550.h>
5
#include "delays.h"
6
7
//Config
8
9
#pragma config CPUDIV = OSC1_PLL2
10
#pragma config FOSC=HS
11
#pragma config FCMEN = OFF, IESO = OFF
12
#pragma config PWRT = ON
13
#pragma config BOR = OFF
14
#pragma config WDT = OFF
15
#pragma config LVP = OFF
16
17
18
//defines
19
#define  OUT1     LATBbits.LATB0
20
#define  OUT2     LATBbits.LATB1
21
#define  OUT3      LATBbits.LATB2
22
#define  OUT4    LATBbits.LATB3
23
#define  OUT5    LATBbits.LATB4
24
#define  OUT6    LATBbits.LATB5
25
#define  OUT7    LATBbits.LATB6
26
#define  OUT8    LATBbits.LATB7
27
28
#define  IN1      PORTAbits.RA0
29
#define  IN2      PORTAbits.RA1
30
#define  IN3      PORTAbits.RA2
31
#define   OUT9    LATAbits.LATA3
32
#define  OUT10    LATAbits.LATA4
33
34
void Test(void);              // Prototyp Serviceprogramm
35
#pragma code Timer=0xF9E      // Einsprung Adresse F9Eh für PIR1 Datenblatt S.66
36
37
void Timer (void)              // Hilfsfunktion        
38
{
39
 _asm GOTO Test _endasm       // Sprung nach Testfunktion
40
}
41
42
#pragma code                     // System Codebereich
43
#pragma interrupt Test        // Kennwort interrupt veranlasst
44
45
46
47
#pragma code
48
#pragma interrupt Test
49
50
void Test (void)
51
{
52
  PORTB++;
53
  PIR1=0;
54
}
55
56
void main (void)
57
{
58
  T1CON= 0b10110001;              //Timer1 aktivieren
59
  TMR1H=0;                  //High Byte T1
60
  TMR1L=0;                  //Low Byte T1
61
  ADCON1 = 0x0F;                  //Alle Port Pins auf digital gesetzt
62
  TRISA=0xE7;                   //PORTA Ein- und Ausgänge 
63
  TRISB=0;                   //PORTB - Ausgang (Dioden)
64
  PORTB=0;                  //PORTB auf Null setzen
65
  INTCON=0b10000000;              //
66
  PIE1=1;                    //bei Auslösen PIR1(bit0)=0 setzen
67
68
  while (1)
69
  {
70
    OUT9=1;                    //Funktionstest Diode
71
    OUT10=1;                  //Funktionstest Diode
72
  }  
73
}

von Bastian B. (hobby-pic)


Lesenswert?

Muss ich die Funktion Test nochmal extra im main aufrufen.

Ich habe da ein riesen Verständnisproblem.

Kennt sich irgendwer mit Interrupts aus?

von Karl H. (kbuchegg)


Lesenswert?

Bastian B. schrieb:
> Muss ich die Funktion Test nochmal extra im main aufrufen.

nein.

> Ich habe da ein riesen Verständnisproblem.

Seh ich auch so. Es ist vom Prinzip her viel einfacher als du denkst.

> Kennt sich irgendwer mit Interrupts aus?

Das Problem ist, dass ich die Syntax nicht kenne, die dein Compiler 
benutzt um Interrupt FUnktionen zu vereinbaren.

Aber für Timer generell kannst du dir das mal durchlesen:
http://www.mikrocontroller.net/articles/FAQ#Timer

WICHTIG: Das ist für einen anderen µC Typ! Das Prinzip ist bei deinem µC 
auch nicht anders. Daher: Wenn du das liest, achte nicht zu sehr auf die 
Details sondern auf das generelle Prinzip.

von Bastian B. (hobby-pic)


Lesenswert?

Ich habe mir das durchgelesen und ich denke ich kann das Problem jetzt 
besser eingrenzen.


1. Ich möchte das Overflow Interrupt Flag Bit des Timer1 meines 18F2550 
abtasten.

TMR1IF nennt sich dieses Bit laut Datenblatt und ist das Bit0 im PIR1 
Register.

2. Ich möchte dass bei Auslösen dieses Interrups der PORTB einen 
aufwärts zählt.

Ich habe leider zu wenig Erfahrung um zu wissen wie man das umsetzt.

Hat da jemand vielleicht ein übersichtliches Beispielprogramm oder kann 
mir irgendwie anders helfen?

Danke, Basti

von Holger W. (holgerw)


Lesenswert?

schau dir dochmal dieses Beispiel im C18 Verzeichnis an:

...\Program Files\Microchip\mplabc18\example\Interrupt\main.c

Holger

von EGS_TI (Gast)


Lesenswert?

MPLAB_C18_Users_Guide ist auch empfehlenswert!

Seite 58+59 geben bspw. folgendes Preis:
1
Chapter 5. Sample Application
2
The following sample application will flash LEDs connected to PORTB of a PIC18C452
3
microcontroller. The command line used to build this application is:
4
mcc18 -p 18c452 -I c:\mcc18\h leds.c
5
where c:\mcc18 is the directory in which the compiler is installed. This sample
6
application was designed for use with a PICDEM™ 2 demo board. This sample covers
7
the following items:
8
1. Interrupt handling (#pragma interruptlow, interrupt vectors, interrupt
9
service routines and context saving)
10
2. System header files
11
3. Processor-specific header files
12
4. #pragma sectiontype
13
5. Inline assembly
1
/* 1 */ #include <p18cxxx.h>
2
/* 2 */ #include <timers.h>
3
/* 3 */
4
/* 4 */ #define NUMBER_OF_LEDS 8
5
/* 5 */
6
/* 6 */ void timer_isr (void);
7
/* 7 */
8
/* 8 */ static unsigned char s_count = 0;
9
/* 9 */
10
/* 10 */ #pragma code low_vector=0x18
11
/* 11 */ void low_interrupt (void)
12
/* 12 */ {
13
/* 13 */ _asm GOTO timer_isr _endasm
14
/* 14 */ }
15
/* 15 */
16
/* 16 */ #pragma code
17
/* 17 */
18
/* 18 */ #pragma interruptlow timer_isr save=PROD
19
/* 19 */ void
20
/* 20 */ timer_isr (void)
21
/* 21 */ {
22
/* 22 */ static unsigned char led_display = 0;
23
/* 23 */
24
25
/* 24 */ INTCONbits.TMR0IF = 0;
26
/* 25 */
27
/* 26 */ s_count = s_count % (NUMBER_OF_LEDS + 1);
28
/* 27 */
29
/* 28 */ led_display = (1 << s_count++) - 1;
30
/* 29 */
31
/* 30 */ PORTB = led_display;
32
/* 31 */ }
33
/* 32 */
34
/* 33 */ void
35
/* 34 */ main (void)
36
/* 35 */ {
37
/* 36 */ TRISB = 0;
38
/* 37 */ PORTB = 0;
39
/* 38 */
40
/* 39 */ OpenTimer0 (TIMER_INT_ON & T0_SOURCE_INT & T0_16BIT);
41
/* 40 */ INTCONbits.GIE = 1;
42
/* 41 */
43
/* 42 */ while (1)
44
/* 43 */ {
45
/* 44 */ }
46
/* 45 */ }
1
Line 1: This line includes the generic processor header file. The correct processor is
2
selected via the -p command-line option. (See Section 2.5.1 “System Header
3
Files” and Section 2.10 “Processor-specific Header Files”)
4
Line 10: For PIC18 devices, the low interrupt vector is found at 000000018h. This line of
5
code changes the default code section to the absolute code section named
6
low_vector located at address 0x18. (See Section 2.9.1 “#pragma
7
sectiontype” and Section 2.9.2.3 “Interrupt Vectors”)
8
Line 13: This line contains inline assembly that will jump to the ISR. (See
9
Section 2.8.2 “Inline Assembly” and Section 2.9.2.3 “Interrupt Vectors”)
10
Line 16: This line returns the compiler to the default code section. (See Section 2.9.1
11
“#pragma sectiontype” and Table 2-6)
12
Line 18: This line specifies the function timer_isr as a low-priority interrupt service
13
routine. This is required in order for the compiler to generate a RETFIE instruction
14
instead of a RETURN instruction for the timer_isr function. In addition, it
15
ensures that PROD special function register will be saved. (Section 2.9.2
16
“#pragma interruptlow fname / #pragma interrupt fname” and
17
Section 2.9.2.4 “ISR Context Saving”)
18
Line 19-20: These lines define the timer_isr function. Notice that it does not take any
19
parameters, and does not return anything (as required by ISRs). (See
20
Section 2.9.2.2 “Interrupt Service Routines”)
21
Line 24: This line clears the TMR0 interrupt flag to stop the program from processing the
22
same interrupt multiple times. (See Section 2.10 “Processor-specific Header
23
Files”)
24
Line 30: This line demonstrates how to modify the special function register PORTB in C.
25
(See Section 2.10 “Processor-specific Header Files”)
26
Line 36-37: These lines initialize the special function registers TRISB and PORTB. (See
27
Section 2.10 “Processor-specific Header Files”)
28
Line 39: This line enables the TMR0 interrupt, setting up the timer as an internal 16-bit
29
clock.
30
Line 40: This line enables global interrupts. (See Section 2.10 “Processor-specific
31
Header Files”)

von Bastian B. (hobby-pic)


Lesenswert?

Danke erstmal für die Hilfe.

Ich habe da nochmal etwas versucht.

PIC 18F2550, PORTB (Diodenleiste) soll aufwärts zählen.

Ergebnis: Bei PORTB tut sich nichts.
          RA3 wird aus unerfindlichen Gründen angesteuert

Es wäre echt super, wenn jemand nochmal über den übersichtlichen 
Quelltext schaut und etwas findet.

Danke, Basti
1
//Testschaltung
2
3
//Includes
4
#include <p18f2550.h>
5
#include "delays.h"
6
7
//Config
8
9
#pragma config CPUDIV = OSC1_PLL2
10
#pragma config FOSC=HS
11
#pragma config FCMEN = OFF, IESO = OFF
12
#pragma config PWRT = ON
13
#pragma config BOR = OFF
14
#pragma config WDT = OFF
15
#pragma config LVP = OFF
16
17
unsigned char j=0;
18
//defines
19
#define  OUT1     LATBbits.LATB0
20
#define  OUT2     LATBbits.LATB1
21
#define  OUT3      LATBbits.LATB2
22
#define  OUT4    LATBbits.LATB3
23
#define  OUT5    LATBbits.LATB4
24
#define  OUT6    LATBbits.LATB5
25
#define  OUT7    LATBbits.LATB6
26
#define  OUT8    LATBbits.LATB7
27
28
#define  IN1      PORTAbits.RA0
29
#define  IN2      PORTAbits.RA1
30
#define  IN3      PORTAbits.RA2
31
#define   OUT9    LATAbits.LATA3  //Dieser Ausgang wird aus irgendwelchen Gründen angesteuert
32
#define  OUT10    LATAbits.LATA4
33
34
void Test(void);              // Prototyp Serviceprogramm
35
#pragma code Timer=0x0008      // Interrupt to CPU Vector to Location 0008h (DB S.98)
36
37
void Timer (void)              // Hilfsfunktion        
38
{
39
 _asm GOTO Test _endasm       // Sprung nach Testfunktion
40
}
41
42
#pragma code                     // System Codebereich
43
#pragma interrupt Test        // Kennwort interrupt veranlasst
44
45
46
void Test (void)
47
{
48
    j=1; 
49
}
50
51
void main (void)
52
{
53
  
54
  T1CON= 0b10110001;              //Timer1 aktivieren
55
  TMR1H=0;                  //High Byte T1
56
  TMR1L=0;                  //Low Byte T1
57
  ADCON1 = 0x0F;                  //Alle Port Pins auf digital gesetzt
58
  TRISA=0xE7;                   //PORTA Ein- und Ausgänge 
59
  TRISB=0;                   //PORTB - Ausgang (Dioden)
60
  PORTB=0;                  //PORTB auf Null setzen
61
  PORTA=0;                  //PORTA ausschalten
62
  INTCON=  0b000000001;            //Interrupts zulassen
63
  PIE1=  0b000000001;            //bei Auslösen PIR1[bit0(TMR1IF)]=0 setzen
64
65
  while (1)
66
  {
67
    if (j==1)
68
    {
69
      PIR1=0;                //PIR-Register 0 setzten, damit TMR1IF=0
70
      PORTB++;              //PORTB aufzählen
71
      j=0;                //globale Hilfsvariable zurücksetzen
72
    }   
73
  }  
74
}

von schner (Gast)


Lesenswert?

volatile!

von HolgerW (Gast)


Lesenswert?

Also mal auf die schnelle was getestet, aber Achtung, ist PortC und ein 
anderer PIC, den hatte ich gerade passend auf dem Board.
Musst du also nochmal etwas anpaassen (Include/ Config)

Holger
1
//Testschaltung
2
3
#include    <p18F4420.h>            // for TRIS and PORT declarations
4
#include    <stdlib.h>
5
6
#pragma     config OSC  = HSPLL     // PLL ein
7
8
#pragma     config LVP  = OFF
9
#pragma     config MCLRE= OFF       // kein Reseteingang
10
11
unsigned char j=0;
12
unsigned char i=0;
13
14
//defines
15
16
void InterruptHandlerHigh(void);
17
void InterruptHandlerLow(void);
18
//----------------------------------------------------------------------------
19
// High priority interrupt vector (legacy interrupt)
20
#pragma code InterruptVectorHigh = 0x08
21
void
22
InterruptVectorHigh (void)
23
{
24
  _asm goto InterruptHandlerHigh  _endasm
25
}
26
// Low priority interrupt vector
27
#pragma code InterruptVectorLow = 0x18
28
void
29
InterruptVectorLow (void)
30
{
31
  _asm  goto InterruptHandlerHigh  _endasm
32
}
33
//----------------------------------------------------------------------------
34
// High priority interrupt routine (legacy interrupt)
35
36
#pragma code
37
#pragma interrupt InterruptHandlerHigh
38
39
void InterruptHandlerHigh ()
40
{
41
   PIR1bits.TMR1IF=0;                  // IRQ Flag zurücksetzen
42
   j=1;
43
   
44
}
45
                
46
47
void main (void)
48
{   
49
  TMR1H=0;                  //High Byte T1
50
  TMR1L=0;                  //Low Byte T1
51
   
52
  TRISC=0;
53
  
54
  ADCON1= 0xff;       // Configure A/D or digital inputs
55
  CMCON = 0x07;       // Configure comparators for digital input
56
  
57
  T1CONbits.TMR1ON = 1;   // Timer 1 on
58
  PIE1bits.TMR1IE=1;      // IRQ enable
59
  INTCONbits.PEIE = 1;    // periphäre Interrups erlauben
60
  INTCONbits.GIE = 1;     // Interrupts generell erlauben
61
  
62
  while (1)
63
  {      
64
    if (j==1)
65
    { 
66
      i++;     
67
      PORTC=i;              //PORTB aufzählen      
68
      j=0;                //globale Hilfsvariable zurücksetzen
69
    }   
70
  }  
71
}

von Bastian B. (hobby-pic)


Lesenswert?

Oh Mann. Manchmal steht auch einfach mal ne "0" zuviel im Raum herum :-)

Besten Dank Holger. Dein Programm hat sehr geholfen.

Hier mein fertiges Programm für Menschen mit gleichem Problem
1
//Testschaltung
2
3
//Includes
4
#include <p18f2550.h>
5
#include "delays.h"
6
7
//Config
8
9
#pragma config CPUDIV = OSC1_PLL2
10
#pragma config FOSC=HS
11
#pragma config FCMEN = OFF, IESO = OFF
12
#pragma config PWRT = ON
13
#pragma config BOR = OFF
14
#pragma config WDT = OFF
15
#pragma config LVP = OFF
16
17
unsigned char j=0, i=0;
18
19
20
//defines
21
22
23
void Test(void);              // Prototyp Serviceprogramm
24
25
//Interrupts
26
27
#pragma code Timer=0x0008      // Interrupt to CPU Vector to Location 0008h (DB S.98)
28
void High (void)              // Hilfsfunktion        
29
{
30
 _asm goto Test _endasm       // Sprung nach Testfunktion
31
}
32
33
#pragma code Low = 0x18
34
void Low (void)
35
{
36
  _asm  goto Test  _endasm
37
}
38
39
//Interrupt-Routine
40
41
#pragma code                     // System Codebereich
42
#pragma interrupt Test        // Kennwort interrupt veranlasst
43
44
void Test ()
45
{
46
    PIR1bits.TMR1IF=0;
47
  j=1; 
48
}
49
50
void main (void)
51
{
52
  
53
54
  TMR1H=0;                  //High Byte T1
55
  TMR1L=0;                  //Low Byte T1
56
  ADCON1 = 0x0F;                  //Alle Port Pins auf digital gesetzt
57
  TRISA=0xE7;                   //PORTA Ein- und Ausgänge
58
  CMCON = 0x07;                //Komparatoreingänge deaktivieren
59
  TRISB=0;                   //PORTB - Ausgang (Dioden)
60
  PORTB=0;                  //PORTB auf Null setzen
61
  PORTA=0;                  //PORTA ausschalten
62
  T1CON=  0b10110001;              //Timer1 aktivieren (16bit)
63
  INTCON=  0b11000000;              //Interrupts zulassen
64
  PIE1=  0b00000001;              //bei Auslösen PIR1[bit0(TMR1IF)]=0 setzen
65
66
67
  while (1)
68
  {
69
    if (j==1)
70
    {
71
      i++;
72
      PORTB=i;              //PORTB aufzählen
73
      j=0;                //globale Hilfsvariable zurücksetzen
74
    }   
75
  }  
76
}

von Holger W. (Gast)


Lesenswert?

statt PORTB sollte man besser LATB verwenden, das ist mir untergegangen.
Ich würde dir raten von Anfang an die Bezeichnung der Bits zu verwenden 
sonst musst du immer wieder nachsehen wie die Bedeutung war:
1
PIE1=  0b00000001;

Wenn du mehrere Interupts hast musst du natürlich auch noch abfragen 
welder aufgetreten ist, also
1
if (PIR1bits.TMR1IF=1) ....
bzw. auch noch mit dem Enable Bit verknüpfen
1
 if ((PIR1bits.TMR1IF=1) & (PIE1bits.TMR1IE=1) ...

von Bastian B. (hobby-pic)


Lesenswert?

alles klar. werde ich machen.

Danke nochmal

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.