Forum: Mikrocontroller und Digitale Elektronik PIC18F4520 - AD Channel wechselt nicht?


von Vincent H. (vinci)


Lesenswert?

Hallo

Ich hab aktuell ein Problem mit einer Joystick Applikation mit einem 
PIC18F4520. Bin in Punkto AD leider auch kein Profi...
Im Programm soll ein 10kOhm Joystick an den Pins AN0 und AN1 erfasst 
werden. Leider scheint das Programm jedoch stets ausschließlich AN0 zu 
lesen, obwohl ich im entsprechenden Interrupt den Channel wechsle?

Interruptcode:
if (ADIF==1)
  {if (CHS0==1)
    {adc_schiene = (ADRESH<<8)+(ADRESL);
    }
   if (CHS0==0)
    {adc_horizont = (ADRESH<<8)+(ADRESL);
    }
    CHS0 = ~CHS0;
    ADIF = 0;
  }

Das Hauptprogramm tut aktuell nichts anderes, als bei Werten == 0 eine 
LED einzuschalten. Eine LED für die "X-Achse", eine für die "Y-Achse" 
des Joysticks. Leider schaltet mir das Programm stets beide LEDs ein, 
wenn ich ausschließlich in der X-Achse auf 0 fahre... Die Y-Achse ist 
vollkommen egal und wird überhaupt nicht beachtet?

Es scheinen also beide Variablen stets den Wert des Pins AN0 anzunehmen.

Woran kann das liegen?

ACQT hab ich entsprechend gesetzt, im Hauptprogramm befindet sich 
mittlerweile sogar ein 5ms Delay... das müsste doch mehr als ausreichend 
sein?

Danke
Vinci

von holger (Gast)


Lesenswert?

Versuchs doch mal so:
1
if (ADIF==1)
2
  {if (CHS0==1)
3
    {adc_schiene = (ADRESH<<8)+(ADRESL);
4
     CHS0 = 0;
5
    }
6
   else
7
    {adc_horizont = (ADRESH<<8)+(ADRESL);
8
     CHS0 = 1;
9
    }
10
    ADIF = 0;
11
  }

von Nico (nico123)


Lesenswert?

Zeige uns den gesammten Code, sonst ist das Helfen ziemlich schwer!

von holger (Gast)


Lesenswert?

Setz hier das GO/DONE bit auch noch mal wieder auf 1.

    GO = 1; // Schau bei deinem Compiler nach wie dat heisst
    ADIF = 0;
}

von Martin S. (drunkenmunky)


Lesenswert?

kannst du nicht nicht debuggen? So ein PICkit3 für ca. 50 Euro zahlt 
sich schnell aus, finde ich...

Sonst kannst du es mit dem Microchip Simulator testen und kucken ob sich 
die Registern korrekt ändern.

von Vincent H. (vinci)


Lesenswert?

Der Hauptcode sieht folgendermaßen aus:
    GODONE = 1;               // ADC starten
    while(GODONE==1);

    *2x IF für die LEDs*

    DelayMs(5);

GODONE ist definitiv richtig benannt, nutze hier Hi-Tech C und habe im 
entsprechenden 18F4520 file nachgesehen. Der Simulator sagt mir, dass 
der Channel richtig umgeschaltet wird.

Werden morgen mal holgers Vorschlag ausprobieren, Debugger besitze ich 
leider (noch) keinen.

von Martin S. (drunkenmunky)


Lesenswert?

ohne den kompletten Code zu sehen, kann man natürlich kaum helfen.

Steht da wo "*2x IF für die LEDs* steht der Code für den Interrupt? Ich 
hoffe nicht...

Dann wäre es kein Interrupt sondern polling.

Also poste doch mal deinen kompletten Code.

von Vincent H. (vinci)


Lesenswert?

interrupt isr()
{if (ADIF==1)             // ADC Interrupt
  {if (CHS0==1)
    {adc_schiene = (ADRESH<<8)+(ADRESL);
    }
   if (CHS0==0)
    {adc_horizont = (ADRESH<<8)+(ADRESL);
    }
    CHS0 = ~CHS0;         // schaltet abwechselnd AN0 / AN1
    ADIF = 0;        // ADC Interrupt-Flag löschen
  }


 if (TMR2IF==1)           // Timer2 @125µs
  {TMR2IF = 0;
   TMR2 = 37;

   if (schiene_enable==1)
    {counter_schiene--;
     if (counter_schiene==0)
     {CLK_schiene = 1;
      counter_schiene = counter_schiene_save;
      DelayUs(10);
      CLK_schiene = 0;
     }
    }

   if (horizont_enable==1)
    {counter_horizont--;
     if (counter_horizont==0)
     {CLK_horizont = 1;
      counter_horizont = counter_horizont_save;
      DelayUs(10);
      CLK_horizont = 0;
     }
    }

  }


 if (INT0IF==1)             // Schranke
  {
   INT0IF = 0;
  }

}

void main()
{TRISA = 0b11010011;      // RA2,RA3,RA5 Output
 TRISB = 0b11111111;      // RB0 Input (Schranke), Rest ebenfalls Input
 TRISC = 0b00000110;      // RC1-RC2 Input; RC0,RC3-RC7 Output
 TRISD = 0b11000000;      // RD6,RD7 Input; RD0-RD5 Output
 TRISE = 0b00001100;      // RE0,RE1 Output; RE2,RE3 Input

 PCFG0 = 1;
 PCFG1 = 0;
 PCFG2 = 1;
 PCFG3 = 1;               // RA0(AN0) und RA1(AN1) analog, Rest digital

 PORTA = 0x00;            // Initialize PORTA
 PORTB = 0x00;            // Initialize PORTB
 PORTC = 0x00;            // Initialize PORTC
 PORTD = 0x00;          // Initialize PORTD
 PORTE = 0x00;            // Initialize PORTE

 // ADC Einstellung (DA HATS NOCH WAS bezügl. ACQT usw.?)
 ADCS0 = 0;
 ADCS1 = 1;
 //ADCS2 = 0;               // FOSC/32

 ACQT0 = 0;
 ACQT1 = 0;
 ACQT2 = 0;               // Aquisition Time manuell vergeben

 VCFG0 = 0;               // Voltage Reference VDD
 VCFG1 = 0;          // Voltage Reference VSS

 CHS0 = 1;
 CHS1 = 0;
 CHS2 = 0;
 CHS3 = 0;          // AN1 auswählen

 ADFM = 1;          // right justified conversion

 ADON = 1;          // ADC enabled

 ADIF = 0;
 INT0IF = 0;
 TMR2IF = 0;              // Interrupt Flags löschen

 ADIE = 1;          // ADC Interrupt zulässig
 PEIE = 1;                // Peripheral Interrupts zulässig
 GIE = 1;                 // Global Interrupts zulässig


 // ext. Interrupt ; Timer2 @ 125µs
 INTEDG0 = 0;        // Interrupt on falling edge of RB0/INT0

 //INT0IE = 1;          // ext. Interrupt INT0 enable

 T2OUTPS0 = 1;
 T2OUTPS1 = 1;
 T2OUTPS2 = 0;
 T2OUTPS3 = 0;            // Postscaler Timer 2 select (1:8)

 T2CKPS0 = 0;
 T2CKPS1 = 0;             // Prescaler Timer2 select (1:1)
 TMR2 = 37;               // Timer2 @ 125µs


 do
 {


//--------------------------------------
  //
  while(modus1==0 && modus2==0)
  {led1 = 0;
   led2 = 0;

   TMR2ON = 1;               // Timer2 starten
   TMR2IE = 1;               // Timer2 Interrupt zulassen

   while(save_taster==0)
   {// Speichern der vorherigen Werte, um den Counter nicht fälschlich 
neu zu setzen
    swcase_schiene_save = swcase_schiene;
    swcase_horizont_save = swcase_horizont;

    GODONE = 1;               // ADC starten
    while(GODONE==1);

    if ((adc_horizont/64)==0)
     {CLK_horizont=1;
     }

    if ((adc_schiene/64)==0)
     {CLK_schiene=1;
     }

    if ((adc_horizont/64)>0)
     {CLK_horizont=0;
     }

    if ((adc_schiene/64)>0)
     {CLK_schiene=0;
     }


   //Aquisition Time? ka ob des automatisch hinhaut...
   DelayMs(5);

   }

   // Video starten
   //TRISB = 0b11111110;
   //RB0 = 0;

   /*TMR2IE = 0;              // Timer2 Interrupt ausschalten
   led1 = 0;
   led2 = 0;
   DelayBigMs(500);
   led1 = 1;
   led2 = 1;
   DelayBigMs(500);
   led1 = 0;
   led2 = 0;
   DelayBigMs(500);
   led1 = 1;
   led2 = 1;
   DelayBigMs(500);
   led1 = 0;
   led2 = 0;*/

  }

//--------------------------------------


//--------------------------------------
  //
  while(modus1==0 && modus2==1)
  {led1 = 0;
   led2 = 1;
  }
//--------------------------------------


//--------------------------------------
  //
  while(modus1==1 && modus2==0)
  {led1 = 1;
   led2 = 0;
  }
//--------------------------------------


//--------------------------------------
  //
  while(modus1==1 && modus2==1)
  {led1 = 1;
   led2 = 1;
  }
//--------------------------------------


 }while(1);

}




Wie gesagt... imho unnötig viel Information ;)
Da wo die ich vorher die Sternchen gemacht hab stehen natürlich nur die 
IFs... kein Interrupt.

von Martin S. (drunkenmunky)


Lesenswert?

und jetzt noch die eingestellte Taktrate...

von Vincent H. (vinci)


Lesenswert?

HSLPP @ 8 bzw. 32 MHZ

von Vincent (Gast)


Lesenswert?

keiner eine Idee mehr?

von Martin S. (drunkenmunky)


Lesenswert?

die Acquistion Time auf Null zu stellen war sicherlich nicht die klügste 
Lösung.

Hast du Tad mal ausgerechnet, wie groß du es eingestellt hast?
Dann ist im Datenblatt eine Beispielrechnug wie groß die Aquisition Time 
berechnet wird. Das musst du auch ausrechnen und einstellen.

von Vincent (Gast)


Lesenswert?

Martin S. schrieb:
> die Acquistion Time auf Null zu stellen war sicherlich nicht die klügste
> Lösung.
>
> Hast du Tad mal ausgerechnet, wie groß du es eingestellt hast?
> Dann ist im Datenblatt eine Beispielrechnug wie groß die Aquisition Time
> berechnet wird. Das musst du auch ausrechnen und einstellen.


Ich verstehe nicht so ganz. Die ACT wird doch eingehalten, da in der 
Programmschleife, die ich aktuell ausschließlich durchlaufe (1.while), 
ein Delay von 5ms erfolgt. Diese Zeit ist laut Datenblatt ein vielfaches 
der benötigten (10k Impedanz @ max. 50°C).

ACQT auf 0 heißt somit nur, dass ich die ACT manuell mittels 
Programmcode berücksichtige.

von Martin S. (drunkenmunky)


Lesenswert?

Ja ok man kann es auch auf 0 einstellen, wenn man es selbst überwacht. 
Aber einfacher wäre es einfach einzustellen.

Dein Programm ist etwas wirr. Vielleicht kannst du mal kurz beschreiben 
was es denn mal können soll.

Z.B. Wo wird modus1 und modus2 gesetzt? Auf welchem Ausgang liegt led1 
und led2. Das ist immer noch nicht der komplette Code.

Sonst mach dich doch einfach mal mit dem Simulator vertraut. So schlecht 
ist der gar nicht. Dann kannst du mal schauen ob er den Kanal überhaupt 
richtig wechselt, wenn du schon kein Debugger hast.

von Vincent (Gast)


Lesenswert?

Martin S. schrieb:
> Ja ok man kann es auch auf 0 einstellen, wenn man es selbst überwacht.
> Aber einfacher wäre es einfach einzustellen.
>
> Dein Programm ist etwas wirr. Vielleicht kannst du mal kurz beschreiben
> was es denn mal können soll.
>
> Z.B. Wo wird modus1 und modus2 gesetzt? Auf welchem Ausgang liegt led1
> und led2. Das ist immer noch nicht der komplette Code.
>
> Sonst mach dich doch einfach mal mit dem Simulator vertraut. So schlecht
> ist der gar nicht. Dann kannst du mal schauen ob er den Kanal überhaupt
> richtig wechselt, wenn du schon kein Debugger hast.


Sag ja, dass der komplette Code nur verwirrt... und doch, das ist der 
komplette Code.

folgender Code wird aktuell durchlaufen und ist relevant:

interrupt isr()
{if (ADIF==1)             // ADC Interrupt
  {if (CHS0==1)
    {adc_schiene = (ADRESH<<8)+(ADRESL);
    }
   if (CHS0==0)
    {adc_horizont = (ADRESH<<8)+(ADRESL);
    }
    CHS0 = ~CHS0;         // schaltet abwechselnd AN0 / AN1
    ADIF = 0;             // ADC Interrupt-Flag löschen
  }

}

void main()
{TRISA = 0b11010011;      // RA2,RA3,RA5 Output
 TRISB = 0b11111111;      // RB0 Input (Schranke), Rest ebenfalls Input
 TRISC = 0b00000110;      // RC1-RC2 Input; RC0,RC3-RC7 Output
 TRISD = 0b11000000;      // RD6,RD7 Input; RD0-RD5 Output
 TRISE = 0b00001100;      // RE0,RE1 Output; RE2,RE3 Input

 PCFG0 = 1;
 PCFG1 = 0;
 PCFG2 = 1;
 PCFG3 = 1;               // RA0(AN0) und RA1(AN1) analog, Rest digital

 PORTA = 0x00;            // Initialize PORTA
 PORTB = 0x00;            // Initialize PORTB
 PORTC = 0x00;            // Initialize PORTC
 PORTD = 0x00;            // Initialize PORTD
 PORTE = 0x00;            // Initialize PORTE


 ADCS0 = 0;
 ADCS1 = 1;
 ADCS2 = 0;               // FOSC/32

 ACQT0 = 0;
 ACQT1 = 0;
 ACQT2 = 0;               // Aquisition Time manuell vergeben

 VCFG0 = 0;               // Voltage Reference VDD
 VCFG1 = 0;               // Voltage Reference VSS

 CHS0 = 1;
 CHS1 = 0;
 CHS2 = 0;
 CHS3 = 0;          // AN1 auswählen

 ADFM = 1;          // right justified conversion

 ADON = 1;          // ADC enabled

 ADIF = 0;
 INT0IF = 0;
 TMR2IF = 0;              // Interrupt Flags löschen

 ADIE = 1;                // ADC Interrupt zulässig
 PEIE = 1;                // Peripheral Interrupts zulässig
 GIE = 1;                 // Global Interrupts zulässig


 // ext. Interrupt ; Timer2 @ 125µs
 INTEDG0 = 0;        // Interrupt on falling edge of RB0/INT0

 //INT0IE = 1;          // ext. Interrupt INT0 enable

 T2OUTPS0 = 1;
 T2OUTPS1 = 1;
 T2OUTPS2 = 0;
 T2OUTPS3 = 0;            // Postscaler Timer 2 select (1:8)

 T2CKPS0 = 0;
 T2CKPS1 = 0;             // Prescaler Timer2 select (1:1)
 TMR2 = 37;               // Timer2 @ 125µs


 do
 {


//--------------------------------------
  //
  while(modus1==0 && modus2==0)
  {led1 = 0;
   led2 = 0;

   TMR2ON = 1;               // Timer2 starten
   TMR2IE = 1;               // Timer2 Interrupt zulassen

   while(save_taster==0)
   {GODONE = 1;               // ADC starten
    while(GODONE==1);


   // - IRGENDWELCHE IF-ANWEISUNGEN //


   //Aquisition Time
   DelayMs(5);

   }

  }

 }while(1);

}


Das ganze ist jetzt hoffentlich ein wenig einfacher zu verstehen. Was 
ich von DIESEM Code will, ist ausschließlich eine AD-Conversion einer 
Joystick-Position (X und Y Werte quasi). Später steuere ich damit 2 
Stepper an, aber das spielt aktuell keine Rolle, da wie gesagt der 
Channelwechsel nicht funktioniert.
Im SIMULATOR funktioniert der Wechsel jedoch!

Die anderen Programmteile sind andere Modi, andere Funktionen... für 
mein konkretes Problem nicht relevant, deswegen wollte ich auch nicht 
den gesamten Code posten.

Unter "IRGENDWELCHE IF-Anweisungen" hab ich wirklich nur irgendwelche 
Werteabfragen reingeschrieben um die Funktion zu prüfen. Also z.B.

if (Position X < 123)
{Schalte LED ein
}

if (Position Y < 123)
{Schalte 2.LED ein
}

Damit hab ich ausgetestet, ob der AD Wandler die Joystick Positionen 
richtig erkennt. Zu diesem Zeitpunkt hab ich erst gemerkt, dass stets 
nur die X-Achse des Joysticks gewandelt wird.

Also egal ob ich X oder Y Achse des Joysticks betätige, ich bekomme 
stets ausschließlich die X-Werte, die aber auch als Y interpretiert 
werden. Wenn ich also auf meiner X-Achse (Channel0) mit dem Stick 
spazieren fahr, so werden diese Werte für Channel0 und Channel1 
angennommen. Fahr ich auf der Y-Achse spazieren, so tut sich gar nichts.

Hab auch bereits geprüft, ob ich nicht in der Hardware einen Kurzen oder 
so hab... aber nichts dergleichen, sollte alles passen.

von Martin S. (drunkenmunky)


Lesenswert?

Das ist doch nicht der komplette Code.  Wo sind die 
Variablendeklarationen?

Du bleibst doch immer in der while Schleife, oder?
1
   while(save_taster==0)
2
   {// Speichern der vorherigen Werte, um den Counter nicht fälschlich
3
neu zu setzen
4
    swcase_schiene_save = swcase_schiene;
5
    swcase_horizont_save = swcase_horizont;
6
7
    GODONE = 1;               // ADC starten
8
    while(GODONE==1);
9
10
    if ((adc_horizont/64)==0)
11
     {CLK_horizont=1;
12
     }
13
14
    if ((adc_schiene/64)==0)
15
     {CLK_schiene=1;
16
     }
17
18
    if ((adc_horizont/64)>0)
19
     {CLK_horizont=0;
20
     }
21
22
    if ((adc_schiene/64)>0)
23
     {CLK_schiene=0;
24
     }
25
26
27
   //Aquisition Time? ka ob des automatisch hinhaut...
28
   DelayMs(5);
29
30
   }

save_taster wird doch nirgends verändert... Sind dann CLK_horizont und 
CLK_schiene deine LEDs? Woher soll man das wissen?

Und weißt du was du mit der Abfrage machst?
((adc_horizont/64)==0)

von Vincent H. (vinci)


Lesenswert?

Die "CLK_****" Variablen sind eigentlich Clock Signale für einen TB6560, 
testweise hab ich hier aber LEDs ein und ausgeschalten.
Die "ADC_****" Variablen, sprich die ADC Werte die ich erhalte, teile 
ich durch 64 um eine Joystick Achse in 16 Stufen zu unterteilen. Quasi 
1024 / 64 = 16.
Jede Stufe entspricht einer gewissen Periodendauer, mit der das Clock 
Signal erzeugt wird.

Das spielt aber, wie ich bereits erwähnt habe, für die AD Wandlung 
eigentlich keine Rolle. Das Problem, das nach wie vor besteht, ist, dass 
ich von 2 verschiedenen AD Channeln nur 1 Signal bekomme.


__
Man stelle sich folgendes Beispiel mit 2 Potis vor, die an AD0 und AD1 
hängen.
Drehe ich Poti 1, so soll ab einem gewissen Schwellwert eine LED 
eingeschaltet werden. Drehe ich an Poti 2, so soll eine andere LED ab 
dem selben Schwellwert eingeschaltet werden.
Problem ist nun: Ich drehe an Poti 1 und BEIDE LEDS leuchten.
Ich drehe an Poti 2, nichts passiert.
__

Natürlich verstehe ich, dass der Code hierfür wichig ist, man anstelle 
sich an Stelle der If's einfach die Abfragen für den ADC Wert und die 
LEDS vor. Diese waren 100% richtig! Die gepostete Schleife stellt nur 
einen Teil des Codes dar, der aktuell aber NICHT verlassen wird. Später 
soll dieser Teil nur einen Modus darstellen, der ab Programmstart 
ausgewählt werden kann.

von Martin S. (drunkenmunky)


Lesenswert?

Wähl doch einfach den MPLAB Simulator als Debugger und schau nach warum 
die LEDs gleich geschalten werden. Oder funktionierte da richtig?

von Vincent H. (vinci)


Lesenswert?

Im MPLAB Sim funktioniert die AD-Wandlung. Da schaltet er mir meine 
Ausgänge nicht ständig gleichzeitig.

von Martin S. (drunkenmunky)


Lesenswert?

Hast du mal die Spannung an den Pins mit einem Messgerät gemessen?

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.