Moin,
ich möchte den internen ADC einen ATMEGA644 nutzen. Prinzipiel klappt
dies auch, allerdings habe ich mitunter etwas unplausible Werte. Es
scheint mir, als ob diese zum Teil einen recht großen Offset aufweisen.
Hier die Initialisierung:
ADCSRA=(1<<ADEN)|(1<<ADIE);// ADC aktivieren und Interrupt einschalten
6
7
ADCSRA|=((1<<ADPS1)|(1<<ADPS2));
8
9
_delay_ms(1);
10
11
//warm-up des ADC
12
for(uint8_ti=0;i<5;i++)
13
{
14
ADCSRA|=(1<<ADSC);
15
while(ADCSRA&(1<<ADSC))
16
{
17
18
}
19
20
first_result=(uint16_t)(ADCL+(ADCH<<8));
21
_delay_ms(10);
22
first_result=0;
23
}
Die Port-Register müssen doch nicht noch anderweitig gesetzt werden?
Auch das Abschalten der Digitalstufen ist soweit ich gelesen habe eher
optional.
Ist meine Routine zur Initialisierung des ADCs schlüssig oder habe ich
etwas signifikantes dabei vergessen?
Währen der Programmlaufzeit wird periodisch zwischen den Kanälen 0,1,2,3
und 6 umgeschaltet. Sollte nach jeder Umschaltung ein Dummy read
erfolgen oder kann der abgerufenen Wert dann als nutzbar gelten?
Gruß
Ich messe den Strom mit einer OP-Schaltung mit nachgeschaltetem
Impedanzwandler und Serienwiderstand. Direkt am Ausgang des OPs ist der
Wert (mit dem Multimeter gemessen) wie zu erwarten bei ca. 60mV. Am ADC
liegen allerdings 0.42V an. Sowohl mit dem Multimeter als auch dem
internen ADC gemessen.
Marco G. schrieb:> ich möchte den internen ADC einen ATMEGA644 nutzen. Prinzipiel klappt> dies auch, allerdings habe ich mitunter etwas unplausible Werte. Es> scheint mir, als ob diese zum Teil einen recht großen Offset aufweisen.
Wie groß? Zahlen.
Ist AREF extern mit 100nF beschaltet?
> Hier die Initialisierung:uint16_t first_result = 0;>> DIDR0 |= (1<<ADC0D) | (1<<ADC1D) | (1<<ADC2D) | (1<<ADC3D) |> (1<<ADC6D) | (1<<ADC7D);> ADCSRA = (1<<ADEN) | (1<<ADIE); // ADC aktivieren und Interrupt> einschalten
Wozu die Interrupts einschalten, wenn du den nicht nutzt? Das geht
schief, vor allem, wenn dazu keine passende ISR besteht, dann macht dein
AVR einen Reset.
>> ADCSRA |= ((1<<ADPS1) | (1<<ADPS2));>> _delay_ms(1);>> //warm-up des ADC> for (uint8_t i = 0; i < 5; i++)
Dafür braucht es keine Schleife. Nur bei der ERSTMALIGEN Aktivierung des
ADC bzw. beim Wechsel der Referenzspannung sollte man EINE ADC Messung
machen und das Ergebnis wegwerfen.
> {> ADCSRA |= (1<<ADSC);> while (ADCSRA & (1<<ADSC))> {>> }
Jaja, schön lehrbuchartige Klammern ;-)
while (ADCSRA & (1<<ADSC));
reicht auch.
> first_result = (uint16_t) (ADCL + (ADCH<<8));
Brauchst du beim avr gcc nicht, der liefert das Ergebnis als 16 Bit Wert
mit ADCW und kümmert sich dabei um den richtigen Zugriff auf ADCL und
ADCH.
first_result = ADCW.
> _delay_ms(10);> first_result = 0;
Wozu die Verzögerung NACH der Messung? Willst du den AVR nicht so
hetzen?
> Die Port-Register müssen doch nicht noch anderweitig gesetzt werden?
Nein.
> Auch das Abschalten der Digitalstufen ist soweit ich gelesen habe eher> optional.
Ja.
> Ist meine Routine zur Initialisierung des ADCs schlüssig oder habe ich> etwas signifikantes dabei vergessen?
Mit deinen Einstellung musst du an AREF von außen eine Referenzspannung
einspeisen. Die 100nF an dem Pin braucht es auch. Hast du beides?
> Währen der Programmlaufzeit wird periodisch zwischen den Kanälen 0,1,2,3> und 6 umgeschaltet.
Dann tu das, mit der gescheiten Funktion. Sprich, nur den MUX
umschalten, den Rest im Register nicht verändern. Siehe
Bitmanipulation.
> Sollte nach jeder Umschaltung ein Dummy read> erfolgen
Nö.
> oder kann der abgerufenen Wert dann als nutzbar gelten?
Sicher.
Schwankende ADC-Werte können folgende Ursachen haben
Der Wert schwankt WIRKLICH
Es fehlt ein kleiner Kondensator am ADC-Eingang, so 1-10nF.
Marco G. schrieb:> Ich messe den Strom mit einer OP-Schaltung mit nachgeschaltetem> Impedanzwandler und Serienwiderstand. Direkt am Ausgang des OPs ist der> Wert (mit dem Multimeter gemessen) wie zu erwarten bei ca. 60mV. Am ADC> liegen allerdings 0.42V an. Sowohl mit dem Multimeter als auch dem> internen ADC gemessen.
C12 ist ARG groß. Kann man machen, ist aber eher unsinnig. Wenn am
OPV-Ausgang der richtige Wert anliegt, am ADC-Eingang aber 420mF, ist
was faul. Da müßte ja durch R20 ca. I= U / R = (420-60)mV/324R = 1,1mA
fließen. Da müßte ein 5k Pull Up gegen +5V am ADC-Eingang geschaltet
sein. Die internen Pull-Ups sind ca. 10 mal so groß. Klingt nach einem
Löt- oder Schaltungsfehler.
An AREF wird eine Spannung 2,048V eingespeist. Der 100n ist dort
vorhanden.
Den Interrupt nutze ich zur Auswertung.
Ich hatte erst nur das Auslesen eines Wertes, hatte zur Sicherheit die
Schleife für mehrere Werte eingebaut. Die Umschaltung der Kanäle klappt
auch problemlos. Die Info war lediglich für die Frage nach dem
Dummy-Read nach dem Umschaltung gedacht.
Das die Werte leicht schwanken ist ok, das erwarte ich auch in gewissen
Umfang. Allerdings kommt mir der Offset an einem Kanal (siehe vorheriger
Beitrag) komisch vor.
Marco G. schrieb:> An AREF wird eine Spannung 2,048V eingespeist. Der 100n ist dort> vorhanden.> Den Interrupt nutze ich zur Auswertung.
Den sehen wir aber nicht!
> Ich hatte erst nur das Auslesen eines Wertes, hatte zur Sicherheit die> Schleife für mehrere Werte eingebaut. Die Umschaltung der Kanäle klappt> auch problemlos. Die Info war lediglich für die Frage nach dem> Dummy-Read nach dem Umschaltung gedacht.>> Das die Werte leicht schwanken ist ok, das erwarte ich auch in gewissen> Umfang.
In WELCHEM!!!!
Ich habe mehrere Schaltungen mit AVRs und dessen ADC gebaut, die waren
immer ERSTAUNLICH ruhig, da war teilweis kaum mehr als 1 LSB Rauschen
drauf!
> Allerdings kommt mir der Offset an einem Kanal (siehe vorheriger> Beitrag) komisch vor.
Ist er auch. Muss man suchen. Siehe Fehlersuche.
Einen
Falk B. schrieb:> C12 ist ARG groß. Kann man machen, ist aber eher unsinnig. Wenn am> OPV-Ausgang der richtige Wert anliegt, am ADC-Eingang aber 420mF, ist> was faul. Da müßte ja durch R20 ca. I= U / R = (420-60)mV/324R = 1,1mA> fließen. Da müßte ein 5k Pull Up gegen +5V am ADC-Eingang geschaltet> sein. Die internen Pull-Ups sind ca. 10 mal so groß. Klingt nach einem> Löt- oder Schaltungsfehler.
Den Kondensator kann ich testweise verkleinern. Bei der Betrachtung
unter dem Mikroskop ist mir kein Lötfehler aufgefallen, sah soweit gut
aus. Schaltungsfehler würde ich ausschließen, da genau dieselbe
Beschaltung an einer andern Stelle wie erwartet funktioniert.
Die Weiterverabeitung der Werte erfolgt dann in der Main.
Ich meinte, dass die Werte von Natur aus schwanken. Die Stromaufnahme
die ich messe ist nicht konstant. Daher die erwartbaren Schwankungen der
Messwerte.
Marco G. schrieb:> Hier die vorläufige ISRISR(ADC_vect)> {> adc_value = ADCW;>> adc_result = calculate_adc(adc_value);>> if (adc_modus == 0)> {> Temperature = calculate_temp(adc_result);> TempAvailable = 1;> }
diese vielen if() kann man sinnvollerweise durch ein switch() ersetzen.
Das aber nur nebenbei.
> else if (adc_modus == 1)> {> RTCVoltageAvailable = 1;> RTCVoltage = calculate_RTCVoltage(adc_result);> }> else if (adc_modus == 2)> {> CurrentAvailable = 1;> Current = calculate_current(adc_result);> }> else if (adc_modus == 3)> {> VoltageAvailable = 1;> Voltage = calculate_Voltage(adc_result);>> }> else if (adc_modus == 6)> {> BatVoltageAvailable = 1;> BatVoltage = calculate_BatteryVoltage(adc_result);> }> }>> Die Weiterverabeitung der Werte erfolgt dann in der Main.
Wozu dann die Umrechung in der ISR? Das kann man auch in Main machen.
Mit der ISR gewinnt man kaum was. Eine einfache Abfrage mit Warten auf
den ADC ist hier vollkommen ausreichend.
> Ich meinte, dass die Werte von Natur aus schwanken. Die Stromaufnahme> die ich messe ist nicht konstant. Daher die erwartbaren Schwankungen der> Messwerte.
Ja was denn nun? Wenn ich einen ADC auf Rauschen und Drift testen will,
dann lege ich ein DEFINITIV KONSTANTES Signal an, und kein Wald und
Wiesen Signal, das halt irgendwie schwankt.
Wird in der Isr auch der Mux geändert braucht die Main sich nicht mehr
um das Messen zu kümmern (drauf zu warten) und hat stets aktuelle Werte.
Dummymessungen werden sinnvoll/nötig wenn die Adc Eingänge hochohmig
beschaltet sind. ( z.B. 100kOhm Ntc)
Marco G. schrieb:> Sollte nach jeder Umschaltung ein Dummy read erfolgen
Nein. Du musst einfach nur dafür sorgen, dass deine AD-Beschaltung
ausreichend niedeohmig ist.
Marco G. schrieb:> Am ADC liegen allerdings 0.42V an.> Sowohl mit dem Multimeter als auch dem internen ADC gemessen.
Dann funktioniert der ADC doch wie er soll.
> Direkt am Ausgang des OPs ...
... solltest du mal mit einem Oszi messen. Mich würde nicht wundern,
wenn dieser GHZ-Bolide einfach nur schwingen würde. Wozu einen OP, der
zehntausendmal schneller als nötig ist?
A. H. schrieb:> Dummymessungen werden sinnvoll/nötig wenn die Adc Eingänge hochohmig> beschaltet sind. ( z.B. 100kOhm Ntc)
Und genau da (relativ langsame Temperaturänderungen) kann man einfach
einen 100nF zwischen ADSC-Pin und GND klemmen um die Impedanz zu
reduzieren. Dann ist die Leermessung unnötig, denn dieser externe 100nF
Kondensator lädt in der Sampling-Phase des ADC einen
15pF-Sample-Kondensator auf. Und weil das Verhältnis 100n/15pF = 6666
ist, bricht die Spannung am ADC-Pin nicht spürbar maximal um 1/6666 ein.
A. H. schrieb:> Wird in der Isr auch der Mux geändert braucht die Main sich nicht mehr> um das Messen zu kümmern (drauf zu warten) und hat stets aktuelle Werte.
Tut sie aber nicht.
Lothar M. schrieb:>> Direkt am Ausgang des OPs ...> ... solltest du mal mit einem Oszi messen. Mich würde nicht wundern,> wenn dieser GHZ-Bolide einfach nur schwingen würde. Wozu einen OP, der> zehntausendmal schneller als nötig ist?
AUA! LTC6252, 720MHz GBP. Wenn's scheeeeee macht! ;-)
https://www.analog.com/en/products/ltc6252.html
Aber das erklärt nicht die Spannungsdifferenz. Der OPV spuckt ja
anscheinend die richtige Spannung aus, aber am ADC-Eingang ist die
Spannung HÖHER!
Vielen Dank für die kontruktiven Vorschläge. Die Umschaltung des MUX
soll auch noch in die ISR wandern.
Folgendes war das Problem:
1
PORTA|=(1<<PC2)|(1<<PC5)|(1<<PC6);
Das macht natürlich keinen Sinn und muss natürlich
1
PORTC|=(1<<PC2)|(1<<PC5)|(1<<PC6);
heißen. Mit dem korrigierten Eintrag ist der Offset auch nicht mehr
vorhanden, die gemessenen Werte sind jetzt wie erwartet.
Macht es denn wie hier Sinn, die Umrechnung der Werte in der ISR
vorzunehmen oder sollte dies lieber in die Main verlagert werden?
Marco G. schrieb:> Macht es denn wie hier Sinn, die Umrechnung der Werte in der ISR> vorzunehmen
Ich mache das so: in der ISR (= Unterbrechung des üblichen, normalen
Programmablaufs) werden die Werte nur aus dem ADC-Register abgeholt und
als Rohwert gespeichert. Später werden aus diesen gespeicherten
Rohwerten die nötigen Ergebnisse ausgerechnet.
Denn auch ich möchte gerne, wenn ich z.B. grade beim Essen sitze oder
einen netten Film anschaue (= aktueller Programmablauf), nicht unnötig
lange dabei unterbrochen werden.
Ich gehe also kurz raus zum klingelnden Postboten (= Interrupt), hole
die Rechnung (= ADC-Wert) ab, überweise aber den Rechnungsbetrag (=
Bearbeitung des ADC-Werts) nicht sofort, sondern erst nach dem Essen
oder dem Film.
Marco G. schrieb:> Macht es denn wie hier Sinn, die Umrechnung der Werte in der ISR> vorzunehmen oder sollte dies lieber in die Main verlagert werden?
Eine gute Strategie ist, Rechnungen zum spätest möglichen Zeitpunkt zu
machen, z.B. vor der ersten Verwendung.
Außerdem sind float und Division auf dem AVR teuer und da der AVR keine
Interruptlevel kann, mehrfach teuer. Andere Interrupts könnten störend
ausgebremst werden.