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.
:
Bearbeitet durch User
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.
"while (ADCSRA & (1<<ADSC) ) { // auf Abschluss der Konvertierung " Die Zeile macht dir deine Zeitmessung komplett kaputt.
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?
BoJack schrieb: > wie kann ich die Taktfrequenz des Controllers prüfen? z.B. mit:
1 | #define F_CPU 16000000 |
2 | #include <avr/io.h> |
3 | #include <util/delay.h> |
4 | |
5 | int main() |
6 | { |
7 | DDRB = ( 1 << PB4 ); |
8 | |
9 | while( 1 ) |
10 | { |
11 | PORTB |= ( 1 << PB4 ); |
12 | _delay_ms( 1000 ); |
13 | PORTB &= ~( 1 << PB4 ); |
14 | _delay_ms( 1000 ); |
15 | } |
16 | } |
Hier muss PB4 jede Sekunde toggeln. Wenn das nicht stimmt ist F_CPU nicht der tatsächliche Wert des µC-Taktes. Gruß Dietrich
:
Bearbeitet durch User
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
1 | PINB |= (1<<PB0); |
lauten muß.
:
Bearbeitet durch User
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.
:
Bearbeitet durch User
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
Initialisierung des ADC: int main(void) { DDRB = 0x01; ADMUX |= (1<<REFS0) | (1<<MUX0); ADCSRA |= (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADIE); sei(); ADCSRA |= (1<<ADSC); while(1) { } }
> ADCSRA |= (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADIE)
Also Vorteiler=64. Ergibt für den ADC 52 us, der Rest wird für den
Interruptaufruf benötigt.
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
BoJack schrieb: > ADCSRA |= (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADIE); Das ist Prescaler = 64. Passt doch.
1 | int main(void) |
2 | {
|
3 | DDRB = 0x01; |
4 | ADMUX |= (1<<REFS0) | (1<<MUX0); |
5 | ADCSRA |= (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADIE)|(1 << ADATE); |
6 | //ADATE = Auto Trigger Enable
|
7 | sei(); |
8 | ADCSRA |= (1<<ADSC); |
9 | }
|
10 | ISR(ADC_vect) |
11 | {
|
12 | unsigned int adc = ADC: |
13 | PINB = (1<<PB0); |
14 | _delay_us(5); |
15 | PINB = (1<<PB0); |
16 | }
|
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.
:
Bearbeitet durch User
Hi
>Dazu muss aber ADATE in ADCSRA gesetzt werden.
Wenn das nicht gesetzt ist, dann ist das eh kein (echter)
Free-Running-Mode.
MfG Spess
ja mit dem free running hab ich grad auch gesehen. hab noch ADATE gesetzt. In der ISR steht jetzt:
1 | ISR(ADC_vect) |
2 | {
|
3 | PINB = (1<<PB0); |
4 | |
5 | U_e = ((adcval+44.09)*5050)/1024; |
6 | |
7 | }
|
Also das Toggeln + die Umrechnung meiner ADC-Werte
Und, wie sieht's aus? Schafft es der C-Compiler, diese Berechnung in den gut 800 Takten unterzubringen?
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?
:
Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.