Forum: Mikrocontroller und Digitale Elektronik PIC16F886 resetet sich selbstständig beim ADC auslesen


von ThinkPic (Gast)


Lesenswert?

Moin Leute,

leider habe ich ein Problem mit meinem PIC. Dieser resetet sich 
automatisch, sobald ich
1
return ((ADRESH<<8)+ADRESL);
 ausführe. Leider finde ich keine wirkliche Ursache dafür. Kann mir 
jemand helfen?

Vielen Dank.
1
#pragma config FOSC = XT        // Oscillator Selection bits (XT oscillator)
2
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
3
#pragma config PWRTE = ON       // Power-up Timer Enable bit (PWRT enabled)
4
#pragma config BOREN = ON       // Brown-out Reset Enable bit (BOR enabled)
5
#pragma config LVP = OFF        // Low-Voltage (Single-Supply) 
6
#pragma config CPD = OFF        // Data EEPROM Memory Code Protection bit 
7
#pragma config WRT = OFF        // Flash Program Memory Write Enable bits 
8
#pragma config CP = OFF         // Flash Program Memory Code Protection bit 
9
 
10
#define _XTAL_FREQ 8000000  
11
#include "xc.h"
12
13
int result;
14
15
void InitPWM(){
16
    
17
    CCP1CON = 0b00001100;
18
    CCP2CON = 0b00111100;
19
    PSTRCON = 0b00000100;
20
    CCPR1L = 40;
21
    CCPR1H = 0x00;
22
}
23
24
void InitTMR2(){
25
    
26
    T2CON=0b01111100; 
27
    PR2=100;            //100 us
28
    TMR2=0;
29
    TMR2IF = 0;
30
    TMR2ON = 1;
31
}
32
33
void InitADC()
34
{
35
  ADCON0 = 0x11000000;
36
  ADCON1 = 0b10000000; 
37
  ADCON0bits.ADON = 1;
38
  __delay_ms(10);
39
}
40
41
42
43
int ADC_Read(unsigned char channel)
44
{
45
  __delay_ms(2); 
46
  GO_nDONE = 1; 
47
  while(GO_nDONE); 
48
  return ((ADRESH<<8)+ADRESL); 
49
}
50
51
int main(void) {
52
    
53
    OSCCON = 0x66; //Interner Oszi
54
    
55
    int dc = 0;
56
    int a = 0;
57
    
58
    TRISA = PORTA = TRISB = PORTB = TRISC = PORTB =0x00;
59
    ANSEL = 0x01;
60
    
61
    TRISBbits.TRISB1=0;
62
    PORTBbits.RB1=0;
63
    
64
    TRISAbits.TRISA0=1;
65
    PORTAbits.RA0=1;
66
    
67
    TRISCbits.TRISC2=0;
68
    PORTCbits.RC2=0;
69
    TRISCbits.TRISC3=0;
70
    PORTCbits.RC3=0;
71
    TRISCbits.TRISC4=0;
72
    PORTCbits.RC4=0;
73
    
74
    TRISAbits.TRISA1=0;
75
    PORTAbits.RA1=0;
76
    TRISAbits.TRISA2=0;
77
    PORTAbits.RA2=0;
78
    TRISAbits.TRISA5=0;
79
    PORTAbits.RA5=0;
80
    
81
    INTCON = 0;
82
    PIE1   = 0;
83
    PIE2   = 0;
84
    
85
    InitTMR2();
86
    InitPWM();
87
    InitADC();
88
    
89
    while(1){
90
        
91
        PORTCbits.RC4 = 1;
92
        PORTAbits.RA2 = 1;
93
94
        result = ADC_Read(0); //Analog Channel 0
95
        
96
        for (dc = 0; dc < 17; dc++) 
97
        {
98
            CCPR1L = dc;
99
            CCPR2L = 17-dc;
100
            __delay_ms(100);
101
        }
102
103
        for (dc = 16; dc > 0; dc--) 
104
        {
105
            CCPR1L = dc;
106
            CCPR2L = 17-dc;
107
            __delay_ms(100);
108
}
109
        
110
}

von Volker S. (vloki)


Lesenswert?

ThinkPic schrieb:
> leider habe ich ein Problem mit meinem PIC. Dieser resetet sich
> automatisch, sobald ich
1
return ((ADRESH<<8)+ADRESL);
> ausführe.

Das scheint mir eh irgendwie falsch. Was soll das denn tun?
Hast du schon mal return ADRES; getestet? Könnte funktionieren.

Woher weißt du, dass es genau an dieser Zeile liegt? Debugger?

(ADRESH<<8 gibt vermutlich immer 0, warum das einen Reset auslösen 
solle, weiß ich allerdings auch nicht)

von HyperMario (Gast)


Lesenswert?

ThinkPic schrieb:
> int ADC_Read(unsigned char channel)
..
> return ((ADRESH<<8)+ADRESL);

Solche Konstrukte würde ich lieber sein lassen. Der Compiler muss für 
den return Wert vor dem Interrupt Speicher reservieren. Ob es das macht?

1
global Irgend_nTyp InterrupReturnValue;
2
3
void InterruptRoutine(Irgend_nTyp Irgend_nVarname) {
4
5
   InterrupReturnValue = RegisterXY
6
7
}

Funzt bei mir zuverlässig.

von HyperMario (Gast)


Lesenswert?

Da ist ja kein ADC Interrupt. Vergiss es oder probier ob es so läuft.

Sorry.

von ThinkPic (Gast)


Lesenswert?

Moin,

ich bin mir nicht sicher, ob es direkt an der Zeile liegt. Im Debugger 
lande ich danach immer direkt wieder an den Anfang der Main.

Leider scheint der ADC aktuell gar nicht zu funktionieren. Ich bekomme 
immer als return 0x0000. Auch wenn ich
1
result = ((ADRESH<<8)+ADRESL); //Returns Result
 mache.

Ist meine ADC Konfiguration falsch? Möchte VDD als Ref verwenden und A0 
als Input.

von ThinkPic (Gast)


Lesenswert?

Volker S. schrieb:
> ThinkPic schrieb:
>> leider habe ich ein Problem mit meinem PIC. Dieser resetet sich
>> automatisch, sobald ichreturn ((ADRESH<<8)+ADRESL);> ausführe.
>
> Das scheint mir eh irgendwie falsch. Was soll das denn tun?
> Hast du schon mal return ADRES; getestet? Könnte funktionieren.
>
> Woher weißt du, dass es genau an dieser Zeile liegt? Debugger?
>
> (ADRESH<<8 gibt vermutlich immer 0, warum das einen Reset auslösen
> solle, weiß ich allerdings auch nicht)

Return ADRES funktioniert nicht. Kennt der Compiler nicht.

von HyperMario (Gast)


Lesenswert?

ThinkPic schrieb:
> Return ADRES funktioniert nicht. Kennt der Compiler nicht.

Weil es kein ADRES Register gibt.

Probier  doch mal das Beispiel aus dem Datenblatt.Ist zwar in Assembler, 
lässt sich aber leicht nach C umschreiben.

von Volker S. (vloki)


Lesenswert?

HyperMario schrieb:
> Weil es kein ADRES Register gibt.

Oft hat aber jemand dem Compiler gesagt, was es bedeutet.
#define ADRES *(uint_16t*)(&ADRESL) oder so ähnlich

Das geht beim 16F886 leider nicht, weil die Register nicht 
dementsprechend liegen. Bei den neueren funktioniert es vermutlich 
immer.

@ThinkPIC
setz doch mal einen Breakpoint auf den Resetvector, bzw. den 
Interruptvector (im Program Memory View) und schau, ob er vielleicht 
dahin springt.

Oder setz den Breakpoint auf das return, und geh dann im Single Step 
weiter.
(single step auch im Program Memory View)

Wenn das (ADRESH<<8) später immer 0 liefert, dann probier 
((uint16_t)ADRESH)<<8)

von Peter D. (peda)


Lesenswert?

ThinkPic schrieb:
> GO_nDONE = 1;
>   while(GO_nDONE);

Vermutlich wird GO_nDONE niemals auf 0 gesetzt, d.h. Endlosschleife und 
irgendwann schlägt der Watchdogreset zu.

von Patrick B. (p51d)


Lesenswert?

Peter D. schrieb:
> ThinkPic schrieb:
>> GO_nDONE = 1;
>>   while(GO_nDONE);
>
> Vermutlich wird GO_nDONE niemals auf 0 gesetzt, d.h. Endlosschleife und
> irgendwann schlägt der Watchdogreset zu.

Würde ich auch schätzen.

ThinkPic schrieb:
> int ADC_Read(unsigned char channel)
> {
>   __delay_ms(2);
>   GO_nDONE = 1;
>   while(GO_nDONE);
>   return ((ADRESH<<8)+ADRESL);
> }

Finde ich sowieso etwas speziell. Normalerweise startet man doch eine 
Messung über ein ADC Flag und wartet bis der ADC ein Done Flag setzt. 
Oder das ganze ist im Freilauf. Aber selber irgendwelche Flags 
erfinden...

von Volker S. (vloki)


Lesenswert?

Peter D. schrieb:
> Vermutlich wird GO_nDONE niemals auf 0 gesetzt, d.h. Endlosschleife und
> irgendwann schlägt der Watchdogreset zu.

ThinkPic schrieb:
> #pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT
> disabled)


Wenn der ADC richtig konfiguriert wurde, dann geht GO_nDONE nach 
erfolgter Wandlung wieder auf 0

ThinkPic schrieb:
> ADCON0 = 0x11000000;

Weiß zwar nicht, warum eine falsche Konfiguration einen RESET auslösen 
sollte, aber mach mal trotzdem das x zu einem b ;-)



Patrick B. schrieb:
> Aber selber irgendwelche Flags erfinden...

Wie kommst du jetzt darauf? Noch nie PICs programmiert?



####################################################################
Ich bin weiterhin für Debuggen!

: Bearbeitet durch User
von Lutz (Gast)


Lesenswert?

ich kenne den pic zwar nicht aber muss man die Register nicht verodern? 
Wie groß ist ein int?

von Peter D. (peda)


Lesenswert?

Lutz schrieb:
> muss man die Register nicht verodern?

Nein.
Links schieben füllt immer mit 0 auf, es ist also egal ob | oder +.

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.