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!
1
#include<avr/io.h>
2
3
intmain(void){
4
5
//ADC enable
6
ADCA.CTRLA=0x01;
7
//unsigned mode und 8Bit, Einzelsignale und Interne Signale können gemessen werden
8
ADCA.CTRLB=0x04;
9
//Interne 1.0V Referenz
10
ADCA.REFCTRL=0x00;
11
12
uint8_ttemp;
13
14
while(1){
15
//input mode single ended Port A Kanal 0, die Messung wird gestartet
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:
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
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/]
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.
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 ;).
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/]
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.
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.
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.