Ich nutze ein Attiny84A und ein analogen Temperatursensor STLM20.
Ich lasse mir die Spannung durch den internen ADC in einen Digitalwert
umwandeln, um ihn dann in eine Spannung umzurechnen.
Mein problem ist, dass die gemessenen Temperaturen stimmen, allerdings
manchmal komplett unrealistische Werte ausspuckt. Als Beispiel bei 30°C
Umgebungstemperatur spuckt er immer 30°C aus, aber ab und zu auch mal
15Ĉ oder 48°C.
Woran könnte das liegen? Ist die ADC nicht immer zuverlässig? Ich habe
zur besseren Ansicht die Messergebnisse im Bildanhang grafisch
ausgewertet.
ADC Prozeduren
Marius D. schrieb:> int16_t volatile adcval;
ist 16 Bit (=2 Bytes) lang. der AVR ist ein 8-bitter.
Du musst zugriffe auf 16-Bit Variablen, die du auch im Interrupt (worauf
dein Volatile hindeutet) verwendest, so sperren, dass immer beide Bytes
auf einmal geschrieben werden.
Wenn der ADC durchlaufen soll, mache ich eine ADC_Complete ISR, die nach
dem erstmaligen Starten das Abfragen und Neustarten des ADC im
Hintergrund erledigt.
1
// ADC
2
#define ROOF_ADC 2 // 0010
3
#define ROOM_ADC 3 // 0011
4
5
// globals
6
volatileuint16_troomtemp,rooftemp;
7
8
/* Once again the multi channel ADC Readout and Restart Routine.
9
* it examines the MUX setting, stores the corresponding value
10
* and then switches to the next channel
11
* The ISR ends with restarting the ADC
12
* a little bit of averaging is done,too.
13
* note that this routine will never fire if you miss to start the ADC
Einmal beim ADC Init starten. Da das ganze in der ISR passiert, ist mit
Atomic Problemen hier nicht zu rechnen. Beachte, das ich immer ein wenig
runde durchs Aufaddieren und Schieben.
Matthias S. schrieb:> Da das ganze in der ISR passiert, ist mit> Atomic Problemen hier nicht zu rechnen. Beachte, das ich immer ein wenig> runde durchs Aufaddieren und Schieben.
Ich bin mir da nicht so sicher, denn der Zugriff auf das ADC-Ergebnis
passiert gepuffert und unabhängig vom i-Flag.
Irgendwo im Datenblatt steht, wie genau der Zugriff auf solche
16-Bit-Werte ablaufen sollte. Vermutlich kümmert sich der C-Compiler
aber selber um solche Feinheiten.
Aber wie gesagt -- bin nicht ganz sicher. :-)
Beim lesen von ADCL wird ADCH automatisch gesperrt bis auch ADCH gelesen
wurde. Beim Zugriff auf ADC macht der AVR-GCC das automatisch richtig.
am 16-Byte zugriff liegt es also eher nicht.
Ich wuerde den Sensor mal durch einen Spannungsteiler, resp Referenz
ersetzen und die Messung nochmals machen. Es kann immer noch Hardware,
zB als EMV sein
Markus W. schrieb:> Ich bin mir da nicht so sicher, denn der Zugriff auf das ADC-Ergebnis> passiert gepuffert und unabhängig vom i-Flag.
Doch, da kann man sich sicher sein. Das Atomic Problem entsteht ja, weil
eine etwaige Unterbrechung durch ISRn zwischen dem Lesen/Schreiben von
High- und Lowbyte zu Fehlern führt. Das kann in der AVR Architektur
nicht passieren, wenn man die ISR nicht explizit als ISR_NOBLOCK
deklariert, denn eine ISR kann nicht durch eine andere unterbrochen
werden.
Der ADC setzt ADIF erst, wenn er mit der Wandlung fertig ist. Ob
gepuffert oder nicht ist in dem Fall also ohne Bedeutung.
Die Routine oben funktioniert immer korrekt, solange die Hardware ok ist
(und ist eine meiner Standardfunktionen seit Jahren).
Stefan R. schrieb:> am 16-Byte zugriff liegt es also eher nicht.
Es liegt nicht am 16-Bit LESE-Zugriff auf ADC, sondern an konkurierenden
16-Bit-Lese&Sschreib-Zugriffen auf
1
int16_tvolatileadcval;
Diese Variable wird im nicht-sichtbaren Codeteil (vermutlich Timer-ISR)
ebenfalls gelesen, dann wohl per UART o.Ä. weiterversendet.
Der Schreibzugriff auf die 16 Bit ist nicht Atomar.
Also Beispiel:
adcval startet z.B. mit 0x0100;
Der Wert sinkt ein bischen. adcval würde um eins verringert werden.
also auf 0x00FF. Geschrieben wird in zwei Etappen:
High-Byte auf 0x00:
adcval = 0x0000
<ISR sendet adcval raus. ist um 255 Zähler falsch>
Low-Byte auf 0xFF:
adcval = 0x00FF.
=> Alles wieder richtig.
Beim umgekehrten Sprung 0x00FF -> 0x0100 dasselbe Spiel, nur dass hier
0x01FF als Zwischenwert rauskommt.
Deshalb: High- und Lowbyte von "adcval" gleichzeitig, ohne
ISR-Unterbrechung dazwischen, schreiben.
dazu gibt's cli()/sei() oder ATOMIC_BLOCK.
Ist mir nach dem Absenden auch aufgefallen, konnte meinen Beitrag aber
nicht mehr editieren da bereits eine Antwort gekommen ist.
Das Problem dürfte aber trotzdem nicht daher komme. Zwischen 0x00FF und
0x01FF würden nämlich über 700°C liegen.
Was mir zu dem Problem noch eingefalle ist - wird immer nur der gleiche
ADC Kanal ausgelesen?
Wenn man zwischen den ADC Kanälen wechselt und sofort eine Messung
durchführen versucht und an den beiden Kanälen unterschiedliche
Spannungen anliegen kann es passieren, dass der ADC in dem momment
misst, an dem sich dir Spannung gerade ändert. Dadurch kommt dann
natürlich ein falsches Ergebnis zu stande.
Ich kann nur jeden auffordern noch schnell einen Tip abzugeben bevor der
TO weitere sachdienliche Indizien liefert. Danach werden keine Wetten
mehr angenommen.
M. K. schrieb:> Meiner Erfahrung am Atmega328 ist, dass ADMUX nicht immer sicher> umgestellt wird wenn ADEN auf 1 ist.
Das ist Quatsch.
Der MUX wird gelatcht, während eine Wandlung läuft.
Peter D. schrieb:> Das ist Quatsch.> Der MUX wird gelatcht, während eine Wandlung läuft.
Ich schrieb extra "meiner Erfahrung nach" und wenn ich ADEN nicht aus
machen bekomme ich auch ab und an schöne Wandlungsfehler, mach ich ADEN
aus, änder MUX und mach ADEN wieder an hatte ich bisher noch nie
(unerwartete) Wandlungsfehler.
M. K. schrieb:> Ich schrieb extra "meiner Erfahrung nach" und wenn ich ADEN nicht aus> machen bekomme ich auch ab und an schöne Wandlungsfehler, mach ich ADEN> aus, änder MUX und mach ADEN wieder an hatte ich bisher noch nie> (unerwartete) Wandlungsfehler.
Muxer umschalten, minimale Samplezeit abwarten, Wandlung starten. Dazu
muß man den ADC nicht ausschalten. Und wenn man das beachtet, wird aus
dem ADC auch kein Zufallsgenerator.
MfG Klaus
Klaus schrieb:> Muxer umschalten, minimale Samplezeit abwarten, Wandlung starten.
Auch eine Möglichkeit, wird aber hier
Marius D. schrieb:> ADMUX = (ADMUX & ~(0x3F)) | (channel & 0x3F);> ADCSRA |= ( 1 << ADSC);
nicht gemacht, deshalb eben kurz den Wandler aus, Mux umschalten,
Wandler ein. Man kann auch die minimale Wandlerzeit abwarten, keine
Frage.
Wow... hier kam aber reichlich resonanz. Vielen Dank an eure Mithilfe.
Ich werde es jezt mal mit cli() und sei() versuchen und ein neuen
Testlaufstarten. Werde dann berichten.
Ja ich nutze adcval und schicke es über I2C weiter. Habe mir da eine
Funktion gebaut die die Daten zuverlässig wegschickt. Das ist recht
umfangreich. Kann mir nicht vorstellen, dass bei der Übertragung was
verloren geht, da es mit anderen Daten wunderbar funktioniert. Kurz
zusammengefasst wird auf ein I2C-Interrupt reagiert, auf die dann die
Temperatur zurückgeschickt wird.
1
//Main() habe das jetzt so gemacht. Hoffe es wirkt.
M. K. schrieb:> nicht gemacht, deshalb eben kurz den Wandler aus, Mux umschalten,> Wandler ein.
Das dauernde ein und Ausschalten des Wandlers verbessert sicher die
Qualität des Ergegnisses, ist ja jedesmal frisch und neu gestartet.
MfG Klaus
Marius D. schrieb:> umfangreich. Kann mir nicht vorstellen, dass bei der Übertragung was> verloren geht, da es mit anderen Daten wunderbar funktioniert. Kurz> zusammengefasst wird auf ein I2C-Interrupt reagiert, auf die dann die> Temperatur zurückgeschickt wird.
Öhm. Genau da liegt gerne mal der Hase im Pfeffer. Hatte hier auch bei
zigtausend Samples paar Ausreißer, sehr sporadisch, vielleicht einmal am
Tag. Leitungslänge und zu klein dimensionierte Pullups waren schuld. Die
ab Werk verbauten 10kOhm am anderen Ende der Strippe um 1kOhm ergänzt
(ich weiß, knapp außerhalb der Spec, auch bei 3.3V Speisung). Seitdem
sind meine Kurven "glatt wie ein Babypopo", ohne Ausreißer.
Ich würde die I2C-Übertragung als Fehlerquelle daher nicht einfach
ausklammern ...
P.S.: Sind 100nF eigentlich auch an die Versorgung gepackt? Falls der
ATtiny AVcc hat, auch dort? Versorgt via Ferritperle zum Abblocken
höherfrequenter Störungen?
> Das dauernde ein und Ausschalten des Wandlers verbessert sicher die> Qualität des Ergegnisses, ist ja jedesmal frisch und neu gestartet.
Das aus und EInschalten hilft deswegen, weil die erste Wandlung nach dem
Einschalten einige Takte länger dauert. Zuerst initialisiert sich der
ADC, dann erst beginnt er mit der Messung. Bis dahin ist der S&H
Kondensator aufgeladen.
Klaus schrieb:> Das dauernde ein und Ausschalten des Wandlers verbessert sicher die> Qualität des Ergegnisses, ist ja jedesmal frisch und neu gestartet.
Vielleicht vor jeder Messung noch einen Reset vom µC auslösen. Dann ist
er immer wieder frisch.
Hier gibt's ja heute wieder die heißen Tipps :-(
Wenn ich mir die oben gezeigte Kurve ansehe, sehe ich u.a. Werte von 16,
32 und 48. Das kommt vermutlich nicht vom ADC.
m.n. schrieb:> Klaus schrieb:>> Das dauernde ein und Ausschalten des Wandlers verbessert sicher die>> Qualität des Ergegnisses, ist ja jedesmal frisch und neu gestartet.>> Vielleicht vor jeder Messung noch einen Reset vom µC auslösen. Dann ist> er immer wieder frisch.
Hab ich mich nicht getraut zu schreiben. Aber so bekommt man genug Noise
auf die Referenz, daß man mit Oversampling die Auflösung massiv erhöhen
kann.
MfG Klaus
klinke schrieb:> Ich kann nur jeden auffordern noch schnell einen Tip abzugeben bevor der> TO weitere sachdienliche Indizien liefert. Danach werden keine Wetten> mehr angenommen.
da gibt's nix zu Wetten.
Bild im Eröffnungs-Post anschauen.
Die meisten der genannten Ursachen (ADC zu oft enabled, zu Hochohmig, zu
schneller MUX-Wechsel, falsche REF-Spannung, blah blah blah) können
dieses Fehlerbild nicht erklären.