www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik ADC ATXMega256A3 initialisieren


Important announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
Autor: Jan-Henrik Bathelt (vedaykin)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Guten Tag,

ich versuche einen der ADC-Wandler eines ATXMegas256A3 zum Einlesen 
einer Gleichspannung zu programmieren. Ich habe mich bisher stark am 
XMEGA-C-Tutorial vom stromflo orientiert.

Der Code unten lässt sich ohne Fehlermeldung hochladen, jedoch messe ich 
am Port A Kanal 0 mit dem Oszi eine Gleichspannung von ca. 1.5V, ohne 
das überhaupt ein externes Signal anliegt. In der Variable "temp" steht 
dann immer der Wert 255. An VTG messe ich die 3.6Volt Betriebsspannung.

Habe ich die ADC-Ports falsch initialisert, bzw. muss ich was in der 
Hardwarekonfiguration ändern, sodass ich an Port A0 auch 0V messe? Ich 
bin für jeden Hinweis dankbar!

#include <avr/io.h>
 
int main (void) {

//ADC enable  
ADCA.CTRLA = 0x01;     
//unsigned mode und 8Bit, Einzelsignale und Interne Signale können gemessen werden
ADCA.CTRLB = 0x04;      
//Interne 1.0V Referenz
ADCA.REFCTRL = 0x00;    

uint8_t temp;

while(1) {  
//input mode single ended Port A Kanal 0, die Messung wird gestartet
ADCA.CH0.CTRL = 0b10000001;
//Ergebnis der Messung wird in temp gespeichert
temp = ADCA_CH0RES;
}
}


Autor: Matthias Sch. (Firma: Matzetronics) (mschoeldgen)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Hab dir mal was rausgesucht, was allerdings einen 3-Kanal Channel Sweep 
im Freerun macht:
#define ADC ADCA
// Setup the ADC for 3-Channel sampling
// runs free with 3 channels , zero effort
void ADCInit(void)
{
  ADC.CTRLA = 0x01;
  ADC.CTRLB = ADC_RESOLUTION_12BIT_gc | (ADC_MODE << 4) ;  // 12 bit right adjusted
  ADC.PRESCALER = ADC_PRESCALER_DIV256_gc;
// reference set to Vcc/1.6
  ADC.REFCTRL = ADC_REFSEL_VCC_gc;
  ADC.EVCTRL = ADC_SWEEP_012_gc ;
// get calibration data , ymmv
  ADC.CALL = adc_get_calibration_data() && 0xff;
  ADC.CALH = (uint16_t)adc_get_calibration_data() >> 8;
// speed input
  ADC.CH0.CTRL = ADC_CH_INPUTMODE_SINGLEENDED_gc;
  ADC.CH0.MUXCTRL = ADC_CH_MUXPOS_PIN0_gc;
// current measure
  ADC.CH1.CTRL = ADC_CH_INPUTMODE_SINGLEENDED_gc;
  ADC.CH1.MUXCTRL = ADC_CH_MUXPOS_PIN1_gc;
// temperature
  ADC.CH2.CTRL = ADC_CH_INPUTMODE_SINGLEENDED_gc;
  ADC.CH2.MUXCTRL = ADC_CH_MUXPOS_PIN2_gc;
  ADC.CTRLB = ADC_RESOLUTION_12BIT_gc | (ADC_MODE << 4) | (1 << 3);  // enable freerun
}
// read result from channel 0
uint16_t ReadChannel0(void) {
 return ADC.CH0RES;
}
Oh, noch vergessen. Du solltest natürlich deine ADC Inputs auf ISC 
Disable konfigurieren:
#define USER_PORT PORTA
#define ADCMASK 0x07
// Port A 0-2 are analog inputs
  PORT_ConfigurePins(&USER_PORT,
      ADCMASK,
      0,
      0,
      PORT_OPC_PULLDOWN_gc,
      PORT_ISC_INPUT_DISABLE_gc);
Ich benutze hier eine Library Funktion, aber das kannst du natürlich 
auch anders hinfummeln.

Autor: Jan-Henrik Bathelt (vedaykin)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Ich habe die Bibliothek port_driver.h aus dem AVR Tuturial "AVR1313" 
eingefügt. Es scheint noch Probleme mit dem Befehl: ADC.CALH = 
(uint16_t)adc_get_calibration_data() >> 8; zu geben. Wenn ich das 
richtig verstanden habe muss ein Wert zwecks Kalibrierung in das 
Register CALH geschrieben werden. Fragt sich nur welcher ;)? Der initial 
Wert ist 0b00000000 glaube ich. Du möchtest einen Wert 8 mal nach rechts 
schieben, um die Kalbrierung durchzuführen richtig?

Auch die Variable: ADC_Mode kann ich noch nicht richtig nachvollziehen. 
In der Bibliothek iox256a3.h bedeutet ADC_RESOLUTION_12BIT_gc: 12-bit 
right-adjusted result. Ist das nicht schon genau das was ich möchte? Ich 
verstehe gerade nicht so ganz die Oderverknüpfung ADC_MODE << 4 in: 
ADC.CTRLB = ADC_RESOLUTION_12BIT_gc | (ADC_MODE << 4) | (1 << 3);  // 
enable freerun

Mein noch nicht funktionierender Code sieht jetzt so aus:

#include <avr/io.h>
#include <C:/Program Files (x86)/Atmel/Drivers/AVR1313/port_driver.h>
#include <C:/Program Files (x86)/Atmel/Drivers/AVR1313/port_driver.c>

#define ADC ADCA
#define USER_PORT PORTA
#define ADCMASK 0x07

uint16_t t;

int main(void)
{
    ADCInit();
    PORT_ConfigurePins(&USER_PORT,ADCMASK,0,0,PORT_OPC_PULLDOWN_gc,PORT_ISC_INPUT_DISABLE_gc);
  
    while(1)
    {
    t = ADC.CH0RES; //Variable für Watchdog, um z.B. Sinus am Port A Pin 0 zu beobachten
    }
}

// Setup the ADC for 3-Channel sampling
// runs free with 3 channels , zero effort
void ADCInit(void)
{    
  ADC.CTRLA = 0x01;
  // 12 bit right adjusted
  ADC.CTRLB = ADC_RESOLUTION_12BIT_gc | (ADC_MODE << 4);  
  ADC.PRESCALER = ADC_PRESCALER_DIV256_gc;
  // reference set to Vcc/1.6
  ADC.REFCTRL = ADC_REFSEL_VCC_gc;
  ADC.EVCTRL = ADC_SWEEP_012_gc ;
  // get calibration data , ymmv
  ADC.CALL = adc_get_calibration_data() && 0xff;
  ADC.CALH = (uint16_t)adc_get_calibration_data() >> 8;
  // speed input
  ADC.CH0.CTRL = ADC_CH_INPUTMODE_SINGLEENDED_gc;
  ADC.CH0.MUXCTRL = ADC_CH_MUXPOS_PIN0_gc;
  // current measure
  ADC.CH1.CTRL = ADC_CH_INPUTMODE_SINGLEENDED_gc;
  ADC.CH1.MUXCTRL = ADC_CH_MUXPOS_PIN1_gc;
  // temperature
  ADC.CH2.CTRL = ADC_CH_INPUTMODE_SINGLEENDED_gc;
  ADC.CH2.MUXCTRL = ADC_CH_MUXPOS_PIN2_gc;
  // enable freerun
  ADC.CTRLB = ADC_RESOLUTION_12BIT_gc | (ADC_MODE << 4) | (1 << 3);  
}
// read result from channel 0
uint16_t ReadChannel0(void) {
 return ADC.CH0RES;
}



Autor: Jan-Henrik Bathelt (vedaykin)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Zusatz: Die Variable ADC_MODE verstehe ich nicht nur, sie ist auch in 
der Bibliothek nicht definiert.

Autor: Alex (Gast)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Hallo,

>>Wenn ich das
richtig verstanden habe muss ein Wert zwecks Kalibrierung in das
Register CALH geschrieben werden.

Zum Testen braucht man keinen Kalibrier-Wert!

>> Fragt sich nur welcher ;)?
Aus der "User Signature Row" - habe das aber noch nicht benutzt



>> In der Bibliothek iox256a3.h bedeutet ADC_RESOLUTION_12BIT_gc: 12-bit
right-adjusted result. Ist das nicht schon genau das was ich möchte?
Das schätze ich auch - warum nur 8Bit einlesen, wenn du 12 Bit haben 
kannst?

>> Ich verstehe gerade nicht so ganz die Oderverknüpfung ADC_MODE << 4 in:
ADC.CTRLB = ADC_RESOLUTION_12BIT_gc | (ADC_MODE << 4) | (1 << 3);  //
enable freerun

Die Konstante "ADC_MODE" bedeutet wohll "CONVMODE": Laut 
A-Family-Manual:
"this bit is zero and the ADC is then configured for unsigned mode where 
single ended and internal signals can be measured." Das sollte passen.

Prinzipelle Fehler:
- Der ADC benötigt einige Zeit, bis das Ergebnis verfügbar ist. Deshalb 
zuerst das INTFLAG in INTFLAGS des Kanals löschen (1 darauf schreiben), 
Wandlung starten und nun auf das IF-Bit warten, dann Wert lesen und Flag 
wieder löschen....
- Wie in den Beispielen musst du ADC.CHx.MUXCTRL auf Port A0 setzen!

PS: (Tipp 1) Halte dir die iox256A3.h im Editor offen und benutze die 
dort definierten Konstante (Suchfunktion verwenden!), das ist viel(!) 
leserlicher!

(Tipp 2) Wenn du einen Debugger hast, schaue dir im IO-View die 
tatsächlichen Einstellungen des ADCA an.

Gruß

Alex

Autor: Jan-Henrik Bathelt (vedaykin)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Hallo Alex,

ich muss glaube ich nochmal erwähnen, dass ich recht neu bin was die 
muPc-Programmierung angeht ;). Ich habe unten mal versucht Deinen 
Kommentar umzusetzen. Ich habe Deine Vorgehensweise versucht in der 
while-Schleife umzusetzen.

1) INTFLAGS löschen
2) Wandlung starten
3) IF-Bit? sorry, wie frage ich das ab?
4) Kanal auslesen in Variable t speichern
5) INTFLAGS löschen

Irgendwas hat sich zum positiven hin geähndert. In der Variable t steht 
schon mal ein Wert um 180, obwohl 1,6Volt an PORT A Kanal 0 anliegen. 
Ich weiß nicht woher die 1,6V kommen, ich habe nur das Oszi dran. 
Irgendwas stimmt noch überhaupt nicht mit der Initialisierung, ansonsten 
würde ich doch nicht einfach immer 1,6V an PORTA0 messen oder? Es soll 
doch ein Eingang sein und nicht ein Ausgang. Die anderen Kanäle 1-7 sind 
auf 0V.

[c]
#include <avr/io.h>
#include <C:/Program Files (x86)/Atmel/Drivers/AVR1313/port_driver.h>
#include <C:/Program Files (x86)/Atmel/Drivers/AVR1313/port_driver.c>

#define ADC ADCA
#define USER_PORT PORTA
#define ADCMASK 0x07
#define ADC_MODE 0x00

uint8_t t;

int main(void)
{
  ADCInit();
  PORT_ConfigurePins(&USER_PORT,ADCMASK,0,0,PORT_OPC_PULLDOWN_gc,PORT_ISC_ 
INPUT_DISABLE_gc);

    while(1)
    {
    //1) INTFLAGS des Kanals löschen
    ADC.CH0.INTFLAGS = 0x01;
    //2) Wandlung starten
    ADC.CH0.MUXCTRL = ADC_CH_MUXPOS_PIN0_gc;
    //4) dann Wert lesen => Watchdog zeigt den Wert 180. Es liegen ca. 
1,6Volt an Port A Kanal 0
    t = ADC.CH0RES;
    //5) INTFLAGS des Kanals löschen
    ADC.CH0.INTFLAGS = 0x01;
    }
}

// Setup the ADC for sampling
void ADCInit(void)
{
  ADC.CTRLA |= 0x01;
  // 12 bit right adjusted
  ADC.CTRLB |= ADC_RESOLUTION_12BIT_gc | (ADC_MODE << 4);
  ADC.PRESCALER |= ADC_PRESCALER_DIV16_gc;
  // reference set to Vcc/1.6
  ADC.REFCTRL |= ADC_REFSEL_VCC_gc;
  ADC.EVCTRL |= ADC_SWEEP_012_gc ;
  // speed input
  ADC.CH0.CTRL |= ADC_CH_INPUTMODE_SINGLEENDED_gc;
  // enable freerun
  ADC.CTRLB |= ADC_RESOLUTION_12BIT_gc | (ADC_MODE << 4) | (1 << 3);
}
[c/]

Autor: Matthias Sch. (Firma: Matzetronics) (mschoeldgen)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Jan-Henrik Bathelt schrieb:
> Zusatz: Die Variable ADC_MODE verstehe ich nicht nur, sie ist auch in
> der Bibliothek nicht definiert.

Ich bin gerade nochmal meine Sourcen durchgegangen. ADC_MODE ist bei mir 
auch als 0 definiert, wenn ich unsigned benutze. Ein anderer Sensor bei 
mir erfordert ADC_MODE 1, weil der den Verstärker braucht.

Jan-Henrik Bathelt schrieb:
> ADC.CTRLA |= 0x01;

Tue das nicht. Schreibe lieber wirklich:
 ADC.CTRLA = 0x01;
Damit du nicht irgendwelches Zeugs in den Registern mit verODERerst, was 
du nicht haben willst. Solltest du im ganzen ADCInit() ändern.

Beachte, das mein Code den Freerun Mode benutzt. Das heisst, das der ADC 
einmal angestossen, selbstständig weiterarbeitet und nicht immer wieder 
gestartet wird. Der 012 Sweep sampelt also alleine die 3 Kanäle 0,1 und 
2. (Deswegen der 'zero effort' Kommentar) Die Resultate findest du in 
den ADC.CHnRES Registern als 12 bit Werte. Dein t muss also eine 
uint16_t sein und nicht nur eine uint8_t.

Ohne irgendwelche Eingangspulldowns driften die ADC Eingänge in die 
Gegend der halben Betriebsspannung. Mit einem z.B. 10k Pulldown gehen 
sie dann runter.

Autor: Jan-Henrik Bathelt (vedaykin)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Ok, ich habe alles zurückgeändert und es schwankt um den Wert 3070 in 
der Variable t. Irgendwas scheint er ja schonmal zu messen. Trotz 10k 
Pulldown Widerstands ist die Spannung aber immer noch per default auf 
1.5V an PortA0. Ich werde nochmal die Einführungsbeispiele von AVR 
durchgehen. Falls Du noch einen einen Tipp hast, ich bin ganz Ohr ;).

Autor: Jan-Henrik Bathelt (vedaykin)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Also ich habe jetzt noch mal die Pins PORTA1 und PORTA2 getestet. Dort 
ist keine Offsetspannung zu messen und ich kann über t einen Messwert 
porportional zur Eingangsspannung erfassen. Irgendwas scheint mit Kanal 
0 nicht richtig zu sein. An sich gute Nachrichten, aber irgendwie auch 
ein wenig unbefriedigend, da ich nicht nachvollziehen kann woran es 
liegen könnte. Ich verwende ein STK600.

Auf die Gefahr hin das ich mich stark wiederhole hier nochmal der 
funktionierende Code für die Kanäle 1 und 2. Kanal 0 ist noch immer die 
1.5V Spannung trotz Pulldown Widerstand zu messen.

[c]

#include <avr/io.h>
#include <C:/Program Files (x86)/Atmel/Drivers/AVR1313/port_driver.h>
#include <C:/Program Files (x86)/Atmel/Drivers/AVR1313/port_driver.c>

#define ADC ADCA
#define USER_PORT PORTA
#define ADCMASK 0x07
#define ADC_MODE 0x00

uint16_t t = 0;

int main(void)
{
  ADCInit();
  PORT_ConfigurePins(&USER_PORT,ADCMASK,0,0,PORT_OPC_PULLDOWN_gc,PORT_ISC_ 
INPUT_DISABLE_gc);

    while(1)
  {
    t = ADC.CH2RES;
    }
}

// Setup the ADC for sampling
void ADCInit(void)
{
  ADC.CTRLA = 0x01;
  // 12 bit right adjusted
  ADC.CTRLB = ADC_RESOLUTION_12BIT_gc | (ADC_MODE << 4);
  ADC.PRESCALER = ADC_PRESCALER_DIV256_gc;
  // reference set to Vcc/1.6
  ADC.REFCTRL = ADC_REFSEL_VCC_gc;
  ADC.EVCTRL = ADC_SWEEP_012_gc ;
  // get calibration data , ymmv
  //ADC.CALL = adc_get_calibration_data() && 0xff;
  //ADC.CALH = (uint16_t)adc_get_calibration_data() >> 8;
  // speed input
  ADC.CH0.CTRL = ADC_CH_INPUTMODE_SINGLEENDED_gc;
  ADC.CH0.MUXCTRL = ADC_CH_MUXPOS_PIN0_gc;
  // current measure
  ADC.CH1.CTRL = ADC_CH_INPUTMODE_SINGLEENDED_gc;
  ADC.CH1.MUXCTRL = ADC_CH_MUXPOS_PIN1_gc;
  // temperature
  ADC.CH2.CTRL = ADC_CH_INPUTMODE_SINGLEENDED_gc;
  ADC.CH2.MUXCTRL = ADC_CH_MUXPOS_PIN2_gc;
  // enable freerun
  ADC.CTRLB = ADC_RESOLUTION_12BIT_gc | (ADC_MODE << 4) | (1 << 3);
}
[c/]

Beitrag #2603079 wurde vom Autor gelöscht.
Autor: Jan-Henrik Bathelt (vedaykin)
Datum:
Angehängte Dateien:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Hier nochmal die IO View der Ports. Ich meine die Konfiguration stimmt 
doch soweit? Was mir noch unklar ist, der Internal Input Select 
unterscheidet sich. Bei Kanal 0 steht dieser Wert auf 0 = > Temperature 
Reference, bei Kanal 1 steht dieser Wert auf 1 => Bandgap Reference. 
Könnte das weiterhelfen? Anbei ein Screenshot.

Autor: Matthias Sch. (Firma: Matzetronics) (mschoeldgen)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Nee, das stimmt schon, solange die Channel Control Register auf 0x01 
stehen, denn in diesem Fall stehen in den MUXCTRL Registern nur die 
Nummern der externen Pins. Der Simulator/Debugger zeigt den Fall, wenn 
die Channel Controls auf 0x00 stehen würden, das wären die internen 
Signalquellen, was ja offensichtlich nicht mit deinen Ergebnissen von 
Kanal 1 und 2 übereinstimmt. Könnte schwören, das dir auf Kanal 2 
'ScaledVCC' angezeigt wird.
Im Moment weiss ich nicht, was mit deinem Kanal 0 sein kann, ausser das 
er kaputt ist (Schluss des internen Sample Kondensators). Im schlimmsten 
Fall müsstest du auf Kanal 1 oder 2 ausweichen, wenn du einen 
Schaltungsfehler ausschliessen kannst und die Port Initialisierung 
richtig ist.
Alles was ich dir sagen kann, ist, das der o.a. Code auf XMega128A3 und 
XMega192A3 funktioniert, auch auf Kanal 0. Xmega256A3 hatte ich hier 
noch nicht, aber da gibt es keine relevanten Unterschiede.

Du könntest nochmal probieren, mit einen richtig harten Pulldown (100R 
oder so) den Eingang runterzuziehen, oder mal zu schauen, ob er als 
normaler Port richtig funktioniert.

Autor: Jan-Henrik Bathelt (vedaykin)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Was 'ScaledVCC' betrifft hast Du selbstverständlich Recht ;). Dann werde 
ich nur die Kanäle 1-3 verwenden. Wenn ich Kanal 0 zu hart runterziehe 
fängt die böse rote LED auf dem Board an zu blinken. Das gleiche 
Phänomen beobachte ich auf Port B an Kanal 0. Daher glaube ich nicht, 
dass der ADC-Kanal defekt ist. Als reiner I/O-Port funktioniert er noch. 
Eventuell wird bei der nächsten Bestellung noch mal ein neuer Chip 
mitgekauft und mal getestet, ob ein Austausch das Problem behebt. Da ich 
nur 2 Kanäle brauche reichts erstmal aus. Der Code von Dir war also von 
Anfang an korrekt und es liegt wohl irgendwie an meiner 
Hardwarekonfiguration. Ein weiteres mal Danke für die Unterstützung.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel




Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder GIF-Format hochladen.
Siehe Bildformate
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken erkennst du die Nutzungsbedingungen an.

webmaster@mikrocontroller.netImpressumNutzungsbedingungenWerbung auf Mikrocontroller.net