Hallo,
ich verwende einen Atmega 328p zum Erfassen von Messwerten über den ADC.
Nun möchte ich die Abtastzeit bestimmen.
Rechnerisch bin ich wie folgt vorgegangen:
f_osz= 16MHz
N_prescaler=128
f_s= f_osz / (N_prescaler*13) = 9615,38
Also eine Abtastzeit T_s = 104µs
Bei den Messungen habe ich allerdings festgestellt, dass diese Zeit
nicht stimmen kann. Also wollte ich einen Ausgangspin toggeln, um die
Laufzeit des Schleife zu bestimmen.
Dazu habe ich folgenden Code verwendet:
while(1)
{
adcval = ADC_Read(0);
PINB = (1<<PB0);
...
}
Die Zeit zwischen den Flanken am Oszi beträgt 3,24ms was mir ziemlich
lang erscheint.
Daher wollte ich fragen, ob mir jemand einen Hinweis geben kann, wo mein
Rechen- bzw. Denkfehler liegt und wie ich vielleicht den Wert
rechnerisch, sowie Messtechnisch ermitteln kann.
Vielen Dank schon mal
BoJack schrieb:> adcval = ADC_Read(0);
Was tut denn diese Funktion? Gibt's da Quellcode dazu?
> PINB = (1<<PB0);
Das macht bestenfalls eine einzige Flanke, nicht mehrere.
BoJack schrieb:> while(1)> {> adcval = ADC_Read(0);> PINB = (1<<PB0);> ...> }
Außer, daß du weisst, wie man effektiv toggelt, sagt das ja nun gar
nichts aus.
> ADC_Read(0); Was ist das das? Arduino-Zeugs?
Was du auf deinem Oszi siehst, ist nicht die Zeit der Wandlung, sondern
die Ausführungszeit dieser ominösen Funktion. Daß die Arduino-Funktionen
einem das erwartete Timing versauen, ist ja nun nichts neues.
Du mußt deine Wandlung mit Interrupt komplett selbst programmieren. Dann
wirst du in der ISR auch das angestrebte Timing sehen.
Markus schrieb:>> PINB = (1<<PB0);>> Das macht bestenfalls eine einzige Flanke, nicht mehrere.
Aber sicher macht es das.
BoJack schrieb:> Daher wollte ich fragen, ob mir jemand einen Hinweis geben kann, wo mein> Rechen- bzw. Denkfehler liegt und wie ich vielleicht den Wert> rechnerisch, sowie Messtechnisch ermitteln kann.
Die Rechnung ist für mich erstmal plausibel.
Was mir als mögliche Ursachen einfällt:
- läuft der µC wirklich mit 16MHz?
- Zeitbedarf der Software. Das hängt auch davon ab, wie der ADC
initialisiert ist bzw. wie ADC_Read() arbeitet.
Gruß Dietrich
Hey, danke für dei Antworten. Die ADC_Read ist hier aus dem Tutorial und
liest jeweils einen Einzelwert ein:
uint16_t ADC_Read( uint8_t channel )
{
// Kanal waehlen, ohne andere Bits zu beeinflußen
ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
ADCSRA |= (1<<ADSC); // eine Wandlung "single conversion"
while (ADCSRA & (1<<ADSC) ) { // auf Abschluss der Konvertierung
warten
}
return ADCW; // ADC auslesen und zurückgeben
}
wie kann ich die Taktfrequenz des Controllers prüfen?
Dass mit den 104µs etwas nicht stimmt hab ich festgestellt, da die
Messwerte aufsummiert werden sollen und ich die Zeit mitgestoppt habe
und dann mit der LCD-Ausgabe verglichen.
Beim toggeln des Pins bekomme ich abwechselnde Low und High-PEgel am
Oszi. Wie sollte ansonsten getoggelt werden? mit bitweisem xor
vermutlich oder?
Richtiges Vorgehen an der Stelle: ADC auf free running modus setzen, ADC
Conversion Complete Interrupt aktivieren, die dazugehörige ISR in den
Code setzen und dort dann einen Pin toggeln.
Dann muss man auch nicht mehr rätselraten wie die Implementierung von
ADC_Read() ist. Bei 3,24ms scheint das nicht nur ein blockierender
Aufruf zu sein, da passiert wohl noch was anderes.
Weiterhin:
PINB = (1<<PB0);
Das ist unverständlicher Code. Solltest du in Zukunft vermeiden. Lieber
ganz normale Bitoperationen.
BoJack schrieb:> // Kanal waehlen, ohne andere Bits zu beeinflußen> ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);> ADCSRA |= (1<<ADSC); // eine Wandlung "single conversion"> while (ADCSRA & (1<<ADSC) ) { // auf Abschluss der Konvertierung
Das ist zwar idiotensicher, aber nicht sehr effektiv. Der ADC muß
ständig laufen und die Abfrage muß in einer ISR erfolgen.
BoJack schrieb:> wie kann ich die Taktfrequenz des Controllers prüfen?
Indem du dir die Einstellungen der Fuses ansiehst. Wenn du da noch nie
etwas verändert hast, läuft der Controller mit 1 MHz.
Überprüfen kannst du das mit einer blinkenden LED.
1
while(1)
2
{
3
PINX=(1<<Y);
4
_delay_ms(500);
5
}
Wenn das Blinken im Sekundentakt erfolgt, stimmt die tatsächliche
Taktfrequenz mit deinen Erwartungen überein. Kannst natürlich auch ein
kürzeres toggeln auf dem Oszi anzeigen.
BoJack schrieb:> Wie sollte ansonsten getoggelt werden? mit bitweisem xor> vermutlich oder?
Da hat einer noch nicht mitbekommen, daß die Erde sich nach dem Launch
des Atmega8 weitergedreht hat.
Sascha schrieb:> PINB = (1<<PB0);>> Das ist unverständlicher Code.
Da kann der Code aber nichts dafür, denn der ist einwandfrei, und an der
Stelle absolut richtig.
Oliver
Sascha schrieb:> Das ist unverständlicher Code. Solltest du in Zukunft vermeiden. Lieber> ganz normale Bitoperationen.
Warum soll man das vermeiden? Weil du es nicht verstehst?
Sascha schrieb:> Weiterhin:>> PINB = (1<<PB0);>> Das ist unverständlicher Code. Solltest du in Zukunft vermeiden. Lieber> ganz normale Bitoperationen.
Grööööl.
Vielen Dank schon mal für die vielen Antworten.
Wenn ich den adc im free running mode betreibe brauche ich also gar
keinen Aufruf mehr oder? Das heißt nach jeder Wandlung wird ein
Interrupt ausgelöst, in welchem ich dann den Pin toggeln kann und dieser
sollte dann am Oszi auch die rechnerischen 104µs anzeigen.
Die 16Mhz stimmen soweit. Hab ich so ähnlich schon an anderer Stelle
überprüft.
Mein grosses V. schrieb:> Da hat einer noch nicht mitbekommen, daß die Erde sich nach dem Launch> des Atmega8 weitergedreht hat.
hahaha :D
Mein grosses V. schrieb:> Sascha schrieb:>> Das ist unverständlicher Code. Solltest du in Zukunft vermeiden. Lieber>> ganz normale Bitoperationen.>> Warum soll man das vermeiden? Weil du es nicht verstehst?
Natürlich verstehe ich das, aber nur weil ich extra das DB gelesen habe
und dann da steht, dass es funktioniert.
Code sollte unabhängig von der Hardware Sinn ergeben.
Sascha schrieb:> Code sollte unabhängig von der Hardware Sinn ergeben.
Was bei Code, der Hardware direkt ansteuert, aber nunmal nicht geht. Und
ja, da muß man dann dazu das Datenblatt lesen...
Oliver
Mein grosses V. schrieb:> Sascha schrieb:>> Das ist unverständlicher Code. Solltest du in Zukunft vermeiden. Lieber>> ganz normale Bitoperationen.>> Warum soll man das vermeiden? Weil du es nicht verstehst?
Mein Vorschlag zur Entspannung: man könnte ja im Kommentar vermerken,
dass diese Operation bei diesem µC den Pin toggelt. Dann versteht ein
fremder Leser dies auch.
Gruß Dietrich
BoJack schrieb:> Das heißt nach jeder Wandlung wird ein> Interrupt ausgelöst, in welchem ich dann den Pin toggeln kann und dieser> sollte dann am Oszi auch die rechnerischen 104µs anzeigen.
Richtig. Der ADC lässt sich damit sogar als Timer benutzen.
Sascha schrieb:> Code sollte unabhängig von der Hardware Sinn ergeben.
Was hast du denn gegen den Code? Da wird etwas in ein Register
geschrieben. Was das bewirkt, ist von der spezifischen Hardware abhängig
und steht im Datenblatt. Also, was ist dein Problem?
Wobei es richtig eingesetzt natürlich
Zuerst
Abtastzeit ist ein etwas unglücklich gewählter Begriff. Ohne gesonderte
Definition hätte ich damit eher die sample and hold Zeit darunter
verstanden.
Richtig ist:
Wenn der ADC schon kofiguriert und aktiviert ist, so benötigt die reine
Wandlung des Analogwertes 13 ADC-Takte bis die Informatiion im
ADC-Register steht. Was dann die ISR etc so macht, kann man so pauschal
nicht vorhersagen. Um es exakt durchzugehen müsste man schon auf
Maschinencode bzw Assembler gehen. Und auch da wird es schwierig.
Ich würde ohne Oszi "messen".
Du kannst einen Timer mitlaufen lassen und seinen Wert zu Beginn der
Wandlung und unmittelbar danach auslesen. Die Differenz ist dann deine
Dauer, inklusive eines kleinen Overheads für die Auswertung des Timers.
Alternativ, so ähnlich wie du es nun machst mit dem Toggeln.
Gehe in den free running modus und lasse die ISR zusätzlich eine
Variable hochzählen. Nach einer bestimmten Zeit wertest du diese
Variable aus und kannst die Zeit pro Wandlung berechnen. Dazu muß die
Variable natürlich einen hinreichend großen Wertebereich haben, damit es
nicht zu einem überlauf kommt.
Du kannst auch eine Schleife x-mal eine Wandlung durchführen lassen und
mit einem Timer wie oben mitlaufen lassen und damit die für die ganze
Schleife benötigten Takte zählen. Auch hier ist natürlich wieder zu
beachten, daß der Timer einen Überlauf produzieren kann. Den kann man
aber auch wieder mitzählen.
Carsten R. schrieb:> Richtig ist:
Schön, was du da schreibst. So schön wie unsinnig.
Der ADC-IRQ kommt alle 104µs. Diese 104µs entsprechen ca. 1600
CPU-Takten. Das ist die Zeit, die zur Verfügung steht, um den aktuellen
Wert zu verarbeiten. Dann kommt nämlich der nächste. Aber 1600 Takte
sind verdammt viel.
Um das auf dem Oszi zu zeigen, setzt man am Anfang und am Ende der ISR
jeweils ein Toggle, ausgehend von Port = Low und bekommt eine PWM
angezeigt. Die Periode beträgt dabei 104µs, also knapp 10KHz und der
"Duty", der die Dauer der ISR repräsentiert, sollte, wenn der Controller
sich auch noch um andere Dingen kümmern soll, möglichst kurz sein.
kurzes Update:
Betreibe den adc jetzt im free running mode. Die ISR sieht
folgendermaßen aus:
ISR(ADC_vect)
{
PINB = (1<<PB0);
ADCSRA |= (1<<ADSC);
}
Bekomme ein PWM-Signal am Oszi, das eine Pulsbreite von 56µs hat, also
eine Periode von 112µs.
Sollte dabei aber nicht eine Pulsbreite 112µs herauskommen? Schließlich
ändert sich der Pin ja bei jedem Interrupt. Oder wo steckt mein
Denkfehler?
Danke schon mal
ich hab jetzt noch eine BErechnung in die ISR eingefügt, dadurch ist die
Pulsbreite nochmal gestiegen auf 92µs.
Bedeutet das, dass die Dauer der ISR letztendlich meiner Abtastzeit
entspricht an der ich mich orientieren muss?
Hi
>Betreibe den adc jetzt im free running mode. Die ISR sieht>folgendermaßen aus:>ISR(ADC_vect)>{> PINB = (1<<PB0);> ADCSRA |= (1<<ADSC);>}
Das ADCSRA |= (1<<ADSC); ist nur einmal zum Starten der ersten
Wandlung notwendig (außerhalb der ISR).
MfG Spess
Damit erhälst du an PB0 eine PWM mit ca. 10% Duty Cycle. Das Delay
simuliert dabei die Zeit für die Bearbeitung des gemessenen Wertes.
Der Interrupt kommt alle 52µs.
Auf dem Oszi siehst du jetzt grün auf grau, wann etwas passiert und wie
lange das dauert.
Mein grosses V. schrieb:> Carsten R. schrieb:>> Richtig ist:>> Schön, was du da schreibst. So schön wie unsinnig.>> Der ADC-IRQ kommt alle 104µs. Diese 104µs entsprechen ca. 1600> CPU-Takten. Das ist die Zeit, die zur Verfügung steht, um den aktuellen> Wert zu verarbeiten. Dann kommt nämlich der nächste. Aber 1600 Takte> sind verdammt viel.
Da hast du dich etwas weit aus dem Fenster gelehnt. Ich schrieb daß es
13 ADC-Takte sind, nicht CPU-Takte Da kommt noch der Prescaler hinzu.
Damit bestätigte ich nur die überschlägige Rechnung im Eingangspost. So
findet man es auch im Datenblatt.
Deinen Daten entsprechend hast du einen CPU-Takt von 16 MHz und den
Prescaler auf maximum, nämlich 128. Das ergibt 13*128=1664 CPU-Takte pro
Wandlung, bzw rund 9615 Wandlungen pro sekunde im free running Modus.
Den Prescaler kann man verringern, insbesondere wenn der CPU-Takt
niedriger ist, und somit die Geschwindigkeit des ADC erhöhen. Allerdings
sollte dabei beachten, daß der daraus resultierende ADC-Takt im Rahmen
bleibt. Wird er zu hoch, werden zuerst die letzten Bits ungenau. Wird er
sehr hoch, so verläßt man die Spezifikation. Was das bedeutet sollte
jedem klar sein. Die Spezifikationen stehen im Datenblatt.
Wenn die ISR innerhalb dieser 1664 Takte abgearbeitet werden kann, so
kann beim free running Modus die Softwareauswertung parallel zur
nächsten Hardwarewandlung erfolgen. Das heißt die
Hardwaregeschwindigkeit kann ausgereizt werden.
Ist die Software komplexer, so ist sie der begrenzende Faktor.
Geht es um einzenle ADC-Wandlungen, so addieren sich Hardware- und
Softwareausführungszeiten, da sie nacheinander ablaufen. Um dann die
Latenz exakt auszurechnen, müßte man jeden Maschinenbefehl der Software
kennen. Eine Hochsprache hilft da nicht.
Oder man mißt per Oszi bzw. zählt per Timer die Takte, auch wenn
letzteres einen kleinen zusätzlichen Softwareoverhead bedeutet.
Die Frage ist was man wissen will.
Die Latenz, bis die Software das Ergebnis ausspuckt oder die maximale
Samplingrate, die ausgewertet werden kann?