Hallo Leute,
Ich versuche mich grad in C und die Entwicklung von Programmen für den
ATMega328P einzuarbeiten. Jetzt habe ich ein kleines Problem mit dem
Casten von großen Integer Zahlen bzw. mit dem Rechnen großer Integer
Zahlen. Konkret geht es um diese beiden Zeilen:
1
uint16_tpwm1=((Hall1-Min1)/(Max1-Min1))*1023;
1
uint8_tpwm2=((Hall2-Max2)/(Min2-Max2))*255;
Hall1 und Hall2 sind Analogwerte, die vom Hallsensor mit der ADC_R
Funktion eingelesen werden. Diese Werte sollen dann mit obiger Formel in
einen neuen Wertebereich berechnet werden. Hab das so von der Arduino
IDE übernommen, da gibts nämlich die MAP Funktion, die im Grunde
genommen das Gleiche ist.
Ich habe nun das Problem, dass bei ((Hall1-Min1)/(Max1-Min1))*1023 das
Ergebnis 0 wird, weil (Hall1-Min1)/(Max1-Min1) gleich 0, irgendwas ist.
Wenn ich es anders schreiben, so z. B. (Hall1-Min1)*1023)/(Max1-Min1)
wird das Zwischenergebnis (Hall1-Min1)*1023) größer als 16 Bit. Um
später aber meinen PWM Dutycycle einstellen zu können brauche ich eine
16Bit Zahl.
Ich habe auch schon probiert das Ergebnis in einer 32-Bit variable zu
speichern. Wenn ich diese dann allerdings an OCR1A übergebe, gibt mir
meine Uart Schnittstelle keine richtigen Werte aus und meine PWM
Dutycycle passt auch nicht. Jetzt müsste ich vermutlich mit einem Cast
arbeiten oder? Ich habe das schon probiert und im Internet etwas
recherchiert, habe aber noch keine zufriedenstellende Lösung gefunden.
Kann mir da jemand weiterhelfen? Wahrscheinlich ist das Ganze einfach zu
lösen, aber ich stehe schon mehrere Stunden auf dem Schlauch und komme
nicht weiter...
Wäre sehr dankbar, wenn jemand den ein oder anderen Ratschlag dazu
hätte.
1
while(1)
2
{
3
ADC1=ADC_R(1);//read Hall1 Signal
4
5
charBuffer[20];
6
sprintf(Buffer,"ADC1:%i",ADC1);//only for tests
7
uart_puts(Buffer);
8
_delay_ms(2000);//delete for normal operation
9
uart_puts("\r\n");
10
11
12
uint16_tpwm1=((ADC1-Min1)/(Max1-Min1))*1023;//change of value range
13
if(pwm1>1023){pwm1=1023;}//only for testing
14
elseif(pwm1<0){pwm1=0;}
15
OCR1A=pwm1;//set duty cycle
16
17
sprintf(Buffer,"PWM1:%i",pwm1);
18
uart_puts(Buffer);
19
_delay_ms(3000);//delete for normal operation
20
uart_puts("\r\n");
21
22
23
ADC2=ADC_R(0);//read Hall2 Signal
24
25
sprintf(Buffer,"ADC2:%i",ADC2);
26
uart_puts(Buffer);
27
_delay_ms(2000);//delete for normal operation
28
uart_puts("\r\n");
29
30
uint8_tpwm2=((ADC2-Max2)/(Min2-Max2))*255;//change of value range
Das ist ja auch von Anfang an eine Integer-Berechnung.
Ich meine, du mußt bei der Multiplikation mit den Konstanten "*1023.0"
bzw. "*255.0" schreiben. Dann sollte der Compiler daraus eine
FP-Berechnung machen.
Wie zeitkritisch sind denn die Berechnungen?
Ich habe mir angewöhnt, Analogwerte in float zu rechnen.
Ist einfach viel bequemer, als sich ständig mit Überläufen und
Rundungsfehlern ins Knie zu schießen.
basti schrieb:> Wenn ich es anders schreiben, so z. B. (Hall1-Min1)*1023)/(Max1-Min1)> wird das Zwischenergebnis (Hall1-Min1)*1023) größer als 16 Bit. Um> später aber meinen PWM Dutycycle einstellen zu können brauche ich eine> 16Bit Zahl.
Nur der Vollständigkeit halber, falls es doch mal zeitkritischr wird:
Rechne das Zwischenergebnis in 32 bit, und das Endergebnis der Division
castest du dann wieder nach 16 bit.
Oliver
Wenn Du das Problem zerlegst, wirst Du feststellen, dass bei der
Subtraktion nichts passieren kann.
Natürlich nur wenn beide Werte brav im positiven Bereich rumhängen.
Die Sache mit der Multiplikation hast Du aber falsch angepackt.
Hier gilt: Möglichst früh multiplizieren!
Das kann allerdings bedeuten, das der jeweilige Bereich uint16_t bzw.
uint8_t, VOR der Division nicht, ausreicht.
Also besser:
((Hall1-Min1)*1023)/((Max1-Min1)*1023);
statt:
((Hall1-Min1)/(Max1-Min1))*1023;
Muss aber nicht sein.
Matthias L. schrieb:> uint16_t pwm1 = ((Hall1-Min1)/(Max1-Min1))*1023;> uint8_t pwm2 = ((Hall2-Max2)/(Min2-Max2))*255;>> Das muss 1024 bzw 256 sein.
Ich habe noch keinen 10- bzw. 8-Bit Wandler gesehen, der 1024 bzw. 256
ausgeben kann.
Nehmen wir ganz einfach mal an:
Max1 = 1023; Min1 = 0; Hall1 = 1023
Warum sollt da 1024 raus kommen?
Wolfgang schrieb:> Ich habe noch keinen 10- bzw. 8-Bit Wandler gesehen, der 1024 bzw. 256> ausgeben kann.
Natürlich nicht. Aber das sind die Endwerte. Ein 8bit Wandler skaliert
die Wert 0..255, was 256 mögliche Werte ergibt. Deshalb.
Wenn also das Ausgangssignal 157 ist, heisst das, das Analogsignal ist
grösser 157/256, aber kleiner als 158/256-stel der Referenz.
Peter D. schrieb:> Ich habe mir angewöhnt, Analogwerte in float zu rechnen.
und ich bleibe lieber bei int mit mV(milliV) dV(deziV) oder cV(centiV)
Nötige Konstanten kann man vorher rechnen und einsetzen, dezimal Punkte
oder Komma kann man hinterher locker als Char einfügen ohne int zu
verlassen. Die float Lib muss auch nicht eingebunden werden!
> Ich habe mir angewöhnt, Analogwerte in float zu rechnen.
Wo um alles in der Welt gibt es bei einem ATMega Analogwerte?
Sollte es die wirklich geben, so hast Du diese erst - meist recht
aufwändig - aus Integerzahlen errechnet. So verbrauchen diese unnötig
viel Platz und Rechenzeit.
Joachim B. schrieb:> Die float Lib muss auch nicht eingebunden werden!
Die ist recht schlank (~1kB), kostet also kaum was.
Float printf/scanf Support ist etwas größer.
Die float Division ist sogar schneller als die für 32 Bit, da float ja
nur 24 Bit ausrechnen muß.
Sebastian S. schrieb:> Wo um alles in der Welt gibt es bei einem ATMega Analogwerte?
Ich hab z.B. eine Strommessung über mehrere Dekaden. Ein 16Bit-ADC wird
in 4 Bereiche umgeschaltet. Für jeden Bereich erfolgt eine Kalibration
mit float-Werten (Gain, Offset) im EEPROM. Mit Integer wäre sowas ein
Albtraum.
Eine eigene Frage?
Dieser Thread ist über 5 Jahre alt.
Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.