Forum: Mikrocontroller und Digitale Elektronik PIC24FJ64 ADC Problem


von Alexander (Gast)


Lesenswert?

Guten Tag,

aktuell bin ich an einem Projekt für das es nötig ist zwei Spannungen zu 
vergleichen.
Die eine Spannung ist konstant und die andere kann schwanken. Sollte 
diese Spannung schwanken soll die über LEDs angezeigt werden.

Nun zu meinem Problem:
Es ist völlig egal was am PIC an Spannung anliegt. Er gibt für jede 
Spannung den gleichen Wert aus. Ich hatte mal ein kleines Programm was 
lief, wiederrum gab es da noch Probleme mit einer externen Hardware, 
welche die Spannungen liefert. Dieses Problem ist nun behoben aber das 
Programm funktioniert nicht mehr bzw. der PIC liefert falsche 
Ergebnisse.

Merkwürdig ist, dass wenn ich das AD1CON3 register ändere sich auch der 
Ergebnis der Convertierung ändert.

Beispiel:
AD1CON3  = 0b0000000100000010; : ADCrefValue = ADCValue = 127

AD1CON3  = 0b0001111100000111; : ADCrefValue = ADCValue = 0

AD1CON3  = 0b1001111100000010; : ADCrefValue = ADCValue = 1023

Hier ist der Code von meinem Testprogramm was nur dazu dient um zu 
schauen ob der PIC die Spannung korrekt umconvertiert.

#include <p24FJ64GB106.h>
#include <stdio.h>
#include <stdlib.h>

_CONFIG1 (FWDTEN_OFF)           //Watchdogtimer off


int main(void)

{
int ADCrefValue = 0;

PORTB    = 0b0000000000000000;
TRISB    = 0b1111111111111001;   //RB1-2 = Output, RB0/3-15 = Input


AD1CON1  = 0b0000000011100000;   // SSRC<2:0> = 111 implies internal 
counter ends sampling and starts converting.
AD1CON2  = 0b0010000000000000;  // Vr+ = Vref+
AD1CON3  = 0b1001111100000010;   // Sample time = 31Tad, Tad = 3Tcy
AD1PCFG  = 0b1111111111100110;  // AN0, AN3 & AN4 as Analog Input
AD1PCFGL = 0b1111111111100110;  // AN0, AN3 & AN4 as Analog Input
AD1PCFGH = 0b0000000000000011;
AD1CSSL  = 0b0000000000000000;
AD1CSSH  = 0b0000000000000000;
AD1CHS   = 0b0000000000000011;    // Negative Input Vr-, analog signal 
from PIR (MUX A = AN4)


AD1CON1bits.ADON = 1;          //Turn on ADC

while (1)               // repeat continuously
{

  AD1CON1bits.SAMP=1;          //start sampling
  while(!AD1CON1bits.DONE);      //conversion done?
  AD1CON1bits.SAMP=0;          //start sampling
  ADCrefValue = ADC1BUF0;      //yes then get ADC value

  if (ADCrefValue > 300){
    PORTB = 0b0000000000000110;
  }
  else
    PORTB = 0b0000000000000010;


}                   // repeat

}

In diesem Fall liegt der Wert den der PIC einliest knapp über 300 (es 
leuchten beide LEDs an RB1 und RB2) aber auch hier ist es egal wie hoch 
die Spannung ist (auch wenn garkeine Spannung an dem PIN anliegt ist das 
Ergebnis wie sonst auch). Ich hab leider keine andere Möglichkeit mir 
den Wert der in ADC1BUF0 steht anders als über die beiden LEDs 
anzuzeigen.

Besteht vllt. die Möglichkeit der der interne AD-Converter defekt ist?
Ich würde mich über jede Art von Hilfe sehr freuen.
Danke schonmal im Vorraus.

Gruß Alex

von Andreas Häusler (Gast)


Lesenswert?

Schau Dir mal diese Routinen an, vielleicht helfen sie weiter:
1
void initADC( int amask)
2
{
3
  AD1PCFG = amask; // select analog input pins
4
  AD1CON1 = 0; // manual conversion sequence control
5
  AD1CSSL = 0; // no scanning required
6
  AD1CON2 = 0; // use MUXA, AVss and AVdd are used as Vref+/-
7
  AD1CON3 = 0x1F02; // Tad = 2 x Tcy = 125ns >75ns
8
  AD1CON1bits.ADON = 1; // turn on the ADC
9
} //initADC
10
11
int readADC( int ch)
12
{
13
  AD1CHS = ch; // 1. select analog input channel
14
  AD1CON1bits.SAMP = 1; // 2. start sampling
15
  TMR1 = 0; // 3. wait for sampling time
16
17
  while (TMR1< 100); // 6.25 us
18
19
  AD1CON1bits.DONE = 1; // 4. start the conversion
20
21
  while (!AD1CON1bits.DONE); // 5. wait for the conversion to complete
22
23
  return ADC1BUF0; // 6. read the conversion result
24
} // readADC

Automatic sampling timing:
1
void initADC( int amask)
2
{
3
  AD1PCFG = amask; // select analog input pins
4
  AD1CON1 = 0x00E0; // automatic conversion start after sampling
5
  AD1CSSL = 0; // no scanning required
6
  AD1CON2 = 0; // use MUXA, AVss and AVdd are used as Vref+/-
7
  AD1CON3 = 0x1F02; // Tsamp = 32 x Tad; Tad=125ns
8
  AD1CON1bits.ADON = 1; // turn on the ADC
9
} //initADC
10
11
int readADC( int ch)
12
{
13
AD1CHS = ch; // 1. select analog input channel
14
AD1CON1bits.SAMP = 1; // 2. start sampling
15
while (!AD1CON1bits.DONE); // 3. wait for the conversion to complete
16
return ADC1BUF0; // 4. read the conversion result
17
} // readADC
18
19
main ()
20
{
21
int a;
22
// initializations
23
initADC( AINPUTS); // initialize the ADC for the Explorer16 analog inputs
24
TRISA = 0xff00; // select the PORTA pins as outputs to drive the LEDs
25
// main loop
26
  while( 1)
27
  {
28
    a = readADC( POT); // select the POT input and convert
29
    // reduce the 10-bit result to a 3 bit value (0..7)
30
    // (divide by 128 or shift right 7 times
31
    a >>= 7;
32
    // turn on only the corresponding LED
33
    // 0 -> leftmost LED.... 7-> rightmost LED
34
    PORTA = (0x80 >> a);
35
  } // main loop
36
} // main

von Alexander (Gast)


Lesenswert?

Erstmal Danke Andreas.
Leider klappt es so auch nicht.

Erst habe ich mal eine Frage:
Kann ich den Inputchannel für den ADC überhaupt ändern wenn der ADC 
eingeschaltet ist? Weil zuerst wird bei dir die initADC aufgerufen, in 
der der ADC eingeschaltet wird, und dann wird erst der Channel 
ausgewählt welchen der ADC auslesen soll.

Bei deim ersten Beispiel hängt der PIC dann vor dem "while (TMR1< 100); 
// 6.25 us" und geht an dort nicht weiter.
Muss der Timer noch besonders initialisiert werden?

Beim 2ten Beispiel bekomme ich wieder den Wert "0" raus.

Ich hab auch die Beispiele von Microchip selbst versucht aber bei denen 
ist das gleiche Bild :(

Hat jemand vllt. mal ein ähnliches Problem gehabt und eine Lösung bzw. 
weitere Vorschläge?

von Manuel (Gast)


Lesenswert?

Hallo,

bei mir funktioniert folgender Code auf einem PIC24FJ256GA110 mit dem 
C30 Compiler.

Ich habe Autosampling gewählt damit ich irgendwo im Code den AD-Wert aus 
dem register ADC1BUF3 lesen kann.
1
void ADC_Init(void)
2
  {
3
  AD1PCFGLbits.PCFG3 = 0; // AN3 PIN Analogfunktion
4
  TRISBbits.TRISB3 = 1;   // Input
5
  
6
  // Analogwert lesen von ADC1BUF3 !!!!
7
  
8
  AD1CON1bits.FORM = 0b00;   // integer format
9
  AD1CON1bits.SSRC = 0b111;  // auto convert
10
  AD1CON1bits.ASAM = 1;      // Auto-Sampling aktivieren
11
 
12
  AD1CON2bits.VCFG = 0b000;  // AVdd und AVss als referenz
13
  AD1CON2bits.CSCNA = 1;     // Scan inputs
14
  AD1CON2bits.SMPI = 0b1111; // 16 Werte in den den Buffer schreiben  
15
  
16
  AD1CON3bits.SAMC = 0b1111; // Auto Sample Time Tad
17
  AD1CON3bits.ADCS = 0b00111111;  // 64 TCY 
18
  
19
  AD1CSSL = 0xFFFF;    // Alle 16 AN Pins scanen auch wenn digital
20
                       // damit z.B. AN7 Wert im Buffer ADC1BUF7 liegt
21
      
22
  AD1CON1bits.ADON = 1; // ADC einschalten
23
  }
24
25
26
int main(void)  //  main
27
{
28
  uint16_t i,j,AdWert;
29
30
  ADC_Init();
31
32
  while(1)
33
  {
34
  for(i = 0; i < 65000; i++)
35
    {
36
      for(j = 0; j < 1000; j++)
37
      {
38
        AdWert = ADC1BUF3;
39
        printf("AD Wert: %d \r\n",AdWert); 
40
41
        Nop();
42
        Nop();
43
        Nop();
44
      }
45
    }
46
  }
47
}

Gruss Manuel

von Andreas Häusler (Gast)


Lesenswert?

Ja, Du kannst meiner Meinung nach die Eingänge beim Auslesen ändern, 
auch wenn der ADC aktiv ist.

Ja, der Timer muss natürlich initiallisiert sein. Du kannst für Tests 
aber auch nur ein Delay (>6,25us) einfügen.

Hast Du die entsprechenden Inputs auch als Analogeingänge definiert?

Bsp: AD1PCFG = 0xFFFE;  // Select analog input pins

von Andreas Häusler (Gast)


Lesenswert?

Hier übrigens noch der Link zu einem sehr guten Buch für den Einstieg in 
die Pic24 Welt. Kann ich wärmstens empfehlen... (englisch)

http://www.amazon.com/Programming-16-Bit-PIC-Microcontrollers-Technology/dp/0750682922

Programming 16-Bit PIC Microcontrollers in C - Learning to Fly the PIC24 
- Di Jasio (Newnes Verlag)

von Alexander (Gast)


Lesenswert?

Die jeweiligen Pins sind im Analogmodus. Leider funktioniert auch das 
Programm von Manuel nicht.
Ich denke das es nicht am Programm liegt sondern an der Hardware, was 
natürlich der schlimmste Fall wäre.
Aber mit dem Programm womit es mal lief, klappt es nun auch nicht mehr 
und ein anderes Programm macht auf diesem PIC leider auch Probleme die 
auf anderen Boards mit gleichem PIC fehlerfrei laufen.

Kann es sein das der ADC irgendwie einen Defekt hat, der PIC aber 
troztdem andere Aufgabe bearbeitet? Wenn ja gibt es eine Möglichkeit das 
festzustellen?

Ich bedanke mich aber schonmal für die bisherige Hilfe.
Gruß Alex

von Arc N. (arc)


Lesenswert?

Ist das zufälligerweise ein PIC24FJ64GB mit 64-Pins? Dann könnte es auch 
daran liegen:
http://ww1.microchip.com/downloads/en/DeviceDoc/80369k.pdf
"When using PGEC1 and PGED1 to debug an application on any 64-pin 
devices in this family, all voltage references will be disabled. This 
includes VREF+, VREF-, AVDD and AVSS. Any A/D conversion will always 
equal 0x3FF."

von Alexander (Gast)


Lesenswert?

Ja es handelt sich um ein PIC24FJ64 GB106 also ein PIC24 mit 64 PINs. 
Aber das bezieht sich nur aufs "Debuggen" und nicht aufs "Realeasen" 
oder irre ich?
Den debug-Modus kann ich leider garnicht nutzen wegen eines Fehlers 
(PK3Err0040).
In einem anderen Programm wiederrum funktioniert das debuggen weswegen 
ich dort auch hänge aber da es mal funktioniert hat (auch ohne den 
ebug-Modus) habe ich mich mit dem Problem nicht weiter beschäftigt.

von Alexander (Gast)


Lesenswert?

Guten Tag,

ich habe das ganze nun mal im "debug-Modus" laufen lassen und mir dann 
die Register angeguckt.

Die Register AD1CON2, AD1CON3, AD1CHS, AD1PCFGL, AD1PCFGH, AD1CSSL und 
AD1CSSH sind alle so wie ich sie eingestellt habe.
Im AD1CON1 Register wiederrum steht etwas völlig falsches.

Im Programmcode habe ich stehen: AD1CON1 = 0b0000000011100000;
Im Special Function Register Fenster wird mir aber angezeigt das im 
AD1CON1 Register xC0E1 steht. Müsste da nicht x00E0 stehen?

Ich Debuge mit PEGC2/PEGD2, also an dem Problem worauf mich Arc Net 
(danke nochmal) aufmerksam gemacht hat kann es nicht liegen.

Hat einer einen Rat für mich?

Gruß Alex

von Alexander (Gast)


Lesenswert?

OK die Frage hat sich selbst beantwortet.
Das C verwirrt zwar, da das bit 14 dem Datenblatt nach "unimplemented" 
ist und als 0 gelesen werden soll, aber auf 1 steht und somit nach dem 
setzten der ADON bits nicht 8 sondern C steht.

Auch das SAMP bit arbeitet völlig korrekt und setzt sich automatisch von 
1 auf 0 wenn das Sampeln fertig ist.

Ich werde nun erstmal schauen woran es genau liegt und mich nochmal 
melden.

von Alexander (Gast)


Lesenswert?

Ich habe es nun ans Laufen bekommen.
Ich bedanke mich nochmal für die Hilfe.
Im Endeffekt hat mir das Programm von Manuel geholfen.

Ich habe hier nochmal den gesamten Programmcode gepostet.
Zum Verständniss:

ADCrefValue ist eine feste Spannung die 1/2*Vref+ beträgt.
ANAFValue ist die Spannung die aus dem Bewegungsmelder kommt und bei 
keiner Bewegung gleich ADCrefValue sein sollte und bei Bewegungen 
kleiner oder größer wird was dann über das leuchten der LEDs an RB1 und 
RB2 angezeigt wird.


Hier der Code:

#include <p24FJ64GB106.h>
#include <stdio.h>
#include <stdlib.h>

_CONFIG1(JTAGEN_OFF & GCP_OFF & GWRP_OFF & COE_OFF & FWDTEN_OFF & 
ICS_PGx2 & FWPSA_PR32 & WDTPS_PS2048);//& WINDIS_OFF & FWPSA_PR32 & 
WDTPS_PS2048

int reference(void);
int analog(void);
int difference(void);
int delay(void);

/**********************MAIN************************/

int main(void)

{
int ADCrefValue = 0;
int ANAFValue = 0;
int dif = 200;
int ug = 0;
int og = 0;

PORTB = 0b0000000000000000;
TRISB = 0b1111111111111001;     //RB1-2 = Output, RB0/3-15 = Input
PORTC = 0x0000;
TRISC = 0xFFFF;
PORTD = 0x0000;
TRISD = 0xFFFF;
PORTE = 0x0000;
TRISE = 0xFFFF;
PORTF = 0x0000;
TRISF = 0xFFFF;
PORTG = 0x0000;
TRISG = 0xFFFF;

T1CON = 0b0000000000110000;      //Stops the Timer1 and reset control 
reg.
TMR1 = 0x0000;             //Clear contents of the timer register
PR1 = 0xFFFF;             //Load the Period register with the value 
0xFFFF
IPC0bits.T1IP = 0x01;         //Setup Timer1 interrupt for desired 
priority level
                  // (This example assigns level 1 priority)
IFS0bits.T1IF = 0;           //Clear the Timer1 interrupt status flag
IEC0bits.T1IE = 1;           //Enable Timer1 interrupts


AD1CON1  = 0b0000000011100100;    // SAMP bit=0 ends sampling
AD1CON2  = 0b0010010000111100;    // Vref+ (^= Original reference signal 
from PIR (Vcc/2)
AD1CON3  = 0b0001111100111111;
AD1PCFGL = 0b1111111111000110;    // AN0, AN3 & AN4 as Analog Input
AD1PCFGH = 0b0000000000000000;
AD1CSSL  = 0b1111111111111111;    // Analog channel omitted from input 
scan

AD1CON1bits.ADON = 1;

while(1)
  {

    ADCrefValue = ADC1BUF4;
    ANAFValue = ADC1BUF3;
    og = ADCrefValue+dif;
    ug = ADCrefValue-dif;

      if ((ANAFValue < ug)||(ANAFValue > og)){
          PORTB = 0b0000000000000110;
        } //if ((ANAFValue < ADCrefValue)||(ANAFValue > ADCrefValue))
      else
          PORTB = 0b0000000000000000;
  } //while(1)

} //main

/******************END*MAIN************************/


Gruß Alex

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.