Hallo zusammen, ich möchte mit meinem ATmega168 die Quellspannung vor meinem 5V Regler rückmessen. Dazu habe ich einen Spannungsteiler R1=22k und R2 = 10k sowie zum "ruhig" Messen einen 0.680 µF Kondensator an meinem Kanal ADC0. Am PIN0 liegt auch die erwartete Spannung an z.B. U_q = 12V PIN ADC = 3,75V. Der ADC ist an AVref mit 5V und ADCC ebenfalls 5V beschalten. (davor jeweils ein LC Tiefpass). Möchte AVref verwenden. Jetzt wollte ich mit angehängtem Code eine LED AN schalten wenn U_q größer 10V ist und aus wenn U_q kleiner 10 V ist. Leider geht die LED aber erst an wenn Uq ca. 13 V ist. Wenn ich die 10V grenze auf z.B. 8V reduziere geht die LED erst bei ca. 10 V an. Denke an der HW ist alles ok da die erwarteten Werte an den PINS anliegen. Findet vielleicht jemand in meinem Code einen Fehler? Bin noch Anfänger. Danke schonmal & Gruß Andi
Ohne das jetzt konkret analysiert zu haben, ob das deine Ergebnisse bestätigt: return_temp = ((temp*5)/1024)*3.2; Siehe http://www.mikrocontroller.net/articles/FAQ#Datentypen_in_Operationen und denk darüber nach, wie sich das auf die Division durch 1024 bei dir auswirkt. Generell: Du möchtest in einem Ausdruck Divisionen so weit wie möglich nach rechts schieben, so dass sie so spät wie möglich abgearbeitet werden.
Ein Fehler:
> return_temp = ((temp*5)/1024)*3.2;
^
streiche "5", setze "5.0"
Alternativ kannst auch explizit casten.
HTH
erstmal danke daran hät ich noch ewig gesucht! Karl heinz Buchegger schrieb: > denk darüber nach, wie sich das auf die Division durch 1024 bei dir > auswirkt. also der Compiler macht eine Interger Division wodurch ich keine Nachkommastellen habe. Jetzt geht die LED ab 10.03 V etwas Flakernt an ab 10.04V ganz an. Das es nicht bei 10.01V angeht liegt denk ich an den Tolleranzen des Spannunsteiler, die Auflösung des ADC wären ja 4,88 mV. Danke nochmal!
Hab noch eine Frage: Wenn ich für die Rechnung alles mit Double mache funktioniert es. Jetzt will ich aber nur mit Interger rechenen und das Ergebniss in mV. Also ändere ich alle meine Datentypen in unsigned int und die Rückrechnung: return_temp = (temp*125)/8; Wenn jetzt z.B. am ADC PIN 3 V anliegen: ADC = 3V*1024 /5 = 614,4 müsste im ADC Register 614 stehen. Wenn ich das nun oben als temp Auslese: temp = ADCL; temp += (ADCH<<8); return_temp = (temp*125)/8 = 9593,75 mV also liefert retun_temp 9593 zurück. Jetzt lass ich in meiner main schleife vergleichen V_quelle = get_Bat(); if (V_Quelle > 9000) Do_LED2_On(); ...aber die LED2 bleibt aus. Warum? Danke & Gruß Andi
Andi schrieb: > Also ändere ich alle meine Datentypen in unsigned int und die > Rückrechnung: > > return_temp = (temp*125)/8; Das ist nur die halbe Miete. Du musst auch auf die Zahlenbereiche achten. > ADC = 3V*1024 /5 = 614,4 > müsste im ADC Register 614 stehen. OK. > > temp = ADCL; > temp += (ADCH<<8); Warum so kompliziert. Lass das doch den Compiler machen. Der weiß schon, wie man mit 16 BIt Registern umgeht: temp = ADCW; > return_temp = (temp*125)/8 = 9593,75 mV Nö. Du hast das Zwischenergebnis vergessen 614 * 125 = 76750 Mööp. 76750 ist zu groß für einen 16 Bit unsiged int. Bei 65535 ist Schluss. Darüber gibts einen Overflow. Der Compiler wählt nicht nur die Art der Berechnung anhand der Datentypen der Operation aus, sondern auch in welchem Datentyp das Ergebnis vorliegen wird. Bei dir ist das alles unsigned int. Du hast also eine 16 Bit * 16 Bit Multiplikation, die ein 16 Bit Ergebnis bringen wird. Und dein Zwischenergebnis ist schlicht und ergreifend zu groß für 16 Bit. > also liefert retun_temp 9593 zurück. nicht ganz. Es liefert 11214 / 8 = 1401 11214, weil die Multiplikation überlaäuft und du nicht 76750 sondern 76750 - 65536 = 11214 bekommst return_temp = (temp * 125UL) / 8; Jetzt müsste das richtige rauskommen. Durch die 125UL forciere ich eine Berechnung als unsigned long
Karl heinz Buchegger schrieb: > return_temp = (temp * 125UL) / 8; > Jetzt müsste das richtige rauskommen. Durch die 125UL forciere ich eine > Berechnung als unsigned long Vielen dank so geht es. Ich hatte mir selbst auch gedacht, dass das zwischen ergebniss zu groß ist. Hatte es dann mit "uint32_t" als Typ probiert aber das ging nicht. aber mit 125UL geht es. Danke nochmal!
Andi schrieb: > Hatte es dann mit "uint32_t" als Typ probiert aber das ging nicht. > aber mit 125UL geht es. Doch, das geht genauso. Nur wirst du es falsch gemacht habe :-) Du musst temp auf uint32_t casten, BEVOR die Multiplikation stattfindet. Danach ist es zu spät return_temp = ( (uint32_t)temp * 125) / 8;
Karl heinz Buchegger schrieb: > Doch, das geht genauso. > Nur wirst du es falsch gemacht habe :-) ja ich hatte return_temp als uint32_t und temp normal unsigned int gelassen...;)
Andi schrieb: > Karl heinz Buchegger schrieb: >> Doch, das geht genauso. >> Nur wirst du es falsch gemacht habe :-) > > ja ich hatte return_temp als uint32_t und temp normal unsigned int > gelassen...;) :-) dachte ich mir schon. Also der klassische Fall aus der FAQ
1 | Warum kriege ich bei |
2 | |
3 | double i = 5 / 8; |
4 | |
5 | keine Nachkommastellen? |
Gleiche Ursache :-) Weil es für den Compiler irrelevant ist, was mit einem Ergebnis gemacht wird. Die Art und Weise wie eine bestimmte Operation durchgeführt wird (im Zahlenraum welchen Datentyps gerechnet wird) hängt einzig und alleine von den beteiligten Operanden ab und von sonst gar nichts. temp * 125 wird, so wie es da steht, im Zahlenraum unsigned int gerechnet. Selbst wenn du das Ergebnis in 128 Byte Fliesskommaformat-Variablen mit hyperbolischem Exponenten in trinärer Darstellung und gelbem Kommapunkt abspeicherst: Diese Multiplikation findet in unsigned int statt und in sonst nichts anderem. Da fährt die Eisenbahn drüber. Erst mit dem Ergebnis wird dann wieder etwas gemacht. Aber dann ist es zu spät sich über Overflows den Kopf zu zerbrechen. Willst du das nicht, dann muss mindestens einer der beiden, entweder temp oder 125 oder alle beide, zu einem unsigned long werden. Nur die beiden Teilnehmer an der Operation sind relevant und steuern das.
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.