Mit einem ATMega8 soll eine Phasenanschnitts-Steuerung unter Beachtung
des Ausgangssignals realisiert werden.
Dafür wird aus der Wechselspannung ein Trigger-Signal hergestellt,
welches hier mit einem Oszilloskop dargestellt ist (Sync).
Darüber hinaus wird dann zusätzlich die Ausgangsspannung und der
Ausgangsstrom überwacht, wozu 2 weitere AD-Eingänge verwendet werden.
Es werden also nacheinander insgesamt 3 AD-Eingänge abwechselnd gelesen,
hier wird aber nur das erste Signal als Beispiel dargestellt.
Wenn man das Signal mit einer Single-Shot-Conversion jeweils für die 3
Eingänge einliest funktioniert das wunderbar (Single-Conversion).
Nun soll dieser Vorgang jedoch über den ADC-Interrupt im Hintergrund
erfolgen, alleine schon um das lästige Polling zu vermeiden.
Im ersten Versuch wurde der Free-Running-Modus verwendet, beim dem
jedoch ein Umschalten der Eingänge scheinbar nicht vorgesehen ist.
Egal wie man es macht, es kommt immer kompletter Datenschrott raus
(Free-Running).
Nun gut - dann also mit Single-Conversion, jedoch führt dies ebenfalls
nicht zu brauchbaren Ergebnissen sobald man den Interrupt verwendet
(Interrupt-Single-Shot).
Der Quellcode dazu sieht folgendermaßen aus:
Initialisierung
ADCSRA|=(1<<ADSC);// Eine Wandlung "single conversion"
9
10
if(CurADC==3){
11
Oszi[CountOszi]=ADCWert[0];
12
CountOszi++;
13
}
14
15
// Weitere Auswertung
16
}
Das Array Oszi[CountOszi] wird dann später ausgegeben und als Diagramm
visualisiert wie in den Bildern zu sehen ist.
Hat jemand eine Idee was am Code falsch ist oder woran dies liegen
könnte?
Hallo,
Was soll das Auslesen des ADC Wert im Interrupt Betrieb? Hast du dir mal
Gedanken gemacht, wie man so einen Eingang umschalten kann?
Steht natürlich im Datenblatt.
Darüber hinaus ist unklar, wie die ADC Eingänge beschaltet sind.
Sind sie nieder ohmig genug?
Daraus ergibt sich, dass CurADC die Werte 2 bis 4 haben muss. Du fängst
aber mit 0 an, da du die Variable nicht initialisiert hast. Innerhalb
der ISR wirst du daher irgendwo falsch in RAM schreiben, da kann alles
Mögliche passierten.
Der delay hat in der ISR nichts verloren.
Bedenke, dass der ADC im freilaufenden Modus bereits mit der nächsten
Messung beschäftigt ist, während die ISR abgearbeitet wird. Der Kanal,
den du in der ISR einstellt, wirkt sich daher erst auf die übernächste
Messung aus.
Ich hatte damit auch Schwierigkeiten und es damals bei meiner ersten
Anwendung ganz ähnlich wie du umgesetzt. Der folgende Code war für einen
Atmega16:
1
// Werte vom ADC
2
// Index 0-7 entsprechen Kanal 0-7
3
staticvolatileuint16_tadc_value[8];
4
5
// Unterbrechung vom ADC
6
ISR(ADC_vect){
7
8
// Messergebnis auslesen
9
uint16_tvalue=ADC;
10
uint8_tchannel=ADMUX&7;
11
adc_value[channel]=value;
12
13
// Zähle von 0-7
14
if(++channel>7){
15
channel=0;
16
}
17
18
// Schalte zum nächsten Kanal
19
ADMUX=(ADMUX&~7)|channel;
20
21
// Starte nächste Messung
22
ADCSRA|=(1<<ADSC);
23
}
24
25
voidinit(){
26
// Aktiviere ADC Konvertierung im Einzelschritt Modus
So funktioniert das jedenfalls tadellos, nur eben nicht freilaufend. Ist
bei deiner Anwendung wichtig, dass die Messintervalle in 100%
gleichmäßigen Intervallen stattfinden? Wenn ja, dann solltest du den
freilaufenden Modus benutzen.
Karl M. schrieb:> Was soll das Auslesen des ADC Wert im Interrupt Betrieb?
Irgendwie muss er ihn ja zu den richtigen Zeitpunkten auslesen. Wie
hättest du das denn anders gemacht?
Karsten M. schrieb:> jedoch führt dies ebenfalls nicht zu brauchbaren Ergebnissen sobald man> den Interrupt verwendet (Interrupt-Single-Shot).
Was sieht man denn überhaupt auf den Bildern? Und was stimmt damit
nicht?
Karl M. schrieb:> Darüber hinaus ist unklar, wie die ADC Eingänge beschaltet sind.> Sind sie nieder ohmig genug?
Wenn nicht, dann verschleppt man sich gern Teile des Messwerts vom zuvor
gewandelten Kanal auf den eigentlichen Messwert, weil der S&H
Kondensator nicht komplett umgeladen wird.
Stefan ⛄ F. schrieb:> Daraus ergibt sich, dass CurADC die Werte 2 bis 4 haben muss. Du fängst> aber mit 0 an, da du die Variable nicht initialisiert hast. Innerhalb> der ISR wirst du daher irgendwo falsch in RAM schreiben, da kann alles> Mögliche passierten.
Stimmt - aber daran liegt es (leider) nicht.
> Der delay hat in der ISR nichts verloren.
Die wurde später testweise hinzugefügt und ist überflüssig.
> Bedenke, dass der ADC im freilaufenden Modus bereits mit der nächsten> Messung beschäftigt ist, während die ISR abgearbeitet wird. Der Kanal,> den du in der ISR einstellt, wirkt sich daher erst auf die übernächste> Messung aus.
Ja genau - daher ist ein Umschalten der Eingänge nicht vorgesehen.
> Ich hatte damit auch Schwierigkeiten und es damals bei meiner ersten> Anwendung ganz ähnlich wie du umgesetzt. Der folgende Code war für einen> Atmega16:>>
1
...
2
>
>> So funktioniert das jedenfalls tadellos, nur eben nicht freilaufend.
Ein prinzipieller Unterschied ist zu dem Code für den ATMega8 nicht zu
sehen.
Dennoch ist das Ergebnis beim Einlesen in der Interrupt-Routine
fundamental anders.
> Ist bei deiner Anwendung wichtig, dass die Messintervalle in 100%> gleichmäßigen Intervallen stattfinden? Wenn ja, dann solltest du den> freilaufenden Modus benutzen.
Nein - das Messintervall muß ja nur innerhalb 20 ms einigermaßen
gleichmäßig sein, wobei die Interruptroutine ja sehr gleichmäßig läuft,
da die Auswertung erst später erfolgt.
Lothar M. schrieb:> Karsten M. schrieb:>> jedoch führt dies ebenfalls nicht zu brauchbaren Ergebnissen sobald man>> den Interrupt verwendet (Interrupt-Single-Shot).> Was sieht man denn überhaupt auf den Bildern? Und was stimmt damit> nicht?
Geben die Bilder Single-Conversion.png und Interrupt-Single-Shot.png für
Dich den gleichen Signalverlauf wieder?
> Karl M. schrieb:>> Darüber hinaus ist unklar, wie die ADC Eingänge beschaltet sind.>> Sind sie nieder ohmig genug?> Wenn nicht, dann verschleppt man sich gern Teile des Messwerts vom zuvor> gewandelten Kanal auf den eigentlichen Messwert, weil der S&H> Kondensator nicht komplett umgeladen wird.
Mag alles sein, aber das erklärt nicht das recht perfekte Ergebnis bei
single-conversion im Vergleich zu dem Ergebnis der gleichen
Vorgehensweise in einem ADC-Interrupt.
Hier noch ein weiteres aktuelles Ergebnis mit einer korrekten
Initialisierung
1
volatileuint8_tCurADC=2;
und ohne die 10µs verschwendete Wartezeit.
Die grüne Linie ist übrigens ein gleitender Mittelwert über 5 Werte.
Das ist hier ebenfalls nicht von Bedeutung.
Karsten M. schrieb:>> Daraus ergibt sich, dass CurADC die Werte 2 bis 4 haben muss. Du fängst>> aber mit 0 an, da du die Variable nicht initialisiert hast. Innerhalb>> der ISR wirst du daher irgendwo falsch in RAM schreiben, da kann alles>> Mögliche passierten.> Stimmt - aber daran liegt es (leider) nicht.
Den seine Beiträge musst du irgnorieren.
Karsten M. schrieb:> Im ersten Versuch wurde der Free-Running-Modus verwendet, beim dem> jedoch ein Umschalten der Eingänge scheinbar nicht vorgesehen ist.> Egal wie man es macht, es kommt immer kompletter Datenschrott raus> (Free-Running).
Datasheet:
1
If both ADFR and ADEN is written to one, an interrupt event can occur at any time. If the
2
ADMUX Register is changed in this period, the user cannot tell if the next conversion is based
3
on the old or the new settings. ADMUX can be safely updated in the following ways:
4
1. When ADFR or ADEN is cleared
5
2. During conversion, minimum one ADC clock cycle after the trigger event
6
3. After a conversion, before the Interrupt Flag used as trigger source is cleared
7
When updating ADMUX in one of these conditions, the new settings will affect the next ADC
Oliver S. schrieb:> 2. During conversion, minimum one ADC clock cycle after the trigger> event
Ich denke, das ist erfüllt, wenn man den Multiplexer innerhalb der ISR
umschaltet.
Torn schrieb:> Den seine Beiträge musst du irgnorieren.
Dennoch wurden konkrete Fehler aufgezeigt.
Das ist näher an der Lösung drann als die anderen Tips.
Die Impedanz am Eingang liegt übrigens unter 47 KOhm.
Karsten M. schrieb:> Hier noch ein weiteres aktuelles Ergebnis mit einer korrekten> Initialisierung
Wie wäre es wenn du mal anfängst auf die Rückfragen zu antworten:
1. Wie ist die Eingangsbeschaltung
2. Wie sehen die gemessenen Werte aus, und welch Werte erwartest du?
3. Woher weisst du daß deine gemessenen Werte falsch sind, was ist deine
Referenz?
Wenn die grüne Kurve die "schlechte" sein soll, dann könnte das genau
der Effekt einer zu hohen (dynamischen) Impedanz der Quelle sein.
Ja - aber es bleibt dabei - das nächste sample & hold ist bereits im
Gange.
Hier kann man nicht sinnvoll für jede einzelne Messung den Eingang
umschalten.
Höchstens in mit einem Timer in einem anderen getimten Interrupt und
selbst dann ist es fragwürdig ob das sauber funktioniert.
Oliver S. schrieb:> Hm...
Bitte verrate uns dein Geheimnis!
Der Filter 0x1F ist mir auch aufgefallen, aber ich glaube nicht, dass er
hier ein Problem auslöst.
Karsten M. schrieb:> Hier kann man nicht sinnvoll für jede einzelne Messung den Eingang> umschalten.
Doch kann man. Man muss halt nur beachten, dass das Ergebnis erst im
übernächsten Interrupt eintrifft. Der Multiplexer-Wert muss einfach nur
um zwei Positionen versetzt rotieren.
Stefan ⛄ F. schrieb:> Oliver S. schrieb:>> Hm...>> Bitte verrate uns dein Geheimnis!>> Der Filter 0x1F ist mir auch aufgefallen, aber ich glaube nicht, dass er> hier ein Problem auslöst.
Diese Zeile wurde aus irgendeiner Library einfach übernommen.
Das lässt sich geschickter ausdrücken.
Aber es funktioniert ja wenn man in der Main-Loop damit die Eingänge
setzt und ausliest wie man oben sehen kann.
Stefan ⛄ F. schrieb:> Doch kann man. Man muss halt nur beachten, dass das Ergebnis erst im> übernächsten Interrupt eintrifft. Der Multiplexer-Wert muss einfach nur> um zwei Positionen versetzt rotieren.
Könntest Du Deine Rotation bitte in einem Beispiel ausdrücken?
> Könntest Du Deine Rotation bitte in einem Beispiel ausdrücken?
Horizontal läuft die Zeit (=ISR Aufrufe).
falsch:
1
CurADC 2 3 4 2 3 4 2 3 4
2
Array-Index 0 1 2 0 1 2 0 1 2
richtig:
1
CurADC 2 3 4 2 3 4 2 3 4
2
Array-Index 2 0 1 2 0 1 2 0 1
Wobei CurADC hier nicht anzeigt, welchen Channel du gerade gemessen
hast, sondern welcher Channel in diesem ISR Aufruf in den MUX
geschrieben wird.
Also erst CurADC inkrementieren, dann ADC auslesen, dann gemäß obigem
Mapping ins Array schreiben, dann den neuen Wert ins ADMUX Register
schreiben.
Oder meinetwegen auch: Erst CurADC inkrementieren, den neuen Wert ins
ADMUX Register schreiben, dann ADC auslesen, dann gemäß obigem Mapping
ins Array schreiben.
Stefan ⛄ F. schrieb:> Karsten M. schrieb:>> Die Impedanz am Eingang liegt übrigens unter 47 KOhm.>> Sie soll unter 10kΩ liegen!
Wo steht das?
Bei 100 MOhm Eingangswiderstand?
Außerdem funktioniert es ja prächtig wenn man single-conversion
außerhalb des Interrupt durchführt.
Stefan ⛄ F. schrieb:> Bitte verrate uns dein Geheimnis!>> Der Filter 0x1F ist mir auch aufgefallen, aber ich glaube nicht, dass er> hier ein Problem auslöst.
Ok, ich hab da was verwechselt.
Stefan ⛄ F. schrieb:> Karsten M. schrieb:>> Hier kann man nicht sinnvoll für jede einzelne Messung den Eingang>> umschalten.>> Doch kann man. Man muss halt nur beachten, dass das Ergebnis erst im> übernächsten Interrupt eintrifft. Der Multiplexer-Wert muss einfach nur> um zwei Positionen versetzt rotieren.
Das führt dann aber nur dazu, daß die Kanäle komplett rotiert sind,
nicht zur (vermuteten) Versmischung der Daten.
Karsten M. schrieb:> Die grüne Linie ist übrigens ein gleitender Mittelwert über 5 Werte.> Das ist hier ebenfalls nicht von Bedeutung.
Da es ja offensichtlich an all dem, was du bisher gezeigt hast, eher
nicht liegt, wird es Zeit, mal daß ganze Programm zu zeigen.
Oliver
Das erste Messergebnis musst du verwerfen weil es im falschen
Array-Index gespeichert wird. Alle folgenden sind verwendbar.
Der ADC ist schnell genug, um einfach alle 8 Kanäle Zyklisch zu messen.
Wenn du willst, kannst du das natürlich auch auf weniger Kanäle
reduzieren.
Karsten M. schrieb:>>> Die Impedanz am Eingang liegt übrigens unter 47 KOhm.>> Sie soll unter 10kΩ liegen!> Wo steht das?
Weiß ich nicht auswendig. ich glaube in irgendeiner Appnote. Je höher
die Impedanz der Quelle ist, umso ungenauer die Messergebnisse und umso
mehr verschleift man Spannungen von einem Eingang zum nächsten, weil der
Sample&Hold Kondensator im ADC Überreste der vorherigen Messung enthält.
> Außerdem funktioniert es ja prächtig wenn man single-conversion> außerhalb des Interrupt durchführt.
Im Freilaufenden Modus ist die Zeitspanne zwischen dem Umschalten des
Multiplexers und der Messung kürzer.
Stefan ⛄ F. schrieb:> Karsten M. schrieb:>>>> Die Impedanz am Eingang liegt übrigens unter 47 KOhm.>>> Sie soll unter 10kΩ liegen!>> Wo steht das?>> Weiß ich nicht auswendig. ich glaube in irgendeiner Appnote.
Das steht auch im Datenblatt, wo sonst.
Oliver
Karsten M. schrieb:> Allerdings hat das IC nur 6 AD-Eingänge und es werden nur 3 benutzt.
Das ist egal, dann nutzt du eben nicht alle Werte aus dem Array. Das
sind doch nur ein paar Bytes!
Dafür hast du eine schöne 1:1 Zuordnung von ADC Kanal zu Array-Index und
kannst alle Kanäle Nutzen.
Wenn du das auf die 3 Kanäle reduzierst, wird der Code schwer
verständlich und vermutlich auch größer.
Zwischendurch kam die Kritik das nicht der vollständige Code
veröffentlich worden ist.
Da ist grundsätzlich was drann, daher hier eine Minimalversion mit ihrem
dazugehörigen Ergebnis.
Der Fehler lag einfach daran das bei der Ausgabe der Werte auf der UART
der Interrupt nicht deaktiviert worden ist!
Dadurch wurde das Array Oszi während der Ausgabe überschrieben.
Hier der Democode (bis auf die UART) der zeigt das es funktioniert.
Daher gilt wie immer: Kaum macht man es richtig und schon
funktioniert's.
Stefan ⛄ F. schrieb:> Karsten M. schrieb:>> Die Impedanz am Eingang liegt übrigens unter 47 KOhm.>> Sie soll unter 10kΩ liegen!
Wichtiger ist deren dynamisches Verhalten, und das verbessert man durch
Stützkondensatoren direkt an den ADC-Pins. Jeweils 4,7 nF nach Masse,
mit weniger als 5 mm Leitungslänge angebunden.
Sonst gibt es beim Umladen des Sample&Hold-Kondensators einen kurzen
Spannungseinbruch am Pin (den man teilweise sogar von aussen mit dem
Oszilloskop sehen kann), und der Messwert hängt immer ein bisschen vom
vorherigen Kanal ab (Übersprechen).
soul e. schrieb:> Stützkondensatoren direkt an den ADC-Pins. Jeweils 4,7 nF nach Masse,
Ein toller Beitrag. Hast du dir mal die Impedanz von 4,7nF bei nur 10kHz
ausgerechnet? Nicht jeder User will Gleichstrom messen. Ein Kondensator
am ADC-Pin ist die schlechteste Variante. Du bekommst bei
nicht-sinusförmigen Signalen Verzerrungen. Ein vernünftig ausgelegter
OpAmp ist da angesagt.
Stefan ⛄ F. schrieb:> Wenn du das auf die 3 Kanäle reduzierst, wird der Code schwer> verständlich und vermutlich auch größer.
Mit 4 Kanälen lässt sich der Code genauso kompakt formulieren und er
wird kein Bisschen größer.
Wolfgang schrieb:> Mit 4 Kanälen lässt sich der Code genauso kompakt formulieren und er> wird kein Bisschen größer.
Stimmt, nur ünglücklicherweise nutzt er die Kanäle 2, 3 und 4. Kanal 4
liegt gerade über der Schwelle.
Die Lösung mit dem Switch-Case finde ich auch Ok. Ist nicht ganz so
kompakt aber gut lesbar, und das ist am wichtigsten.
Stefan ⛄ F. schrieb:> Stimmt, nur ünglücklicherweise nutzt er die Kanäle 2, 3 und 4. Kanal 4> liegt gerade über der Schwelle.
Das kostet dich genau ein "+1" in der Zeile
Stefan ⛄ F. schrieb:> Ist nicht ganz so> kompakt aber gut lesbar, und das ist am wichtigsten.
Ist so eben auch easy, für verschiedene Kanäle verschiedene Setups zu
machen, da kann man Referenzspannung oder bei anderen AVR auch auf
bipolar usw. umschalten, ohne am Programm irgendwas zu ändern. Define
ändern und fertisch.