C Kommastellen verwerfen

  • 0,5 kannst Du nicht in einem int speichern, da gehen nur ganze Zahlen.
    Sinn macht das ganze Thema ja nur, wenn Du mit Fließkommazahlen rechnest, die Du dann hinterher auf Ganzzahlen rundest, so war ja die Ausgangsfrage.

    Also evtl. so:

    Quellcode

    1. float val = 0.5;
    2. int16_t valganz;
    3. valganz = val / 4;


    Jetzt wird 0.5 / 4 gerechnet, ergibt also 0.125. Das kannst Du nun mit einer der bereits genannten Methoden je nach Anforderung runden. Eine einfache Zuweisung an einen int (wie hier) schneidet einfach die Nachkommastellen ab, ergibt also in diesem Fall wieder 0.
    5.0 / 4 als anderes Beispiel würde 1.25 ergeben, auf int zugewiesen ergibt 1 usw...
    AC/64 - C64 Umbau auf 9V Wechselspannung: Thread | Homepage
    Jocopod - Joystick to Controlport Dongle: Thread | Homepage
  • ähm, ich glaub ich hab den Fehler gefunden.
    Hab mir da irgendwie selbst ein Ei gelegt.

    Jedenfalls ist die Zählung noch nicht so wie ich gerne hätte.
    Vielleicht hat dazu jemand einen Tipp.

    Diese Funktion hier zählt mir beim erreichen des Wertes 9999 wieder von vorne beginnend weiter, also 0:

    Quellcode

    1. void Print(uint16_t num)
    2. {
    3. uint8_t i=0;
    4. uint8_t j;
    5. if (num>9999) return;
    6. while (num)
    7. {
    8. digits[i]=num%10;
    9. i++;
    10. num=num/10;
    11. }
    12. for (j=i;j<4;j++) digits[j]=0;
    13. }
    14. Jetzt hätte ich aber gerne das beim Rückwärtszählen wieder von 9999 an gezählt wird.
    15. Also beim erreichen von 0 soll als nächstes 9999 erscheinen und von dort rückwärts weiter gezählt werden.
    16. Läßt sich diese Funktion diesbezüglich entsprechend anpassen? Wenn ja, wie muß das aussehen?
    Alles anzeigen

    -= Neu in meiner Sammlung =-
    -= Modding =-


    Nichts hält länger als ein Provisorium
  • Diese Funktion "zählt" doch gar nichts. Die wandelt die übergebenen Zahl "num", die max. 9999 groß sein darf in BCD-Code im Array digits[] um (das wohl global definiert ist). Praktisch um die Zahl irgendwie anzuzeigen, addiere z.B. ziffernweise jeweils 0x30 und Du hast ASCII-Zeichen.

    EDIT:
    Wenn Du also z.B. Print(1234) aufrufst, steht nachher in digits:
    digits[0] = 4;
    digits[1] = 3;
    digits[2] = 2;
    digits[3] = 1;
    Print zerlegt also eine Zahl in ihre dezimalen Ziffern.

    Wenn Du irgendwas hoch oder runterzählen willst, dann musst Du das außerhalb von Print() machen. Was genau soll denn das Programm machen?
    AC/64 - C64 Umbau auf 9V Wechselspannung: Thread | Homepage
    Jocopod - Joystick to Controlport Dongle: Thread | Homepage
  • Also:

    C-Quellcode

    1. #include <avr/io.h>
    2. #include <avr/interrupt.h>
    3. // CPU: ATtiny2313-20PU 8MHz
    4. // =======================================================================
    5. // Ein-/Ausgänge
    6. // -----------------------------------------------------------------------
    7. #define MOTOR_PORT PORTA
    8. #define MOTOR_DDR DDRA
    9. #define SEVEN_SEGMENT_PORT PORTB
    10. #define SEVEN_SEGMENT_DDR DDRB
    11. #define FOTODIODE_PORT PORTD
    12. #define FOTODIODE_DDR DDRD
    13. #define TASTER_PIN PIND
    14. #define FOTODIODE_A (PIND & (1 << PD6))
    15. #define FOTODIODE_B (PIND & (1 << PD5))
    16. #define TASTER (1 << PD4)
    17. #define MOTOR (1 << PA0)
    18. #define DP_DIGIT 7 // Dezimalpunkt an Bit 7
    19. //Schaltmuster
    20. //--------------------------------------------------
    21. #define ALL_LOW 0x00 // 0000 0000
    22. #define ALL_HI 0xFF // 1111 1111
    23. #define FOTO_EINGANG 0x0F // 0000 1111
    24. #define FOTO_PULLUP 0xF0 // 1111 0000
    25. #define MOTOR_AUSGANG 0x07 // 0000 0111
    26. // .gfedcba
    27. uint8_t digitPattern[] = {
    28. 0b00111111,
    29. 0b00000110,
    30. 0b01011011,
    31. 0b01001111,
    32. 0b01100110,
    33. 0b01101101,
    34. 0b01111101,
    35. 0b00000111,
    36. 0b01111111,
    37. 0b01101111,
    38. };
    39. // Tastendruck
    40. // -----------------------------------------------------------------------
    41. #define REPEAT_MASK (TASTER)
    42. #define REPEAT_START 250
    43. #define REPEAT_NEXT 100
    44. volatile uint8_t key_state;
    45. volatile uint8_t key_press;
    46. volatile uint8_t key_rpt;
    47. volatile uint8_t digits[4];
    48. volatile int8_t enc_delta;
    49. volatile int8_t dp_ein = 0;
    50. volatile int8_t motor_aus = 0;
    51. static int8_t last;
    52. // Timer-Routine für 7-Segment
    53. // -----------------------------------------------------------------------
    54. ISR(TIMER0_OVF_vect)
    55. {
    56. static uint8_t i=0;
    57. FOTODIODE_PORT &= FOTO_PULLUP;
    58. if (i==3)
    59. {
    60. i=0;
    61. }
    62. else
    63. {
    64. i++;
    65. }
    66. SEVEN_SEGMENT_PORT = digitPattern[ digits[i] ];
    67. FOTODIODE_PORT |= (1<<i);
    68. if ((i == 0) & (dp_ein == 1))
    69. SEVEN_SEGMENT_PORT |= (1 << DP_DIGIT);
    70. }
    71. // Timer-Routine für IR-Dioden und Tastendruck
    72. // -----------------------------------------------------------------------
    73. ISR(TIMER0_COMPA_vect)
    74. {
    75. int8_t neu, diff;
    76. neu = 0;
    77. if (FOTODIODE_A)
    78. neu = 3;
    79. if (FOTODIODE_B)
    80. neu ^= 1;
    81. diff = last - neu;
    82. if (diff & 1)
    83. {
    84. last = neu;
    85. enc_delta += (diff & 2) - 1;
    86. }
    87. uint8_t i;
    88. static uint8_t ct0, ct1, rpt;
    89. i = key_state ^ ~TASTER_PIN;
    90. ct0 = ~(ct0 & i);
    91. ct1 = ct0 ^ (ct1 & i);
    92. i &= ct0 & ct1;
    93. key_state ^= i;
    94. key_press |= key_state & i;
    95. if ((key_state & REPEAT_MASK) == 0)
    96. rpt = REPEAT_START;
    97. if (--rpt == 0)
    98. {
    99. rpt = REPEAT_NEXT;
    100. key_rpt |= key_state & REPEAT_MASK;
    101. }
    102. }
    103. // IR-Dioden auswerten
    104. // -----------------------------------------------------------------------
    105. int8_t encode_read(void)
    106. {
    107. int8_t val;
    108. cli();
    109. val = enc_delta;
    110. enc_delta = val & 1;
    111. sei();
    112. return val >> 1;
    113. }
    114. // 7-Segment Anzeige darstellen
    115. // -----------------------------------------------------------------------
    116. void Print(uint16_t num)
    117. {
    118. uint8_t i=0;
    119. uint8_t j;
    120. if (num>9999) return;
    121. while (num)
    122. {
    123. digits[i]=num%10;
    124. i++;
    125. num=num/10;
    126. }
    127. for (j=i;j<4;j++) digits[j]=0;
    128. }
    129. // Tastendruck auswerten
    130. // -----------------------------------------------------------------------
    131. uint8_t get_key_press(uint8_t key_mask)
    132. {
    133. cli();
    134. key_mask &= key_press;
    135. key_press ^= key_mask;
    136. sei();
    137. return key_mask;
    138. }
    139. uint8_t get_key_rpt(uint8_t key_mask)
    140. {
    141. cli();
    142. key_mask &= key_rpt;
    143. key_rpt ^= key_mask;
    144. sei();
    145. return key_mask;
    146. }
    147. uint8_t get_key_short(uint8_t key_mask)
    148. {
    149. cli();
    150. return get_key_press(~key_state & key_mask);
    151. }
    152. uint8_t get_key_long(uint8_t key_mask)
    153. {
    154. return get_key_press(get_key_rpt(key_mask));
    155. }
    156. // Encoder initialisieren
    157. // -----------------------------------------------------------------------
    158. void encode_init(void)
    159. {
    160. int8_t neu;
    161. neu = 0;
    162. if (FOTODIODE_A)
    163. neu = 3;
    164. if (FOTODIODE_B)
    165. neu ^= 1;
    166. last = neu;
    167. enc_delta = 0;
    168. }
    169. // Ein-/Ausänge zuweisen
    170. // -----------------------------------------------------------------------
    171. void ioinit(void)
    172. {
    173. OCR0A = (uint8_t)(F_CPU / 64.0 * 1e-3 - 0.5);
    174. TCCR0B |= (1<<CS01 | 1<<CS00);
    175. TIMSK |= (1<<OCIE0A | 1<<TOIE0);
    176. TCNT0 = 0;
    177. FOTODIODE_DDR &= ~FOTO_EINGANG; // Fotodioden als Eingang
    178. FOTODIODE_PORT |= FOTO_PULLUP; // Fotodioden Pull-Up aktivieren
    179. SEVEN_SEGMENT_DDR = ALL_HI; // 7-Segment Anzeige als Ausgang
    180. SEVEN_SEGMENT_PORT = ALL_LOW; // 7-Segment Pin's auf LOW
    181. MOTOR_DDR = MOTOR_AUSGANG; // Motorsteuerung als Ausgang
    182. MOTOR_PORT = ALL_LOW; // Motorsteuerung Pins's auf LOW
    183. }
    184. // Hauptprogramm
    185. // -----------------------------------------------------------------------
    186. int main(void)
    187. {
    188. int16_t valganz = 0;
    189. int16_t val = 0;
    190. int16_t zahl_speicher = 0;
    191. int8_t wert_ungleich = 0;
    192. ioinit();
    193. encode_init();
    194. sei();
    195. for (;;)
    196. {
    197. valganz += encode_read();
    198. val = valganz / 4;
    199. if (get_key_short(TASTER))
    200. {
    201. valganz = 0;
    202. MOTOR_PORT &= ~MOTOR;
    203. }
    204. if (get_key_long(TASTER))
    205. {
    206. if (dp_ein == 0)
    207. {
    208. if (val |= 0)
    209. {
    210. zahl_speicher = val;
    211. dp_ein = 1;
    212. }
    213. }
    214. else
    215. {
    216. zahl_speicher = 0;
    217. dp_ein = 0;
    218. wert_ungleich = 0;
    219. }
    220. }
    221. if ((dp_ein == 1) & (val != zahl_speicher) & (wert_ungleich == 0))
    222. wert_ungleich = 1;
    223. if ((wert_ungleich == 1) & (val == zahl_speicher))
    224. MOTOR_PORT |= MOTOR;
    225. if (dp_ein == 0)
    226. MOTOR_PORT &= ~MOTOR;
    227. Print(val);
    228. }
    229. }
    Alles anzeigen


    Das ganze wird ja auf einer Segmentanzeige angezeigt.
    Und soll wie ein Zählwerk das Rückwerts zählt funktionieren. Das ja auch nicht bei 0 stoppt sondern dann wieder bei 9999 beginnt rückwerts zu zählen.

    -= Neu in meiner Sammlung =-
    -= Modding =-


    Nichts hält länger als ein Provisorium
  • Aha, langsam lichtet sich der Nebel...
    Es geht um ein Zählwerk mit einem Encoder. Der aktuelle Zählerstand steht in valganz und zur Anzeige teilst du den Wert vorher durch 4 (in val). Korrekt?
    Und was geht jetzt und was geht nicht?
    Mal angenommen die Encoder-Auswertung funktioniert (was ich momentan nicht überblicke), dann wird valganz beim Runterzählen irgendwann negativ. Das könnte Dein Problem sein.

    Du könntest folgendes versuchen:

    Quellcode

    1. for (;;)
    2. {
    3. valganz += encode_read();
    4. if (valganz < 0)
    5. valganz += 10000;
    6. val = valganz / 4;

    oder so ähnlich.
    AC/64 - C64 Umbau auf 9V Wechselspannung: Thread | Homepage
    Jocopod - Joystick to Controlport Dongle: Thread | Homepage
  • Was lvr sagt - wobei ich den Test und das "+= 10000" allerdings auf val, nicht auf valganz anwenden würde.

    Sonstiges:

    In C ist das Ergebnis der Operation "Negativer Wert nach rechts geshiftet" undefiniert. Das wird hier vermutlich nicht das Problem sein, kann einem aber den Tag verderben. [AxelStoll] Muss man nur wissen [/AxelStoll].

    In Zeile 245 steht "if (val |= 0)" - ich weiß nicht, was Du da wolltest, aber das ist es vermutlich nicht.

    EDIT: Den Unterschied zwischen "&" und "&&" bzw. "|" und "||" kennst Du? Kommt in diesem Fall aufs Gleiche raus, aber normalerweise nimmt man in Fällen wie hier die logischen Operatoren (also die doppelten).
    Ach ja, und Du solltest an Deinen Variablennamen arbeiten, und mehr Kommentare schreiben. ;)
    Yes, I'm the guy responsible for the ACME cross assembler
  • Ja, genau.
    Die Auswertung des Encoders funktioniert problemlos. In meinem Falle ist es jedoch kein Encoder der die Werte liefert sondern zwei Lichtschranken die mittels einer Scheibe ... Mehr dazu kommt noch.

    Dein Tipp ist schon der richtige Weg wie mir scheint, aaaaber, er zählt seltsamerweise nach 0 von 2500 rückwerts.

    Also:
    3
    2
    1
    0
    2500
    2499
    ...


    Ähm, Logo: 9999 / 4

    -= Neu in meiner Sammlung =-
    -= Modding =-


    Nichts hält länger als ein Provisorium
  • Zirias schrieb:

    Sind mir zu viele arithmetische Operationen, nur dafür, das möglichst tricky aussehen zu lassen :p


    Regel 2 der Real Programmers:

    - Real programmers don't comment their code. If it was hard to write, it should be hard to read.

    Der Code kann nicht der selbe sein .... man müsste sich aber mal den output anschauen um zu sehen was schneller ist ... bne/beq bzw. jmp sind recht lahm wenn ich mich da recht erinnere ...
  • ja, bitweise oder logische Verknüpfung.

    if (val |= 0) war genau das Problem das ich zuerst hatte.
    Im Falle eines Nullwertes sollte bei einem langem Tastendruck nichts passieren.

    Der Lange Tastendruck speichert den aktuellen Zählerstand in eine Variable. Was bei einem Nullwert nicht viel Sinn hätte.
    Das dürfte jetzt auch so funktioniren.

    Jedoch läuft die Zählung nun nicht korrekt.
    Das Zählen beim Vorspulen ist langsamer als beim zurückspulen.

    Hängt wohl jetzt mit dem

    if (val < 0)
    val += 10000;


    zusammen.


    Wieder falsch. Es liegt an der Datasette.
    Die dürfte schneller spulen wenn das Band auf einer Rolle weniger ist. In dem Falle beim zurückspulen. Also ich denke mal das es daran liegt.

    Zählen tut er korrekt.

    -= Neu in meiner Sammlung =-
    -= Modding =-


    Nichts hält länger als ein Provisorium
  • Mac Bacon schrieb:

    wobei ich den Test und das "+= 10000" allerdings auf val, nicht auf valganz anwenden würde.

    Oh, natürlich. :drunk:

    DerSchatten schrieb:

    In meinem Falle ist es jedoch kein Encoder der die Werte liefert sondern zwei Lichtschranken die mittels einer Scheibe ...

    Also ein selbstgebauter Encoder. ;)
    AC/64 - C64 Umbau auf 9V Wechselspannung: Thread | Homepage
    Jocopod - Joystick to Controlport Dongle: Thread | Homepage
  • Richtig. Sogar ein Selbstdrehender :)

    Ein kleiner Schönheitsfehler ist mir nun jedoch noch aufgefallen:
    Wen von 9999 nach 0 gezählt wird bleibt die 0 doppelt so lange stehen wie wenn ein wechsel von 0 auf 1 auf 2 auf 3 ... erfolgt.

    Beim zurückzählen wird's wohl genauso seind enke ich. Kann ich aber nicht 100% sagen da beim spulen natürlich das ganze recht schnell läuft.
    Bei PLAY merkt man es aber.

    Das liegt vermutlich daran weil -1 / 4 ebenfalls noch 0 ergibt.

    -= Neu in meiner Sammlung =-
    -= Modding =-


    Nichts hält länger als ein Provisorium
  • Ich glaub es ist doch einfacher auf valganz zu testen, sonst springt zwar val beim Runterzählen über 0 wieder auf 9999, aber valganz bleibt ja negativ.
    Und wo ist eigentlich der Test auf positiven Überlauf 9999->0?

    Quellcode

    1. valganz += encode_read();
    2. if (valganz < 0)
    3. valganz += 40000;
    4. else if (valganz > 39999)
    5. valganz = 0;
    6. val = valganz / 4;

    Oder hab ich jetzt wieder nen Denkfehler drin?
    AC/64 - C64 Umbau auf 9V Wechselspannung: Thread | Homepage
    Jocopod - Joystick to Controlport Dongle: Thread | Homepage
  • Eine Prüfung auf Überlauf gibt's in dem Sinne nicht.

    Das ganze wird ja nur durch diese Funktion hier begrenzt, soweit ich jetzt verstanden habe:

    Quellcode

    1. void print(uint16_t num)
    2. {
    3. uint8_t i=0;
    4. uint8_t j;
    5. if (num>9999) return;
    6. while (num)
    7. {
    8. digits[i]=num%10;
    9. i++;
    10. num=num/10;
    11. }
    12. for (j=i;j<4;j++) digits[j]=0;
    13. }
    Alles anzeigen


    Meinst du in deinem Beispiel wirklich 40000 und 39999 ?

    Da meckert der Compiler:
    ../main.c:236: warning: comparison is always false due to limited range of data type

    Und das Ergebnis ist ganz verstümmelt. Vor null kommt 3615.

    -= Neu in meiner Sammlung =-
    -= Modding =-


    Nichts hält länger als ein Provisorium
  • Die Print() Funktion prüft nur auf > 9999 und bricht ab, wenn der Wert größer ist. Es wird dann einfach nichts angezeigt, der eigentliche Zähler macht aber weiter.

    Statt der > 39999 kannst Du auch auf >= 40000 prüfen, wenn Dir das besser gefällt, kommt aber auf's gleiche raus.

    OK, die Warnung liegt am Datentyp int16_t, der geht von -32768 bis +32767, kann also nie > 39999 werden. Der Compiler hat Recht. :)
    Entweder auf int32_t ändern oder auf einen unsigned int, also uint16_t, der geht dann von 0 bis 65535. Da muss man aber die Abfrage auf Unterlauf ändern. Probiers zuerst mal mit int32_t.
    AC/64 - C64 Umbau auf 9V Wechselspannung: Thread | Homepage
    Jocopod - Joystick to Controlport Dongle: Thread | Homepage