Go Down

Topic: Problem: Integer division (Read 609 times) previous topic - next topic

findusdwarf

Hi,
ich bin ein absoluter Arduino-Neuling und bin gerade dabei, mich da rein zu werkeln.
Normalerweise programmiere ich in vb.net und bin da nicht gerade Anfänger. Aber C macht mich wahnsinnig ;)

Ich habe da ein eigentlich ganz einfaches Problem: Ich möchte z.B. den gemessenen Wert eines Potis in Stufen von 0...10 umrechnen (wahlweise auch 0...255 etc.).
Der Poti hat max. 1023 Ohm, eingestellt sind z.B. 345 Ohm.
Rechnerisch heißt das

Stufe = MaxStufe / MaxWiderstand * gemessenerWiederstand, in diesem Beispiel wäre das

Stufe = 10 / 1023 * 345 = 3,372, als ganze Zahl also gerundet 3.

in VB ist das mit der Konvertierungsfunktion cint() kein Problem.

Aber wie geht das in C?
Ich bekomme als Ergebnis immer nur 0 oder 0.00...
Wie funktioniert die Konvertierungsfunktion int?
Ich hab hier so seltsamen Code gesehen wie
Code: [Select]
a = (int)b;das kommt mir aber doch sehr spanisch vor, normalerweise schreibt man ja wohl die Parameter in Klamern und nicht die Funktion. Funktioniert bei mir auch nicht, obwohl der Compiler keinen Fehler anzeigt. Aber
Code: [Select]
a = int(b);funktioniert in dem Zusammenhang auch nicht.
Kann mich bitte mal jemand aufklären?

k

Das Grundgerüst ist so:
Code: [Select]

int sensorPin = A0;    // select the input pin for the potentiometer
int sensorValue = 0;  // variable to store the value coming from the sensor (Poti)

int val;    // variable to read the value from the analog pin
int Pause = 1000;

int PotiMax = 1023;
int maxSteps = 10;
int PotiStep = 0; // Startwert
int Step;
float fl; // zum dividieren

void setup() {       

  Serial.begin(9600);
  Serial.println("* * * * WechselLED5 x* * * ");
}

// the loop routine runs over and over again forever:
void loop() {
   sensorValue = analogRead(sensorPin);   
  Serial.print("sensorValue = ");
  Serial.println(sensorValue);
   
  // Versuch, den Poti-Widerstand in einen Wert 0..5 umzurechnen
  // Max. Potiwert ist 1023
 
// so, und hier geht's nicht mehr weiter, ich bekomme nur Nullen....
  fl = maxSteps / PotiMax * sensorValue;
  Step = maxSteps / PotiMax * sensorValue;
  Serial.print("fl = ");
  Serial.println(fl);
  Serial.print("Step = ");
  Serial.println(Step);
}

Addi

Ersetze die Variable für das Ergebniss von int zu float(Gleitkommazahl).
(int)b ist eine Konvertierung einer Variablen zu einer Interger-Zahl.

Addi
/ \    _|  _| o
 /--\ (_| (_| |

markbee

Hilft Dir evtl. die map()-Funktion weiter?

http://arduino.cc/en/Reference/Map

Re-maps a number from one range to another. That is, a value of fromLow would get mapped to toLow, a value of fromHigh to toHigh, values in-between to values in-between, etc.

Bsp.: y = map(x, 1, 50, 50, 1);
XBee blog: http://lookmanowire.blogspot.com/

mkl0815

Das da 0 oder 0.0 rauskommt ist aber logisch.

Sehen wir mal was passiert bei Deinem Beispiel:

Quote
(float) f1 =  (int)10 / (int)1023 * (int)345

Das (int) davor habe ich mal explizit hingeschrieben, um zu zeigen was der Compiler verwendet. Das macht er deshalb, weil Deine Variablen als solche deklariert sind.

Damit passiert in beiden Fällen (Ergbnis float und Ergebnis integer) immer eine Integer-Division "10/1023" und die ist IMMER "0". 0 * Irgendwas, bleibt leider 0, daher das Ergebnis. Beim "f1" wird erst am Ende der Rechnung ein Float draus.

Du hast nun 2 Möglichkeiten.

Quote
fl = ((float)maxSteps / (float)PotiMax) * sensorValue;
 Step = (long)(maxSteps * sensorValue) / PotiMax;


Einen Datentyp (nicht Funktion) in Klammern vor eine Variable oder einen Ausdruck zu schreiben ist in C korrekt. Man nenn das "Casten" und zwingt damit den Compiler einen bestimmten Datentyp zu verwenden, auch wenn die Variable eigentlich einen anderen Typ hat. Im o.g. Beispiel zwingt man schon bei der Rechnung den Compiler einen anderen Datentyp zu verwenden.

Code: [Select]
void setup() {

Serial.begin(9600);
 int maxSteps = 10;
 int PotiMax = 1023;
 int sensorValue = 345;
 int Step = 0;
 float fl = 0.0;

 fl = maxSteps / PotiMax * sensorValue;
 Step = maxSteps / PotiMax * sensorValue;
 Serial.print("fl = ");
 Serial.println(fl);
 Serial.print("Step = ");
 Serial.println(Step);

  fl = ((float)maxSteps / (float)PotiMax) * sensorValue;
 Step = (long)(maxSteps * sensorValue) / PotiMax;

 Serial.print("fl = ");
 Serial.println(fl);
 Serial.print("Step = ");
 Serial.println(Step);

}

void loop() {

 
}

findusdwarf

Na das nenn ich Service!!  :)

@markbee: Danke, map ist genau das, was ich hier erreichen wollte!

@mkl0815: Danke, jetzt verstehe ich , wie die Konvertierung funktioniert. Für diese Aufgabenstellung ist es jetzt zwar überflüssig geworden- dank map :) - aber hilfreich für die Zukunft.

Aber: bei beiden Lösungen (map-Funktion oder Berechnung per Formel) wird das Ergebnis immer abgerundet!
Wenn sich hier z.B. ein float-Wert von 2.45 ergibt, ist das Ergebnis 2 (ist klar), aber bei einem float-Wert von 2.54 ist das int-Ergebnis immer noch 2.
Da muss ich mir noch was überlegen, damit ab 2.5 nach 3 aufgerundet wird, wie das mathematisch üblich ist.

Nebenbei: Das Beispiel in der map-Hilfe http://arduino.cc/en/Reference/Map verwendet anscheinend meinen Poti mit 1023 Ohm. Woher haben die das gewusst??  XD

uwefed

Auch die Reihenfolge der Berechnung kann das Problem lösen. Zuerst multiplizieren und dann dividieren. Dabei ist aufzupassen, daß der Zwischenwert der Multiplikation kleiner als der max. darstellbare Wert einer vorzeichenbehafteten Int-Zahl ist (32678). bei 10*1023 ist das kein Problem. Zum richtig Runden (0,499 -> 0; 0,500->1) mußt Du noch den halben Wert zwischen 2 Stufen zum gelesenen Potiwert dazuzählen. In diesem Fall wäre das 1023/10/2 = 51
Code: [Select]
Potistellung =  (10 * (ADC-Widerstandwert+51)) / 1023

Grüße Uwe

uwefed


Wenn sich hier z.B. ein float-Wert von 2.45 ergibt, ist das Ergebnis 2 (ist klar), aber bei einem float-Wert von 2.54 ist das int-Ergebnis immer noch 2.
Da muss ich mir noch was überlegen, damit ab 2.5 nach 3 aufgerundet wird, wie das mathematisch üblich ist.

Du mußt 0,5 dazuzählen bevor die Float-Zahl nach int wandelst.

Quote from: findusdwarf

Nebenbei: Das Beispiel in der map-Hilfe http://arduino.cc/en/Reference/Map verwendet anscheinend meinen Poti mit 1023 Ohm. Woher haben die das gewusst??  XD

Der verwendete Widerstandswert ist egal. Das Potentiometer wird zwischen Masse und Versorgungspannung geschaltet. Man mißt die Spannung am Schleifer des Potentiometers. Dei Spannung geht von 0V bis zur Versorgungspannung (ca 5V). Die Versorgungsspannung wird auch als Referenzwert genommen (entspricht dem Wert 1023). mit analogRead bekommst Du eine Zahl von 0 (0V) bis 1023 ( +Versorgungsspannung).

Grüße Uwe

findusdwarf

Danke auch für den Tipp, jetzt passt's.

Das mit den 1023.... ich dachte echt, das ist der max. Widerstand von meinem 1K-Poti^^
Wieder was gelernt.

Danke euch!

Gruß
Klaus

Go Up