Forum: Mikrocontroller und Digitale Elektronik PIC Interrupt Probleme


von Friedrich Ch. (Gast)


Lesenswert?

Hallo zusammen, ich programmiere seit ca. 3 Wochen einen pic18f45k20 uC 
auf dem Demoboard des PICkit3 und bin jetzt zu den Interrupts gekommmen 
und hab jetzt auch schon meine ersten Probleme. Ich benutze MPLABX und 
den XC8 Compiler. Da ich im Internet nicht wirklich fündig geworden bin, 
versuche ich hier mein Glück. :)
Mein Problem: Ich lese das A/D-Ergebnis in der Polling-Schleife ein und 
hab kein Problem, sobald ich dies allerdings über eine Interruptfunktion 
tun möchte, funktioniert gar nichts mehr  ^^ Bitte um Hilfe  :/
Mein Code:
1
#include "p18cxxx.h"
2
#include "xc.h"
3
#include "config.h"
4
5
#define BTN_IGNORE_FACTOR 10
6
#define _XTAL_FREQ 1000000
7
8
volatile unsigned char ad_msb = 0, ad_lsb = 0;
9
10
void delay(int time);
11
void SetupOscillator(void);
12
void SetupPWM(void);
13
void SetupADC(void);
14
void interrupt low_priority Low_Priority_ISR(void);
15
16
void main(void) {
17
18
   unsigned char button = 0, ignorebutton = 0;
19
20
   ANSEL = 0x00;                                //Eingänge Digital
21
   ANSELH = 0x00;
22
23
   INTCONbits.GIEH = 1;            //Alle High-Priority Interrupts enabled
24
   INTCONbits.GIEL = 1;             //Alle Low-Priority Interrupts enabled
25
   RCONbits.IPEN = 1;               //Enable Priority levels on Interrupts
26
27
   SetupOscillator();                        //Frequenz 1MHz
28
   SetupADC();    //Analogsignal RA0 einlesen, Digital RB0 einlesen(Taster)
29
   SetupPWM();       //PWM mit Frequenz 1kHz
30
31
   while (1) {
32
       ADCON0bits.GO_DONE = 1;             //A/D-Wandlung starten
33
       while(ADCON0bits.GO_DONE != 0);   //auf A/D-Wandlung warten
34
       ad_msb = ADRESH;                     //A/D-Ergebnis Bit 2 bis 10
35
       ad_lsb = ADRESL;                        //A/D-Ergebnis Bit 0 und 1
36
       if (PORTBbits.RB0 == 0 && ignorebutton == 0) {   //Taster gedrückt? 
37
            button = button ^ 1;        //Modus toggeln -> PWM/Laufschrift
38
            ignorebutton = BTN_IGNORE_FACTOR; 
39
        }
40
        if (ignorebutton > 0) ignorebutton--;  //Ignorierzeit verringern
41
        if (button == 0) {                     //Modus checken
42
             PIE1bits.ADIE = 0;            //ADC-Interrupt disabled
43
             CCPR2L = ad_msb;
44
             CCPR1L = ad_msb;
45
             CCP2CONbits.DC2B = ad_lsb;    //Steigerung Duty Cycle auf 0,1%
46
             CCP1CONbits.DC1B = ad_lsb;
47
             //PIE1bits.ADIE = 1;                //ADC-Inerrupt enabled
48
             LATD = ad_msb;
49
        }  else{
50
            CCPR1L = CCPR1L - 1;    //PWM Duty Cycle an CCP1/RC2 verringern
51
            delay(25);                       //25ms warten
52
            if(CCPR1L == 0){
53
                button = 0;
54
            }
55
            
56
        }
57
58
    }
59
}
60
61
void delay(int time) {
62
   int counter = 0;
63
   for (counter = 0; counter<time; counter++)
64
        __delay_ms(1);
65
}
66
67
void SetupOscillator(){
68
    OSCCONbits.SCS = 0x3;       //Interner Oscillator
69
    OSCCONbits.OSTS = 0;        //Interner Oscillator
70
    OSCCONbits.IRCF0 = 1;
71
    OSCCONbits.IRCF1 = 1;
72
    OSCCONbits.IRCF2 = 0;       //Oscillatorfrequenz 1MHz
73
    OSCCONbits.IDLEN = 0;       //Sleep-Mode bei sleep
74
}
75
76
void SetupPWM(){
77
   TRISCbits.RC1 = 1;           //Port RC1/CCP2 PWM als Ausgang sperren
78
   TRISCbits.RC2 = 1;           //Port RC2/CCP1 PWM als Ausgang sperren
79
   T2CONbits.T2CKPS = 0x00;     //Timer 2 Eingang: Fosc 1MHz/4 = 250kHz
80
   PR2 = 249;                  //PWM-Periode: PR+1 * 1/250kHz = 1ms, f=1kHz
81
   CCP2CONbits.CCP2M = 0x0F;    //CCP2 auf PWM
82
   CCP1CONbits.CCP1M = 0x0C;    //CCP1 auf PWM
83
   CCP1CONbits.P1M = 0x00;      //CCP1 Single Mode
84
   PSTRCONbits.STRA = 1;        //P1A als Ausgang für CCP1
85
   TRISCbits.RC1 = 0;           //Port RC1/CCP2 PWM als Ausgang
86
   TRISCbits.RC2 = 0;           //Port RC2/CCP1 PWM als Ausgang
87
   T2CONbits.TMR2ON = 1;        //Timer 2 an
88
}
89
90
void SetupADC(){
91
   TRISAbits.RA0 = 1;            //Port RA0 Poti auf Eingang setzen
92
   TRISBbits.RB0 = 1;            //Port RB0 Taster auf Eingang setzen
93
94
   ANSELbits.ANS0 = 1;     //RA0 auf Analogeingang setzen (=1, 0 = digital)
95
   ADCON0bits.ADON = 1;          //A/D-Wandler aktivieren
96
   ADCON0bits.CHS = 0x00;        //AN0/RA0 als A/D-Converter Eingang
97
   ADCON1bits.VCFG1 = 0;  //Negative Voltage Reference (=Vss , 1 = AN2,RA2)
98
   ADCON1bits.VCFG0 = 0;  //Positive Voltage Reference (=Vdd , 1 = AN3,RA3)
99
   ADCON2bits.ADFM = 0;  //A/D-Wandler Conversion Result Format: Links beginnend
100
101
   //PIE1bits.ADIE = 1;            //ADC-Interrupt enabled
102
   IPR1bits.ADIP = 0;            //ADC-Interrupt Low-Priority
103
   PIR1bits.ADIF = 0;            //Interruptflag zurücksetzen
104
   ADCON0bits.GO_DONE = 1;       //A/D-Wandler starten
105
}
106
void low_priority interrupt Low_Priority_ISR(){
107
    if(PIR1bits.ADIF == 1 && ADCON0bits.GO_DONE == 0){
108
        ad_msb = ADRESH;
109
        ad_lsb = ADRESL;
110
        PIR1bits.ADIF = 0;
111
        ADCON0bits.GO_DONE = 1;
112
    }
113
    return;
114
}
wenn ich nun das ADC-Interruptenablebit setze, und das einlesen in der 
Polling-Schleife auskommentier, funktioniert nichts mehr  :/

von ISR (Gast)


Lesenswert?

Ich benutze zwar einen anderen Compiler, aaber da mache ich das immer 
so:

#pragma origin 4                //Der folgende Code steht ab Adresse 4 
(ISR)
interrupt InterruptRoutine(void)//InterruptServiceRoutine
{
GIE  = 0;                       //Interrupts sperren
int_save_registers              //W, STATUS (und PCLATH) retten
char save_FSR = FSR;            //save FSR
if (INTF)                       //Ursache des Interrupts feststellen
{                               //Interrupt an PORTA.2 (INTF)
  blabla....

  INTF = 0;                     //Interrupt wieder scharf machen
}

FSR = save_FSR;                 //restore FSR
int_restore_registers           //W, STATUS (und PCLATH) 
wiederherstellen
GIE  = 1;                       //Interrupts wieder freigeben
}

von Friedrich Ch. (Gast)


Lesenswert?

also das funktioniert bei mir auch nicht... er kennt bei mir nichtmal 
den Befehl origin

von Pascal H. (pase-h)


Lesenswert?

ISR schrieb:
> Ich benutze zwar einen anderen Compiler...

Friedrich Ch. schrieb:
> also das funktioniert bei mir auch nicht...

Fällt dir da was auf?

von Friedrich Ch. (Gast)


Lesenswert?

Was soll mir auffallen? das der XC8 einen anderen Befehlsumfang hat als 
seiner? Oder dass ich einen anderen Compiler benutzen soll?

von Friedrich Ph. (Gast)


Lesenswert?

Ich habe jetzt eine andere Lösung gefunden. Diese Interrupts erfüllen eh 
keinen wirklichen Sinn und deshalb überspringe ich das Kapitel mit den 
Interrupts. Dieser unnütze Kram!

von ISA. (Gast)


Lesenswert?

Das finde ich auch, dass ist eine super Lösung. Nim einfach den 
Programmieradapter und werfe ihn aus dem dritten Stock.

von Friedrich Ph. (Gast)


Lesenswert?

Und wenn der Adapter kaputt geht, wenn ich ihn aus dem dritten Stock 
werfe?

von ISA. (Gast)


Lesenswert?

Dann werf ihn ganz einfach zurück, damit er wieder ganz wird. Das nennt 
man dann ein Rezidif.

von Friedrich Ch. (Gast)


Lesenswert?

Hat den keiner eine einfach ISR für den A/D-Wandler?? oder ne Ahnung was 
ich vergessen hab

von ISA. (Gast)


Lesenswert?

Was willst Du denn nu mit einem AD-Wandler? Verwende doch einfach einen 
analogen Komparator.

von Friedrich Ph. (Gast)


Lesenswert?

Was ist ein analoger Komparator?

von Friedrich Ch. (Gast)


Lesenswert?

Danke für eure qualifizierten Beiträge!!

von Johannes M. (johannesm)


Lesenswert?

Evtl. liegt das folgende Problem vor:
Wenn das Programm in die ISR springt und die if Anweisung nicht 
zutrifft, wird das Interrupt Flag nicht zurückgesetzt und die Kiste 
bleibt hängen.

Bei dem einsamen return in der letzten Zeile hätte ich vermutet, dass 
der Compiler meckert, da die Funktion void als Rückgabetyp hat. Wird 
aber wahrscheinlich einfach ignoriert.

von Peter C. (peter_c49)


Lesenswert?

Hallo

warum ist in main() noch das polling am ADC drin? und du startest dort 
auch wieder eine ADC convertierung?
also, entweder du benutzt interrupts, und startest dort wieder den ADC.
oder in main via polling aber nicht beides.
nicht das dies dein problem seinmuss, aber so wird es sicher auch nichts 
mehr tun,
da du ja in main()
auf das flag schaust,
>while(ADCON0bits.GO_DONE != 0);   //auf A/D-Wandlung warten

das wirst du ev gar nie mehr erleben, hat ja dein interrupt schon 
erledigt.

mfG
Peter

von Friedrich Ch. (Gast)


Lesenswert?

habe das zurücksetzen des Interrupt-Flags aus der if-Anweisung 
herausgetan, sodass es nun immer zurückgesetzt wird, wenn ich in die ISR 
gehe(keine Änderung), aber eigtl dürfte doch kein anderes Interrupt-Flag 
gesetzt sein, da ja der A/D-Wandler enabled ist

und die 2 zeilen
       ADCON0bits.GO_DONE = 1;             //A/D-Wandlung starten
       while(ADCON0bits.GO_DONE != 0);   //auf A/D-Wandlung warten
kommentiere ich ja aus, bevor ich das mit dem Interrupt versuche.. der 
Code oben funktioniert, da ich ja die Aktivierung des ADC-Interrupts 
auskommentiert habe

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


Lesenswert?

In der main() setzt du in der while{} Schleife das Bit 
ADCON0bits.GO_DONE, und hast danach eine leere while{} die solange läuft 
wie dieses Bit gesetzt ist.

In deiner ISR hast du dann aber nochmals "ADCON0bits.GO_DONE = 1" wenn 
der ADC Interrupt zutrifft.

Somit wird in deiner while{}-Warteschleife dieses Bit niemals 0, und 
somit hängt er dann auch dort fest. Ebenso setzt du die ad_msb sowie 
ad_lsb an beiden Stellen, also in der main als auch im Interrupt. Da du 
aber eine neue Konvertierung im Interrupt startest, wird dann in der 
main bei der Zuweisung ein ungenaues Ergebnis herauskommen.

Nimm das "ADCON0bits.GO_DONE = 1" und die Zuweisung zu ad_msb und as_lsb 
in dem Interrupt raus. In der main entfernst du dann den Block
1
       ADCON0bits.GO_DONE = 1;             //A/D-Wandlung starten
2
       while(ADCON0bits.GO_DONE != 0);   //auf A/D-Wandlung warten
3
       ad_msb = ADRESH;                     //A/D-Ergebnis Bit 2 bis 10
4
       ad_lsb = ADRESL;                        //A/D-Ergebnis Bit 0 und 1

Stattdessen machst du sowas wie
1
       if(ADCON0bits.GO_DONE == 0)
2
       {
3
           ... Ergebnis des ADC auswerten, etc ...
4
           ADCON0bits.GO_DONE = 1; // ADC neu starten
5
       }

Grüße,

Chris

Edit: Das GO_DONE Bit brauchst im Interrupt auch gar nicht abfragen, 
wenn der IRQ ausgelöst wird ist auch dieses Bit auf 0.

: Bearbeitet durch User
von Friedrich Ch. (Gast)


Lesenswert?

den Block
       ADCON0bits.GO_DONE = 1;             //A/D-Wandlung starten
       while(ADCON0bits.GO_DONE != 0);   //auf A/D-Wandlung warten
       ad_msb = ADRESH;                     //A/D-Ergebnis Bit 2 bis 10
       ad_lsb = ADRESL;                        //A/D-Ergebnis Bit 0 und 
1
habe ich bereits entfernt, dieser Block war lediglich ein Stück des 
alten Codes, den ich nur zu Testzwecken noch drin hatte, habe ihn aber 
immer mit /**/ auskommentiert, wenn ich ISR getestet habe.

dein Vorschlag funktioniert, ist allerdings nur eine schönere 
Programmierung des alten Blocks oder nicht? und meine ISR ist wieder 
ohne Funktion, ich wollte eben die ad_msb = ..... Zuweisung in der ISR
mfg Chris F.

von Erich (Gast)


Lesenswert?

>Ich benutze MPLABX und den XC8 Compiler.
>Da ich im Internet nicht wirklich fündig geworden bin,
>versuche ich hier mein Glück.

Naja, häste eben mal beim Hersteller nachgeforscht...

http://ww1.microchip.com/downloads/en/DeviceDoc/52116A.pdf
http://ww1.microchip.com/downloads/en/DeviceDoc/PICkit3_Starter_Kit.zip

Siehe in der ZIP das Kapitel 10  ( src\pic18\c\10 Interrupt )

Allerdings:
Du hast den "alten" Stand aus 2009 mit dem pic18f45k20 .
Damals gab es kein MPLABX .
Die aktuellen o.g. Beispiele befassen sich mit dem PIC18F14K22 (20-pin) 
auf Board DM164130-9.

http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1406&dDocName=en559454
http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1406&dDocName=en559587

Insofern musste für das bisherige Board mit dem pic18f45k20 (44-Pin) die 
geänderte Compiler-Syntax für die Interrupts sinngemäß übertragen.

Gruss

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


Lesenswert?

Friedrich Ch. schrieb:
> den Block
>        ADCON0bits.GO_DONE = 1;             //A/D-Wandlung starten
>        while(ADCON0bits.GO_DONE != 0);   //auf A/D-Wandlung warten
>        ad_msb = ADRESH;                     //A/D-Ergebnis Bit 2 bis 10
>        ad_lsb = ADRESL;                        //A/D-Ergebnis Bit 0 und
> 1
> habe ich bereits entfernt, dieser Block war lediglich ein Stück des
> alten Codes, den ich nur zu Testzwecken noch drin hatte, habe ihn aber
> immer mit /**/ auskommentiert, wenn ich ISR getestet habe.
>
> dein Vorschlag funktioniert, ist allerdings nur eine schönere
> Programmierung des alten Blocks oder nicht? und meine ISR ist wieder
> ohne Funktion, ich wollte eben die ad_msb = ..... Zuweisung in der ISR
> mfg Chris F.

Du kannst die Zuweisung von ad_msb und ad_lsb natürlich auch in der IRQ 
Routine machen. Allerdings solltest du den ADC dann nicht mehr neu 
starten im IRQ. Das Problem ist nämlich das du ja zwei Werte ausliest. 
Nun kann es passieren das du grad ad_msb in der main benutzt, dann 
passiert der ADC Interrupt, und als nächstes benutzt due dann ad_lsb in 
der main. Da aber der ADC Interrupt dazwischen war, gehört _lsb dann zum 
neuen Wert, währen der _msb dann noch vom alten Wert war.

Stell dir vor der alte ADC Wert war 0x00FF und der neue Wert ist 0x0100. 
Zuerst liest du den _msb, also 0x00 vom alten Wert. ADC Interrupt kommt, 
und als nächstes liest du den _lsb, der nun aber ebenfalls 0x00 ist. im 
Ergebnis hast du dann also 0x0000 bekommen, was natürlich nicht dem 
wirklichen ADC Ergebnis entspricht.

Grüße,

Chris

: Bearbeitet durch User
von Friedrich Ch (Gast)


Lesenswert?

ja das versteh ich, aber habe ich eben diesen Fehler nicht durch das 
sperren des ADC-Interrupts verhindert??
             PIE1bits.ADIE = 0;            //ADC-Interrupt disabled
             CCPR2L = ad_msb;
             CCPR1L = ad_msb;
             CCP2CONbits.DC2B = ad_lsb;    //Steigerung Duty Cycle auf 
0,1%
             CCP1CONbits.DC1B = ad_lsb;
             PIE1bits.ADIE = 1;                //ADC-Inerrupt enabled

ich denke, ich komme dem Problem schon näher, ich habe ein bisschen 
probiert und bin auf das Ergebnis gekommen, dass der Code erst dann 
nicht funktioniert, wenn ich in der ISR die Zuweisung
        ad_msb = ADRESH;
        ad_lsb = ADRESL;
mache. Wenn ich nun das mit der Zwischenspeicherung lasse und das 
Ergebnis ADRESH direkt meinen LED´s in der ISR mit
        LATD = ADRESH;
zuweise funktioniert dies.
Aber warum will diese Zwischenspeicherung nicht funktionieren, ich muss 
doch die Möglichkeit haben, eine Variable der main in der ISR zu 
bearbeiten?

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


Lesenswert?

Friedrich Ch schrieb:
> ja das versteh ich, aber habe ich eben diesen Fehler nicht durch das
> sperren des ADC-Interrupts verhindert??
>              PIE1bits.ADIE = 0;            //ADC-Interrupt disabled
>              CCPR2L = ad_msb;
>              CCPR1L = ad_msb;
>              CCP2CONbits.DC2B = ad_lsb;    //Steigerung Duty Cycle auf
> 0,1%
>              CCP1CONbits.DC1B = ad_lsb;
>              PIE1bits.ADIE = 1;                //ADC-Inerrupt enabled
>
> ich denke, ich komme dem Problem schon näher, ich habe ein bisschen
> probiert und bin auf das Ergebnis gekommen, dass der Code erst dann
> nicht funktioniert, wenn ich in der ISR die Zuweisung
>         ad_msb = ADRESH;
>         ad_lsb = ADRESL;
> mache. Wenn ich nun das mit der Zwischenspeicherung lasse und das
> Ergebnis ADRESH direkt meinen LED´s in der ISR mit
>         LATD = ADRESH;
> zuweise funktioniert dies.
> Aber warum will diese Zwischenspeicherung nicht funktionieren, ich muss
> doch die Möglichkeit haben, eine Variable der main in der ISR zu
> bearbeiten?

Inwiefern funktioniert die Zuweisung nicht? Wie sieht es aus wenn du den 
oberen Block, in dem du CCPRxx behandelst, mal auskommentierst, in der 
IRQ die Zuweisung drinlässt, und dafür im main Loop ein "LATD = ad_msb;" 
machst? Funktioniert das dann?

Ich sehe nämlich das du irgendwas komisches in dem obigen Block machst: 
CCP2CONbits.DC2B = ad_lsb;
Du versuchst hier einem einzelnen Bit ein Byte zuzuweisen. Vielleicht 
verchluckt er sich ja daran? Normalerweise sollte er das Bit  löschen 
wenn ad_lsb = 0 ist, und ansonsten immer setzen. Wenn du aber 10 Bit PWM 
willst, musst du aber die DCxB0 und DCxB1 in beiden CCPxCON Registern 
von Hand setzen.

Reduziere das ganze doch erstmal, ungefähr so:
1
#include "p18cxxx.h"
2
#include "xc.h"
3
#include "config.h"
4
5
#define BTN_IGNORE_FACTOR 10
6
#define _XTAL_FREQ 1000000
7
8
volatile unsigned char ad_msb = 0, ad_lsb = 0;
9
10
void delay(int time);
11
void SetupOscillator(void);
12
void SetupPWM(void);
13
void SetupADC(void);
14
void interrupt low_priority Low_Priority_ISR(void);
15
16
void main(void) 
17
{
18
   unsigned char button = 0, ignorebutton = 0;
19
20
   ANSEL = 0x00;                                //Eingänge Digital
21
   ANSELH = 0x00;
22
23
   INTCONbits.GIEH = 1;            //Alle High-Priority Interrupts enabled
24
   INTCONbits.GIEL = 1;             //Alle Low-Priority Interrupts enabled
25
   RCONbits.IPEN = 1;               //Enable Priority levels on Interrupts
26
27
   SetupOscillator();                        //Frequenz 1MHz
28
   SetupADC();    //Analogsignal RA0 einlesen, Digital RB0 einlesen(Taster)
29
   SetupPWM();       //PWM mit Frequenz 1kHz
30
31
   while (1) 
32
   {
33
       PIE1bits.ADIE = 0;                //ADC-Inerrupt disabled
34
       LATD = ad_msb;
35
       PIE1bits.ADIE = 1;                //ADC-Inerrupt enabled
36
   }
37
}
38
39
void SetupOscillator()
40
{
41
    OSCCONbits.SCS = 0x3;       //Interner Oscillator
42
    OSCCONbits.OSTS = 0;        //Interner Oscillator
43
    OSCCONbits.IRCF0 = 1;
44
    OSCCONbits.IRCF1 = 1;
45
    OSCCONbits.IRCF2 = 0;       //Oscillatorfrequenz 1MHz
46
    OSCCONbits.IDLEN = 0;       //Sleep-Mode bei sleep
47
}
48
49
void SetupPWM()
50
{
51
   TRISCbits.RC1 = 1;           //Port RC1/CCP2 PWM als Ausgang sperren
52
   TRISCbits.RC2 = 1;           //Port RC2/CCP1 PWM als Ausgang sperren
53
   T2CONbits.T2CKPS = 0x00;     //Timer 2 Eingang: Fosc 1MHz/4 = 250kHz
54
   PR2 = 249;                  //PWM-Periode: PR+1 * 1/250kHz = 1ms, f=1kHz
55
   CCP2CONbits.CCP2M = 0x0F;    //CCP2 auf PWM
56
   CCP1CONbits.CCP1M = 0x0C;    //CCP1 auf PWM
57
   CCP1CONbits.P1M = 0x00;      //CCP1 Single Mode
58
   PSTRCONbits.STRA = 1;        //P1A als Ausgang für CCP1
59
   TRISCbits.RC1 = 0;           //Port RC1/CCP2 PWM als Ausgang
60
   TRISCbits.RC2 = 0;           //Port RC2/CCP1 PWM als Ausgang
61
   T2CONbits.TMR2ON = 1;        //Timer 2 an
62
}
63
64
void SetupADC()
65
{
66
   TRISAbits.RA0 = 1;            //Port RA0 Poti auf Eingang setzen
67
   TRISBbits.RB0 = 1;            //Port RB0 Taster auf Eingang setzen
68
69
   ANSELbits.ANS0 = 1;     //RA0 auf Analogeingang setzen (=1, 0 = digital)
70
   ADCON0bits.ADON = 1;          //A/D-Wandler aktivieren
71
   ADCON0bits.CHS = 0x00;        //AN0/RA0 als A/D-Converter Eingang
72
   ADCON1bits.VCFG1 = 0;  //Negative Voltage Reference (=Vss , 1 = AN2,RA2)
73
   ADCON1bits.VCFG0 = 0;  //Positive Voltage Reference (=Vdd , 1 = AN3,RA3)
74
   ADCON2bits.ADFM = 0;  //A/D-Wandler Conversion Result Format: Links beginnend
75
76
   IPR1bits.ADIP = 0;            //ADC-Interrupt Low-Priority
77
   PIR1bits.ADIF = 0;            //Interruptflag zurücksetzen
78
   PIE1bits.ADIE = 1;            //ADC-Interrupt enabled
79
   ADCON0bits.GO_DONE = 1;       //A/D-Wandler starten
80
}
81
82
void low_priority interrupt Low_Priority_ISR()
83
{
84
    if(PIR1bits.ADIF == 1)
85
    {
86
        ad_msb = ADRESH;
87
        ad_lsb = ADRESL;
88
        PIR1bits.ADIF = 0;
89
        ADCON0bits.GO_DONE = 1;
90
    }
91
}

Wenn das dann funktioniert, du also an PortD die ADC Bits siehst, dann 
baue die weiteren Sachen Stück für Stück ein.

Grüße,

Chris

von Chris (Gast)


Lesenswert?

Friedrich Ch schrieb:
> Aber warum will diese Zwischenspeicherung nicht funktionieren, ich muss
> doch die Möglichkeit haben, eine Variable der main in der ISR zu
> bearbeiten?

Ich muss zu meiner Schande gestehen das ich mir deinen Code oben jetzt 
nicht genau angesehen habe. Kenne mich mit dem XC8 auch noch nicht aus.

Allerdings können Probleme mit eigendlich zulässigen Befehlen in der 
Interruptschleife die verschwinden wenn diese Befehle entfernt werden im 
Grunde ja nur zwei Ursachen haben:

1. Die Interruptschleife ist für die gewählten Timingparameter schlicht 
zu langsam. Praktisch sofort nach dem Rücksprung ins Hauptprogramm 
erfolgt direkt eine neue Interruptauslösung mit dem Ergebniss das es zu 
keiner sinnvollen Abarbeitung des Hauptprogramms mehr kommt.

2.Nicht beachtete Seiteneffekte des Befehls. Diese können dabei auch auf 
Grund eines Hardwarefehlers auftreten (Errata Sheet studieren)

Die erste Möglichkeit ist mit Abstand die häufigste Variante, gefolgt 
von Missverständnissen hinsichtlich der Seiteneffekte. Aber auch die 
Hardwarefehler sind leider bei den modernen Mikrocontrollern ALLER 
Fabrikate ein Problem das immer wieder auftritt. So gut wie jeder der 
beruflich mit Mikrocontrollern arbeitet wird schon das eine oder andere 
Mal auf dieses Problem gestossen sein.

Daher immer daran Denken: Das Problem sitzt zwar in den meisten Fällen 
vor dem Bildschirm, aber doch nicht immer.
Deshalb immer auch die Errata Sheets studieren wenn man in einem solchen 
Fall nicht weiter weiss, denn da finden sich die aktuell bekannten 
Fehler nebst Empfehlungen zum Workarround.
Im beruflichen Umfeld sollte das Errata Sheet natürlich schon zum 
Zeitpunkt der Bauteilauswahl bekannt sein, denn ein Problem das für den 
Hobbybastler nur ärgerlich ist kann da zu Problemen führen die 
Zigtausende von Euros kosten können.

Bei dir glaube ich aber an den Fall 1.

lg Chris

von Friedrich Ch. (Gast)


Lesenswert?

Hallo Chis1,
habe den code schon ausprobiert, funktioniert auch nicht. Leuchten immer 
zufällige LEDs, nach dem ersten brennen die binäre 2, dann 0 und jetzt 
die binäre 130...

danke Chris2, für die Info, denke wohl auch, dass es wohl in die 
Richtung gehen werden... wie löst man den so ein Problem? kann man die 
AD-Wandlung langsamer machen, oder einfach keine ISR für ADC schreiben, 
sondern in der Polling-schleife behandeln... oder sollte ich Fosc 
hochschrauben, takte ja lediglich mit 1MHz

lg Chris

von Chris2 (Chris W) (Gast)


Lesenswert?

Friedrich Ch. schrieb:
> wie löst man den so ein Problem? kann man die
> AD-Wandlung langsamer machen, oder einfach keine ISR für ADC schreiben,
> sondern in der Polling-schleife behandeln... oder sollte ich Fosc
> hochschrauben, takte ja lediglich mit 1MHz

Das du keine ISR für die AD Wandlung verwendest würde sich das Problem 
natürlich erledigen, allerdings ist das nicht immer eine Option.

Die Alternative ist das du die Dauer der AD Wandlung im Verhältniss zur 
Taktfrequenz erhöhst!. Also nicht NUR die Fosc hochschrauben sondern 
gleichzeitig auch die Dauer für 1 T_ad heraufsetzen.
Im Moment (Initwert) liegt die Dauer für ein T_ad bei 2µs. Die Wandlung 
dauert immer elf T_ad, also im Moment 22µs.

Der PIC braucht bei 1MHz für einen Befehlszyklus 4µs. Damit kann der PIC 
in dieser Zeit maximal 5 Assemblerbefehle ausführen. Je nach Art des 
Befehls sogar weniger.
Ein einzelner C Befehl wird bei der Compilation ja in Assembler 
übersetzt. Es kann sein das ein ASM Befehl für einen C Befehl reicht, 
meistens sind es aber deutlich mehr.
Damit kann es sein das der PIC in der Zeit nach Beginn der Wandlung 
gerade mal einen C Befehl schafft. eventuell nicht mal dies.

Du könntest ja mal versuchen die Taktfrequenz auf 16Mhz zu setzen und 
T_ad auf Fosc/16. Ist der Fehler dann weg kannst du ja immer noch 
schrittweise die Frequenz wieder absenken.
Statt der 16MHz kannst du natürlich auch gleich auf 64 MHz gehen.

lg Chris

von Friedrich Ch. (Gast)


Lesenswert?

das wars, jetzt läufts  ;)
hab jetzt auf Fosc = 8MHz und T_ad = Fosc/8
danke, vielen vielen Dank!!

wäre glaub ich nie darauf gekommen, dass es das ist

mfg Chris

von Kein Name (Gast)


Lesenswert?

Wenn du sowieso einen Pickit3 hast - solltest dich zuerst in den 
In-Circuit-Debugger einarbeiten. Das lohnt sich. In Summe hast du dein 
Programm schneller am laufen.

von Norbert S. (pianoforte)


Lesenswert?

Ich wollte nur mal erwähnen das die Interrupts bei der Aktuellen Version 
von XC8 Compiler so aussehen müssen:

void __interrupt(high_priority) my_isr(void){
// mach was....z.Bsp.

if (INTCONbits.TMR0IF) {
INTCONbits.TMR0IF=0;
}
}


oder einfach so:

void __interrupt() my_isr_2(void){

// mach was ....

}

von Teo D. (teoderix)


Lesenswert?

Norbert S. schrieb:
> Ich wollte nur mal erwähnen das die Interrupts bei der Aktuellen Version
> von XC8 Compiler so aussehen müssen:

Muss nicht! Sieh Handbuch....

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.