Forum: Compiler & IDEs Zähler mit Grenzwerten effizient implementieren


von Klaus W. (Firma: privat) (texmex)


Lesenswert?

Hallo!!

Ich habe ein ganz einfaches Problem:

Ein Zähler soll Werte zwischen 0 und 255 annehmen können. Also eine 8 
Bit Variable.

Natürlich kann diese einfach in-/dekrementiert werden.

Aber gibt es einen Trick effizient den Über- bzw. Unterlauf zu 
verhindern?

Jedesmal mit if zu prüfen produziert so schrecklich viel Assembler 
Code...

Vielleicht eine arithmetische Lösung?


Viele Grüße,
Klaus

von Detlev T. (detlevt)


Lesenswert?

Warum nimmst du nicht einfach eine 16-Bit-Variable, die in- und 
dekrementiert wird? Wenn dann das High-Byte ungleich null ist, gab es 
einen Überlauf.

Subtraktion des High-Bytes vom Lowbyte, ohne if-Abfrage, müsste dann das 
leisten, was du willst.

von Matthias L. (Gast)


Lesenswert?

Was willst du denn tun?
soll durch +1 rechnen die variable bei 255 stehen bleiben?

icrgendwei versteh ich dich nicht

von Klaus W. (Firma: privat) (texmex)


Lesenswert?

Matthias Lipinsky wrote:
> Was willst du denn tun?
> soll durch +1 rechnen die variable bei 255 stehen bleiben?
>
> icrgendwei versteh ich dich nicht

Ja, genau.

In Assembler würde ich halt einfach das Carry-Flag wegwerfen...

von Matthias L. (Gast)


Lesenswert?

>In Assembler würde ich halt einfach das Carry-Flag wegwerfen...
Das stimmt nicht. Wenn du das Carrayflag wegwirst, dann schmeist du die 
Info weg, dass er übergelaufen ist..


>Jedesmal mit if zu prüfen produziert so schrecklich viel Assembler
>Code...
Poste den vielen Code doch mal!

von (prx) A. K. (prx)


Lesenswert?

@texmex: Was soll das den bringen? Dann folgt auf 255 die 0. Wenn du bei 
255 stehen bleiben willst, dann musst du nach add +1 das C Flag wieder 
abziehen.

von (prx) A. K. (prx)


Lesenswert?

So übel finde ich den Code übrigens nicht.

Aus
  if (++i == 0) i = 255;
wird
  subi r17,lo8(-(1))
  brne .L2
  ldi r17,lo8(-1)
.L2:

Ok, ein bischen unsauber ist der Quellcode schon, denn er setzt zwingend 
voraus, dass die Variable als 8bit Byte implementiert ist und nicht etwa 
in 9 Bits. Ist heutzutage aber recht verbreitet.

von Matthias L. (Gast)


Lesenswert?

einfacher gehts auch nicht

von Klaus W. (Firma: privat) (texmex)


Lesenswert?

Matthias Lipinsky wrote:
>>In Assembler würde ich halt einfach das Carry-Flag wegwerfen...
> Das stimmt nicht. Wenn du das Carrayflag wegwirst, dann schmeist du die
> Info weg, dass er übergelaufen ist..
>
>
>>Jedesmal mit if zu prüfen produziert so schrecklich viel Assembler
>>Code...
> Poste den vielen Code doch mal!

Naja, beim Dekrementieren z.B. so:
1
 if ( dcf77_accuracy ) dcf77_accuracy--;
2
     234a:       80 91 b5 01     lds     r24, 0x01B5
3
     234e:       88 23           and     r24, r24
4
     2350:       29 f0           breq    .+10    ; 0x235c <__vector_3+0x$
5
     2352:       80 91 b5 01     lds     r24, 0x01B5
6
     2356:       81 50           subi    r24, 0x01       ; 1
7
     2358:       80 93 b5 01     sts     0x01B5, r24

Aber wenn ich mir das gerade so ansehe, liegt das wohl eher daran, dass 
die Variable erst direkt aus dem Ram geladen wird und nicht in einem 
Register steht!?!?

von (prx) A. K. (prx)


Lesenswert?

@lippy: Doch:
  ldi r0,1
  add r,r0
  sbci r,0
ist zwar nicht kürzer, aber schneller.

@texmex: Speicher ist eines, aber dann auch noch volatile...

von Klaus W. (Firma: privat) (texmex)


Lesenswert?

A. K. wrote:
> @texmex: Was soll das den bringen? Dann folgt auf 255 die 0. Wenn du bei
> 255 stehen bleiben willst, dann musst du nach add +1 das C Flag wieder
> abziehen.

Hm, stimmt.

von Gast (Gast)


Lesenswert?

>Jedesmal mit if zu prüfen produziert so schrecklich viel Assembler
>Code...

In Assembler selbst sind beide Teilaufgaben mit je vier Instruktionen 
erledigt:
1
LimitedIncrement:
2
    cpi   t, MAXVALUE
3
    breq  LimitedIncrementEnd
4
    inc   t  
5
LimitedIncrementEnd:
6
    ret
7
8
9
LimitedDecrement:
10
    cpi   t, MINVALUE
11
    breq  LimitedDecrementEnd
12
    dec   t  
13
LimitedDecrementEnd:
14
    ret

Wenn Du es noch kompakter haben willst und zwei Register freizustellen 
bereit bist, kannst Du auch die cpse-Instruktion verwenden:
1
; (Register min und max irgendwo im Initialisierungsteil 
2
; des Programms mit MINVALUE resp. MAXVALUE laden)
3
4
LimitedIncrement:
5
    cpse  t, max
6
    inc   t  
7
LimitedIncrementEnd:
8
    ret
9
10
11
LimitedDecrement:
12
    cpse  t, min
13
    dec   t  
14
LimitedDecrementEnd:
15
    ret

von Klaus W. (Firma: privat) (texmex)


Lesenswert?

A. K. wrote:
> @lippy: Doch:
>   ldi r0,1
>   add r,r0
>   sbci r,0
> ist zwar nicht kürzer, aber schneller.
>
> @texmex: Speicher ist eines, aber dann auch noch volatile...

Ah, verstehe! Deshalb wird das dann anschließend gleich nochmal aus dem 
Ram geladen.
Wäre es denn evtl. sinnvoll die volatile Variable am Anfang der 
Interrupt Routine in eine lokale Variable zu kopieren (die dann 
vermutlich in einem Register landet), wenn sie häufiger modifiziert 
wird?

Oder macht man "sowas" ganz anders?


viele Grüße,
Klaus

von Gast (Gast)


Lesenswert?

Oh, das ist ja das GCC-Forum hier (ups, erst jetzt gesehn). Sollte Dich 
Assemblercode nicht interessieren: Sorry!

von (prx) A. K. (prx)


Lesenswert?

Klaus W. wrote:

> Oder macht man "sowas" ganz anders?

Nö, genau so. Standardproblem von volatile.

von Klaus W. (Firma: privat) (texmex)


Lesenswert?

Gast wrote:
> Oh, das ist ja das GCC-Forum hier (ups, erst jetzt gesehn). Sollte Dich
> Assemblercode nicht interessieren: Sorry!

Nun, das eine schliesst das andere ja nicht aus :-).

von Klaus W. (Firma: privat) (texmex)


Lesenswert?

A. K. wrote:
> Klaus W. wrote:
>
>> Oder macht man "sowas" ganz anders?
>
> Nö, genau so. Standardproblem von volatile.

Mir kam da grade die Speicherklasse "register" in den Sinn. Tatsächlich 
findet sich in 
http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_regbind
ein entsprechender Hinweis.
Aber das bietet wohl auch so allerlei Fallstricke....

von IRQ (Gast)


Lesenswert?

> Ich habe ein ganz einfaches Problem:
No Problem for me !

> Ein Zähler soll Werte zwischen 0 und 255 annehmen können. Also eine 8
> Bit Variable.
All right.

> Natürlich kann diese einfach in-/dekrementiert werden.
Lets go.

> Aber gibt es einen Trick effizient den Über- bzw. Unterlauf zu
> verhindern?
Yes, sir !

> Jedesmal mit if zu prüfen produziert so schrecklich viel Assembler
> Code...
Yeah, thats true.

> Vielleicht eine arithmetische Lösung?
CPU boardmittel !

Also, hier die ultimative Lösung:

Du programmierst einen Timer und setzt das Kompareflag ! Dann Vergleicht 
die CPU und erzeugt einen Interrupt, unhabhängig vom Programm und alles 
ist bestens ! 100% Effe ;-)

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.