Forum: Mikrocontroller und Digitale Elektronik Division fehlerhaft


von Gustav (Gast)


Lesenswert?

Hallo zusammen,
wenn ich den Rückgabewert der Funktion "ReadChannel" durch 2 dividiere 
bekomme ich einen deutlich größeren Wert als den ursprünglichen 
return-Wert. Wenn ich aber aber z.B. einfach schreibe "return 95;" dann 
funktioniert die anschließende Division durch 2.
Meine Frage ist also warum die Division nur in dem zweiten Fall 
funktioniert.
Gruß Gustav

1
uint16_t ReadChannel(uint8_t mux)
2
{
3
  uint8_t i;
4
  uint16_t result = 0;         
5
                               
6
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS0);    
7
                   
8
  ADMUX = mux;                      
9
  ADMUX |= (1<<REFS1);
10
11
   
12
  ADCSRA |= (1<<ADSC);              
13
  while ( ADCSRA & (1<<ADSC) );   
14
15
  
16
  for(i=0;i<5;i++)
17
  {
18
   ADCSRA |= (1<<ADSC);            
19
   while ( ADCSRA & (1<<ADSC) );  
20
   result += ADCW;        
21
  }
22
  ADCSRA &= ~(1<<ADEN);             
23
24
  result /= 5;                   
25
26
  return result;
27
}
28
29
30
int main(){
31
    uint16_t res = ReadChannel(0) / 2 ; 
32
}

von Uwe (de0508)


Lesenswert?

Hallo,

wenn du eine Dummywandlung machst, musst Du auch den Wert auslesen:
1
ADCSRA |= (1<<ADSC);              
2
while ( ADCSRA & (1<<ADSC) );  
3
(void)ADCW;

Es mach auch keinen Sinn den ADC immer Ein und wieder Aus zuschalten!

Dann würde ich für die Mittelwertbildung die Anzahl der Messungen auf 
2^n; n E {1,..,64} setzen.

Bitte ändere das in deinem Programm und posten den Assembler output.

von Gustav (Gast)


Lesenswert?

Hier ist der Assembler Code (nach der Änderung) :
1
  .file  "slowRouter.c"
2
__SP_H__ = 0x3e
3
__SP_L__ = 0x3d
4
__SREG__ = 0x3f
5
__tmp_reg__ = 0
6
__zero_reg__ = 1
7
  .section  .text.ReadChannel,"ax",@progbits
8
.global  ReadChannel
9
  .type  ReadChannel, @function
10
ReadChannel:
11
/* prologue: function */
12
/* frame size = 0 */
13
/* stack size = 0 */
14
.L__stack_usage = 0
15
  ldi r25,lo8(-123)
16
  sts 122,r25
17
  sts 124,r24
18
  lds r24,124
19
  ori r24,lo8(-128)
20
  sts 124,r24
21
  lds r24,122
22
  ori r24,lo8(64)
23
  sts 122,r24
24
.L2:
25
  lds r24,122
26
  sbrc r24,6
27
  rjmp .L2
28
  lds r24,120
29
  lds r25,120+1
30
  ldi r20,lo8(4)
31
  ldi r24,0
32
  ldi r25,0
33
.L4:
34
  lds r18,122
35
  ori r18,lo8(64)
36
  sts 122,r18
37
.L3:
38
  lds r18,122
39
  sbrc r18,6
40
  rjmp .L3
41
  lds r18,120
42
  lds r19,120+1
43
  add r24,r18
44
  adc r25,r19
45
  subi r20,lo8(-(-1))
46
  brne .L4
47
  movw r18,r24
48
  lsr r19
49
  ror r18
50
  lsr r19
51
  ror r18
52
  movw r24,r18
53
  ret
54
  .size  ReadChannel, .-ReadChannel
55
  .section  .text.startup.main,"ax",@progbits
56
.global  main
57
  .type  main, @function
58
main:
59
/* prologue: function */
60
/* frame size = 0 */
61
/* stack size = 0 */
62
.L__stack_usage = 0
63
  ldi r24,lo8(-2)
64
  out 0x7,r24
65
  out 0x8,r24
66
  ldi r24,lo8(-52)
67
  out 0xa,r24
68
  ldi r24,lo8(-1)
69
  out 0xb,r24
70
  ldi r24,lo8(-3)
71
  out 0x4,r24
72
  ldi r24,lo8(-6)
73
  out 0x5,r24
74
.L9:
75
  ldi r24,0
76
  rcall ReadChannel
77
  rjmp .L9
78
  .size  main, .-main
79
  .ident  "GCC: (AVR_8_bit_GNU_Toolchain_3.4.2_939) 4.7.2"

von (prx) A. K. (prx)


Lesenswert?

Gustav schrieb:
> int main(){
>     uint16_t res = ReadChannel(0) / 2 ;
> }

Die Division ist überflüssig und wird wegoptimiert.
Wo also ist das Problem?

von Gustav (Gast)


Lesenswert?

Wieso soll das überflüssig sein ? Die Funktion ReadChanel liefert das 
Ergebnis 95 und das möchte ich noch durch 2 teilen.

von (prx) A. K. (prx)


Lesenswert?

Gustav schrieb:
> Wieso soll das überflüssig sein ? Die Funktion ReadChanel liefert das
> Ergebnis 95 und das möchte ich noch durch 2 teilen.

Aber da das Ergebnis nicht genutzt wird gibt es keinen Code dazu. 
Insofern ist auch völlig unklar, woraus du deine ursprüngliche Aussage 
beziehst.

: Bearbeitet durch User
von Uwe (de0508)


Lesenswert?

Hallo,

A. K. hat Recht, du müsstest die Variable |res| als volatile 
deklarieren, dann taucht sie auch im Assembler-Listing auf.

von Gustav (Gast)


Lesenswert?

A. K. schrieb:
> Aber da das Ergebnis nicht genutzt wird gibt es keinen Code dazu.
> Insofern ist auch völlig unklar, woraus du deine ursprüngliche Aussage
> beziehst.

Das Ergebnis wird eigentlich auf einer char - Variable gespeichert und 
dann auf einen Attiny 2313 geschickt und dort an einem LED Display 
ausgegeben. Da dies jedoch ziemlich viel Code ist habe ich es oben 
weggelassen. Das Prinzip der Übertragung funktioniert und eine "normale" 
Division, da ich folgendes probiert habe:
1
 return 95
  Funkioniert:  die Zahl 95 wird angezeigt
1
 return result
  Funkioniert: das Ergebnis (95) wird angezeigt
1
 return 95/2
  Funkioniert: das Ergebnis (47) wird angezeigt
1
 return result/2
  Fehler !! : das Ergebnis (127) wird angezeigt

von Kaj (Gast)


Lesenswert?

Meine fresse nochmal: poste doch einfach den gesamten code! Mit so 
einem mist hier ist das nichts als raten!
Und stell dir vor: man kann hier auch dateien anhängen, du must den code 
nicht per copy&paste einfügen...

Gustav schrieb:
> return result/2

Das ist was anderes als

>     uint16_t res = ReadChannel(0) / 2 ;

Für dich mag es das selbe sein, aber nicht zwingend für den 
compiler!
Also: häng den orginal code als datei an oder troll dich woanders!

von Tobias (Gast)


Lesenswert?

Moin Kaj,

ich hoffe Du hast ausgeschlafen. Warst gestern abend scheinbar sehr müde 
und reizbar!
Woher ich das weiß? Deine Ausdrucksweise ließ sehr zu wünschen übrig. 
Hoffe, Du bekommst auf Fragen, oder auch schlecht gestellte Fragen, 
nicht auch so patzige Antworten. Trink nen Kaffee, geh in Dich, und 
antworte nur, wenn Du dazu körperlich in der Verfassung bist...
Würdest Du auch mit jemandem so reden, wenn er vor Dir steht? Glaube ich 
kaum.

Besten Gruß

von (prx) A. K. (prx)


Lesenswert?

Die Form war etwas hart, inhaltlich hat er aber schon recht. Zu einer 
Frage über seltsames Verhalten eines Programms nur Code abzuliefern, der 
das beschriebene Problem überhaupt nicht haben kann, darf man schon als 
Verarschung betrachten.

Es ist schon ein sinnvolles Vorgehen, ein grösseres Programm so weit wie 
möglich zu reduzieren, bevor man es in ein Posting wirft. Nur sollte man 
sehr darauf achten und verifizieren, dass man dabei das Problem nicht 
ebenfalls rauskürzt.

: Bearbeitet durch User
von ... (Gast)


Lesenswert?

...ach beim Zusammenkürzen würde er den Fehler doch schon selbst finden 
und verstehen und der Post wär überflüssig....
Das ist aber auch nicht leicht in einem Forum. Beschäftigt man sich mit 
dem Fehler gut genug, um ihn zu dokumentieren, löst man ihn selbst, 
beschäftigt man sich nicht damit, kann das Forum nicht helfen :-O
Schmaler Grat!

von Paul Baumann (Gast)


Lesenswert?

@Gustav
Ich verstehe nichts von "C", vermute aber dennoch, daß einer der
beteiligten Datentypen zu "klein" für das Resultat ist. Deshalb kommt
es zu Überläufen und mistigen Ergebnissen.

Schön an "C" finde ich, daß der Kompiler einfach Sachen nach Gutdünken
wegoptimiert. Das ist einer der Gründe, warum ich diese Sprache meide.
:-(

MfG Paul

von (prx) A. K. (prx)


Lesenswert?

Paul Baumann schrieb:
> Ich verstehe nichts von "C", vermute aber dennoch, daß einer der
> beteiligten Datentypen zu "klein" für das Resultat ist. Deshalb kommt
> es zu Überläufen und mistigen Ergebnissen.

Der Überlauf bei ganzzahliger Division muss erst noch erfunden werden. 
Im obigen Programm - was immer das wert ist - werden 5 ADC-Werte 
aufsummiert. In 16 Bits ist das kein Problem.

> Schön an "C" finde ich, daß der Kompiler einfach Sachen nach Gutdünken
> wegoptimiert.

Assembler-Fan? Für jeden Compiler, ob C oder nicht, gilt, dass 
Optimierungen, die am Ergebnis nichts ändern, zulässig sind. Und dass 
der Compiler auch mal Fehler haben kann.

von Paul Baumann (Gast)


Lesenswert?

A.K. schrub:
>Der Überlauf bei ganzzahliger Division muss erst noch erfunden werden.

Zitat Gustav:

> return result/2

> Fehler !! : das Ergebnis (127) wird angezeigt

Ich würde sage, daß "Result" ein ganzzahliger Wert ist, den er sich aus
dem AD-Wandler-Register holt.

A.K. frug:
>Assembler-Fan?

Unter Anderem auch.

>Für jeden Compiler, ob C oder nicht, gilt, dass
>Optimierungen, die am Ergebnis nichts ändern, zulässig sind.

Das mag sein, trotzdem würde ich zum Testen des Programmes erzwingen
wollen, daß nichts optimiert wird. Wenn es dann fehlerlos läuft, von
mir aus...

>Und dass
>der Compiler auch mal Fehler haben kann.

Das ist wohl wahr.

MfG Paul

von Kaj (Gast)


Lesenswert?

Tobias schrieb:
> Moin Kaj,
Mahlzeit

Tobias schrieb:
> ich hoffe Du hast ausgeschlafen.
ja, hab ich, danke der nachfrage, und selbst?

Tobias schrieb:
> Deine Ausdrucksweise ließ sehr zu wünschen übrig.
Ich hab mich noch zurück gehalten

Tobias schrieb:
> Hoffe, Du bekommst auf Fragen, oder auch schlecht gestellte Fragen,
> nicht auch so patzige Antworten.
Hab ich hier im Forum mehr als einmal, und das jedes mal zu recht. Auf 
eine dumme und/oder unvollständige Frage folgt eine dumme und/oder 
patzige Antwort, und das in 99,99% aller Fälle volkommen zu recht.

Tobias schrieb:
> Würdest Du auch mit jemandem so reden, wenn er vor Dir steht?
Wenn er mir etwas zeigt, in dem ein "Problem" vorhanden sein soll, das 
gezeigte aber eine stark abgewandelte version, und damit mit dem orginal 
gar nicht zu vergleichen ist, dann ja.
Stell dir vor: da kommt jemand mit einem Fahrrad zu dir und erzählt dir 
was von Problem xy... in wirklich geht es aber um ein Motorrad mit 
Beiwagen...

Paul Baumann schrieb:
> daß der Kompiler einfach Sachen nach Gutdünken
> wegoptimiert
Für die Optimierung gibt es ja Regeln und eine ist die "as if" Regel. 
Die Funktion des Codes muss nach der optimierung so sein als ob der 
Compiler nichts getan hätte. Das der Code nicht immer das macht was der 
Programmiere sich denkt, dafür kann der Compiler ja nichts.
Ein kleines Beispiel:
1
void foo(uint8_t a)
2
{
3
  if( ( a >= 0 ) && ( a <= 5 ) )
4
  {
5
    // tu was
6
  }
7
}
Den Ausdruck "(a>=0)" darf und wird der Compiler (weg)optimieren weil 
die Variable "a" ein unsigned datentyp ist. "a" ist also immer größer 
oder gleich 0, dieser vergleich ist also immer wahr. Warum sollte der 
Compiler das also nicht optimieren, und daraus ein:
1
void foo(uint8_t a)
2
{
3
  if( a <= 5 )
4
  {
5
    // tu was
6
  }
7
}
machen?
Ich gebe zu, das ich auch nicht immer nachvollziehen kann, was der 
Compiler wann wie und wo wegoptimiert. Aber das zeigt mir jedesmal 
wieder auf's neue, das ich die Sprache wohl doch nicht so gut kann und 
auch den Compiler nicht so gut kenne, wie ich manchmal denke.

Paul Baumann schrieb:
> Das mag sein, trotzdem würde ich zum Testen des Programmes erzwingen
> wollen, daß nichts optimiert wird
Einfach die Optimierung ausschalten.

Grüße

von Gustav (Gast)


Lesenswert?

Hey vielen Dank für eure Antworten !
Also erst mal möchte ich mich Entschuldigen, dass ich die Sache wohl 
gestern Abend unnötig kompliziert gemacht habe in dem ich das Programm 
"vereinfacht" habe.

Paul Baumann schrieb:
> Ich verstehe nichts von "C", vermute aber dennoch, daß einer der
> beteiligten Datentypen zu "klein" für das Resultat ist.

Du hast natürlich Recht !! Das ist mir eben auch eingefallen ,deswegen 
wollte ich mich auch eigentlich noch mal melden um die Verwirrung zu 
beheben. Der Wert den die Funktion zurück gibt liegt über dem 
Wertebereich eines Char Datentyps. Die Zahl 95 die auf meinem Display 
angezeigt wird ist also nur der abgeschnittene Rest, sodass in 
Wirklichkeit eine ganz andere Zahl durch 2 geteilt wird.

Mit freundlichen Grüßen
Gustav

von Paul Baumann (Gast)


Lesenswert?

Gustav schrabte:
>Du hast natürlich Recht !!

Blindes Huhn und Korn und so weiter...
;-)
MfG Paul

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
Noch kein Account? Hier anmelden.