hallihallo! Wer kann mir helfen, dieses ADC Programm zum laufen zu bringen? Ich finde den Fehler nicht! Der Code sieht so aus: include <avr/io.h> #include <avr/interrupt.h> #include <stdlib.h> #include "adc.h" #include "USART.h" volatile uint16_t result; volatile unsigned char buffer[20]; ISR(ADC_vect) { result = ADCL + ADCH; // 10 Bit Ergebnis ADCSRA &= ~(1<<ADEN); //ADEN "0" : ADC ausschalten itoa(result,buffer,10); } uint16_t ADC_free_running(uint8_t channel) { DDRC = 0x00; //PC0 als Eingang definieren PORTC = 0x00; //Pullup an PORTC aus ADCSRA = ( (1<<ADFR) | (1<<ADPS2) | (1<<ADPS1) |(1<<ADIE) ); ADMUX = (1<<REFS0) | channel; ADCSRA |=( (1<<ADEN) | (1<<ADSC) ); //ADEN "1"...ADC enable //ADSC set to "1" : start conversation sei(); } int main(void) { init_USART(); uart_puts("Controller online..... OK\n\r"); for(int i=0;i<20;i++) { uart_puts("Aktueller Wert auf PC1 :"); ADC_free_running(1); uart_puts(buffer); uart_puts("\n"); } } Thx!
Es "stechen" mehrere Dinge ins "Auge". Aua. Im Prinzip ist Deine Vorgehensweise ja korrekt, allerdings solltest Du im ADC nicht Laufzeitroutinen der C-Bibliothek aufrufen. Das kann arge Interferenzen mit dem Hauptprogramm geben, da evtl. gemeinsame Variablen benutzt werden. Ausserdem erfährt das Hauptprogramm gar nicht, wann den nun eine Messung fertig ist. Daher wird wohl mit maximaler Geschwindigkeit "Käse" auf der seriellen Schnittstelle ausgegeben. Statt result = ADCL + ADCH solltest Du besser result = ADCL<<8 + ADCH schreiben, womit Du gleich result = ADC schreiben kannst. Vorschlag: Du nimmst ja schon ADEN zum Starten einer Wandlung her. Warte doch im Hauptprogramm, bis ADEN durch den Interrupt zurückgesetzt wird. Dann wirfst Du im Hauptprogramm den itoa an, und schiebst den Buffer raus. Viel Glück :-)
Danke euch beiden für die Antwort... @Christian: Du meinst, ich soll den Aufruf von Standartbibliothelsfunktionen in der ISR vermeiden, nehmen ich an... Das dachte ich mir auch schon ;-) Naja, ich könnte das warten, bis ADEN zurückgesetzt wird, im main ja so machen, oder? while((ADCSRA & (1<<ADEN))==1 ); //Warte, bis ADEN gelöscht Was das ADC Ergebnis betrifft. Ich dachte, wenn ich result = ADCL+ADCH schreibe, werden die zwei Regsiter aneinandergereiht in der richtigen Reihenfolge,also das MSB (das 10. Bit) ganz links.... naja, wohl ein Irrtum :-) Kann mir die "+" Operation irgendjemand erklären, also wie das nun genau auszusehen hat bzw. wie die arbeitet?! Danke!
Ich meine, wenn ich mir die Verschiebung so vorstelle, sollte Miha Recht haben und es müsste lauten : result=ADCL + (ADCH<<8); Nur, was genau passiert, wenn ich es so (falsch) schreibe, wie ich ursprünglich. Wie addiert der Compiler dann diese zwei Register?
Denn obwohl ich nun diese Sache ausgebessert habe, bekomme ich über die USART weiterhin folgendes: Controller online..... OK Aktueller Wert auf PC1 :0 Es kommt jedesmal "0" als Ergebnis ?! :-P
@Miha war nur ein Test für den aufmerksamen Leser schluck @snoopy Naja, was erwartest Du von einem '+'? Es werden zwei Werte addiert. Das ADC-Ergebnis liegt in zwei Registern vor ADCL für die unteren 8 Bit und ADCH für die oberen 2 Bit. Aber der Compiler weiss ja nicht, was Du von Ihm willst, bzw. was er mit den Werten tun soll. Das musst Du ihm schon selber mitteilen. Und mit der Operation result=ADCL + (ADCH<<8); // Gell, Miha? erreichst Du genau dieses: ADCL: . . . . . . . . 7 6 5 4 3 2 1 0 ADCH<<8: . . . . . . 9 8 . . . . . . . . result: . . . . . . 9 8 7 6 5 4 3 2 1 0 Statt '+' darf auch verodert ('|') werden.
Ok,der ADC Wert wird nun korrekt ausgegeben! Was aber die Frage bezüglich der Addition betrifft: Angenommen der Fall, man hätte zwei Register namens "a" und "b". Beide Register enthielten 4 Bits. a = 1111 und b = 0000 Würde ich nun schreiben: result = a + b; Bekomme ich dann "1111" als Ergebnis, also die "normale " Addition des 1.Bits von "a" mit dem 1.Bit von "b" ... das 2. Bit von "a" mit dem 2. Bit von "b"....... usw ?? Und wenn ich dann schreibe: result = (a << 4) + b; als Ergebnis : "11110000" oder "111100000000" ? Mit anderen Worten: Werden beim verschieben von "a" um vier Stellen nach links, die STellen wo voher die "1111" standen,dieser Platz mit "0000" aufgefüllt oder was steht an diesen Stellen? Danke!
Nachdem ich jetzt aus dem ADC Wert die Spannung am Pin berechnen möchte, muss ich ja wohl die Formel Vin = (ADC*Vref) /1024 verwenden. Als Referenz verwende ich derzeit die Spannung VCC, also in meinem Falle 3.3V mit dem "mega8L". Da komme ich wohl kaum drumherum, float- variablen zu verwenden, ohne das Ergebnis grob zu verhunzen ( dann sit die 10 Bit - Genauigkeit auch für nix :-) ) Grüße...
Es wird mit nullen aufgefüllt. Kleiner Rundschlag:
Schieben um eine Positon nach links entspricht der Multiplikation mit
2; Schieben nach recht entspricht der Division durch 2.
C führt immer ein arithmetisches Schieben aus. Dies macht aber nur
einen Unterschied bei vorzeichenbehafteten Zahlen (speziell negativen)
die nach rechts geschoben werden. Aus -4 >> 2 wird also in der Tat -1.
Nur in diesem Fall wird links mit Einsen aufgefüllt:
-4 = 11111100
>> 2
-1 = 11111111
Das gilt also für signed int, signed char usw... Wenn man nicht
aufpasst, beist man sich daran die Zähne aus.
> Naja, ich könnte das warten, bis ADEN zurückgesetzt wird, im main > ja so machen, oder? > > while((ADCSRA & (1<<ADEN))==1 ); //Warte, bis ADEN gelöscht Nein, so nicht. Du mußt das "==1" weglassen. Außerdem wäre es an sich besser, ADSC zu verwenden. Das wird automatisch vom ADC zurückgesetzt#, wenn die Wandlung beendet ist. Übrigens: Warum benutzt du den ADC eigentlich im "free-running mode", wenn du ihn sowieso nach jeder Wandlung wieder ausschaltest? Da wäre es besser, diesen Modus nicht zu benutzen. Das hat auch den Vorteil, daß der ADC nicht nach jeder Wandlung komplett neu initialisiert werden muß und daß die Wandlung daher schneller ist. Letztendlich kannst du dir die ganze ADC-ISR sparen, wenn du sowieso in der Hauptschleife auf den ADC-Wert wartest, bevor du die nächste Wandlung anstößt.
float ist was für Dilletanten :-) Als Übergang und wenn das Platz mal wieder etwas knapper ist, empfiehlt sich der Umweg über einen long: unsigned int Vin = ADC*3300L/1024; // Result in mV und zur Ausgabe: printf_P(PSTR("Vin = %d.%03dV\r\n"),Vin/1000,Vin%1000); Man beachte das dezente 'L' hinter '3300'.
Statt Vin/1000 und dann Vin%1000 zu rechnen, empfielt sich übrigens die Funktion div().
>Nein, so nicht. Du mußt das "==1" weglassen.
Wieso? Solange der Ausdruck
(ADCSRA & (1<<ADEN))
eins ist, solange ist das Bit doch gesetzt, und ich will ja in der
Schleife bleiben, bis das nicht merh der Fall ist?!
Ohne dem "==1", würde die Schleife so lange laufen, solange der
Ausdruck "ungleich "0" , oder?
Würde dann zwar hier auch gehen, aber warum nicht mit "==1"?
Das mit dem "ADSC" ist wohl korrekt und auch deine weiteren Aussagen.
Ich werde das alles noch besser machen, ich bin momentan noch am
"spielen", um mich mit der Materie vertraut zu machen! ;-)
Das mit der Funktion div() muss ich mir erst genauer ansehen, morgen
:-)
Der Ausdruck ist nicht gleich 1, sondern (1<<ADEN). "&" ist eine bitweise Verknüpfung, d.h. daß im Ergebnis genau die Bits gesetzt sind, die in beiden Operanden gesetzt sind. > Ohne dem "==1", würde die Schleife so lange laufen, solange der > Ausdruck "ungleich "0" , oder? Genau. > Das mit der Funktion div() muss ich mir erst genauer ansehen, > morgen :-) div() hat den Vorteil, daß es dir gleich Divisionsergebnis und -rest zusammen zurückgibt. Bei einer Integer-Division fällt der Rest automatisch als "Abfallprodukt an". Wenn du die Operatoren / und % benutzt, werden zwei Divisionen durchgeführt. Bei einer wird das Ergebnis verwendet und der Rest weggeworfen, bei der anderen umgekehrt. Mit div() braucht's nur eine Division.
Aha, seh schon :-) Mann, ihr seid ja echt aufs optimieren aus, gell? :-) Fällt eine Division mehr oder weniger tatsächlich so ins Gewicht? ICh denke da immer an eine Taktgeschwindigkeit von ein paar MHz :-) Aber nicht falsch verstehen, ich finde es absolut spitze, wie ihr Anfängern( in diesem Falle mir) weiterhelft und bin dankbar für jeden Ratschlag!! Grüße...
@Rolf Das mit div() scheint interessant zu sein: printf_P(PSTR("Vin = %d.%03dV\r\n"),div(Vin,1000)); ist fies, könnte aber zum erhofften Ergebnis führen, da der struct gleich wieder exakt in der gewollten Reihenfolge auf den Stack kommt. Aber das nur am Rande. Muss ich selber mal testen :-)
So... das mit dem div() und dem umrechnen muss ich mir erst zu Gemüte führen, jedenfalls hab ich nun mal die "single conversion" verwendet, und zwar möchte ich die Anzahl der Abtastungen wählen können. Am Ende sollte das Ergebnis der Mittelwert aus der Anzahl der Abtastunge sein... Ich hab mir das so gedacht: uint16_t ADC_single_con(uint8_t channel,uint8_t anzahl) { DDRC = 0x00; PORTC = 0x00; ADCSRA = ( (1<<ADPS2) | (1<<ADPS1) ); ADMUX = (1<<REFS0) | channel; ADCSRA |=( (1<<ADEN) | (1<<ADSC) ); for(int i=1;i==anzahl;i++) { while(ADCSRA & (1<<ADSC) ); if(i==1) //Erstes Ergebnis vorhanden { result = ADCL + (ADCH<<8); } else { result+= ADCL + (ADCH<<8); } } result / = anzahl; } Ist das so richtig?!
Naja, eigentlich ist das der typische Programmierloop: Programmieren - Testen - Programmieren - .... Deiner ist jetzt: Programmieren - Posten - Testen :-) Aber gut: Auf Anhieb sieht man, dass es so besser ist: result = 0; for (int i=0; i<anzahl; i++) { while(ADCSRA & (1<<ADSC) ) ; result += ADCL + (ADCH<<8); } result /= anzahl; In einer for-Schleife schreibt man nicht die Bedingung für den Abbruch, sondern die Bedingung für die Fortführung der for-Schleife!
>result += ADCL + (ADCH<<8);
Nimm besser ADC anstatt ADCL und ADCH. Es ist bei diesen Registern
essentiell wichtig, dass sie in der richtigen Reihenfolge ausgelesen
werden (erst Low-, dann High-Byte). Wenn der Compiler das schon für
Dich sicherstellt, solltest Du die Funktion auch nutzen.
Gut, aber von der Funktion her betrachtet, mus es doch das selbe sein, ob ich nun for(int i=0;i<anzahl;i++) oder for(int i=1;i==anzahl;i++) schreibe. Die Anzahl der Durchläufe ist beide Male gleich und auf das komtm es ja an?! Nun habe ich jedoch einen Compilererror : Und zwar in der Zeile: result / = anzahl; error: syntax error before '=' token Ach und was bedeutet das?! itoa(result,buffer,10); warning: passing arg 2 of `itoa' discards qualifiers from pointer target type
Ok, den error hab ich nun behoben: Bei: result /= anzahl; Darf zwischen den "/" und dem "=" wohl kein Leerzeichen sein. Auf das muss man mal kommen :-)
>for(int i=0;i<anzahl;i++) >oder >for(int i=1;i==anzahl;i++) Hast du dir schon mal ein C-Buch angegguckt, und darin die Definintion der for-Schleife? 1. Parameter: Startbedingung 2. Parameter: Schleifenbedingung, solange diese erfüllt ist, wird die Schleife ausgeführt. 3. Parameter (kann ich mir wohl sparen)
Naja, ich hab mir nur gedacht, weil ich das getestet und laut meiner Ansicht laufen beide Varianten gleich. Es hat funktioniert..
> Gut, aber von der Funktion her betrachtet, mus es doch das selbe > sein, ob ich nun > > for(int i=0;i<anzahl;i++) > > oder > > for(int i=1;i==anzahl;i++) Mach mal ein Gedankenexperiment. Das 2.-te Argument in der for-Schleife liest Du dabei als: "Wiederhole, solange gilt, dass" Dann wirst Du sehen, dass wenn 'anzahl' nicht grade den Wert 1 besitzt, Deine for Schleife überhaupt nicht ausgeführt wird. eine for Schleife ist equivalent zu: i = 1; while( i == anzahl ) { ... i++; } > result / = anzahl; > error: syntax error before '=' token Nimm das Leerzeichen raus. /= ist 1 Token und muss auch als solches geschrieben werden. Genauso wie ++ oder <= > warning: passing arg 2 of `itoa' discards qualifiers from pointer > target type Ein qualifier ist eine Attributierung des Datentyps, wie zb volatile oder const. Beim Aufruf von itoa geht die verloren. zb. const char* string = "check"; schreibst du jetzt eine Funktion void foo( char* a ); dann wird dir der Compiler mitteilen, dass ein 'qualifier' discarded wird. Er meint damit das const. Beim Aufrufer gilt das const noch und der Compiler stellt sicher, dass es auch eingehalten wird (dass keine Zuweisungen an string erfolgen). Übergibst du allerdings das 'string' an die Funktion foo, so ist es innerhalb der Funktion nicht mehr const und theoretisch könnte innerhalb der Funktion genau das passieren: das string verändert wird. Ich denke mal, in Deinem Fall wird die Sachlage so sein, dass 'buffer' ein volatile char Array sein wird. Und dieses volatile geht verloren, wenn du es an itoa übergibst. In diesem speziellen Fall ist das aber kein Beinbruch, und du kannst den Compiler ruhig stellen: itoa( result, ( char* )Buffer, 10 ); Mit dem cast castest du das volatile weg, bevor das ganze an itoa geht. Aber Achtung: cast's sind Waffen! Im Endeffekt überschreibst du damit die Typprüfung des Compilers und stellst ihn ruhig. Manchmal muss man casten um Warnungen des Compilers wegzukriegen allerdings muss sichergestellt sein, dass der Programmierer weiss was er tut.
> Auf das muss man mal kommen :-)
Nun ja. Das steht in jedem schlechteren Lehrbuch über C
drinnen. Hab ich Dir schon angeraten, (mindestens) ein Buch
zu kaufen? Du wirst es brauchen.
> for(int i=0;i<anzahl;i++) Hier wird i vor de Schleife auf 0 gesetzt. Sie wird ausgeführt, solange i kleiner als anzahl ist, und bei jedem Durchlauf wird i um 1 erhöht. Die Zahl der Schleifendurchläufe entspricht also anzahl. > for(int i=1;i==anzahl;i++) Dies hier setzt i vor der Schleife auf 1. Solange i denselben Wert hat, wie anzahl, wird die Schleife durchlaufen. Es wird wieder bei jedem Durchlauf i inkrementiert. Falls anzahl einen Wert von 1 hat, wird die Schleife also einmal durchlaufen. Für jeden anderen Wert von anzahl wird sie niemals ausgeführt. > Ach und was bedeutet das?! > > itoa(result,buffer,10); > > warning: passing arg 2 of `itoa' discards qualifiers from pointer > target type Es sieht so aus, als ob du versuchst, mit itoa in einen String zu schreiben, der eigentlich konstant ist.
Aja.... mal wieder was gelernt :-) Dann hab ich bislang wohl tatsächlich die for-Schleife nicht ganz verstanden... Also das mit den cast´s ist mir im Moment ein wenig zu hoch, aber ich werd mcih damit auseinandersetzen... Das Problem ist halt, man braucht dazu nicht nur einfach "C-Kenntnisse", sondern wie es scheint auch spezielle Kenntnisse über den Compiler :-P Naja, jedenfalls hab ich heute meine C-Lektüre erhalten. Hab mir das Buch "C von A-Z" von Jürgen Wolf besorgt.. Ich hoffe, ich werde damit klüger :-) Grüße...
Bezüglich diesem Ausdruck: unsigned int Vin = ADC*3300L/1024; // Result in mV Das "L" bedeutet da ja, dass der Compiler die Konstante davor als "long" nehmen soll... Nur, die zahl 3300 ist ja eh nicht so groß, das Ergebnis ( Vin )der Multiplikation überschreitet also einen uint16_t. Warum steht also das "L" also nach der zahl 3300 und nicht bei der variable "Vin" dabei?? Danke!
Die Zuweisung wird in C als letztes gemacht. Erst wird der Term auf der rechten Seite ausgewertet. Und wenn da nicht nach long gecastet wird, gibts Müll. Selbst wenn Du den Müll (der dann evtl. tatsächlich uint16_t ist) einem long zuweist, fehlt dann was.
Man kann's auch so formulieren: Der Datentyp, mit dem die Operation durchgeführt wird, hängt ausschließlich von den Operanden ab und nicht davon, was du mit dem Ergebnis nachher machst. Ohne das L würde die Berechnung daher nur mit 16 Bits durchgeführt werden. Das L sorgt dafür, daß ein Operand 32 Bit breit ist, wodurch auch der andere nach 32 Bit konvertiert und die Rechnung dann entsprechend durchgeführt wird.
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.