PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : ATXmega128A1 Rev. H unerklärliches ADC-Phänomen



Oreas
21.11.2012, 15:47
Hallo zusammen,

ich habe ein mir nicht ganz erklärliches Phänomen bei einem Projekt von mir.
Wie der Titel schon vermuten lässt, geht es um die AD-Wandlung auf meinem ATXmega128A1-Breakoutboard.

Ich habe zwei Linearpotentiometer als unbelasteten Spannungsteiler, mit denen ich eine Position ermitteln möchte, was auch vom Prinzip her funktioniert, dank der Hilfe von Kampis Tutorial. Leider tritt an bestimmten Stellungen der Potentiomer das Phänomen auf, dass die AD-Wandlung 10 mal funktioniert und dann wird der maximale Wert, der als Referenz gemessen wurde über USART rausgeschrieben. Ich bilde zusätzlich den Mittelwert der letzten 10 Messwerte, um ein wenig das Flattern bei der 12Bit-Auflösung rauszunehmen.

Die Ausgabe sieht dann zum Beispiel so aus:


USART up and running.
lower: 745 upper: 812
lower:3644 upper:3645
Correction Lower: 2.899 Correction Upper: 2.833

...
lower: 1014 upper: 6
lower: 1014 upper: 1014
lower: 1014 upper: 7
lower: 1014 upper: 6
lower: 1014 upper: 7
lower: 1014 upper: 7
lower: 1014 upper: 7
lower: 1014 upper: 8
lower: 1014 upper: 7
lower: 1014 upper: 7
lower: 1014 upper: 7
lower: 1014 upper: 7
lower: 1014 upper: 1014
lower: 1014 upper: 8
lower: 1014 upper: 4
lower: 1014 upper: 8
lower: 1014 upper: 4
...


An diesem Beispiel ist glaube ich relativ ersichtlich, dass die Messungen mit upper:1014 zwischendrinne ziehmlicher mumpitz sind.
Ein ähnliches Phänomen habe ich in der genau anderen Richtung auch, nur dass da ein negativer Wert auftauch mit -430. Als Ausgabe sieht das dann ungefähr so aus:


...
lower: 4 upper: 1014
lower: 3 upper: -430
lower: 0 upper: 1014
lower: 1 upper: 1014
lower: 3 upper: 1014
lower: 4 upper: 1014
lower: 3 upper: 1014
lower: 5 upper: 1014
lower: 5 upper: 1014
lower: 4 upper: 1014
lower: 3 upper: 1014
lower: 2 upper: 1014
lower: 5 upper: -430
lower: 3 upper: 1014
lower: 4 upper: 1014
lower: 2 upper: 1014
lower: 3 upper: 1014
...


Jetzt ist vielleicht noch interessant, wie ich auf diese Werte komme und das passiert mit folgender Berechnung:

Position = (Messwert - Offset)/((maximalerMesswert - minimalerMesswert)/anzahlMesspunkte)

AnzahlMesspunkte ist in meinem Fall 1000.

Damit wäre beschrieben, wie ich auf diese lustigen Werte komme.
Da der Beitrag wahrscheinlich schon lang genug ist, packe ich die wichtigsten Auszüge aus meinem Code in den Anhang.

Die ADC ist als 12Bit Singleended unsigned mit einer externen Referenzspannung von 3,3V an Pin0 initalisiert. (näheres im Code)

Um die Frage nochmal zu konkretisieren, die ich habe:
Kennt jemand das Problem und hat evtl. eine Lösung dafür, oder muss ich mit diesen "Ausreißern" leben, die mir evtl das Leben ein wenig schwerer machen könnten. Sprich ich muss eine Plausibilitätsprüfung für die Werte einfügen.

Achja, wer auch eine Idee hat, wie ich die werksseitigen Kalibrierungswerte rausbekomme, so dass ich zum Schluss kein Offset mehr habe von der ADC, der darf sie auch gerne Posten, da wäre ich nicht sauer drüber ;)

Danke und Grüße

Oreas

P.S.: habe versucht so viel wie geht zusammen zu kürzen im Anhang und hoffe alles ausreichend kommentiert zu haben. Sollte dem nicht der Fall sein, so stehe ich gerne Rede und Antwort. Und sry, dass alles in einer Datei gelandet ist ^^

Kampi
21.11.2012, 17:44
Hey,

Kalibrationsregister auslesen habe ich hier beschrieben:

http://kampis-elektroecke.de/?page_id=1302

Was ich gestern nicht gesehen habe....du hast in deiner ISR vom Timer einen 2ms Delay. Delay-Funktionen sind mehr so Schätzwerte.....du hast geschrieben das die ISR mit 100kHz ausgelöst wird aber du wartest dort 2ms drin? Sicher das der Unsinn bei der Ausgabe nicht deswegen kommt?
Weil bei so einem periodischen Fehler würde ich wirklich auf einen Timer tippen.....
Wenn dir der Timer während des Delays usw. dazwischen funkt kann es Probleme geben.

Oreas
22.11.2012, 09:00
Hey Daniel,

hatte ich auf deiner HP auch schon gesehen, aber der Compiler wehrt sich wehement mit folgender Fehlermeldung dagegen:
"expected identifier before '__builtin_offsetof'"
und da bin ich seit gestern Abend noch nicht weiter gekommen.
Ich hatte in einem anderen Forum http://www.mikrocontroller.net/topic/120008 diesen Beitrag gefunden und das mal eingebaut und der Compiler beschwert sich nicht. Was mich daran aber etwas Stutzig macht ist, dass
ADCA. CALL = 255
ADCA.CALH = 0
ist und das kann ich irgendwie schwer glauben.

Das Delay hatte ich eingebaut, weil ich festgestellt hatte, dass die Werte dann zuverlässiger kommen, als wenn ich das weg lasse, aber ich nehme es mal raus. Das mit den 100kHz im Kommentar (habe ich gerade eben erst gesehen) stimmt leider nicht meht ^^ das sind nur 10kHz wir im Kommentar zum Timer beschrieben. Lade dann noch gleich eine geänderte Version hoch, sry.

Aber im Endeffekt wirst du wahrscheinlich recht haben, dass ich ein richtiges Timing-Problem generiert habe, dass der Interrupt einfach zu lange braucht und ich mir was andere einfallen lassen muss.

Trotzdem gerne weiter Kommentare/Anregungen posten, wie ich mein Problem lösen kann. Ich entwickel auch gerne unter Anleitung selbst, solange jemand dann mal drüber schaut, ob das, was ich gemacht habe nicht vllt doch etwas Mist ist ;)

EDIT: da ist sie schon :)

Kampi
22.11.2012, 09:20
Hey,

nein die Calibrationsregister sind immer leer. Du musst sie während des Programmdurchlaufs mit den Werten aus den werksseitig beschriebenen Registern beschreiben.
Aber selbst bei 10kHz ist ein 2ms Delay viel zu lang :)....10kHz heißt ja das die ISR 10000x pro Sekunde aufgerufen wird....also 10x pro Milisekunde. Und dann wartest du in der ISR 2ms? In der Zeit sind schon wieder 20 Interrupts geschehen (oder sehe ich das falsch ;) )?
Ich schau heute Nachmittag mal in meinem C-Programm....evtl. habe ich nur was vergessen mit rein zu schreiben. Weil bei mir meckert der Compiler nicht.

Edit:
Füge mal den Include mit ein:



#include <stdlib.h>


Und fürs Kalibrieren kannst du dir mal in meinem Bastelprogramm:

https://www.dropbox.com/s/8d18pojnfu27b7g/XMega.c

Die Funktion "LeseKalibrationsregister" und "DACB_Cal" anschauen.

Oreas
22.11.2012, 09:38
jaja, ich sehs ja ein, dass ich mich da mit den 2 ms verschätzt habe ;)
aber selbst ohne ist es nicht besser geworden.

Habe nochmal nachgedacht: da es sich um eine Positionsbestimmung von einem nicht hochdynamischen System handelt, sollte es auch ausreichen, wenn man zum Beispiel 250 Hz (also 250 Messungen die Sekunde macht).

Kampi
22.11.2012, 09:42
Musst du mal ausprobieren wie viel du brauchst. Ansonsten musst du auf einen externen ADC umsteigen da diese glaube ich schneller sind als die internen (hängt ja zum Teil auch mit dem Verfahren zusammen wie die messen).

Oreas
22.11.2012, 10:09
Interessant finde ich nur, dass wenn ich ausschließlich die ADC in der Main laufen lasse und nur diese beiden Werte ausgebe, dass das wunderbar funktioniert.

Naja ich glaube ich werde um einen neuen Aufbau meines Codes nicht drumherum kommen und vielleicht behebt sich das Problem dann damit.

Oreas
28.11.2012, 12:24
sooooo,

nach diversen Terminen habe ich endlich die Zeit gefunden eine Lösung für die unkonstanten Werte zu finden. Ergibt sich aus den Errata aus den Manuals ...
Siehe in Kapitel 35: Errata unter Punkt 8

"8. Accuracy lost on first three samples after switching input to ADC gain stage
Due to memory effect in the ADC gain stage, the first three samples after changing input
channel must be disregarded to achieve 12-bit accuracy.
Problem fix/Workaround
Run three ADC conversions and discard these results after changing input channels to ADC
gain stage."

also einfach sowas wie:


for(int i = 0; i <= 2; i++){
get_adcb(&(ADCA.CH0),3);
delay_us(10);
}
value = get_adcb(&(ADCA.CH0),3);

für jede AD-Wandlung einbauen (vorausgesetzt man verwendet mehr als einen Kanal).

Ein Problem, was ich aber immer noch nicht gelöst habe ist die Kalibrierung der ADC. Ich habe leider immer noch ein Offset von im Schnitt 50-75 Inkrementen die eine genaue Bestimmung der Werte vermiesen und eine Ungenauigkeit von ca 11% hineinbringen. Also wenn da jemand ne gute Idee hat ... immer her damit ;)